[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Report crashes, rendering issues etc.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\nPlease describe your issue as accurately as possible. Include screenshots or videos when relevant.\n\nIf you use Windows, please check the following page: https://github.com/doitsujin/dxvk/wiki/Windows\n\n### Software information\nName of the game, settings used etc. If using mods or addons, list them aswell.\n\n### System information\n- GPU:\n- Driver:\n- Wine version: \n- DXVK version: \n\n### Apitrace file(s)\n- Put a link here\n\nFor instructions on how to use apitrace, see: https://github.com/doitsujin/dxvk/wiki/Using-Apitrace\n\n### Log files\nPlease attach Proton or Wine logs as a text file:\n- When using Proton, set the Steam launch options for your game to `PROTON_LOG=1 %command%` and attach the corresponding `steam-xxxxx.log` file in your home directory.\n- When using regular Wine, use `wine game.exe > game.log 2>&1` and attach the resulting `game.log` file.\n- On Windows, log files will usually appear next to the games exe or in its base folder.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/other.md",
    "content": "---\nname: Other\nabout: Questions, feature requests etc.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n<!--\nNot for reporting bugs. Please use the Bug report template instead\n-->\n"
  },
  {
    "path": ".github/workflows/artifacts.yml",
    "content": "name: Artifacts (Package)\n\non: [push, pull_request, workflow_dispatch]\n\njobs:\n  artifacts-mingw-w64:\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout code\n      id: checkout-code\n      uses: actions/checkout@v4\n      with:\n        submodules: recursive\n        fetch-depth: 0\n\n    - name: Setup problem matcher\n      uses: Joshua-Ashton/gcc-problem-matcher@v3\n\n    - name: Build release\n      id: build-release\n      uses: Joshua-Ashton/arch-mingw-github-action@v8\n      with:\n        command: |\n          export VERSION_NAME=\"${GITHUB_REF##*/}-${GITHUB_SHA##*/}\"\n          ./package-release.sh ${VERSION_NAME} build --no-package\n          echo \"VERSION_NAME=${VERSION_NAME}\" >> $GITHUB_ENV\n\n    - name: Upload artifacts\n      id: upload-artifacts\n      uses: actions/upload-artifact@v4\n      with:\n        name: dxvk-win-${{ env.VERSION_NAME }}\n        path: build/dxvk-${{ env.VERSION_NAME }}\n        if-no-files-found: error\n\n  artifacts-steamrt-sniper:\n    runs-on: ubuntu-latest\n    container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:beta\n\n    steps:\n    - name: Checkout code\n      id: checkout-code\n      uses: actions/checkout@v4\n      with:\n        submodules: recursive\n        fetch-depth: 0\n\n    - name: Setup problem matcher\n      uses: Joshua-Ashton/gcc-problem-matcher@v3\n\n    - name: Build release\n      id: build-release\n      shell: bash\n      run: |\n        export VERSION_NAME=\"${GITHUB_REF##*/}-${GITHUB_SHA##*/}\"\n        ./package-native.sh ${VERSION_NAME} build\n        echo \"VERSION_NAME=${VERSION_NAME}\" >> $GITHUB_ENV\n\n    - name: Upload artifacts\n      id: upload-artifacts\n      uses: actions/upload-artifact@v4\n      with:\n        name: dxvk-native-${{ env.VERSION_NAME }}\n        path: build/dxvk-native-${{ env.VERSION_NAME }}.tar.gz\n        if-no-files-found: error\n\n  merge-artifacts:\n    runs-on: ubuntu-latest\n    needs: [artifacts-mingw-w64, artifacts-steamrt-sniper]\n    steps:\n      - name: Get version\n        id: get-version\n        shell: bash\n        run: |\n          echo \"VERSION_NAME=${GITHUB_REF##*/}-${GITHUB_SHA##*/}\" >> $GITHUB_ENV\n\n      - name: Merge Artifacts\n        uses: actions/upload-artifact/merge@v4\n        with:\n          name: dxvk-${{ env.VERSION_NAME }}\n          pattern: dxvk*\n          delete-merged: true\n"
  },
  {
    "path": ".github/workflows/test-build-windows.yml",
    "content": "name: Test Builds on Windows\n\non: [push, pull_request, workflow_dispatch]\n\njobs:\n  build-set-windows:\n    runs-on: windows-latest\n\n    steps:\n    - name: Checkout code\n      id: checkout-code\n      uses: actions/checkout@v4\n      with:\n        submodules: recursive\n        fetch-depth: 0\n\n    - name: Setup glslangValidator\n      shell: pwsh\n      run: |\n        Invoke-WebRequest -Uri \"https://raw.githubusercontent.com/HansKristian-Work/vkd3d-proton-ci/main/glslangValidator.exe\" -OutFile \"glslangValidator.exe\"\n        Write-Output \"$pwd\" | Out-File -FilePath \"${Env:GITHUB_PATH}\" -Append\n\n    - name: Setup Meson\n      shell: pwsh\n      run: pip install meson\n\n    - name: Find Visual Studio\n      shell: pwsh\n      run: |\n        $installationPath = Get-VSSetupInstance `\n          | Select-VSSetupInstance -Require Microsoft.VisualStudio.Workload.NativeDesktop -Latest `\n          | Select-Object -ExpandProperty InstallationPath\n        Write-Output \"VSDEVCMD=${installationPath}\\Common7\\Tools\\VsDevCmd.bat\" `\n          | Out-File -FilePath \"${Env:GITHUB_ENV}\" -Append\n\n    - name: Download D3D8 SDK Headers\n      shell: pwsh\n      run: |\n        Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8.h -OutFile include/d3d8.h\n        Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8types.h -OutFile include/d3d8types.h\n        Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8caps.h -OutFile include/d3d8caps.h\n        \n    - name: Get version\n      id: get-version\n      shell: bash\n      run: |\n        echo \"VERSION_NAME=${GITHUB_REF##*/}-${GITHUB_SHA##*/}\" >> $GITHUB_ENV\n\n    - name: Build MSVC x86\n      shell: pwsh\n      run: |\n        & \"${Env:COMSPEC}\" /s /c \"`\"${Env:VSDEVCMD}`\" -arch=x86 -host_arch=x64 -no_logo && set\" `\n          | % { , ($_ -Split '=', 2) } `\n          | % { [System.Environment]::SetEnvironmentVariable($_[0], $_[1]) }\n        meson --buildtype release --backend vs2022 build-msvc-x86\n        msbuild -m build-msvc-x86/dxvk.sln\n\n    - name: Build MSVC x64\n      shell: pwsh\n      run: |\n        & \"${Env:COMSPEC}\" /s /c \"`\"${Env:VSDEVCMD}`\" -arch=x64 -host_arch=x64 -no_logo && set\" `\n          | % { , ($_ -Split '=', 2) } `\n          | % { [System.Environment]::SetEnvironmentVariable($_[0], $_[1]) }\n        meson --buildtype release --backend vs2022 build-msvc-x64\n        msbuild -m build-msvc-x64/dxvk.sln\n\n    - name: Prepare artifacts\n      shell: pwsh\n      run: |\n        mkdir artifacts\\x32\n        ls -Path build-msvc-x86\\src -Include *.dll,*.pdb -Recurse\n          | cp -Destination (Join-Path -Path (pwd) -ChildPath artifacts\\x32)\n        mkdir artifacts\\x64\n        ls -Path build-msvc-x64\\src -Include *.dll,*.pdb -Recurse\n          | cp -Destination (Join-Path -Path (pwd) -ChildPath artifacts\\x64)\n\n    - name: Upload artifacts\n      uses: actions/upload-artifact@v4\n      with:\n        name: dxvk-${{ env.VERSION_NAME }}-msvc-output\n        path: artifacts\\*\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"include/native/directx\"]\n\tpath = include/native/directx\n\turl = https://github.com/Joshua-Ashton/mingw-directx-headers\n[submodule \"include/vulkan\"]\n\tpath = include/vulkan\n\turl = https://github.com/KhronosGroup/Vulkan-Headers\n[submodule \"include/spirv\"]\n\tpath = include/spirv\n\turl = https://github.com/KhronosGroup/SPIRV-Headers.git\n[submodule \"subprojects/libdisplay-info\"]\n\tpath = subprojects/libdisplay-info\n\turl = https://github.com/doitsujin/libdisplay-info.git\n\tbranch = windows\n[submodule \"subprojects/dxbc-spirv\"]\n\tpath = subprojects/dxbc-spirv\n\turl = https://github.com/doitsujin/dxbc-spirv.git\n"
  },
  {
    "path": "LICENSE",
    "content": "                  Copyright (c) 2017 Philip Rebohle\n                  Copyright (c) 2019 Joshua Ashton\n                  Copyright (c) 2019 Robin Kertels\n                  Copyright (c) 2023 Jeffrey Ellison\n\n                          zlib/libpng license\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n– The origin of this software must not be misrepresented; you must not\n  claim that you wrote the original software. If you use this software\n  in a product, an acknowledgment in the product documentation would be\n  appreciated but is not required.\n\n– Altered source versions must be plainly marked as such, and must not\n  be misrepresented as being the original software.\n\n– This notice may not be removed or altered from any source distribution.\n"
  },
  {
    "path": "README.md",
    "content": "# DXVK\n\nA Vulkan-based translation layer for Direct3D 8/9/10/11 which allows running 3D applications on Linux using Wine.\n\nFor the current status of the project, please refer to the [project wiki](https://github.com/doitsujin/dxvk/wiki).\n\nThe most recent development builds can be found [here](https://github.com/doitsujin/dxvk/actions/workflows/artifacts.yml?query=branch%3Amaster).\n\nRelease builds can be found [here](https://github.com/doitsujin/dxvk/releases).\n\n## How to use\nIn order to install a DXVK package obtained from the [release](https://github.com/doitsujin/dxvk/releases) page into a given wine prefix, copy or symlink the DLLs into the following directories as follows, then open `winecfg` and manually add `native` DLL overrides for `d3d8`, `d3d9`, `d3d10core`, `d3d11` and `dxgi` under the Libraries tab.\n\nIn a default Wine prefix that would be as follows:\n```\nexport WINEPREFIX=/path/to/wineprefix\ncp x64/*.dll $WINEPREFIX/drive_c/windows/system32\ncp x32/*.dll $WINEPREFIX/drive_c/windows/syswow64\nwinecfg\n```\n\nFor a pure 32-bit Wine prefix (non default) the 32-bit DLLs instead go to the `system32` directory:\n```\nexport WINEPREFIX=/path/to/wineprefix\ncp x32/*.dll $WINEPREFIX/drive_c/windows/system32\nwinecfg\n```\n\nVerify that your application uses DXVK instead of wined3d by enabling the HUD (see notes below).\n\nIn order to remove DXVK from a prefix, remove the DLLs and DLL overrides, and run `wineboot -u` to restore the original DLL files.\n\nTools such as Steam Play, Lutris, Bottles, Heroic Launcher, etc will automatically handle setup of dxvk on their own when enabled.\n\n#### DLL dependencies \nListed below are the DLL requirements for using DXVK with any single API.\n\n- d3d8: `d3d8.dll` and `d3d9.dll`\n- d3d9: `d3d9.dll`\n- d3d10: `d3d10core.dll`, `d3d11.dll` and `dxgi.dll`\n- d3d11: `d3d11.dll` and `dxgi.dll`\n\n### Notes on Vulkan drivers\nBefore reporting an issue, please check the [Wiki](https://github.com/doitsujin/dxvk/wiki/Driver-support) page on the current driver status and make sure you run a recent enough driver version for your hardware.\n\n### Online multi-player games\nManipulation of Direct3D libraries in multi-player games may be considered cheating and can get your account **banned**. This may also apply to single-player games with an embedded or dedicated multiplayer portion. **Use at your own risk.**\n\n### HUD\nThe `DXVK_HUD` environment variable controls a HUD which can display the framerate and some stat counters. It accepts a comma-separated list of the following options:\n- `devinfo`: Displays the name of the GPU and the driver version.\n- `fps`: Shows the current frame rate.\n- `frametimes`: Shows a frame time graph.\n- `submissions`: Shows the number of command buffers submitted per frame.\n- `drawcalls`: Shows the number of draw calls and render passes per frame.\n- `pipelines`: Shows the total number of graphics and compute pipelines.\n- `descriptors`: Shows the number of descriptor pools and descriptor sets.\n- `memory`: Shows the amount of device memory allocated and used.\n- `allocations`: Shows detailed memory chunk suballocation info.\n- `gpuload`: Shows estimated GPU load. May be inaccurate.\n- `version`: Shows DXVK version.\n- `api`: Shows the D3D feature level used by the application.\n- `cs`: Shows worker thread statistics.\n- `compiler`: Shows shader compiler activity\n- `samplers`: Shows the current number of sampler pairs used *[D3D9 Only]*\n- `ffshaders`: Shows the current number of shaders generated from fixed function state *[D3D9 Only]*\n- `swvp`: Shows whether or not the device is running in software vertex processing mode *[D3D9 Only]*\n- `scale=x`: Scales the HUD by a factor of `x` (e.g. `1.5`)\n- `opacity=y`: Adjusts the HUD opacity by a factor of `y` (e.g. `0.5`, `1.0` being fully opaque).\n\nAdditionally, `DXVK_HUD=1` has the same effect as `DXVK_HUD=devinfo,fps`, and `DXVK_HUD=full` enables all available HUD elements.\n\n### Logs\nWhen used with Wine, DXVK will print log messages to `stderr`. Additionally, standalone log files can optionally be generated by setting the `DXVK_LOG_PATH` variable, where log files in the given directory will be called `app_d3d11.log`, `app_dxgi.log` etc., where `app` is the name of the game executable.\n\nOn Windows, log files will be created in the game's working directory by default, which is usually next to the game executable.\n\n### Device filter\nSome applications do not provide a method to select a different GPU. In that case, DXVK can be forced to use a given device:\n- `DXVK_FILTER_DEVICE_NAME=\"Device Name\"` Selects devices with a matching Vulkan device name, which can be retrieved with tools such as `vulkaninfo`. Matches on substrings, so \"VEGA\" or \"AMD RADV VEGA10\" is supported if the full device name is \"AMD RADV VEGA10 (LLVM 9.0.0)\", for example. If the substring matches more than one device, the first device matched will be used.\n- `DXVK_FILTER_DEVICE_UUID=\"00000000000000000000000000000001\"` Selects a device by matching its Vulkan device UUID, which can also be retrieved using tools such as `vulkaninfo`. The UUID must be a 32-character hexadecimal string with no dashes. This method provides more precise selection, especially when using multiple identical GPUs.\n\n**Note:** If the device filter is configured incorrectly, it may filter out all devices and applications will be unable to create a D3D device.\n\n### Debugging\nThe following environment variables can be used for **debugging** purposes.\n- `VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation` Enables Vulkan debug layers. Highly recommended for troubleshooting rendering issues and driver crashes. Requires the Vulkan SDK to be installed on the host system.\n- `DXVK_LOG_LEVEL=none|error|warn|info|debug` Controls message logging.\n- `DXVK_LOG_PATH=/some/directory` Changes path where log files are stored. Set to `none` to disable log file creation entirely, without disabling logging.\n- `DXVK_DEBUG=markers|validation` Enables use of the `VK_EXT_debug_utils` extension for translating performance event markers, or to enable Vulkan validation, respecticely.\n- `DXVK_CONFIG_FILE=/xxx/dxvk.conf` Sets path to the configuration file.\n- `DXVK_CONFIG=\"dxgi.hideAmdGpu = True; dxgi.syncInterval = 0\"` Can be used to set config variables through the environment instead of a configuration file using the same syntax. `;` is used as a seperator.\n- `DXVK_SHADER_CACHE=0`: Disables the internal shader cache.\n- `DXVK_SHADER_CACHE_PATH=/some/directory`: Path to internal shader cache files. By default, this will use `%LOCALAPPDATA%/dxvk` in a Windows\n  or Wine environment, and `$HOME/.cache` or `$XDG_CACHE_HOME` in a native Linux environment.\n\n### Graphics Pipeline Library\nOn drivers which support `VK_EXT_graphics_pipeline_library` Vulkan shaders will be compiled at the time the game loads its D3D shaders, rather than at draw time. This reduces or eliminates shader compile stutter in many games when compared to the previous system.\n\nIn games that load their shaders during loading screens or in the menu, this can lead to prolonged periods of very high CPU utilization, especially on weaker CPUs. For affected games it is recommended to wait for shader compilation to finish before starting the game to avoid stutter and low performance. Shader compiler activity can be monitored with `DXVK_HUD=compiler`.\n\n**Note:** Games which only load their D3D shaders at draw time (e.g. most Unreal Engine games) will still exhibit some stutter, although it should still be less severe than without this feature.\n\n## Build instructions\n\nIn order to pull in all submodules that are needed for building, clone the repository using the following command:\n```\ngit clone --recursive https://github.com/doitsujin/dxvk.git\n```\n\n### Requirements:\n- [wine 10.0](https://www.winehq.org/) or newer\n- [Meson](https://mesonbuild.com/) build system (at least version 0.58)\n- [Mingw-w64](https://www.mingw-w64.org) compiler and headers (at least version 10.0)\n- [glslang](https://github.com/KhronosGroup/glslang) compiler\n\n### Building DLLs\n\n#### The simple way\nInside the DXVK directory, run:\n```\n./package-release.sh master /your/target/directory --no-package\n```\n\nThis will create a folder `dxvk-master` in `/your/target/directory`, which contains both 32-bit and 64-bit versions of DXVK, which can be set up in the same way as the release versions as noted above.\n\nIn order to preserve the build directories for development, pass `--dev-build` to the script. This option implies `--no-package`. After making changes to the source code, you can then do the following to rebuild DXVK:\n```\n# change to build.32 for 32-bit\ncd /your/target/directory/build.64\nninja install\n```\n\n#### Compiling manually\n```\n# 64-bit build. For 32-bit builds, replace\n# build-win64.txt with build-win32.txt\nmeson setup --cross-file build-win64.txt --buildtype release --prefix /your/dxvk/directory build.w64\ncd build.w64\nninja install\n```\n\nThe D3D8, D3D9, D3D10, D3D11 and DXGI DLLs will be located in `/your/dxvk/directory/bin`.\n\n### Build troubleshooting\nDXVK requires threading support from your mingw-w64 build environment. If you\nare missing this, you may see \"error: ‘std::cv_status’ has not been declared\"\nor similar threading related errors.\n\nOn Debian and Ubuntu, this can be resolved by using the posix alternate, which\nsupports threading. For example, choose the posix alternate from these\ncommands:\n```\nupdate-alternatives --config x86_64-w64-mingw32-gcc\nupdate-alternatives --config x86_64-w64-mingw32-g++\nupdate-alternatives --config i686-w64-mingw32-gcc\nupdate-alternatives --config i686-w64-mingw32-g++\n```\nFor non debian based distros, make sure that your mingw-w64-gcc cross compiler \ndoes have `--enable-threads=posix` enabled during configure. If your distro does\nship its mingw-w64-gcc binary with `--enable-threads=win32` you might have to\nrecompile locally or open a bug at your distro's bugtracker to ask for it. \n\n# DXVK Native\n\nDXVK Native is a version of DXVK which allows it to be used natively without Wine.\n\nThis is primarily useful for game and application ports to either avoid having to write another rendering backend, or to help with port bringup during development.\n\n[Release builds](https://github.com/doitsujin/dxvk/releases) are built using the Steam Runtime.\n\n### How does it work?\n\nDXVK Native replaces certain Windows-isms with a platform and framework-agnostic replacement, for example, `HWND`s can become `SDL_Window*`s, etc.\nAll it takes to do that is to add another WSI backend.\n\n**Note:** DXVK Native requires a backend to be explicitly set via the `DXVK_WSI_DRIVER` environment variable. The current built-in options are `SDL3`, `SDL2`, and `GLFW`.\n\nDXVK Native comes with a slim set of Windows header definitions required for D3D9/11 and the MinGW headers for D3D9/11.\nIn most cases, it will end up being plug and play with your renderer, but there may be certain teething issues such as:\n- `__uuidof(type)` is supported, but `__uuidof(variable)` is not supported. Use `__uuidof_var(variable)` instead.\n"
  },
  {
    "path": "RELEASE",
    "content": "2.7.1\n"
  },
  {
    "path": "VP_DXVK_requirements.json",
    "content": "{\n    \"$schema\": \"https://schema.khronos.org/vulkan/profiles-0.8.1-224.json#\",\n    \"capabilities\": {\n        \"vulkan10requirements\": {\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"robustBufferAccess\": true\n                }\n            }\n        },\n        \"vulkan11requirements\": {\n            \"features\": {\n                \"VkPhysicalDeviceVulkan11Features\": {\n                    \"multiview\": true\n                }\n            },\n            \"properties\": {\n                \"VkPhysicalDeviceVulkan11Properties\": {\n                    \"maxMultiviewViewCount\": 6,\n                    \"maxMultiviewInstanceIndex\": 134217727\n                }\n            }\n        },\n        \"vulkan12requirements\": {\n            \"features\": {\n                \"VkPhysicalDeviceVulkan12Features\": {\n                    \"uniformBufferStandardLayout\": true,\n                    \"subgroupBroadcastDynamicId\": true,\n                    \"imagelessFramebuffer\": true,\n                    \"separateDepthStencilLayouts\": true,\n                    \"hostQueryReset\": true,\n                    \"timelineSemaphore\": true,\n                    \"shaderSubgroupExtendedTypes\": true\n                }\n            },\n            \"properties\": {\n                \"VkPhysicalDeviceVulkan12Properties\": {\n                    \"maxTimelineSemaphoreValueDifference\": 2147483647\n                }\n            }\n        },\n        \"vulkan13requirements\": {\n            \"features\": {\n                \"VkPhysicalDeviceVulkan12Features\": {\n                    \"vulkanMemoryModel\": true,\n                    \"vulkanMemoryModelDeviceScope\": true,\n                    \"bufferDeviceAddress\": true\n                },\n                \"VkPhysicalDeviceVulkan13Features\": {\n                    \"robustImageAccess\": true,\n                    \"shaderTerminateInvocation\": true,\n                    \"shaderZeroInitializeWorkgroupMemory\": true,\n                    \"synchronization2\": true,\n                    \"shaderIntegerDotProduct\": true,\n                    \"maintenance4\": true,\n                    \"pipelineCreationCacheControl\": true,\n                    \"subgroupSizeControl\": true,\n                    \"computeFullSubgroups\": true,\n                    \"shaderDemoteToHelperInvocation\": true,\n                    \"inlineUniformBlock\": true,\n                    \"dynamicRendering\": true\n                }\n            },\n            \"properties\": {\n                \"VkPhysicalDeviceVulkan13Properties\": {\n                    \"maxBufferSize\": 1073741824,\n                    \"maxInlineUniformBlockSize\": 256,\n                    \"maxPerStageDescriptorInlineUniformBlocks\": 4,\n                    \"maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks\": 4,\n                    \"maxDescriptorSetInlineUniformBlocks\": 4,\n                    \"maxDescriptorSetUpdateAfterBindInlineUniformBlocks\": 4,\n                    \"maxInlineUniformTotalSize\": 4\n                }\n            }\n        },\n        \"dxvk_common_required\": {\n            \"extensions\": {\n                \"VK_KHR_load_store_op_none\": 1,\n                \"VK_KHR_maintenance5\": 1,\n                \"VK_KHR_maintenance6\": 1,\n                \"VK_EXT_depth_clip_enable\": 1\n            },\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"shaderSampledImageArrayDynamicIndexing\" : true\n                },\n                \"VkPhysicalDeviceVulkan12Features\": {\n                    \"descriptorIndexing\" : true,\n                    \"descriptorBindingSampledImageUpdateAfterBind\" : true,\n                    \"descriptorBindingUpdateUnusedWhilePending\" : true,\n                    \"descriptorBindingPartiallyBound\" : true,\n                    \"runtimeDescriptorArray\" : true\n                },\n                \"VkPhysicalDeviceMaintenance5FeaturesKHR\": {\n                    \"maintenance5\": true\n                },\n                \"VkPhysicalDeviceMaintenance6FeaturesKHR\": {\n                    \"maintenance6\": true\n                },\n                \"VkPhysicalDeviceDepthClipEnableFeaturesEXT\": {\n                    \"depthClipEnable\": true\n                }\n            },\n            \"properties\": {\n                \"VkPhysicalDeviceProperties\": {\n                    \"maxPushConstantsSize\" : 256\n                }\n            }\n        },\n        \"dxvk_common_optional\": {\n            \"extensions\": {\n                \"VK_KHR_maintenance7\": 1,\n                \"VK_KHR_maintenance8\": 1,\n                \"VK_KHR_maintenance9\": 1,\n                \"VK_KHR_maintenance10\": 1,\n                \"VK_KHR_present_id2\": 1,\n                \"VK_KHR_present_wait2\": 1,\n                \"VK_KHR_shader_subgroup_uniform_control_flow\": 1,\n                \"VK_KHR_swapchain_maintenance1\": 1,\n                \"VK_KHR_swapchain_mutable_format\": 1,\n                \"VK_KHR_unified_image_layouts\": 1,\n                \"VK_EXT_border_color_swizzle\": 1,\n                \"VK_EXT_extended_dynamic_state3\": 1,\n                \"VK_EXT_line_rasterization\": 1,\n                \"VK_EXT_pageable_device_local_memory\": 1\n            },\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"wideLines\": true\n                },\n                \"VkPhysicalDeviceVulkan11Features\": {\n                    \"shaderInt16\": true,\n                    \"storagePushConstant16\": true\n                },\n                \"VkPhysicalDeviceMaintenance7FeaturesKHR\": {\n                    \"maintenance7\": true\n                },\n                \"VkPhysicalDeviceMaintenance8FeaturesKHR\": {\n                    \"maintenance8\": true\n                },\n                \"VkPhysicalDeviceMaintenance9FeaturesKHR\": {\n                    \"maintenance9\": true\n                },\n                \"VkPhysicalDeviceMaintenance10FeaturesKHR\": {\n                    \"maintenance10\": true\n                },\n                \"VkPhysicalDevicePresentId2FeaturesKHR\": {\n                    \"presentId2\": true\n                },\n                \"VkPhysicalDevicePresentWait2FeaturesKHR\": {\n                    \"presentWait2\": true\n                },\n                \"VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR\": {\n                    \"swapchainMaintenance1\": true\n                },\n                \"VkPhysicalDeviceBorderColorSwizzleFeaturesEXT\": {\n                    \"borderColorSwizzle\": true\n                },\n                \"VkPhysicalDeviceExtendedDynamicState3FeaturesEXT\": {\n                    \"extendedDynamicState3AlphaToCoverageEnable\" : true,\n                    \"extendedDynamicState3DepthClipEnable\" : true,\n                    \"extendedDynamicState3RasterizationSamples\" : true,\n                    \"extendedDynamicState3SampleMask\" : true,\n                    \"extendedDynamicState3LineRasterizationMode\" : true,\n                    \"extendedDynamicState3SampleLocationsEnable\" : true\n                },\n                \"VkPhysicalDeviceLineRasterizationFeaturesEXT\": {\n                    \"rectangularLines\": true,\n                    \"smoothLines\": true\n                },\n                \"VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT\": {\n                    \"pageableDeviceLocalMemory\": true\n                },\n                \"VkPhysicalDeviceUnifiedImageLayoutsFeaturesKHR\": {\n                    \"unifiedImageLayouts\": true\n                },\n                \"VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR\": {\n                    \"shaderSubgroupUniformControlFlow\": true\n                }\n            }\n        },\n        \"d3d9_baseline\": {\n            \"extensions\": {\n                \"VK_EXT_robustness2\": 1\n            },\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"geometryShader\": true,\n                    \"imageCubeArray\": true,\n                    \"depthClamp\": true,\n                    \"depthBiasClamp\": true,\n                    \"fillModeNonSolid\": true,\n                    \"sampleRateShading\": true,\n                    \"shaderClipDistance\": true,\n                    \"shaderCullDistance\": true,\n                    \"textureCompressionBC\": true,\n                    \"occlusionQueryPrecise\": true,\n                    \"independentBlend\": true,\n                    \"fullDrawIndexUint32\": true,\n\n                    \"shaderImageGatherExtended\": true\n                },\n                \"VkPhysicalDeviceVulkan12Features\": {\n                    \"samplerMirrorClampToEdge\": true\n                },\n                \"VkPhysicalDeviceRobustness2FeaturesEXT\": {\n                    \"nullDescriptor\": true,\n                    \"robustBufferAccess2\": true\n                }\n            }\n        },\n        \"d3d9_optional\": {\n            \"extensions\": {\n                \"VK_EXT_memory_priority\": 1,\n                \"VK_EXT_vertex_attribute_divisor\": 1,\n                \"VK_EXT_depth_bias_control\": 1,\n                \"VK_EXT_custom_border_color\": 1,\n                \"VK_EXT_attachment_feedback_loop_layout\": 1,\n                \"VK_EXT_non_seamless_cube_map\": 1,\n                \"VK_EXT_sample_locations\" : 1\n            },\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"depthBounds\": true,\n                    \"vertexPipelineStoresAndAtomics\": true,\n                    \"pipelineStatisticsQuery\": true,\n                    \"samplerAnisotropy\": true\n                },\n                \"VkPhysicalDeviceMemoryPriorityFeaturesEXT\": {\n                    \"memoryPriority\": true\n                },\n                \"VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT\": {\n                    \"vertexAttributeInstanceRateDivisor\": true,\n                    \"vertexAttributeInstanceRateZeroDivisor\": true\n                },\n                \"VkPhysicalDeviceDepthBiasControlFeaturesEXT\": {\n                    \"depthBiasControl\": true,\n                    \"leastRepresentableValueForceUnormRepresentation\": true,\n                    \"floatRepresentation\": true,\n                    \"depthBiasExact\": true\n                },\n                \"VkPhysicalDeviceCustomBorderColorFeaturesEXT\": {\n                    \"customBorderColors\": true,\n                    \"customBorderColorWithoutFormat\": true\n                },\n                \"VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT\": {\n                    \"attachmentFeedbackLoopLayout\": true\n                },\n                \"VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT\": {\n                    \"nonSeamlessCubeMap\": true\n                }\n            }\n        },\n        \"d3d11_baseline\": {\n            \"extensions\": {\n                \"VK_EXT_robustness2\": 1,\n                \"VK_EXT_transform_feedback\": 1\n            },\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"depthBiasClamp\": true,\n                    \"depthClamp\": true,\n                    \"dualSrcBlend\": true,\n                    \"fillModeNonSolid\": true,\n                    \"fullDrawIndexUint32\": true,\n                    \"geometryShader\": true,\n                    \"imageCubeArray\": true,\n                    \"independentBlend\": true,\n                    \"multiViewport\": true,\n                    \"occlusionQueryPrecise\": true,\n                    \"sampleRateShading\": true,\n                    \"shaderClipDistance\": true,\n                    \"shaderCullDistance\": true,\n                    \"shaderImageGatherExtended\": true,\n                    \"textureCompressionBC\": true\n                },\n                \"VkPhysicalDeviceVulkan11Features\": {\n                    \"shaderDrawParameters\": true\n                },\n                \"VkPhysicalDeviceVulkan12Features\": {\n                    \"samplerMirrorClampToEdge\": true\n                },\n                \"VkPhysicalDeviceRobustness2FeaturesEXT\": {\n                    \"nullDescriptor\": true,\n                    \"robustBufferAccess2\": true\n                },\n                \"VkPhysicalDeviceTransformFeedbackFeaturesEXT\": {\n                    \"transformFeedback\": true,\n                    \"geometryStreams\": true\n                }\n            }\n        },\n        \"d3d11_baseline_optional\":{\n            \"extensions\": {\n                \"VK_EXT_memory_priority\": 1,\n                \"VK_EXT_multi_draw\": 1,\n                \"VK_EXT_vertex_attribute_divisor\": 1,\n                \"VK_EXT_custom_border_color\": 1,\n                \"VK_EXT_depth_bias_control\": 1,\n                \"VK_EXT_swapchain_colorspace\": 1,\n                \"VK_EXT_hdr_metadata\": 1\n            },\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"depthBounds\": true,\n                    \"pipelineStatisticsQuery\": true,\n                    \"logicOp\": true,\n                    \"samplerAnisotropy\": true\n                },\n                \"VkPhysicalDeviceMemoryPriorityFeaturesEXT\": {\n                    \"memoryPriority\": true\n                },\n                \"VkPhysicalDeviceMultiDrawFeaturesEXT\": {\n                    \"multiDraw\": true\n                },\n                \"VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT\": {\n                    \"vertexAttributeInstanceRateDivisor\": true,\n                    \"vertexAttributeInstanceRateZeroDivisor\": true\n                },\n                \"VkPhysicalDeviceCustomBorderColorFeaturesEXT\": {\n                    \"customBorderColors\": true,\n                    \"customBorderColorWithoutFormat\": true\n                },\n                \"VkPhysicalDeviceDepthBiasControlFeaturesEXT\": {\n                    \"depthBiasControl\": true,\n                    \"leastRepresentableValueForceUnormRepresentation\": true,\n                    \"depthBiasExact\": true\n                }\n            }\n        },\n        \"d3d11_level11_0\": {\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"drawIndirectFirstInstance\": true,\n                    \"fragmentStoresAndAtomics\": true,\n                    \"multiDrawIndirect\": true,\n                    \"tessellationShader\": true\n                }\n            }\n        },\n        \"d3d11_level11_0_optional\": {\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"shaderFloat64\": true,\n                    \"shaderInt64\": true\n                }\n            }\n        },\n        \"d3d11_level11_1\": {\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"logicOp\": true,\n                    \"vertexPipelineStoresAndAtomics\": true\n                }\n            }\n        },\n        \"d3d11_level12_0\": {\n            \"features\": {\n                \"VkPhysicalDeviceFeatures\": {\n                    \"shaderResourceResidency\": true,\n                    \"shaderResourceMinLod\": true,\n                    \"sparseBinding\": true,\n                    \"sparseResidencyBuffer\": true,\n                    \"sparseResidencyAliased\": true,\n                    \"sparseResidencyImage2D\": true\n                },\n                \"VkPhysicalDeviceVulkan12Features\": {\n                    \"samplerFilterMinmax\": true\n                }\n            },\n            \"properties\": {\n                \"VkPhysicalDeviceProperties\": {\n                    \"sparseProperties\": {\n                        \"residencyStandard2DBlockShape\": true,\n                        \"residencyAlignedMipSize\": false,\n                        \"residencyNonResidentStrict\": true\n                    }\n                }\n            }\n        }\n    },\n    \"profiles\": {\n        \"VP_DXVK_d3d9_baseline\": {\n            \"version\": 1,\n            \"api-version\": \"1.3.204\",\n            \"label\": \"DXVK D3D9 Baseline profile\",\n            \"description\": \"DXVK for D3D9 minimum requirements\",\n            \"capabilities\": [\n                \"vulkan10requirements\",\n                \"vulkan11requirements\",\n                \"vulkan12requirements\",\n                \"vulkan13requirements\",\n                \"dxvk_common_required\",\n                \"d3d9_baseline\"\n            ]\n        },\n        \"VP_DXVK_d3d9_optimal\": {\n            \"version\": 1,\n            \"api-version\": \"1.3.224\",\n            \"label\": \"DXVK D3D9 Optimal profile\",\n            \"description\": \"DXVK for D3D9 including optional capabilities\",\n            \"capabilities\": [\n                \"vulkan10requirements\",\n                \"vulkan11requirements\",\n                \"vulkan12requirements\",\n                \"vulkan13requirements\",\n                \"dxvk_common_required\",\n                \"dxvk_common_optional\",\n                \"d3d9_baseline\",\n                \"d3d9_optional\"\n            ]\n        },\n        \"VP_DXVK_d3d10_level_10_1_baseline\": {\n            \"version\": 1,\n            \"api-version\": \"1.3.204\",\n            \"label\": \"DXVK D3D10 Level 10.1 Baseline profile\",\n            \"description\": \"DXVK for D3D10 Feature Level 10.1 minimum requirements\",\n            \"capabilities\": [\n                \"vulkan10requirements\",\n                \"vulkan11requirements\",\n                \"vulkan12requirements\",\n                \"vulkan13requirements\",\n                \"dxvk_common_required\",\n                \"d3d11_baseline\"\n            ]\n        },\n        \"VP_DXVK_d3d11_level_11_0_baseline\": {\n            \"version\": 1,\n            \"api-version\": \"1.3.204\",\n            \"label\": \"DXVK D3D11 Level 11.0 Baseline profile\",\n            \"description\": \"DXVK for D3D11 Feature Level 11.0 minimum requirements\",\n            \"capabilities\": [\n                \"vulkan10requirements\",\n                \"vulkan11requirements\",\n                \"vulkan12requirements\",\n                \"vulkan13requirements\",\n                \"dxvk_common_required\",\n                \"d3d11_baseline\",\n                \"d3d11_level11_0\"\n            ]\n        },\n        \"VP_DXVK_d3d11_level_11_1_baseline\": {\n            \"version\": 1,\n            \"api-version\": \"1.3.204\",\n            \"label\": \"DXVK D3D11 Level 11.1 Baseline profile\",\n            \"description\": \"DXVK for D3D11 Feature Level 11.1 minimum requirements\",\n            \"capabilities\": [\n                \"vulkan10requirements\",\n                \"vulkan11requirements\",\n                \"vulkan12requirements\",\n                \"vulkan13requirements\",\n                \"dxvk_common_required\",\n                \"d3d11_baseline\",\n                \"d3d11_level11_0\",\n                \"d3d11_level11_1\"\n            ]\n        },\n        \"VP_DXVK_d3d11_level_11_1_optimal\": {\n            \"version\": 1,\n            \"api-version\": \"1.3.204\",\n            \"label\": \"DXVK D3D11 Level 11.1 Optimal profile\",\n            \"description\": \"DXVK for D3D11 Feature Level 11.1 including optional capabilities\",\n            \"capabilities\": [\n                \"vulkan10requirements\",\n                \"vulkan11requirements\",\n                \"vulkan12requirements\",\n                \"vulkan13requirements\",\n                \"dxvk_common_required\",\n                \"dxvk_common_optional\",\n                \"d3d11_baseline\",\n                \"d3d11_baseline_optional\",\n                \"d3d11_level11_0\",\n                \"d3d11_level11_0_optional\",\n                \"d3d11_level11_1\"\n            ]\n        },\n        \"VP_DXVK_d3d11_level_12_0_optimal\": {\n            \"version\": 1,\n            \"api-version\": \"1.3.204\",\n            \"label\": \"DXVK D3D11 Level 12.0 Optimal profile\",\n            \"description\": \"DXVK for D3D11 Feature Level 12.0 including optional capabilities\",\n            \"capabilities\": [\n                \"vulkan10requirements\",\n                \"vulkan11requirements\",\n                \"vulkan12requirements\",\n                \"vulkan13requirements\",\n                \"dxvk_common_required\",\n                \"dxvk_common_optional\",\n                \"d3d11_baseline\",\n                \"d3d11_baseline_optional\",\n                \"d3d11_level11_0\",\n                \"d3d11_level11_0_optional\",\n                \"d3d11_level11_1\",\n                \"d3d11_level12_0\"\n            ]\n        }\n    },\n    \"contributors\": {\n        \"Philip Rebohle\": {\n            \"company\": \"Valve\"\n        },\n        \"Joshua Ashton\": {\n            \"company\": \"Valve\"\n        },\n        \"Pierre-Loup A. Griffais\": {\n            \"company\": \"Valve\"\n        },\n        \"Georg Lehmann\": {\n            \"company\": \"DXVK\"\n        },\n        \"Christophe Riccio\": {\n            \"company\": \"LunarG\"\n        }\n    },\n    \"history\": [\n        {\n            \"revision\": 6,\n            \"date\": \"2025-03-24\",\n            \"author\": \"Philip Rebohle\",\n            \"comment\": \"Add maintenance5 to required extensions\"\n        },\n        {\n            \"revision\": 5,\n            \"date\": \"2025-03-16\",\n            \"author\": \"Philip Rebohle\",\n            \"comment\": \"Update profile with extensions used in 2.6\"\n        },\n        {\n            \"revision\": 4,\n            \"date\": \"2022-12-18\",\n            \"author\": \"Joshua Ashton\",\n            \"comment\": \"Add VK_EXT_swapchain_colorspace and VK_EXT_hdr_metadata to d3d11_baseline_optional\"\n        },\n        {\n            \"revision\": 3,\n            \"date\": \"2022-10-13\",\n            \"author\": \"Christophe Riccio\",\n            \"comment\": \"Factorize history and contributors sections using schema 0.8.1\"\n        },\n        {\n            \"revision\": 2,\n            \"date\": \"2022-08-30\",\n            \"author\": \"Philip Rebohle\",\n            \"comment\": \"Add VP_DXVK_d3d11_level_12_0_optimal profile\"\n        },\n        {\n            \"revision\": 1,\n            \"date\": \"2022-08-22\",\n            \"author\": \"Christophe Riccio\",\n            \"comment\": \"Initial revision\"\n        }\n    ]\n}\n"
  },
  {
    "path": "build-win32.txt",
    "content": "[binaries]\nc = 'i686-w64-mingw32-gcc'\ncpp = 'i686-w64-mingw32-g++'\nar = 'i686-w64-mingw32-ar'\nstrip = 'i686-w64-mingw32-strip'\nwindres = 'i686-w64-mingw32-windres'\n\n[properties]\nneeds_exe_wrapper = true\n\n[host_machine]\nsystem = 'windows'\ncpu_family = 'x86'\ncpu = 'x86'\nendian = 'little'\n"
  },
  {
    "path": "build-win64.txt",
    "content": "[binaries]\nc = 'x86_64-w64-mingw32-gcc'\ncpp = 'x86_64-w64-mingw32-g++'\nar = 'x86_64-w64-mingw32-ar'\nstrip = 'x86_64-w64-mingw32-strip'\nwindres = 'x86_64-w64-mingw32-windres'\n\n[properties]\nneeds_exe_wrapper = true\n\n[host_machine]\nsystem = 'windows'\ncpu_family = 'x86_64'\ncpu = 'x86_64'\nendian = 'little'\n"
  },
  {
    "path": "buildenv.h.in",
    "content": "#pragma once\n\n#define DXVK_TARGET \"@BUILD_TARGET@\"\n#define DXVK_COMPILER \"@BUILD_COMPILER@\"\n#define DXVK_COMPILER_VERSION \"@BUILD_COMPILER_VERSION@\"\n\n"
  },
  {
    "path": "dxvk.conf",
    "content": "# Device filter. Only exposes devices whose Vulkan device name contains\n# the given string. May be useful to force an application to run on a\n# specific GPU, but not applications launched by that application.\n\n# dxvk.deviceFilter = \"\"\n\n\n# Expose the HDR10 ColorSpace (DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)\n# to the application by default.\n# This shows to the game that the global Windows 'HDR Mode' is enabled.\n# Many (broken) games will need this to be set to consider exposing HDR output\n# as determine it based on the DXGIOutput's current ColorSpace instead of\n# using CheckColorSpaceSupport.\n# This defaults to the value of the DXVK_HDR environment variable.\n# \n# Supported values: True, False\n\n# dxgi.enableHDR = True\n\n\n# Expose support for dcomp swap chains with a dummy window.\n#\n# This is not a valid implementation of DirectComposition swapchains,\n# however some games may rely on this functionality to be present while\n# others may require swap chain creation to fail.\n#\n# Supported values: True, False\n\n# dxgi.enableDummyCompositionSwapchain = False\n\n\n# Allows the Vulkan driver to opt-in to exclusive full-screen mode on\n# Windows. Certain features, such as variable refresh rate or HDR, will\n# not work without this setting, however enabling it will break certain\n# games that use additional GDI windows, and it will also break alt+tab.\n#\n# This setting has no effect on non-Windows platforms.\n#\n# Supported values: True, False\n\n# dxvk.allowFse = False\n\n\n# Enables Unreal Engine 4 HDR workarounds for games that do not follow\n# the standard -Win64-Shipping.exe naming scheme. May be needed to avoid\n# crashes in D3D11 games on HDR-enabled systems due to statically linked\n# AMDAGS.\n# \n# Supported values: True, False\n\n# dxgi.enableUe4Workarounds = False\n\n\n# Create the VkSurface on the first call to IDXGISwapChain::Present,\n# rather than when creating the swap chain. Some games that start\n# rendering with a different graphics API may require this option,\n# or otherwise the window may stay black.\n# \n# Supported values: True, False\n\n# dxgi.deferSurfaceCreation = False\n# d3d9.deferSurfaceCreation = False\n\n\n# Enforce a stricter maximum frame latency. Overrides the application\n# setting specified by calling IDXGIDevice::SetMaximumFrameLatency.\n# Setting this to 0 will have no effect.\n# \n# Supported values : 0 - 16\n\n# dxgi.maxFrameLatency = 0\n# d3d9.maxFrameLatency = 0\n\n\n# Enables frame rate limiter. The main purpose of this is to work around\n# bugs in games that have physics or other simulation tied to their frame\n# rate, but do not provide their own limiter.\n#\n# Supported values\n#  0: Default behaviour. Limits the frame rate to the selected display\n#     refresh rate when vertical synchronization is enabled if the\n#     actual display mode does not match the game's one.\n#  n: Limit to n frames per second.\n# -n: Limit to n frames per second if the game runs significantly faster\n#     for a short period of time. This is primarily useful for app profiles,\n#     as the limiter will not engage when running into Vsync or an external\n#     limiter while running at more than n frames per second.\n# -1: Always disables the limiter. This is a special value to deal with\n#     games that default to a low refresh rate with no way to change it.\n\n# dxgi.maxFrameRate = 0\n# d3d9.maxFrameRate = 0\n\n\n# Controls latency sleep and Nvidia Reflex support.\n#\n# Supported values:\n# - Auto: By default, DXVK only supports latency sleep in D3D11 games that\n#         use Reflex if the graphics driver supports VK_NV_low_latency2,\n#         and if dxvk-nvapi is enabled in Proton.\n# - True: Enables built-in latency reduction based on internal timings.\n#         This assumes that input sampling for any given frame happens after\n#         the D3D9 or DXGI Present call returns; games that render and present\n#         asynchronously will not behave as intended.\n#         Similarly, this will not have any effect in games with built-in frame\n#         rate limiters, or if an external limiter (such as MangoHud) is used.\n#         In some games, enabling this may reduce performance or lead to less\n#         consistent frame pacing.\n#         The implementation will either use VK_NV_low_latency2 if supported\n#         by the driver, or a custom algorithm.\n# - False: Disable Reflex support as well as built-in latency reduction.\n  \n# dxvk.latencySleep = Auto\n\n\n# Tolerance for the latency sleep heuristic, in microseconds. Higher values\n# increase latency, but may lead to better frame pacing in some cases. Does\n# not have any effect if NV_low_latency2 is used.\n#\n# Supported values: Any non-negative number\n\n# dxvk.latencyTolerance = 1000\n\n\n# Disables the use of VK_NV_low_latency2. This will make Reflex unavailable\n# in games, and if dxvk.latencySleep is set to True, a custom algorithm will\n# be used for latency control. By default, the extension will not be used in\n# 32-bit applications due to driver issues.\n#\n# Supported values: Auto, True, False\n\n# dxvk.disableNvLowLatency2 = Auto\n\n\n# Override PCI vendor and device IDs reported to the application. Can\n# cause the app to adjust behaviour depending on the selected values.\n#\n# Supported values: Any four-digit hex number.\n\n# dxgi.customDeviceId = 0000\n# dxgi.customVendorId = 0000\n\n# d3d9.customDeviceId = 0000\n# d3d9.customVendorId = 0000\n\n\n# Override the reported device description\n#\n# Supported values: Any string.\n\n# dxgi.customDeviceDesc = \"\"\n# d3d9.customDeviceDesc = \"\"\n\n\n# Report Nvidia GPUs as AMD GPUs. For DXGI, unless NVAPI support\n# is explicitly enabled through Proton, this is done by default in\n# order to work around crashes or low performance with Nvidia-specific\n# code paths in games, especially Unreal Engine. On the D3D8/9 side,\n# it is generally only done for games known to take issue with NVAPI,\n# or otherwise behave poorly on Nvidia.\n#\n# Supported values: Auto, True, False\n\n# dxgi.hideNvidiaGpu = Auto\n# d3d9.hideNvidiaGpu = Auto\n\n\n# Report Nvidia GPUs running on NVK as AMD GPUs.\n#\n# Supported values: Auto, True, False\n\n# dxgi.hideNvkGpu = Auto\n# d3d9.hideNvkGpu = Auto\n\n\n# Report AMD GPUs as Nvidia GPUs. This is only done for games that are\n# known to have issues with AMDAGS or other AMD-specific code paths.\n#\n# Supported values: Auto, True, False\n\n# dxgi.hideAmdGpu = Auto\n# d3d9.hideAmdGpu = Auto\n\n\n# Report Intel GPUs as AMD GPUs. This is only done for games that are\n# known to have issues with Intel-specific libraries such as XESS.\n# Hiding Intel GPUs defaults to True for D3D8/9 in order to circumvent\n# iGPU specific restrictions affecting early Intel hardware.\n#\n# Supported values: Auto, True, False\n\n# dxgi.hideIntelGpu = Auto\n# d3d9.hideIntelGpu = True\n\n\n# Override maximum amount of device memory and shared system memory\n# reported to the application. This may fix texture streaming issues\n# in games that do not support cards with large amounts of VRAM.\n# This is not a hard cap and applications can choose to ignore it.\n#\n# Supported values: Any number in Megabytes.\n\n# dxgi.maxDeviceMemory = 0\n# dxgi.maxSharedMemory = 0\n\n\n# Overrides synchronization interval (Vsync) for presentation.\n# Setting this to 0 disables vertical synchronization entirely.\n# A positive value 'n' will enable Vsync and repeat the same\n# image n times, and a negative value will have no effect.\n#\n# Supported values: Any non-negative number\n\n# dxgi.syncInterval = -1\n# d3d9.presentInterval = -1\n\n\n# Controls tearing behaviour with regards to in-game Vsync settings.\n#\n# True enables the mailbox present mode in case regular Vsync is disabled.\n# This eliminates tearing, but may be unsupported on some systems.\n#\n# False enables the relaxed fifo present mode in case regular Vsync is enabled.\n# This should result in tearing but reduce stutter if FPS are too low,\n# but may be unsupported on some systems.\n#\n# Please do not report issues with this option.\n#\n# Supported values: Auto, True, False\n\n# dxvk.tearFree = Auto\n\n\n# Controls tiler optimizations. Enabling these will alter the behaviour of\n# submission heuristics and enables some non-default behaviour in DXVK.\n# This option is only intended to be changed for performance testing and\n# debugging purposes.\n#\n# Supported values: Auto, True, False\n\n# dxvk.tilerMode = Auto\n\n\n# Override the maximum feature level that a D3D11 device can be created\n# with. Setting this to a higher value may allow some applications to run\n# that would otherwise fail to create a D3D11 device.\n#\n# Supported values: 9_1, 9_2, 9_3, 10_0, 10_1, 11_0, 11_1, 12_0, 12_1\n\n# d3d11.maxFeatureLevel = 12_1\n\n\n# Overrides the maximum allowed tessellation factor. This can be used to\n# improve performance in titles which overuse tessellation.\n# \n# Supported values: Any number between 8 and 64\n\n# d3d11.maxTessFactor = 0\n\n\n# Enables relaxed pipeline barriers around UAV writes.\n#\n# Ignores write-after-write hazards in compute shaders, and all UAV\n# hazards in graphics shaders. This may improve performance in some\n# games, but may also introduce rendering issues. Please don't report\n# bugs with the option enabled.\n#\n# Supported values: True, False\n\n# d3d11.relaxedBarriers = False\n\n\n# Enables relaxed UAV pipeline barriers in graphics shaders only.\n#\n# Similar to the relaxedBarriers option, except it does not apply to\n# compute UAVs. Please do not report bugs with this option enabled.\n#\n# Supported values: True, False\n\n# d3d11.relaxedGraphicsBarriers = False\n\n\n# Overrides anisotropic filtering for all samplers. Set this to a positive\n# value to enable AF for all samplers in the game, or to 0 in order to\n# disable AF entirely. Negative values will have no effect.\n#\n# Forcing anisotropy is known to break passes that rely on\n# bilinear filtering, or in other situations when an application\n# relies on fine grained control over samplers. Please do not report\n# bugs with this option enabled.\n# \n# Supported values: Any number between 0 and 16\n\n# d3d11.samplerAnisotropy = -1\n# d3d9.samplerAnisotropy = -1\n\n\n# Changes the mipmap LOD bias for all samplers. The given number will be\n# added to the LOD bias provided by the application, rather than replacing\n# it entirely. Positive values will reduce texture detail, while negative\n# values may increase sharpness at the cost of shimmer.\n#\n# Supported values: Any number between -2.0 and 1.0\n\n# d3d11.samplerLodBias = 0.0\n# d3d9.samplerLodBias = 0.0\n\n\n# Clamps any negative LOD bias to 0. Applies after samplerLodBias has been\n# applied. May help with games that use a high negative LOD bias by default.\n#\n# Supported values: True, False\n\n# d3d11.clampNegativeLodBias = False\n# d3d9.clampNegativeLodBias = False\n\n\n# Forces per-sample rate shading when MSAA is enabled, rather than per-pixel\n# shading. May improve visual clarity at a significant performance cost, but\n# may also introduce visual issues in some games.\n#\n# Supported values: True, False\n\n# d3d11.forceSampleRateShading = False\n# d3d9.forceSampleRateShading = False\n\n\n# Forces the sample count of all textures to 1, and performs\n# the needed fixups in resolve operations and shaders.\n#\n# Supported values: True, False\n\n# d3d11.disableMsaa = False\n\n\n# Forces insertion of memory and control barriers after writes to group-shared\n# memory in compute shaders. This is only intended to be used as a workaround\n# for games that don't properly synchronize access to groupshard variables, and\n# may have a negative performance impact.\n#\n# Supported values: True, False\n\n# d3d11.forceComputeLdsBarriers = False\n\n\n# Forces insertion of full memory and control barriers after accessing any\n# read-write UAV inside compute shaders. This is only intended to be used as\n# a workaround for games that do not synchronize access to coherent UAVs,\n# and will likely have a negative performance impact.\n#\n# Supported values: True, False\n\n# d3d11.forceComputeUavBarriers = False\n\n\n# Clears mapped memory to zero when suballocated memory is freed. This will\n# drastically increase CPU overhead and should only be used as a last resort\n# if a game does not properly initialize mapped buffers on its own.\n#\n# Supported values: True, False\n\n# dxvk.zeroMappedMemory = False\n\n\n# Allocates dynamic resources with the given set of bind flags in\n# cached system memory rather than uncached memory or host-visible\n# VRAM, in order to allow fast readback from the CPU. This is only\n# useful for buggy applications, and may reduce GPU-bound performance.\n#\n# Supported values: Any combination of the following:\n# - v: Vertex buffers\n# - i: Index buffers\n# - c: Constant buffers\n# - r: Shader resources\n# - a: All dynamic resources\n\n# d3d11.cachedDynamicResources = \"\"\n\n\n# Disables direct image mapping. This is used to work around bugs\n# where the game ignores the implementation-defined row pitch for\n# mapped dynamic images and expects it to be tightly packed, which\n# can result in visual corruption.\n#\n# Supported values: True, False\n\n# d3d11.disableDirectImageMapping = False\n\n\n# Force-enables the D3D11 context lock via the ID3D10Multithread\n# interface. This may be useful to debug race conditions.\n#\n# Supported values: True, False\n\n# d3d11.enableContextLock = False\n\n\n# Exposes or hides support for driver command lists\n#\n# Some games use the feature flag to decide whether to use deferred\n# contexts or not. We enable this by default, but in some situations\n# this can lead to issues if games detect an AMD GPU where command\n# lists are not natively supported on Windows.\n#\n# Supported values: True, False\n\n# d3d11.exposeDriverCommandLists = True\n\n\n# Reproducible Command Stream\n#\n# Ensure that for the same D3D commands the output VK commands\n# don't change between runs. Useful for comparative benchmarking,\n# can negatively affect performance and can break some games\n# that don't use queries correctly.\n#\n# Supported values:\n# - True/False\n\n# d3d11.reproducibleCommandStream = False\n# d3d9.reproducibleCommandStream = False\n\n\n# Sets number of pipeline compiler threads.\n# \n# If the graphics pipeline library feature is enabled, the given\n# number of threads will be used for shader compilation. Some of\n# these threads will be reserved for high-priority work.\n#\n# Supported values:\n# - 0 to use all available CPU cores\n# - any positive number to enforce the thread count\n\n# dxvk.numCompilerThreads = 0\n\n\n# Toggles raw SSBO usage.\n# \n# Uses storage buffers to implement raw and structured buffer\n# views. Enabled by default on hardware which has a storage\n# buffer offset alignment requirement of 4 Bytes (e.g. AMD).\n# Enabling this may improve performance, but is not safe on\n# hardware with higher alignment requirements.\n# \n# Supported values:\n# - Auto: Don't change the default\n# - True, False: Always enable / disable\n\n# dxvk.useRawSsbo = Auto\n\n\n# Controls graphics pipeline library behaviour\n#\n# Can be used to change VK_EXT_graphics_pipeline_library usage for\n# debugging purpose. Doing so will likely result in increased stutter\n# or degraded performance.\n#\n# Supported values:\n# - Auto: Enable if supported, and compile optimized pipelines in the background\n# - True: Enable if supported, but do not compile optimized pipelines\n# - False: Always disable the feature\n\n# dxvk.enableGraphicsPipelineLibrary = Auto\n\n\n# Controls descriptor model\n#\n# Enables or disables VK_EXT_descriptor_heap usage.\n#\n# Supported values:\n# - Auto: Enable if supported on known-good drivers\n# - True: Enable if supported, regardless of GPU driver\n# - False: Always disable the feature\n\n# dxvk.enableDescriptorHeap = Auto\n\n\n# Controls descriptor model\n#\n# Enables or disables VK_EXT_descriptor_buffer usage. If the descriptor heap\n# feature is enabled and supported by the driver, it will take precedence over\n# descriptor buffers.\n#\n# Supported values:\n# - Auto: Enable if supported on known-good drivers\n# - True: Enable if supported, regardless of GPU driver\n# - False: Always disable the feature\n\n# dxvk.enableDescriptorBuffer = Auto\n\n\n# Controls image layout path\n#\n# Can be used to disable VK_KHR_unified_image_layouts usage on systems that\n# support the extension, as well as opportunistic paths on devices that do\n# not support it. Only useful for debugging purposes.\n\n# dxvk.enableUnifiedImageLayouts = True\n\n\n# Controls implicit resolves\n#\n# Some games will attempt to sample multisampled images as regular textures,\n# which is undefined behaviour in Vulkan and will cause rendering issues on\n# Nvidia (striped patterns). To work around this, DXVK will perform SAMPLE_ZERO\n# resolves on demand.\n#\n# This option can be used to disable the workaround for games that do not\n# actually need it, e.g. because they have runtime checks inside shaders\n# on whether the given texture safe to access.\n\n# dxvk.enableImplicitResolves = True\n\n\n# Controls pipeline lifetime tracking\n#\n# If enabled, pipeline libraries will be freed aggressively in order\n# save memory and address space. Has no effect if graphics pipeline\n# libraries are not supported or disabled.\n#\n# Supported values:\n# - Auto: Enable tracking for 32-bit applications only\n# - True: Always enable tracking\n# - False: Always disable tracking\n\n# dxvk.trackPipelineLifetime = Auto\n\n\n# Controls memory defragmentation\n#\n# By default, DXVK will try to defragment video memory if there is a\n# significant amount of memory wasted, or if the allocation budget of\n# the application is exceeded. This option is provided solely for\n# debug purposes.\n#\n# Supported values:\n# - True: Enable defragmentation\n# - Auto: Enable defragmentation, except on blocked drivers\n# - False: Disable defragmentation\n\n# dxvk.enableMemoryDefrag = Auto\n\n\n# Sets enabled HUD elements\n# \n# Behaves like the DXVK_HUD environment variable if the\n# environment variable is not set, otherwise it will be\n# ignored. The syntax is identical.\n\n# dxvk.hud = \n\n\n# Reported shader model\n#\n# The shader model to state that we support in the device\n# capabilities that the application queries. Note that\n# the value will be limited to 1 for D3D8 applications.\n# \n# Supported values:\n# - 0: Fixed-function only\n# - 1: Shader Model 1\n# - 2: Shader Model 2\n# - 3: Shader Model 3\n\n# d3d9.shaderModel = 3\n\n\n# DPI Awareness\n# \n# Decides whether we should call SetProcessDPIAware on device\n# creation. Helps avoid upscaling blur in modern Windows on\n# Hi-DPI screens/devices.\n#\n# Supported values:\n# - True, False: Always enable / disable\n\n# d3d9.dpiAware = True\n\n\n# Lenient Clear\n#\n# Decides whether or not we fastpath clear anyway if we are close enough to\n# clearing a full render target.\n#\n# Supported values:\n# - True, False: Always enable / disable\n\n# d3d9.lenientClear = False\n\n\n# Max available memory\n#\n# Changes the max initial value used in tracking and GetAvailableTextureMem\n# Value in Megabytes\n#\n# Supported values:\n# - Max Available Memory: Any int32_t\n# - Memory Tracking Testing: True, False\n\n# d3d9.maxAvailableMemory = 4096\n# d3d9.memoryTrackTest = False\n\n\n# Force enable/disable floating point quirk emulation\n#\n# Force toggle anything * 0 emulation\n# Setting it to True will use a faster but less accurate approach that works for most games.\n# Supported values:\n# - True: Use a faster but less accurate approach. Good enough for most games\n# - False: Disable float emulation completely\n# - Strict: Use a slower but more correct approach. Necessary for some games\n# - Auto: DXVK will pick automatically\n\n# d3d9.floatEmulation = Auto\n\n\n# Force enable/disable custom sine/cosine approximation\n#\n# On some hardware, this may be more accurate than native sin/cos,\n# but will come at a performance cost.\n#\n# Supported values:\n# - True: Always enable emulation path\n# - Auto: Only enable emulation on drivers with poor sin/cos precision\n# - False: Always enable native path\n\n# dxvk.lowerSinCos = Auto\n\n\n# Device Local Constant Buffers\n#\n# Enables using device local, host accessible memory for constant buffers in D3D9.\n# This tends to actually be slower for some reason on AMD,\n# and the exact same performance on NVIDIA.\n#\n# Supported values:\n# - True/False\n\n# d3d9.deviceLocalConstantBuffers = False\n\n# Support DF formats\n#\n# Support the vendor extension DF floating point depth formats on AMD and Intel.\n# Note that this config is ignored and disabled by default on Nvidia, or when\n# spoofing a Nvidia GPU, as it does not support these formats natively.\n#\n# Supported values:\n# - True/False\n\n# d3d9.supportDFFormats = True\n\n# Use D32f for D24\n#\n# Useful for reproducing AMD issues on other hw.\n#\n# Supported values:\n# - True/False\n\n# d3d9.useD32forD24 = False\n\n# Support X4R4G4B4\n#\n# Support the X4R4G4B4 format.\n# The Sims 2 is a very broken game.\n#\n# Supported values:\n# - True/False\n\n# d3d9.supportX4R4G4B4 = True\n\n# Disable A8 as a Render Target\n#\n# Disable support for A8 format render targets\n# Once again, The Sims 2 is a very broken game.\n#\n# Supported values:\n# - True/False\n\n# d3d9.disableA8RT = False\n\n# Force Sampler Type Spec Constants\n#\n# Useful if games use the wrong image and sampler\n# type combo like Halo: CE or Spellforce.\n# Can fix rendering in older, broken games in some instances.\n#\n# Supported values:\n# - True/False\n\n# d3d9.forceSamplerTypeSpecConstants = False\n\n# Force Aspect Ratio\n#\n# Only exposes modes with a given aspect ratio.\n# Useful for titles that break if they see ultra-wide.\n#\n# Supported values:\n# - Any ratio, ie. \"16:9\", \"4:3\"\n\n# d3d9.forceAspectRatio = \"\"\n\n# Force Refresh Rate\n#\n# Only exposes modes with a given refresh rate.\n# Useful for titles such as\n# Metal Gear Rising: Revengeance, that select\n# the lowest supported refresh rate. A value\n# of 0 will disable the option.\n#\n# Note that many titles are known to break in\n# various ways (physics, rendering, etc.) when\n# forcing high refresh rates. USE WITH CAUTION.\n#\n# Please do not report issues with this option.\n#\n# Supported values:\n# - Any integer refresh rate value in Hz, ie. 60, 120\n\n# dxgi.forceRefreshRate = 0\n# d3d9.forceRefreshRate = 0\n\n# Mode Count Compatibility\n#\n# Attempts to aggressively limit the advertised mode count\n# by only listing the current desktop resolution and refresh\n# rate, along with a minimal list of fallback resolutions.\n#\n# Useful for titles that encounter issues when too many modes\n# are reported, e.g., AquaNox, AquaNox 2: Revelation.\n#\n# Can be used in conjunction with forceAspectRatio\n# and forceRefreshRate to further restrict reported modes.\n#\n# Supported values:\n# - True/False\n\n# d3d9.modeCountCompatibility = False\n\n# Enumerate by Displays\n#\n# Whether we should enumerate D3D9 adapters by display (windows behaviour)\n# or by physical adapter.\n# May be useful in PRIME setups.\n#\n# Supported values:\n# - True/False\n\n# d3d9.enumerateByDisplays = True\n\n# Cached write-only Buffers\n#\n# Allocates write-only resources in D3DPOOL_DEFAULT in\n# cached system memory rather than uncached memory or host-visible\n# VRAM, in order to allow fast readback from the CPU. This is only\n# useful for buggy applications, and may reduce GPU-bound performance.\n#\n# Supported values:\n# - True/False\n\n# d3d9.cachedWriteOnlyBuffers = False\n\n# Seamless Cubes\n#\n# Don't use non seamless cube maps even if they are supported.\n# Non seamless cubes are correct d3d9 behavior, but can produce worse looking edges.\n#\n# Supported values:\n# - True/False\n\n# d3d9.seamlessCubes = False\n\n# Debug Utils\n#\n# Enables debug utils as this is off by default, this enables user annotations like BeginEvent()/EndEvent().\n# Alternatively could be enabled with DXVK_DEBUG=markers environment variable.\n#\n# Supported values:\n# - True/False\n\n# dxvk.enableDebugUtils = False\n\n# Memory limit for locked D3D9 textures\n#\n# How much virtual memory will be used for textures (in MB).\n# 0 to disable the limit.\n# THIS DOES NOT IMPACT ACTUAL MEMORY CONSUMPTION OR TEXTURE QUALITY.\n# DO NOT CHANGE THIS UNLESS YOU HAVE A VERY GOOD REASON.\n\n# d3d9.textureMemory = 100\n\n# Hide integrated graphics from applications\n#\n# Only has an effect when dedicated GPUs are present on the system. It is\n# not recommended to use this option at all unless absolutely necessary for\n# a game to work; prefer using DXVK_FILTER_DEVICE_NAME whenever possible.\n#\n# Supported values:\n# - True/False\n\n# dxvk.hideIntegratedGraphics = False\n\n# Trigger DEVICELOST when losing focus\n#\n# D3D9 requires the application to call Device::Reset after\n# it loses focus in fullscreen.\n# Some games rely on observing a D3DERR_DEVICELOST or D3DERR_NOTRESET.\n# Others don't handle it correctly.\n#\n# Supported values:\n# - True/False\n\n# d3d9.deviceLossOnFocusLoss = False\n\n# Reject Device::Reset if any losable resource is still alive\n#\n# D3D9 rejects Device::Reset if there's still any alive resources of specific types.\n# (State blocks, additional swapchains, D3DPOOL_DEFAULT resources)\n# Some games leak resources leading to a hang.\n#\n# Supported values:\n# - True/False\n\n# d3d9.countLosableResources = True\n\n# Add an extra frame buffer when necessary\n#\n# Some games create a swapchain with only 1 buffer and still expect GetFrontBufferData() to correctly return back the data of the last present.\n# To make that work correctly, we add a second buffer and copy to it every single frame.\n# This is unnecessary for all but a single modded game (Silent Hill 2 Enhanced Edition), that's why it's a config option.\n\n# d3d9.extraFrontbuffer = False\n\n# Dref scaling for DXS0/FVF\n#\n# Some early D3D8 games expect Dref (depth texcoord Z) to be on the range of\n# [0..2^bitDepth - 1]. This option allows DXSO and fixed vertex function to\n# scale it back down to [0..1].\n#\n# Supported values: Any number representing bitDepth (typically 24).\n\n# d3d8.scaleDref = 0\n\n# Shadow perspective divide\n#\n# Older applications designed for Nvidia hardware (or ported from XBox)\n# expect shadow map texture coordinates to be perspective divided, even\n# though D3DTTFF_PROJECTED is never set for any texture coordinates.\n# Older Nvidia cards (GeForce 3, GeForce 4 series) performed this\n# projection directly in hardware.\n#\n# This option forces the D3DTTFF_PROJECTED flag for the necessary stages\n# when a depth texture is bound to slot 0, in order to emulate older\n# Nvidia hardware behavior.\n#\n# Supported values:\n# - True/False\n\n# d3d8.shadowPerspectiveDivide = False\n\n# Force vertex shader declaration\n#\n# Some games rely on undefined behavior by using undeclared vertex shader inputs.\n# The simplest way to fix them is to modify their vertex shader decl.\n#\n# This option takes a comma-separated list of colon-separated number pairs, where\n# the first number is a D3DVSDE_REGISTER value, the second is a D3DVSDT_TYPE value.\n#\n# Supported values:\n# - e.g. \"0:2,3:2,7:1\" for float3 position : v0, float3 normal : v3, float2 uv : v7.\n\n# d3d8.forceVsDecl = \"\"\n\n# Draw call batching\n#\n# Specialized drawcall batcher, typically for games that draw a lot of similar\n# geometry in separate drawcalls (sometimes even one triangle at a time).\n#\n# May hurt performance or introduce graphical artifacts outside of\n# specific games that are known to benefit from it.\n#\n# Supported values:\n# - True/False\n\n# d3d8.batching = False\n\n# P8 texture support workaround\n#\n# Early Nvidia GPUs, such as the GeForce 4 generation cards, included and exposed\n# P8 texture support. However, it was no longer advertised with cards in the FX series\n# and above. ATI/AMD drivers and hardware were most likely in a similar situation.\n#\n# This option will ensure all P8 textures are placed in D3DPOOL_SCRATCH, so that\n# their creation is guaranteed to succeed even if the format is unsupported.\n# Can help older titles that don't properly handle the lack of P8 support.\n#\n# Supported values:\n# - True/False\n\n# d3d8.placeP8InScratch = False\n\n# Legacy discard buffer behavior\n#\n# Older applications may rely on D3DLOCK_DISCARD being ignored for everything\n# except D3DUSAGE_DYNAMIC + D3DUSAGE_WRITEONLY buffers, however this approach\n# incurs a performance penalty.\n#\n# Supported values:\n# - True/False\n\n# d3d8.forceLegacyDiscard = False\n\n\n# Overrides memory budget\n#\n# Can be used to limit the amount of VRAM that DXVK will actually use.\n# This is *only* intended to be a debug option, expect severe performance\n# degradation when enabling this.\n#\n# Supported values:\n# - 0 to disable the budget override\n# - Any positive value to limit the VRAM budget, in Megabytes\n\n# dxvk.maxMemoryBudget = 0\n"
  },
  {
    "path": "include/native/meson.build",
    "content": "install_subdir(\n  'directx',\n  install_dir: get_option('includedir') / 'dxvk',\n  strip_directory: true,\n  exclude_files: '.git'\n)\n\ninstall_subdir(\n  'windows',\n  install_dir: get_option('includedir') / 'dxvk',\n  strip_directory: true,\n)\n\ninstall_headers(\n  'wsi/native_wsi.h',\n  'wsi/native_sdl3.h',\n  'wsi/native_sdl2.h',\n  'wsi/native_glfw.h',\n  subdir: 'dxvk/wsi',\n)\n"
  },
  {
    "path": "include/native/windows/oaidl.h",
    "content": "#pragma once\n\n// Don't care."
  },
  {
    "path": "include/native/windows/objbase.h",
    "content": "#pragma once\n\n// Don't care."
  },
  {
    "path": "include/native/windows/ocidl.h",
    "content": "#pragma once\n\n// Don't care."
  },
  {
    "path": "include/native/windows/ole2.h",
    "content": "#pragma once\n\n// Don't care."
  },
  {
    "path": "include/native/windows/poppack.h",
    "content": "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */\n#if !(defined(lint) || defined(RC_INVOKED))\n#pragma pack(pop)\n#endif\n"
  },
  {
    "path": "include/native/windows/pshpack4.h",
    "content": "/**\n * This file has no copyright assigned and is placed in the Public Domain.\n * This file is part of the mingw-w64 runtime package.\n * No warranty is given; refer to the file DISCLAIMER.PD within this package.\n */\n#if !(defined(lint) || defined(RC_INVOKED))\n#pragma pack(push,4)\n#endif\n"
  },
  {
    "path": "include/native/windows/rpc.h",
    "content": "#pragma once\n\n// Don't care."
  },
  {
    "path": "include/native/windows/rpcndr.h",
    "content": "#pragma once\n\n// Don't care."
  },
  {
    "path": "include/native/windows/unknwn.h",
    "content": "#pragma once\n\n#include \"windows_base.h\"\n\ntypedef interface IUnknown IUnknown;\n\nDEFINE_GUID(IID_IUnknown, 0x00000000,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46)\n\n#ifdef __cplusplus\nstruct IUnknown {\n\npublic:\n\n  virtual HRESULT QueryInterface(REFIID riid, void** ppvObject) = 0;\n  template<class Q>\n  HRESULT STDMETHODCALLTYPE QueryInterface(Q **pp) {\n    return QueryInterface(__uuidof(Q), (void **)pp);\n  }\n\n  virtual ULONG AddRef()  = 0;\n  virtual ULONG Release() = 0;\n\n};\n#else\ntypedef struct IUnknownVtbl\n{\nBEGIN_INTERFACE\n\n  HRESULT (STDMETHODCALLTYPE *QueryInterface)(\n    IUnknown *This,\n    REFIID riid,\n    void **ppvObject\n  );\n  ULONG (STDMETHODCALLTYPE *AddRef)(IUnknown *This);\n  ULONG (STDMETHODCALLTYPE *Release)(IUnknown *This);\n\nEND_INTERFACE\n} IUnknownVtbl;\n\ninterface IUnknown\n{\n    CONST_VTBL struct IUnknownVtbl *lpVtbl;\n};\n\n#define IUnknown_AddRef(This) ((This)->lpVtbl->AddRef(This))\n#define IUnknown_Release(This) ((This)->lpVtbl->Release(This))\n\n#endif // __cplusplus\n\nDECLARE_UUIDOF_HELPER(IUnknown, 0x00000000,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46)\n\n#define IID_PPV_ARGS(ppType) __uuidof(decltype(**(ppType))), [](auto** pp) { (void)static_cast<IUnknown*>(*pp); return reinterpret_cast<void**>(pp); }(ppType)\n"
  },
  {
    "path": "include/native/windows/windows.h",
    "content": "#pragma once\n\n#include \"windows_base.h\"\n#include \"unknwn.h\""
  },
  {
    "path": "include/native/windows/windows_base.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\n#include <cstdint>\n#include <cstring>\n#else\n#include <stdint.h>\n#include <string.h>\n#include <wchar.h>\n#endif // __cplusplus\n\n// GCC complains about the COM interfaces\n// not having virtual destructors\n\n// and class conversion for C...DESC helper types\n#if defined(__GNUC__) && defined(__cplusplus)\n#pragma GCC diagnostic ignored \"-Wnon-virtual-dtor\"\n#pragma GCC diagnostic ignored \"-Wclass-conversion\"\n#endif // __GNUC__ && __cplusplus\n\ntypedef int32_t INT;\ntypedef uint32_t UINT;\n\ntypedef int32_t LONG;\ntypedef uint32_t ULONG;\ntypedef int32_t *LPLONG;\n\ntypedef int32_t HRESULT;\n\ntypedef wchar_t WCHAR;\ntypedef WCHAR *NWPSTR, *LPWSTR, *PWSTR;\ntypedef unsigned char UCHAR, *PUCHAR;\n\ntypedef char CHAR;\ntypedef const CHAR *LPCSTR, *PCSTR;\n\ntypedef INT BOOL;\ntypedef BOOL WINBOOL;\n\ntypedef uint16_t UINT16;\ntypedef uint32_t UINT32;\ntypedef void VOID;\ntypedef void* PVOID;\ntypedef void* LPVOID;\ntypedef const void* LPCVOID;\n\ntypedef size_t SIZE_T;\n\ntypedef int8_t INT8;\ntypedef uint8_t UINT8;\ntypedef uint8_t BYTE;\n\ntypedef int16_t SHORT;\ntypedef uint16_t USHORT;\n\ntypedef int64_t LONGLONG;\ntypedef int64_t INT64;\n\ntypedef uint64_t ULONGLONG;\ntypedef uint64_t UINT64;\n\ntypedef intptr_t LONG_PTR;\ntypedef uintptr_t ULONG_PTR;\n\ntypedef float FLOAT;\n\n#ifndef GUID_DEFINED\n#define GUID_DEFINED\ntypedef struct GUID {\n  uint32_t Data1;\n  uint16_t Data2;\n  uint16_t Data3;\n  uint8_t  Data4[8];\n} GUID;\n#endif // GUID_DEFINED\n\ntypedef GUID UUID;\ntypedef GUID IID;\n#ifdef __cplusplus\n#define REFIID const IID&\n#define REFGUID const GUID&\n#define REFCLSID const GUID&\n#else\n#define REFIID const IID*\n#define REFGUID const GUID*\n#define REFCLSID const GUID* const\n#endif // __cplusplus\n\n#ifdef __cplusplus\n\ntemplate <typename T>\nconstexpr GUID __uuidof_helper();\n\n#define __uuidof(T) __uuidof_helper<T>()\n#define __uuidof_var(T) __uuidof_helper<decltype(T)>()\n\ninline bool operator==(const GUID& a, const GUID& b) { return std::memcmp(&a, &b, sizeof(GUID)) == 0; }\ninline bool operator!=(const GUID& a, const GUID& b) { return std::memcmp(&a, &b, sizeof(GUID)) != 0; }\n\n#endif // __cplusplus\n\ntypedef uint32_t DWORD;\ntypedef uint16_t WORD;\ntypedef DWORD *LPDWORD;\n\ntypedef void* HANDLE;\ntypedef HANDLE HMONITOR;\ntypedef HANDLE HDC;\ntypedef HANDLE HMODULE;\ntypedef HANDLE HINSTANCE;\ntypedef HANDLE HWND;\ntypedef HANDLE HKEY;\ntypedef HANDLE *LPHANDLE;\ntypedef DWORD COLORREF;\n\n#if INTPTR_MAX == INT64_MAX\ntypedef int64_t  INT_PTR;\ntypedef uint64_t UINT_PTR;\n#else\ntypedef int32_t  INT_PTR;\ntypedef uint32_t UINT_PTR;\n#endif\ntypedef INT_PTR*  PINT_PTR;\ntypedef UINT_PTR* PUINT_PTR;\n\n#ifdef STRICT\n#define DECLARE_HANDLE(a) typedef struct a##__ { int unused; } *a\n#else /*STRICT*/\n#define DECLARE_HANDLE(a) typedef HANDLE a\n#endif /*STRICT*/\n\ntypedef char* LPSTR;\ntypedef const wchar_t* LPCWSTR;\n\ntypedef struct LUID {\n  DWORD LowPart;\n  LONG  HighPart;\n} LUID;\n\ntypedef struct POINT {\n  LONG x;\n  LONG y;\n} POINT;\n\ntypedef POINT* LPPOINT;\n\ntypedef struct RECT {\n  LONG left;\n  LONG top;\n  LONG right;\n  LONG bottom;\n} RECT,*PRECT,*NPRECT,*LPRECT;\n\ntypedef struct SIZE {\n  LONG cx;\n  LONG cy;\n} SIZE,*PSIZE,*LPSIZE;\n\ntypedef union {\n  struct {\n    DWORD LowPart;\n    LONG HighPart;\n  };\n\n  struct {\n    DWORD LowPart;\n    LONG HighPart;\n  } u;\n\n  LONGLONG QuadPart;\n} LARGE_INTEGER;\n\ntypedef struct MEMORYSTATUS {\n  DWORD  dwLength;\n  SIZE_T dwTotalPhys;\n} MEMORYSTATUS;\n\ntypedef struct SECURITY_ATTRIBUTES {\n  DWORD nLength;\n  void* lpSecurityDescriptor;\n  BOOL  bInheritHandle;\n} SECURITY_ATTRIBUTES;\n\ntypedef struct PALETTEENTRY {\n  BYTE peRed;\n  BYTE peGreen;\n  BYTE peBlue;\n  BYTE peFlags;\n} PALETTEENTRY, *PPALETTEENTRY, *LPPALETTEENTRY;\n\ntypedef struct RGNDATAHEADER {\n  DWORD dwSize;\n  DWORD iType;\n  DWORD nCount;\n  DWORD nRgnSize;\n  RECT  rcBound;\n} RGNDATAHEADER;\n\ntypedef struct RGNDATA {\n  RGNDATAHEADER rdh;\n  char          Buffer[1];\n} RGNDATA,*PRGNDATA,*NPRGNDATA,*LPRGNDATA;\n\n// Ignore these.\n#define STDMETHODCALLTYPE\n#define __stdcall\n\n#define CONST const\n#define CONST_VTBL const\n\n#define TRUE 1\n#define FALSE 0\n\n#define WAIT_TIMEOUT    0x00000102\n#define WAIT_FAILED\t    0xffffffff\n#define WAIT_OBJECT_0\t\t0\n#define WAIT_ABANDONED  0x00000080\n\n#define interface struct\n#define MIDL_INTERFACE(x) struct\n\n#ifdef __cplusplus\n\n#define DEFINE_GUID(iid, a, b, c, d, e, f, g, h, i, j, k) \\\n  constexpr GUID iid = {a,b,c,{d,e,f,g,h,i,j,k}};\n\n#define DECLARE_UUIDOF_HELPER(type, a, b, c, d, e, f, g, h, i, j, k) \\\n  extern \"C++\" { template <> constexpr GUID __uuidof_helper<type>() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \\\n  extern \"C++\" { template <> constexpr GUID __uuidof_helper<type*>() { return __uuidof_helper<type>(); } } \\\n  extern \"C++\" { template <> constexpr GUID __uuidof_helper<const type*>() { return __uuidof_helper<type>(); } } \\\n  extern \"C++\" { template <> constexpr GUID __uuidof_helper<type&>() { return __uuidof_helper<type>(); } } \\\n  extern \"C++\" { template <> constexpr GUID __uuidof_helper<const type&>() { return __uuidof_helper<type>(); } }\n\n#else\n#define DEFINE_GUID(iid, a, b, c, d, e, f, g, h, i, j, k) \\\n  static const GUID iid = {a,b,c,{d,e,f,g,h,i,j,k}};\n#define DECLARE_UUIDOF_HELPER(type, a, b, c, d, e, f, g, h, i, j, k)\n#endif // __cplusplus\n\n#define __CRT_UUID_DECL(type, a, b, c, d, e, f, g, h, i, j, k) DECLARE_UUIDOF_HELPER(type, a, b, c, d, e, f, g, h, i, j, k)\n\n#define S_OK     0\n#define S_FALSE  1\n\n#define E_INVALIDARG  ((HRESULT)0x80070057)\n#define E_FAIL        ((HRESULT)0x80004005)\n#define E_NOINTERFACE ((HRESULT)0x80004002)\n#define E_NOTIMPL     ((HRESULT)0x80004001)\n#define E_OUTOFMEMORY ((HRESULT)0x8007000E)\n#define E_POINTER     ((HRESULT)0x80004003)\n#define E_ABORT       ((HRESULT)0x80004004)\n\n#define STATUS_TIMEOUT                   ((NTSTATUS)0x00000102)\n\n#define DXGI_STATUS_OCCLUDED                     ((HRESULT)0x087a0001)\n#define DXGI_STATUS_CLIPPED                      ((HRESULT)0x087a0002)\n#define DXGI_STATUS_NO_REDIRECTION               ((HRESULT)0x087a0004)\n#define DXGI_STATUS_NO_DESKTOP_ACCESS            ((HRESULT)0x087a0005)\n#define DXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE ((HRESULT)0x087a0006)\n#define DXGI_STATUS_MODE_CHANGED                 ((HRESULT)0x087a0007)\n#define DXGI_STATUS_MODE_CHANGE_IN_PROGRESS      ((HRESULT)0x087a0008)\n#define DXGI_STATUS_UNOCCLUDED                   ((HRESULT)0x087a0009)\n#define DXGI_STATUS_DDA_WAS_STILL_DRAWING        ((HRESULT)0x087a000a)\n#define DXGI_STATUS_PRESENT_REQUIRED             ((HRESULT)0x087a002f)\n\n#define DXGI_ERROR_INVALID_CALL                  ((HRESULT)0x887A0001)\n#define DXGI_ERROR_NOT_FOUND                     ((HRESULT)0x887A0002)\n#define DXGI_ERROR_MORE_DATA                     ((HRESULT)0x887A0003)\n#define DXGI_ERROR_UNSUPPORTED                   ((HRESULT)0x887A0004)\n#define DXGI_ERROR_DEVICE_REMOVED                ((HRESULT)0x887A0005)\n#define DXGI_ERROR_DEVICE_HUNG                   ((HRESULT)0x887A0006)\n#define DXGI_ERROR_DEVICE_RESET                  ((HRESULT)0x887A0007)\n#define DXGI_ERROR_WAS_STILL_DRAWING             ((HRESULT)0x887A000A)\n#define DXGI_ERROR_FRAME_STATISTICS_DISJOINT     ((HRESULT)0x887A000B)\n#define DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE  ((HRESULT)0x887A000C)\n#define DXGI_ERROR_DRIVER_INTERNAL_ERROR         ((HRESULT)0x887A0020)\n#define DXGI_ERROR_NONEXCLUSIVE                  ((HRESULT)0x887A0021)\n#define DXGI_ERROR_NOT_CURRENTLY_AVAILABLE       ((HRESULT)0x887A0022)\n#define DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED    ((HRESULT)0x887A0023)\n#define DXGI_ERROR_REMOTE_OUTOFMEMORY            ((HRESULT)0x887A0024)\n#define DXGI_ERROR_ACCESS_LOST                   ((HRESULT)0x887A0026)\n#define DXGI_ERROR_WAIT_TIMEOUT                  ((HRESULT)0x887A0027)\n#define DXGI_ERROR_SESSION_DISCONNECTED          ((HRESULT)0x887A0028)\n#define DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE      ((HRESULT)0x887A0029)\n#define DXGI_ERROR_CANNOT_PROTECT_CONTENT        ((HRESULT)0x887A002A)\n#define DXGI_ERROR_ACCESS_DENIED                 ((HRESULT)0x887A002B)\n#define DXGI_ERROR_NAME_ALREADY_EXISTS           ((HRESULT)0x887A002C)\n#define DXGI_ERROR_SDK_COMPONENT_MISSING         ((HRESULT)0x887A002D)\n#define DXGI_ERROR_ALREADY_EXISTS                ((HRESULT)0x887A0036)\n\n#define D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD ((HRESULT)0x887C0004)\n\n#define WINAPI\n#define WINUSERAPI\n\n#define RGB(r,g,b)          ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))\n\n#define MAKE_HRESULT(sev,fac,code) \\\n    ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )\n\n#ifdef __cplusplus\n#define STDMETHOD(name) virtual HRESULT name\n#define STDMETHOD_(type, name) virtual type name\n#else\n#define STDMETHOD(name) HRESULT (STDMETHODCALLTYPE *name)\n#define STDMETHOD_(type, name) type (STDMETHODCALLTYPE *name)\n#endif // __cplusplus\n\n#define THIS_\n#define THIS\n\n#define __C89_NAMELESSSTRUCTNAME\n#define __C89_NAMELESSUNIONNAME\n#define __C89_NAMELESSUNIONNAME1\n#define __C89_NAMELESSUNIONNAME2\n#define __C89_NAMELESSUNIONNAME3\n#define __C89_NAMELESSUNIONNAME4\n#define __C89_NAMELESSUNIONNAME5\n#define __C89_NAMELESSUNIONNAME6\n#define __C89_NAMELESSUNIONNAME7\n#define __C89_NAMELESSUNIONNAME8\n#define __C89_NAMELESS\n#define DUMMYUNIONNAME\n#define DUMMYSTRUCTNAME\n#define DUMMYUNIONNAME1\n#define DUMMYUNIONNAME2\n#define DUMMYUNIONNAME3\n#define DUMMYUNIONNAME4\n#define DUMMYUNIONNAME5\n#define DUMMYUNIONNAME6\n#define DUMMYUNIONNAME7\n#define DUMMYUNIONNAME8\n#define DUMMYUNIONNAME9\n\n#ifdef __cplusplus\n#define DECLARE_INTERFACE(x)     struct x\n#define DECLARE_INTERFACE_(x, y) struct x : public y\n#else\n#ifdef CONST_VTABLE\n#define DECLARE_INTERFACE(x) \\\n    typedef interface x { \\\n        const struct x##Vtbl *lpVtbl; \\\n    } x; \\\n    typedef const struct x##Vtbl x##Vtbl; \\\n    const struct x##Vtbl\n#else\n#define DECLARE_INTERFACE(x) \\\n    typedef interface x { \\\n        struct x##Vtbl *lpVtbl; \\\n    } x; \\\n    typedef struct x##Vtbl x##Vtbl; \\\n    struct x##Vtbl\n#endif // CONST_VTABLE\n#define DECLARE_INTERFACE_(x, y) DECLARE_INTERFACE(x)\n#endif // __cplusplus\n#define DECLARE_INTERFACE_IID_(x, y, z) DECLARE_INTERFACE_(x, y)\n\n#define BEGIN_INTERFACE\n#define END_INTERFACE\n\n#ifdef __cplusplus\n#define PURE = 0\n#else\n#define PURE\n#endif // __cplusplus\n\n#define DECLSPEC_SELECTANY\n\n#define __MSABI_LONG(x) x\n\n#define ENUM_CURRENT_SETTINGS ((DWORD)-1)\n#define ENUM_REGISTRY_SETTINGS ((DWORD)-2)\n\n#define INVALID_HANDLE_VALUE ((HANDLE)-1)\n\n#define DUPLICATE_CLOSE_SOURCE ((DWORD)0x1)\n#define DUPLICATE_SAME_ACCESS ((DWORD)0x2)\n\n#define FAILED(hr) ((HRESULT)(hr) < 0)\n#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)\n\n#define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length))\n#define ZeroMemory RtlZeroMemory\n\n#define MAX_PATH 260\n#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))\n\n#ifndef DEFINE_ENUM_FLAG_OPERATORS\n\n#ifdef __cplusplus\n# define DEFINE_ENUM_FLAG_OPERATORS(type) \\\nextern \"C++\" \\\n{ \\\n    inline type operator &(type x, type y) { return (type)((int)x & (int)y); } \\\n    inline type operator &=(type &x, type y) { return (type &)((int &)x &= (int)y); } \\\n    inline type operator ~(type x) { return (type)~(int)x; } \\\n    inline type operator |(type x, type y) { return (type)((int)x | (int)y); } \\\n    inline type operator |=(type &x, type y) { return (type &)((int &)x |= (int)y); } \\\n    inline type operator ^(type x, type y) { return (type)((int)x ^ (int)y); } \\\n    inline type operator ^=(type &x, type y) { return (type &)((int &)x ^= (int)y); } \\\n}\n#else\n# define DEFINE_ENUM_FLAG_OPERATORS(type)\n#endif\n#endif /* DEFINE_ENUM_FLAG_OPERATORS */\n"
  },
  {
    "path": "include/native/wsi/native_glfw.h",
    "content": "#include <windows.h>\n\n#include <GLFW/glfw3.h>\n\nnamespace dxvk::wsi {\n\n  inline GLFWwindow* fromHwnd(HWND hWindow) {\n    return reinterpret_cast<GLFWwindow*>(hWindow);\n  }\n\n  inline HWND toHwnd(GLFWwindow* pWindow) {\n    return reinterpret_cast<HWND>(pWindow);\n  }\n\n  // Offset so null HMONITORs go to -1\n  inline int32_t fromHmonitor(HMONITOR hMonitor) {\n    return static_cast<int32_t>(reinterpret_cast<intptr_t>(hMonitor)) - 1;\n  }\n\n  // Offset so -1 display id goes to 0 == NULL\n  inline HMONITOR toHmonitor(int32_t displayId) {\n    return reinterpret_cast<HMONITOR>(static_cast<intptr_t>(displayId + 1));\n  }\n\n}"
  },
  {
    "path": "include/native/wsi/native_sdl2.h",
    "content": "#include <windows.h>\n\n#include <SDL.h>\n\nnamespace dxvk::wsi {\n\n  inline SDL_Window* fromHwnd(HWND hWindow) {\n    return reinterpret_cast<SDL_Window*>(hWindow);\n  }\n\n  inline HWND toHwnd(SDL_Window* pWindow) {\n    return reinterpret_cast<HWND>(pWindow);\n  }\n\n  // Offset so null HMONITORs go to -1\n  inline int32_t fromHmonitor(HMONITOR hMonitor) {\n    return static_cast<int32_t>(reinterpret_cast<intptr_t>(hMonitor)) - 1;\n  }\n\n  // Offset so -1 display id goes to 0 == NULL\n  inline HMONITOR toHmonitor(int32_t displayId) {\n    return reinterpret_cast<HMONITOR>(static_cast<intptr_t>(displayId + 1));\n  }\n\n}\n"
  },
  {
    "path": "include/native/wsi/native_sdl3.h",
    "content": "#include <windows.h>\n\n#include <SDL3/SDL.h>\n\nnamespace dxvk::wsi {\n\n  inline SDL_Window* fromHwnd(HWND hWindow) {\n    return reinterpret_cast<SDL_Window*>(hWindow);\n  }\n\n  inline HWND toHwnd(SDL_Window* pWindow) {\n    return reinterpret_cast<HWND>(pWindow);\n  }\n\n  // Offset so null HMONITORs go to -1\n  inline SDL_DisplayID fromHmonitor(HMONITOR hMonitor) {\n    return SDL_DisplayID(reinterpret_cast<uintptr_t>(hMonitor));\n  }\n\n  // Offset so -1 display id goes to 0 == NULL\n  inline HMONITOR toHmonitor(SDL_DisplayID display) {\n    return reinterpret_cast<HMONITOR>(uintptr_t(display));\n  }\n\n}\n"
  },
  {
    "path": "include/native/wsi/native_wsi.h",
    "content": "#pragma once\n\n#ifdef DXVK_WSI_WIN32\n#error You shouldnt be using this code path.\n#elif DXVK_WSI_SDL3\n#include \"wsi/native_sdl3.h\"\n#elif DXVK_WSI_SDL2\n#include \"wsi/native_sdl2.h\"\n#elif DXVK_WSI_GLFW\n#include \"wsi/native_glfw.h\"\n#else\n#error Unknown wsi!\n#endif"
  },
  {
    "path": "include/openvr/LICENSE",
    "content": "Copyright (c) 2015, Valve Corporation\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\nthis list of conditions and the following disclaimer in the documentation and/or\nother materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors\nmay be used to endorse or promote products derived from this software without\nspecific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "include/openvr/openvr.hpp",
    "content": "#pragma once\n\n// openvr.h\n//========= Copyright Valve Corporation ============//\n// Dynamically generated file. Do not modify this file directly.\n\n#ifndef _OPENVR_API\n#define _OPENVR_API\n\n#include <stdint.h>\n\n\n\n// vrtypes.h\n#ifndef _INCLUDE_VRTYPES_H\n#define _INCLUDE_VRTYPES_H\n\n// Forward declarations to avoid requiring vulkan.h\nstruct VkDevice_T;\nstruct VkPhysicalDevice_T;\nstruct VkInstance_T;\nstruct VkQueue_T;\n\n// Forward declarations to avoid requiring d3d12.h\nstruct ID3D12Resource;\nstruct ID3D12CommandQueue;\n\nnamespace vr\n{\n#pragma pack( push, 8 )\n\ntypedef void* glSharedTextureHandle_t;\ntypedef int32_t glInt_t;\ntypedef uint32_t glUInt_t;\n\n// right-handed system\n// +y is up\n// +x is to the right\n// -z is forward\n// Distance unit is  meters\nstruct HmdMatrix34_t\n{\n\tfloat m[3][4];\n};\n\nstruct HmdMatrix44_t\n{\n\tfloat m[4][4];\n};\n\nstruct HmdVector3_t\n{\n\tfloat v[3];\n};\n\nstruct HmdVector4_t\n{\n\tfloat v[4];\n};\n\nstruct HmdVector3d_t\n{\n\tdouble v[3];\n};\n\nstruct HmdVector2_t\n{\n\tfloat v[2];\n};\n\nstruct HmdQuaternion_t\n{\n\tdouble w, x, y, z;\n};\n\nstruct HmdQuaternionf_t\n{\n\tfloat w, x, y, z;\n};\n\nstruct HmdColor_t\n{\n\tfloat r, g, b, a;\n};\n\nstruct HmdQuad_t\n{\n\tHmdVector3_t vCorners[ 4 ];\n};\n\nstruct HmdRect2_t\n{\n\tHmdVector2_t vTopLeft;\n\tHmdVector2_t vBottomRight;\n};\n\n/** Used to return the post-distortion UVs for each color channel. \n* UVs range from 0 to 1 with 0,0 in the upper left corner of the \n* source render target. The 0,0 to 1,1 range covers a single eye. */\nstruct DistortionCoordinates_t\n{\n\tfloat rfRed[2];\n\tfloat rfGreen[2];\n\tfloat rfBlue[2];\n};\n\nenum EVREye\n{\n\tEye_Left = 0,\n\tEye_Right = 1\n};\n\nenum ETextureType\n{\n\tTextureType_DirectX = 0, // Handle is an ID3D11Texture\n\tTextureType_OpenGL = 1,  // Handle is an OpenGL texture name or an OpenGL render buffer name, depending on submit flags\n\tTextureType_Vulkan = 2, // Handle is a pointer to a VRVulkanTextureData_t structure\n\tTextureType_IOSurface = 3, // Handle is a macOS cross-process-sharable IOSurfaceRef\n\tTextureType_DirectX12 = 4, // Handle is a pointer to a D3D12TextureData_t structure\n\tTextureType_DXGISharedHandle = 5, // Handle is a HANDLE DXGI share handle, only supported for Overlay render targets. \n\t\t\t\t\t\t\t\t\t  // this texture is used directly by our renderer, so only perform atomic (copyresource or resolve) on it\n};\n\nenum EColorSpace\n{\n\tColorSpace_Auto = 0,\t// Assumes 'gamma' for 8-bit per component formats, otherwise 'linear'.  This mirrors the DXGI formats which have _SRGB variants.\n\tColorSpace_Gamma = 1,\t// Texture data can be displayed directly on the display without any conversion (a.k.a. display native format).\n\tColorSpace_Linear = 2,\t// Same as gamma but has been converted to a linear representation using DXGI's sRGB conversion algorithm.\n};\n\nstruct Texture_t\n{\n\tvoid* handle; // See ETextureType definition above\n\tETextureType eType;\n\tEColorSpace eColorSpace;\n};\n\n// Handle to a shared texture (HANDLE on Windows obtained using OpenSharedResource).\ntypedef uint64_t SharedTextureHandle_t;\n#define INVALID_SHARED_TEXTURE_HANDLE\t((vr::SharedTextureHandle_t)0)\n\nenum ETrackingResult\n{\n\tTrackingResult_Uninitialized\t\t\t= 1,\n\n\tTrackingResult_Calibrating_InProgress\t= 100,\n\tTrackingResult_Calibrating_OutOfRange\t= 101,\n\n\tTrackingResult_Running_OK\t\t\t\t= 200,\n\tTrackingResult_Running_OutOfRange\t\t= 201,\n};\n\ntypedef uint32_t DriverId_t;\nstatic const uint32_t k_nDriverNone = 0xFFFFFFFF;\n\nstatic const uint32_t k_unMaxDriverDebugResponseSize = 32768;\n\n/** Used to pass device IDs to API calls */\ntypedef uint32_t TrackedDeviceIndex_t;\nstatic const uint32_t k_unTrackedDeviceIndex_Hmd = 0;\nstatic const uint32_t k_unMaxTrackedDeviceCount = 64;\nstatic const uint32_t k_unTrackedDeviceIndexOther = 0xFFFFFFFE;\nstatic const uint32_t k_unTrackedDeviceIndexInvalid = 0xFFFFFFFF;\n\n/** Describes what kind of object is being tracked at a given ID */\nenum ETrackedDeviceClass\n{\n\tTrackedDeviceClass_Invalid = 0,\t\t\t\t// the ID was not valid.\n\tTrackedDeviceClass_HMD = 1,\t\t\t\t\t// Head-Mounted Displays\n\tTrackedDeviceClass_Controller = 2,\t\t\t// Tracked controllers\n\tTrackedDeviceClass_GenericTracker = 3,\t\t// Generic trackers, similar to controllers\n\tTrackedDeviceClass_TrackingReference = 4,\t// Camera and base stations that serve as tracking reference points\n\tTrackedDeviceClass_DisplayRedirect = 5,\t\t// Accessories that aren't necessarily tracked themselves, but may redirect video output from other tracked devices\n};\n\n\n/** Describes what specific role associated with a tracked device */\nenum ETrackedControllerRole\n{\n\tTrackedControllerRole_Invalid = 0,\t\t\t\t\t// Invalid value for controller type\n\tTrackedControllerRole_LeftHand = 1,\t\t\t\t\t// Tracked device associated with the left hand\n\tTrackedControllerRole_RightHand = 2,\t\t\t\t// Tracked device associated with the right hand\n\tTrackedControllerRole_OptOut = 3,\t\t\t\t\t// Tracked device is opting out of left/right hand selection\n\tTrackedControllerRole_Max = 4\n};\n\n\n/** describes a single pose for a tracked object */\nstruct TrackedDevicePose_t\n{\n\tHmdMatrix34_t mDeviceToAbsoluteTracking;\n\tHmdVector3_t vVelocity;\t\t\t\t// velocity in tracker space in m/s\n\tHmdVector3_t vAngularVelocity;\t\t// angular velocity in radians/s (?)\n\tETrackingResult eTrackingResult;\n\tbool bPoseIsValid;\n\n\t// This indicates that there is a device connected for this spot in the pose array.\n\t// It could go from true to false if the user unplugs the device.\n\tbool bDeviceIsConnected;\n};\n\n/** Identifies which style of tracking origin the application wants to use\n* for the poses it is requesting */\nenum ETrackingUniverseOrigin\n{\n\tTrackingUniverseSeated = 0,\t\t// Poses are provided relative to the seated zero pose\n\tTrackingUniverseStanding = 1,\t// Poses are provided relative to the safe bounds configured by the user\n\tTrackingUniverseRawAndUncalibrated = 2,\t// Poses are provided in the coordinate system defined by the driver.  It has Y up and is unified for devices of the same driver. You usually don't want this one.\n};\n\ntypedef uint64_t WebConsoleHandle_t;\n#define INVALID_WEB_CONSOLE_HANDLE\t((vr::WebConsoleHandle_t)0)\n\n// Refers to a single container of properties\ntypedef uint64_t PropertyContainerHandle_t;\ntypedef uint32_t PropertyTypeTag_t;\n\nstatic const PropertyContainerHandle_t k_ulInvalidPropertyContainer = 0;\nstatic const PropertyTypeTag_t k_unInvalidPropertyTag = 0;\n\ntypedef PropertyContainerHandle_t DriverHandle_t;\nstatic const PropertyContainerHandle_t k_ulInvalidDriverHandle = 0;\n\n// Use these tags to set/get common types as struct properties\nstatic const PropertyTypeTag_t k_unFloatPropertyTag = 1;\nstatic const PropertyTypeTag_t k_unInt32PropertyTag = 2;\nstatic const PropertyTypeTag_t k_unUint64PropertyTag = 3;\nstatic const PropertyTypeTag_t k_unBoolPropertyTag = 4;\nstatic const PropertyTypeTag_t k_unStringPropertyTag = 5;\n\nstatic const PropertyTypeTag_t k_unHmdMatrix34PropertyTag = 20;\nstatic const PropertyTypeTag_t k_unHmdMatrix44PropertyTag = 21;\nstatic const PropertyTypeTag_t k_unHmdVector3PropertyTag = 22;\nstatic const PropertyTypeTag_t k_unHmdVector4PropertyTag = 23;\n\nstatic const PropertyTypeTag_t k_unHiddenAreaPropertyTag = 30;\nstatic const PropertyTypeTag_t k_unPathHandleInfoTag = 31;\nstatic const PropertyTypeTag_t k_unActionPropertyTag = 32;\nstatic const PropertyTypeTag_t k_unInputValuePropertyTag = 33;\nstatic const PropertyTypeTag_t k_unWildcardPropertyTag = 34;\nstatic const PropertyTypeTag_t k_unHapticVibrationPropertyTag = 35;\nstatic const PropertyTypeTag_t k_unSkeletonPropertyTag = 36;\n\nstatic const PropertyTypeTag_t k_unOpenVRInternalReserved_Start = 1000;\nstatic const PropertyTypeTag_t k_unOpenVRInternalReserved_End = 10000;\n\n\n/** Each entry in this enum represents a property that can be retrieved about a\n* tracked device. Many fields are only valid for one ETrackedDeviceClass. */\nenum ETrackedDeviceProperty\n{\n\tProp_Invalid\t\t\t\t\t\t\t\t= 0,\n\n\t// general properties that apply to all device classes\n\tProp_TrackingSystemName_String\t\t\t\t= 1000,\n\tProp_ModelNumber_String\t\t\t\t\t\t= 1001,\n\tProp_SerialNumber_String\t\t\t\t\t= 1002,\n\tProp_RenderModelName_String\t\t\t\t\t= 1003,\n\tProp_WillDriftInYaw_Bool\t\t\t\t\t= 1004,\n\tProp_ManufacturerName_String\t\t\t\t= 1005,\n\tProp_TrackingFirmwareVersion_String\t\t\t= 1006,\n\tProp_HardwareRevision_String\t\t\t\t= 1007,\n\tProp_AllWirelessDongleDescriptions_String\t= 1008,\n\tProp_ConnectedWirelessDongle_String\t\t\t= 1009,\n\tProp_DeviceIsWireless_Bool\t\t\t\t\t= 1010,\n\tProp_DeviceIsCharging_Bool\t\t\t\t\t= 1011,\n\tProp_DeviceBatteryPercentage_Float\t\t\t= 1012, // 0 is empty, 1 is full\n\tProp_StatusDisplayTransform_Matrix34\t\t= 1013,\n\tProp_Firmware_UpdateAvailable_Bool\t\t\t= 1014,\n\tProp_Firmware_ManualUpdate_Bool\t\t\t\t= 1015,\n\tProp_Firmware_ManualUpdateURL_String\t\t= 1016,\n\tProp_HardwareRevision_Uint64\t\t\t\t= 1017,\n\tProp_FirmwareVersion_Uint64\t\t\t\t\t= 1018,\n\tProp_FPGAVersion_Uint64\t\t\t\t\t\t= 1019,\n\tProp_VRCVersion_Uint64\t\t\t\t\t\t= 1020,\n\tProp_RadioVersion_Uint64\t\t\t\t\t= 1021,\n\tProp_DongleVersion_Uint64\t\t\t\t\t= 1022,\n\tProp_BlockServerShutdown_Bool\t\t\t\t= 1023,\n\tProp_CanUnifyCoordinateSystemWithHmd_Bool\t= 1024,\n\tProp_ContainsProximitySensor_Bool\t\t\t= 1025,\n\tProp_DeviceProvidesBatteryStatus_Bool\t\t= 1026,\n\tProp_DeviceCanPowerOff_Bool\t\t\t\t\t= 1027,\n\tProp_Firmware_ProgrammingTarget_String\t\t= 1028,\n\tProp_DeviceClass_Int32\t\t\t\t\t\t= 1029,\n\tProp_HasCamera_Bool\t\t\t\t\t\t\t= 1030,\n\tProp_DriverVersion_String                   = 1031,\n\tProp_Firmware_ForceUpdateRequired_Bool      = 1032,\n\tProp_ViveSystemButtonFixRequired_Bool\t\t= 1033,\n\tProp_ParentDriver_Uint64\t\t\t\t\t= 1034,\n\tProp_ResourceRoot_String\t\t\t\t\t= 1035,\n\tProp_RegisteredDeviceType_String\t\t\t= 1036,\n\tProp_InputProfilePath_String\t\t\t\t= 1037, // input profile to use for this device in the input system. Will default to tracking system name if this isn't provided\n\tProp_NeverTracked_Bool\t\t\t\t\t\t= 1038, // Used for devices that will never have a valid pose by design\n\tProp_NumCameras_Int32\t\t\t\t\t\t= 1039,\n\tProp_CameraFrameLayout_Int32\t\t\t\t= 1040, // EVRTrackedCameraFrameLayout value\n\n\t// Properties that are unique to TrackedDeviceClass_HMD\n\tProp_ReportsTimeSinceVSync_Bool\t\t\t\t= 2000,\n\tProp_SecondsFromVsyncToPhotons_Float\t\t= 2001,\n\tProp_DisplayFrequency_Float\t\t\t\t\t= 2002,\n\tProp_UserIpdMeters_Float\t\t\t\t\t= 2003,\n\tProp_CurrentUniverseId_Uint64\t\t\t\t= 2004, \n\tProp_PreviousUniverseId_Uint64\t\t\t\t= 2005, \n\tProp_DisplayFirmwareVersion_Uint64\t\t\t= 2006,\n\tProp_IsOnDesktop_Bool\t\t\t\t\t\t= 2007,\n\tProp_DisplayMCType_Int32\t\t\t\t\t= 2008,\n\tProp_DisplayMCOffset_Float\t\t\t\t\t= 2009,\n\tProp_DisplayMCScale_Float\t\t\t\t\t= 2010,\n\tProp_EdidVendorID_Int32\t\t\t\t\t\t= 2011,\n\tProp_DisplayMCImageLeft_String              = 2012,\n\tProp_DisplayMCImageRight_String             = 2013,\n\tProp_DisplayGCBlackClamp_Float\t\t\t\t= 2014,\n\tProp_EdidProductID_Int32\t\t\t\t\t= 2015,\n\tProp_CameraToHeadTransform_Matrix34\t\t\t= 2016,\n\tProp_DisplayGCType_Int32\t\t\t\t\t= 2017,\n\tProp_DisplayGCOffset_Float\t\t\t\t\t= 2018,\n\tProp_DisplayGCScale_Float\t\t\t\t\t= 2019,\n\tProp_DisplayGCPrescale_Float\t\t\t\t= 2020,\n\tProp_DisplayGCImage_String\t\t\t\t\t= 2021,\n\tProp_LensCenterLeftU_Float\t\t\t\t\t= 2022,\n\tProp_LensCenterLeftV_Float\t\t\t\t\t= 2023,\n\tProp_LensCenterRightU_Float\t\t\t\t\t= 2024,\n\tProp_LensCenterRightV_Float\t\t\t\t\t= 2025,\n\tProp_UserHeadToEyeDepthMeters_Float\t\t\t= 2026,\n\tProp_CameraFirmwareVersion_Uint64\t\t\t= 2027,\n\tProp_CameraFirmwareDescription_String\t\t= 2028,\n\tProp_DisplayFPGAVersion_Uint64\t\t\t\t= 2029,\n\tProp_DisplayBootloaderVersion_Uint64\t\t= 2030,\n\tProp_DisplayHardwareVersion_Uint64\t\t\t= 2031,\n\tProp_AudioFirmwareVersion_Uint64\t\t\t= 2032,\n\tProp_CameraCompatibilityMode_Int32\t\t\t= 2033,\n\tProp_ScreenshotHorizontalFieldOfViewDegrees_Float = 2034,\n\tProp_ScreenshotVerticalFieldOfViewDegrees_Float = 2035,\n\tProp_DisplaySuppressed_Bool\t\t\t\t\t= 2036,\n\tProp_DisplayAllowNightMode_Bool\t\t\t\t= 2037,\n\tProp_DisplayMCImageWidth_Int32\t\t\t\t= 2038,\n\tProp_DisplayMCImageHeight_Int32\t\t\t\t= 2039,\n\tProp_DisplayMCImageNumChannels_Int32\t\t= 2040,\n\tProp_DisplayMCImageData_Binary\t\t\t\t= 2041,\n\tProp_SecondsFromPhotonsToVblank_Float\t\t= 2042,\n\tProp_DriverDirectModeSendsVsyncEvents_Bool\t= 2043,\n\tProp_DisplayDebugMode_Bool\t\t\t\t\t= 2044,\n\tProp_GraphicsAdapterLuid_Uint64\t\t\t\t= 2045,\n\tProp_DriverProvidedChaperonePath_String\t\t= 2048,\n\tProp_ExpectedTrackingReferenceCount_Int32\t= 2049, // expected number of sensors or basestations to reserve UI space for\n\tProp_ExpectedControllerCount_Int32\t\t\t= 2050, // expected number of tracked controllers to reserve UI space for\n\tProp_NamedIconPathControllerLeftDeviceOff_String\t= 2051, // placeholder icon for \"left\" controller if not yet detected/loaded\n\tProp_NamedIconPathControllerRightDeviceOff_String\t= 2052, // placeholder icon for \"right\" controller if not yet detected/loaded\n\tProp_NamedIconPathTrackingReferenceDeviceOff_String\t= 2053, // placeholder icon for sensor/base if not yet detected/loaded\n\tProp_DoNotApplyPrediction_Bool\t\t\t\t= 2054,\n\tProp_CameraToHeadTransforms_Matrix34_Array\t= 2055,\n\tProp_DistortionMeshResolution_Int32\t\t\t= 2056, // custom resolution of compositor calls to IVRSystem::ComputeDistortion\n\tProp_DriverIsDrawingControllers_Bool\t\t= 2057,\n\tProp_DriverRequestsApplicationPause_Bool\t= 2058,\n\tProp_DriverRequestsReducedRendering_Bool\t= 2059,\n\tProp_MinimumIpdStepMeters_Float\t\t\t\t= 2060,\n\tProp_AudioBridgeFirmwareVersion_Uint64\t\t= 2061,\n\tProp_ImageBridgeFirmwareVersion_Uint64\t\t= 2062,\n\tProp_ImuToHeadTransform_Matrix34\t\t\t= 2063,\n\tProp_ImuFactoryGyroBias_Vector3\t\t\t\t= 2064,\n\tProp_ImuFactoryGyroScale_Vector3\t\t\t= 2065,\n\tProp_ImuFactoryAccelerometerBias_Vector3\t= 2066,\n\tProp_ImuFactoryAccelerometerScale_Vector3\t= 2067,\n\n\t// Properties that are unique to TrackedDeviceClass_Controller\n\tProp_AttachedDeviceId_String\t\t\t\t= 3000,\n\tProp_SupportedButtons_Uint64\t\t\t\t= 3001,\n\tProp_Axis0Type_Int32\t\t\t\t\t\t= 3002, // Return value is of type EVRControllerAxisType\n\tProp_Axis1Type_Int32\t\t\t\t\t\t= 3003, // Return value is of type EVRControllerAxisType\n\tProp_Axis2Type_Int32\t\t\t\t\t\t= 3004, // Return value is of type EVRControllerAxisType\n\tProp_Axis3Type_Int32\t\t\t\t\t\t= 3005, // Return value is of type EVRControllerAxisType\n\tProp_Axis4Type_Int32\t\t\t\t\t\t= 3006, // Return value is of type EVRControllerAxisType\n\tProp_ControllerRoleHint_Int32\t\t\t\t= 3007, // Return value is of type ETrackedControllerRole\n\n\t// Properties that are unique to TrackedDeviceClass_TrackingReference\n\tProp_FieldOfViewLeftDegrees_Float\t\t\t= 4000,\n\tProp_FieldOfViewRightDegrees_Float\t\t\t= 4001,\n\tProp_FieldOfViewTopDegrees_Float\t\t\t= 4002,\n\tProp_FieldOfViewBottomDegrees_Float\t\t\t= 4003,\n\tProp_TrackingRangeMinimumMeters_Float\t\t= 4004,\n\tProp_TrackingRangeMaximumMeters_Float\t\t= 4005,\n\tProp_ModeLabel_String\t\t\t\t\t\t= 4006,\n\n\t// Properties that are used for user interface like icons names\n\tProp_IconPathName_String\t\t\t\t\t\t= 5000, // DEPRECATED. Value not referenced. Now expected to be part of icon path properties.\n\tProp_NamedIconPathDeviceOff_String\t\t\t\t= 5001, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others\n\tProp_NamedIconPathDeviceSearching_String\t\t= 5002, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others\n\tProp_NamedIconPathDeviceSearchingAlert_String\t= 5003, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others\n\tProp_NamedIconPathDeviceReady_String\t\t\t= 5004, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others\n\tProp_NamedIconPathDeviceReadyAlert_String\t\t= 5005, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others\n\tProp_NamedIconPathDeviceNotReady_String\t\t\t= 5006, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others\n\tProp_NamedIconPathDeviceStandby_String\t\t\t= 5007, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others\n\tProp_NamedIconPathDeviceAlertLow_String\t\t\t= 5008, // {driver}/icons/icon_filename - PNG for static icon, or GIF for animation, 50x32 for headsets and 32x32 for others\n\n\t// Properties that are used by helpers, but are opaque to applications\n\tProp_DisplayHiddenArea_Binary_Start\t\t\t\t= 5100,\n\tProp_DisplayHiddenArea_Binary_End\t\t\t\t= 5150,\n\tProp_ParentContainer\t\t\t\t\t\t\t= 5151,\n\n\t// Properties that are unique to drivers\n\tProp_UserConfigPath_String\t\t\t\t\t= 6000,\n\tProp_InstallPath_String\t\t\t\t\t\t= 6001,\n\tProp_HasDisplayComponent_Bool\t\t\t\t= 6002,\n\tProp_HasControllerComponent_Bool\t\t\t= 6003,\n\tProp_HasCameraComponent_Bool\t\t\t\t= 6004,\n\tProp_HasDriverDirectModeComponent_Bool\t\t= 6005,\n\tProp_HasVirtualDisplayComponent_Bool\t\t= 6006,\n\n\t// Properties that are set internally based on other information provided by drivers\n\tProp_ControllerType_String\t\t\t\t\t= 7000,\n\tProp_LegacyInputProfile_String\t\t\t\t= 7001,\n\n\t// Vendors are free to expose private debug data in this reserved region\n\tProp_VendorSpecific_Reserved_Start\t\t\t= 10000,\n\tProp_VendorSpecific_Reserved_End\t\t\t= 10999,\n\t\n\tProp_TrackedDeviceProperty_Max\t\t\t\t= 1000000,\n};\n\n/** No string property will ever be longer than this length */\nstatic const uint32_t k_unMaxPropertyStringSize = 32 * 1024;\n\n/** Used to return errors that occur when reading properties. */\nenum ETrackedPropertyError\n{\n\tTrackedProp_Success\t\t\t\t\t\t= 0,\n\tTrackedProp_WrongDataType\t\t\t\t= 1,\n\tTrackedProp_WrongDeviceClass\t\t\t= 2,\n\tTrackedProp_BufferTooSmall\t\t\t\t= 3,\n\tTrackedProp_UnknownProperty\t\t\t\t= 4, // Driver has not set the property (and may not ever).\n\tTrackedProp_InvalidDevice\t\t\t\t= 5,\n\tTrackedProp_CouldNotContactServer\t\t= 6,\n\tTrackedProp_ValueNotProvidedByDevice\t= 7,\n\tTrackedProp_StringExceedsMaximumLength\t= 8,\n\tTrackedProp_NotYetAvailable\t\t\t\t= 9, // The property value isn't known yet, but is expected soon. Call again later.\n\tTrackedProp_PermissionDenied\t\t\t= 10,\n\tTrackedProp_InvalidOperation\t\t\t= 11,\n\tTrackedProp_CannotWriteToWildcards\t\t= 12,\n};\n\n/** Allows the application to control what part of the provided texture will be used in the\n* frame buffer. */\nstruct VRTextureBounds_t\n{\n\tfloat uMin, vMin;\n\tfloat uMax, vMax;\n};\n\n/** Allows specifying pose used to render provided scene texture (if different from value returned by WaitGetPoses). */\nstruct VRTextureWithPose_t : public Texture_t\n{\n\tHmdMatrix34_t mDeviceToAbsoluteTracking; // Actual pose used to render scene textures.\n};\n\nstruct VRTextureDepthInfo_t\n{\n\tvoid* handle; // See ETextureType definition above\n\tHmdMatrix44_t mProjection;\n\tHmdVector2_t vRange; // 0..1\n};\n\nstruct VRTextureWithDepth_t : public Texture_t\n{\n\tVRTextureDepthInfo_t depth;\n};\n\nstruct VRTextureWithPoseAndDepth_t : public VRTextureWithPose_t\n{\n\tVRTextureDepthInfo_t depth;\n};\n\n/** Allows the application to control how scene textures are used by the compositor when calling Submit. */\nenum EVRSubmitFlags\n{\n\t// Simple render path. App submits rendered left and right eye images with no lens distortion correction applied.\n\tSubmit_Default = 0x00,\n\n\t// App submits final left and right eye images with lens distortion already applied (lens distortion makes the images appear\n\t// barrel distorted with chromatic aberration correction applied). The app would have used the data returned by\n\t// vr::IVRSystem::ComputeDistortion() to apply the correct distortion to the rendered images before calling Submit().\n\tSubmit_LensDistortionAlreadyApplied = 0x01,\n\n\t// If the texture pointer passed in is actually a renderbuffer (e.g. for MSAA in OpenGL) then set this flag.\n\tSubmit_GlRenderBuffer = 0x02,\n\n\t// Do not use\n\tSubmit_Reserved = 0x04,\n\n\t// Set to indicate that pTexture is a pointer to a VRTextureWithPose_t.\n\t// This flag can be combined with Submit_TextureWithDepth to pass a VRTextureWithPoseAndDepth_t.\n\tSubmit_TextureWithPose = 0x08,\n\n\t// Set to indicate that pTexture is a pointer to a VRTextureWithDepth_t.\n\t// This flag can be combined with Submit_TextureWithPose to pass a VRTextureWithPoseAndDepth_t.\n\tSubmit_TextureWithDepth = 0x10,\n};\n\n/** Data required for passing Vulkan textures to IVRCompositor::Submit.\n* Be sure to call OpenVR_Shutdown before destroying these resources. */\nstruct VRVulkanTextureData_t\n{\n\tuint64_t m_nImage; // VkImage\n\tVkDevice_T *m_pDevice;\n\tVkPhysicalDevice_T *m_pPhysicalDevice;\n\tVkInstance_T *m_pInstance;\n\tVkQueue_T *m_pQueue;\n\tuint32_t m_nQueueFamilyIndex;\n\tuint32_t m_nWidth, m_nHeight, m_nFormat, m_nSampleCount;\n};\n\n/** Data required for passing D3D12 textures to IVRCompositor::Submit.\n* Be sure to call OpenVR_Shutdown before destroying these resources. */\nstruct D3D12TextureData_t\n{\n\tID3D12Resource *m_pResource;\n\tID3D12CommandQueue *m_pCommandQueue;\n\tuint32_t m_nNodeMask;\n};\n\n/** Status of the overall system or tracked objects */\nenum EVRState\n{\n\tVRState_Undefined = -1,\n\tVRState_Off = 0,\n\tVRState_Searching = 1,\n\tVRState_Searching_Alert = 2,\n\tVRState_Ready = 3,\n\tVRState_Ready_Alert = 4,\n\tVRState_NotReady = 5,\n\tVRState_Standby = 6,\n\tVRState_Ready_Alert_Low = 7,\n};\n\n/** The types of events that could be posted (and what the parameters mean for each event type) */\nenum EVREventType\n{\n\tVREvent_None = 0,\n\n\tVREvent_TrackedDeviceActivated\t\t= 100,\n\tVREvent_TrackedDeviceDeactivated\t= 101,\n\tVREvent_TrackedDeviceUpdated\t\t= 102,\n\tVREvent_TrackedDeviceUserInteractionStarted\t= 103,\n\tVREvent_TrackedDeviceUserInteractionEnded\t= 104,\n\tVREvent_IpdChanged\t\t\t\t\t= 105,\n\tVREvent_EnterStandbyMode\t\t\t= 106,\n\tVREvent_LeaveStandbyMode\t\t\t= 107,\n\tVREvent_TrackedDeviceRoleChanged\t= 108,\n\tVREvent_WatchdogWakeUpRequested\t\t= 109,\n\tVREvent_LensDistortionChanged\t\t= 110,\n\tVREvent_PropertyChanged\t\t\t\t= 111,\n\tVREvent_WirelessDisconnect\t\t\t= 112,\n\tVREvent_WirelessReconnect\t\t\t= 113,\n\n\tVREvent_ButtonPress\t\t\t\t\t= 200, // data is controller\n\tVREvent_ButtonUnpress\t\t\t\t= 201, // data is controller\n\tVREvent_ButtonTouch\t\t\t\t\t= 202, // data is controller\n\tVREvent_ButtonUntouch\t\t\t\t= 203, // data is controller\n\n\tVREvent_DualAnalog_Press\t\t\t= 250, // data is dualAnalog\n\tVREvent_DualAnalog_Unpress\t\t\t= 251, // data is dualAnalog\n\tVREvent_DualAnalog_Touch\t\t\t= 252, // data is dualAnalog\n\tVREvent_DualAnalog_Untouch\t\t\t= 253, // data is dualAnalog\n\tVREvent_DualAnalog_Move\t\t\t\t= 254, // data is dualAnalog\n\tVREvent_DualAnalog_ModeSwitch1\t\t= 255, // data is dualAnalog\n\tVREvent_DualAnalog_ModeSwitch2\t\t= 256, // data is dualAnalog\n\tVREvent_DualAnalog_Cancel\t\t\t= 257, // data is dualAnalog\n\n\tVREvent_MouseMove\t\t\t\t\t= 300, // data is mouse\n\tVREvent_MouseButtonDown\t\t\t\t= 301, // data is mouse\n\tVREvent_MouseButtonUp\t\t\t\t= 302, // data is mouse\n\tVREvent_FocusEnter\t\t\t\t\t= 303, // data is overlay\n\tVREvent_FocusLeave\t\t\t\t\t= 304, // data is overlay\n\tVREvent_Scroll\t\t\t\t\t\t= 305, // data is mouse\n\tVREvent_TouchPadMove\t\t\t\t= 306, // data is mouse\n\tVREvent_OverlayFocusChanged\t\t\t= 307, // data is overlay, global event\n\n\tVREvent_InputFocusCaptured\t\t\t= 400, // data is process DEPRECATED\n\tVREvent_InputFocusReleased\t\t\t= 401, // data is process DEPRECATED\n\tVREvent_SceneFocusLost\t\t\t\t= 402, // data is process\n\tVREvent_SceneFocusGained\t\t\t= 403, // data is process\n\tVREvent_SceneApplicationChanged\t\t= 404, // data is process - The App actually drawing the scene changed (usually to or from the compositor)\n\tVREvent_SceneFocusChanged\t\t\t= 405, // data is process - New app got access to draw the scene\n\tVREvent_InputFocusChanged\t\t\t= 406, // data is process\n\tVREvent_SceneApplicationSecondaryRenderingStarted = 407, // data is process\n\tVREvent_SceneApplicationUsingWrongGraphicsAdapter = 408, // data is process\n\tVREvent_ActionBindingReloaded\t\t = 409, // data is process - The App that action binds reloaded for\n\n\tVREvent_HideRenderModels\t\t\t= 410, // Sent to the scene application to request hiding render models temporarily\n\tVREvent_ShowRenderModels\t\t\t= 411, // Sent to the scene application to request restoring render model visibility\n\n\tVREvent_ConsoleOpened               = 420,\n\tVREvent_ConsoleClosed               = 421,\n\n\tVREvent_OverlayShown\t\t\t\t= 500,\n\tVREvent_OverlayHidden\t\t\t\t= 501,\n\tVREvent_DashboardActivated\t\t\t= 502,\n\tVREvent_DashboardDeactivated\t\t= 503,\n\tVREvent_DashboardThumbSelected\t\t= 504, // Sent to the overlay manager - data is overlay\n\tVREvent_DashboardRequested\t\t\t= 505, // Sent to the overlay manager - data is overlay\n\tVREvent_ResetDashboard\t\t\t\t= 506, // Send to the overlay manager\n\tVREvent_RenderToast\t\t\t\t\t= 507, // Send to the dashboard to render a toast - data is the notification ID\n\tVREvent_ImageLoaded\t\t\t\t\t= 508, // Sent to overlays when a SetOverlayRaw or SetOverlayFromFile call finishes loading\n\tVREvent_ShowKeyboard\t\t\t\t= 509, // Sent to keyboard renderer in the dashboard to invoke it\n\tVREvent_HideKeyboard\t\t\t\t= 510, // Sent to keyboard renderer in the dashboard to hide it\n\tVREvent_OverlayGamepadFocusGained\t= 511, // Sent to an overlay when IVROverlay::SetFocusOverlay is called on it\n\tVREvent_OverlayGamepadFocusLost\t\t= 512, // Send to an overlay when it previously had focus and IVROverlay::SetFocusOverlay is called on something else\n\tVREvent_OverlaySharedTextureChanged = 513,\n\t//VREvent_DashboardGuideButtonDown\t= 514, // These are no longer sent\n\t//VREvent_DashboardGuideButtonUp\t\t= 515,\n\tVREvent_ScreenshotTriggered\t\t\t= 516, // Screenshot button combo was pressed, Dashboard should request a screenshot\n\tVREvent_ImageFailed\t\t\t\t\t= 517, // Sent to overlays when a SetOverlayRaw or SetOverlayfromFail fails to load\n\tVREvent_DashboardOverlayCreated\t\t= 518,\n\tVREvent_SwitchGamepadFocus\t\t\t= 519,\n\n\t// Screenshot API\n\tVREvent_RequestScreenshot\t\t\t\t= 520, // Sent by vrclient application to compositor to take a screenshot\n\tVREvent_ScreenshotTaken\t\t\t\t\t= 521, // Sent by compositor to the application that the screenshot has been taken\n\tVREvent_ScreenshotFailed\t\t\t\t= 522, // Sent by compositor to the application that the screenshot failed to be taken\n\tVREvent_SubmitScreenshotToDashboard\t\t= 523, // Sent by compositor to the dashboard that a completed screenshot was submitted\n\tVREvent_ScreenshotProgressToDashboard\t= 524, // Sent by compositor to the dashboard that a completed screenshot was submitted\n\n\tVREvent_PrimaryDashboardDeviceChanged\t= 525,\n\tVREvent_RoomViewShown\t\t\t\t\t= 526, // Sent by compositor whenever room-view is enabled\n\tVREvent_RoomViewHidden\t\t\t\t\t= 527, // Sent by compositor whenever room-view is disabled\n\n\tVREvent_Notification_Shown\t\t\t\t= 600,\n\tVREvent_Notification_Hidden\t\t\t\t= 601,\n\tVREvent_Notification_BeginInteraction\t= 602,\n\tVREvent_Notification_Destroyed\t\t\t= 603,\n\n\tVREvent_Quit\t\t\t\t\t\t\t= 700, // data is process\n\tVREvent_ProcessQuit\t\t\t\t\t\t= 701, // data is process\n\tVREvent_QuitAborted_UserPrompt\t\t\t= 702, // data is process\n\tVREvent_QuitAcknowledged\t\t\t\t= 703, // data is process\n\tVREvent_DriverRequestedQuit\t\t\t\t= 704, // The driver has requested that SteamVR shut down\n\n\tVREvent_ChaperoneDataHasChanged\t\t\t= 800,\n\tVREvent_ChaperoneUniverseHasChanged\t\t= 801,\n\tVREvent_ChaperoneTempDataHasChanged\t\t= 802,\n\tVREvent_ChaperoneSettingsHaveChanged\t= 803,\n\tVREvent_SeatedZeroPoseReset\t\t\t\t= 804,\n\n\tVREvent_AudioSettingsHaveChanged\t\t= 820,\n\n\tVREvent_BackgroundSettingHasChanged\t\t   = 850,\n\tVREvent_CameraSettingsHaveChanged\t\t   = 851,\n\tVREvent_ReprojectionSettingHasChanged\t   = 852,\n\tVREvent_ModelSkinSettingsHaveChanged\t   = 853,\n\tVREvent_EnvironmentSettingsHaveChanged\t   = 854,\n\tVREvent_PowerSettingsHaveChanged\t\t   = 855,\n\tVREvent_EnableHomeAppSettingsHaveChanged   = 856,\n\tVREvent_SteamVRSectionSettingChanged       = 857,\n\tVREvent_LighthouseSectionSettingChanged    = 858,\n\tVREvent_NullSectionSettingChanged          = 859,\n\tVREvent_UserInterfaceSectionSettingChanged = 860,\n\tVREvent_NotificationsSectionSettingChanged = 861,\n\tVREvent_KeyboardSectionSettingChanged      = 862,\n\tVREvent_PerfSectionSettingChanged          = 863,\n\tVREvent_DashboardSectionSettingChanged     = 864,\n\tVREvent_WebInterfaceSectionSettingChanged  = 865,\n\n\tVREvent_StatusUpdate\t\t\t\t\t= 900,\n\n\tVREvent_WebInterface_InstallDriverCompleted = 950,\n\n\tVREvent_MCImageUpdated\t\t\t\t\t= 1000,\n\n\tVREvent_FirmwareUpdateStarted\t\t\t= 1100,\n\tVREvent_FirmwareUpdateFinished\t\t\t= 1101,\n\n\tVREvent_KeyboardClosed\t\t\t\t\t= 1200,\n\tVREvent_KeyboardCharInput\t\t\t\t= 1201,\n\tVREvent_KeyboardDone\t\t\t\t\t= 1202, // Sent when DONE button clicked on keyboard\n\n\tVREvent_ApplicationTransitionStarted\t\t= 1300,\n\tVREvent_ApplicationTransitionAborted\t\t= 1301,\n\tVREvent_ApplicationTransitionNewAppStarted\t= 1302,\n\tVREvent_ApplicationListUpdated\t\t\t\t= 1303,\n\tVREvent_ApplicationMimeTypeLoad\t\t\t\t= 1304,\n\tVREvent_ApplicationTransitionNewAppLaunchComplete = 1305,\n\tVREvent_ProcessConnected\t\t\t\t\t= 1306,\n\tVREvent_ProcessDisconnected\t\t\t\t\t= 1307,\n\n\tVREvent_Compositor_MirrorWindowShown\t\t= 1400,\n\tVREvent_Compositor_MirrorWindowHidden\t\t= 1401,\n\tVREvent_Compositor_ChaperoneBoundsShown\t\t= 1410,\n\tVREvent_Compositor_ChaperoneBoundsHidden\t= 1411,\n\n\tVREvent_TrackedCamera_StartVideoStream  = 1500,\n\tVREvent_TrackedCamera_StopVideoStream   = 1501,\n\tVREvent_TrackedCamera_PauseVideoStream  = 1502,\n\tVREvent_TrackedCamera_ResumeVideoStream = 1503,\n\tVREvent_TrackedCamera_EditingSurface    = 1550,\n\n\tVREvent_PerformanceTest_EnableCapture\t= 1600,\n\tVREvent_PerformanceTest_DisableCapture\t= 1601,\n\tVREvent_PerformanceTest_FidelityLevel\t= 1602,\n\n\tVREvent_MessageOverlay_Closed\t\t\t= 1650,\n\tVREvent_MessageOverlayCloseRequested\t= 1651,\n\t\n\tVREvent_Input_HapticVibration\t\t\t= 1700, // data is hapticVibration\n\tVREvent_Input_BindingLoadFailed\t\t\t= 1701, // data is process\n\tVREvent_Input_BindingLoadSuccessful\t\t= 1702, // data is process\n\n\t// Vendors are free to expose private events in this reserved region\n\tVREvent_VendorSpecific_Reserved_Start\t= 10000,\n\tVREvent_VendorSpecific_Reserved_End\t\t= 19999,\n};\n\n\n/** Level of Hmd activity */\n// UserInteraction_Timeout means the device is in the process of timing out.\n// InUse = ( k_EDeviceActivityLevel_UserInteraction || k_EDeviceActivityLevel_UserInteraction_Timeout )\n// VREvent_TrackedDeviceUserInteractionStarted fires when the devices transitions from Standby -> UserInteraction or Idle -> UserInteraction.\n// VREvent_TrackedDeviceUserInteractionEnded fires when the devices transitions from UserInteraction_Timeout -> Idle\nenum EDeviceActivityLevel\n{\t\n\tk_EDeviceActivityLevel_Unknown = -1,\t\t\t\t\t\t\t\t\t\n\tk_EDeviceActivityLevel_Idle = 0,\t\t\t\t\t\t// No activity for the last 10 seconds\n\tk_EDeviceActivityLevel_UserInteraction = 1,\t\t\t\t// Activity (movement or prox sensor) is happening now\t\n\tk_EDeviceActivityLevel_UserInteraction_Timeout = 2,\t\t// No activity for the last 0.5 seconds\n\tk_EDeviceActivityLevel_Standby = 3,\t\t\t\t\t\t// Idle for at least 5 seconds (configurable in Settings -> Power Management)\n};\n\n\n/** VR controller button and axis IDs */\nenum EVRButtonId\n{\n\tk_EButton_System\t\t\t= 0,\n\tk_EButton_ApplicationMenu\t= 1,\n\tk_EButton_Grip\t\t\t\t= 2,\n\tk_EButton_DPad_Left\t\t\t= 3,\n\tk_EButton_DPad_Up\t\t\t= 4,\n\tk_EButton_DPad_Right\t\t= 5,\n\tk_EButton_DPad_Down\t\t\t= 6,\n\tk_EButton_A\t\t\t\t\t= 7,\n\t\n\tk_EButton_ProximitySensor   = 31,\n\n\tk_EButton_Axis0\t\t\t\t= 32,\n\tk_EButton_Axis1\t\t\t\t= 33,\n\tk_EButton_Axis2\t\t\t\t= 34,\n\tk_EButton_Axis3\t\t\t\t= 35,\n\tk_EButton_Axis4\t\t\t\t= 36,\n\n\t// aliases for well known controllers\n\tk_EButton_SteamVR_Touchpad\t= k_EButton_Axis0,\n\tk_EButton_SteamVR_Trigger\t= k_EButton_Axis1,\n\n\tk_EButton_Dashboard_Back\t= k_EButton_Grip,\n\n\tk_EButton_Max\t\t\t\t= 64\n};\n\ninline uint64_t ButtonMaskFromId( EVRButtonId id ) { return 1ull << id; }\n\n/** used for controller button events */\nstruct VREvent_Controller_t\n{\n\tuint32_t button; // EVRButtonId enum\n};\n\n\n/** used for simulated mouse events in overlay space */\nenum EVRMouseButton\n{\n\tVRMouseButton_Left\t\t\t\t\t= 0x0001,\n\tVRMouseButton_Right\t\t\t\t\t= 0x0002,\n\tVRMouseButton_Middle\t\t\t\t= 0x0004,\n};\n\n\n/** used for simulated mouse events in overlay space */\nstruct VREvent_Mouse_t\n{\n\tfloat x, y; // co-ords are in GL space, bottom left of the texture is 0,0\n\tuint32_t button; // EVRMouseButton enum\n};\n\n/** used for simulated mouse wheel scroll in overlay space */\nstruct VREvent_Scroll_t\n{\n\tfloat xdelta, ydelta; // movement in fraction of the pad traversed since last delta, 1.0 for a full swipe\n\tuint32_t repeatCount;\n};\n\n/** when in mouse input mode you can receive data from the touchpad, these events are only sent if the users finger\n   is on the touchpad (or just released from it). These events are sent to overlays with the VROverlayFlags_SendVRTouchpadEvents\n   flag set.\n**/\nstruct VREvent_TouchPadMove_t\n{\n\t// true if the users finger is detected on the touch pad\n\tbool bFingerDown;\n\n\t// How long the finger has been down in seconds\n\tfloat flSecondsFingerDown;\n\n\t// These values indicate the starting finger position (so you can do some basic swipe stuff)\n\tfloat fValueXFirst;\n\tfloat fValueYFirst;\n\n\t// This is the raw sampled coordinate without deadzoning\n\tfloat fValueXRaw;\n\tfloat fValueYRaw;\n};\n\n/** notification related events. Details will still change at this point */\nstruct VREvent_Notification_t\n{\n\tuint64_t ulUserValue;\n\tuint32_t notificationId;\n};\n\n/** Used for events about processes */\nstruct VREvent_Process_t\n{\n\tuint32_t pid;\n\tuint32_t oldPid;\n\tbool bForced;\n};\n\n\n/** Used for a few events about overlays */\nstruct VREvent_Overlay_t\n{\n\tuint64_t overlayHandle;\n\tuint64_t devicePath;\n};\n\n\n/** Used for a few events about overlays */\nstruct VREvent_Status_t\n{\n\tuint32_t statusState; // EVRState enum\n};\n\n/** Used for keyboard events **/\nstruct VREvent_Keyboard_t\n{\n\tchar cNewInput[8];\t// Up to 11 bytes of new input\n\tuint64_t uUserValue;\t// Possible flags about the new input\n};\n\nstruct VREvent_Ipd_t\n{\n\tfloat ipdMeters;\n};\n\nstruct VREvent_Chaperone_t\n{\n\tuint64_t m_nPreviousUniverse;\n\tuint64_t m_nCurrentUniverse;\n};\n\n/** Not actually used for any events */\nstruct VREvent_Reserved_t\n{\n\tuint64_t reserved0;\n\tuint64_t reserved1;\n\tuint64_t reserved2;\n\tuint64_t reserved3;\n};\n\nstruct VREvent_PerformanceTest_t\n{\n\tuint32_t m_nFidelityLevel;\n};\n\nstruct VREvent_SeatedZeroPoseReset_t\n{\n\tbool bResetBySystemMenu;\n};\n\nstruct VREvent_Screenshot_t\n{\n\tuint32_t handle;\n\tuint32_t type;\n};\n\nstruct VREvent_ScreenshotProgress_t\n{\n\tfloat progress;\n};\n\nstruct VREvent_ApplicationLaunch_t\n{\n\tuint32_t pid;\n\tuint32_t unArgsHandle;\n};\n\nstruct VREvent_EditingCameraSurface_t\n{\n\tuint64_t overlayHandle;\n\tuint32_t nVisualMode;\n};\n\nstruct VREvent_MessageOverlay_t\n{\n\tuint32_t unVRMessageOverlayResponse; // vr::VRMessageOverlayResponse enum\n};\n\nstruct VREvent_Property_t\n{\n\tPropertyContainerHandle_t container;\n\tETrackedDeviceProperty prop;\n};\n\nenum EDualAnalogWhich\n{\n\tk_EDualAnalog_Left = 0,\n\tk_EDualAnalog_Right = 1,\n};\n\nstruct VREvent_DualAnalog_t\n{\n\tfloat x, y; // coordinates are -1..1 analog values\n\tfloat transformedX, transformedY; // transformed by the center and radius numbers provided by the overlay\n\tEDualAnalogWhich which;\n};\n\nstruct VREvent_HapticVibration_t\n{\n\tuint64_t containerHandle; // property container handle of the device with the haptic component\n\tuint64_t componentHandle; // Which haptic component needs to vibrate\n\tfloat fDurationSeconds;\n\tfloat fFrequency;\n\tfloat fAmplitude;\n};\n\nstruct VREvent_WebConsole_t\n{\n\tWebConsoleHandle_t webConsoleHandle;\n};\n\nstruct VREvent_InputBindingLoad_t\n{\n\tvr::PropertyContainerHandle_t ulAppContainer;\n\tuint64_t pathMessage;\n\tuint64_t pathUrl;\n};\n\n\n/** NOTE!!! If you change this you MUST manually update openvr_interop.cs.py */\ntypedef union\n{\n\tVREvent_Reserved_t reserved;\n\tVREvent_Controller_t controller;\n\tVREvent_Mouse_t mouse;\n\tVREvent_Scroll_t scroll;\n\tVREvent_Process_t process;\n\tVREvent_Notification_t notification;\n\tVREvent_Overlay_t overlay;\n\tVREvent_Status_t status;\n\tVREvent_Keyboard_t keyboard;\n\tVREvent_Ipd_t ipd;\n\tVREvent_Chaperone_t chaperone;\n\tVREvent_PerformanceTest_t performanceTest;\n\tVREvent_TouchPadMove_t touchPadMove;\n\tVREvent_SeatedZeroPoseReset_t seatedZeroPoseReset;\n\tVREvent_Screenshot_t screenshot;\n\tVREvent_ScreenshotProgress_t screenshotProgress;\n\tVREvent_ApplicationLaunch_t applicationLaunch;\n\tVREvent_EditingCameraSurface_t cameraSurface;\n\tVREvent_MessageOverlay_t messageOverlay;\n\tVREvent_Property_t property;\n\tVREvent_DualAnalog_t dualAnalog;\n\tVREvent_HapticVibration_t hapticVibration;\n\tVREvent_WebConsole_t webConsole;\n\tVREvent_InputBindingLoad_t inputBinding;\n} VREvent_Data_t;\n\n\n#if defined(__linux__) || defined(__APPLE__) \n// This structure was originally defined mis-packed on Linux, preserved for \n// compatibility. \n#pragma pack( push, 4 )\n#endif\n\n/** An event posted by the server to all running applications */\nstruct VREvent_t\n{\n\tuint32_t eventType; // EVREventType enum\n\tTrackedDeviceIndex_t trackedDeviceIndex;\n\tfloat eventAgeSeconds;\n\t// event data must be the end of the struct as its size is variable\n\tVREvent_Data_t data;\n};\n\n#if defined(__linux__) || defined(__APPLE__) \n#pragma pack( pop )\n#endif\n\nenum EVRInputError\n{\n\tVRInputError_None = 0,\n\tVRInputError_NameNotFound = 1,\n\tVRInputError_WrongType = 2,\n\tVRInputError_InvalidHandle = 3,\n\tVRInputError_InvalidParam = 4,\n\tVRInputError_NoSteam = 5,\n\tVRInputError_MaxCapacityReached = 6,\n\tVRInputError_IPCError = 7,\n\tVRInputError_NoActiveActionSet = 8,\n\tVRInputError_InvalidDevice = 9,\n\tVRInputError_InvalidSkeleton = 10,\n\tVRInputError_InvalidBoneCount = 11,\n\tVRInputError_InvalidCompressedData = 12,\n\tVRInputError_NoData = 13,\n\tVRInputError_BufferTooSmall = 14,\n\tVRInputError_MismatchedActionManifest = 15,\n};\n\n\n/** The mesh to draw into the stencil (or depth) buffer to perform \n* early stencil (or depth) kills of pixels that will never appear on the HMD.\n* This mesh draws on all the pixels that will be hidden after distortion. \n*\n* If the HMD does not provide a visible area mesh pVertexData will be\n* NULL and unTriangleCount will be 0. */\nstruct HiddenAreaMesh_t\n{\n\tconst HmdVector2_t *pVertexData;\n\tuint32_t unTriangleCount;\n};\n\n\nenum EHiddenAreaMeshType\n{\n\tk_eHiddenAreaMesh_Standard = 0,\n\tk_eHiddenAreaMesh_Inverse = 1,\n\tk_eHiddenAreaMesh_LineLoop = 2,\n\n\tk_eHiddenAreaMesh_Max = 3,\n};\n\n\n/** Identifies what kind of axis is on the controller at index n. Read this type \n* with pVRSystem->Get( nControllerDeviceIndex, Prop_Axis0Type_Int32 + n );\n*/\nenum EVRControllerAxisType\n{\n\tk_eControllerAxis_None = 0,\n\tk_eControllerAxis_TrackPad = 1,\n\tk_eControllerAxis_Joystick = 2,\n\tk_eControllerAxis_Trigger = 3, // Analog trigger data is in the X axis\n};\n\n\n/** contains information about one axis on the controller */\nstruct VRControllerAxis_t\n{\n\tfloat x; // Ranges from -1.0 to 1.0 for joysticks and track pads. Ranges from 0.0 to 1.0 for triggers were 0 is fully released.\n\tfloat y; // Ranges from -1.0 to 1.0 for joysticks and track pads. Is always 0.0 for triggers.\n};\n\n\n/** the number of axes in the controller state */\nstatic const uint32_t k_unControllerStateAxisCount = 5;\n\n\n#if defined(__linux__) || defined(__APPLE__) \n// This structure was originally defined mis-packed on Linux, preserved for \n// compatibility. \n#pragma pack( push, 4 )\n#endif\n\n/** Holds all the state of a controller at one moment in time. */\nstruct VRControllerState001_t\n{\n\t// If packet num matches that on your prior call, then the controller state hasn't been changed since \n\t// your last call and there is no need to process it\n\tuint32_t unPacketNum;\n\n\t// bit flags for each of the buttons. Use ButtonMaskFromId to turn an ID into a mask\n\tuint64_t ulButtonPressed;\n\tuint64_t ulButtonTouched;\n\n\t// Axis data for the controller's analog inputs\n\tVRControllerAxis_t rAxis[ k_unControllerStateAxisCount ];\n};\n#if defined(__linux__) || defined(__APPLE__) \n#pragma pack( pop )\n#endif\n\n\ntypedef VRControllerState001_t VRControllerState_t;\n\n\n/** determines how to provide output to the application of various event processing functions. */\nenum EVRControllerEventOutputType\n{\n\tControllerEventOutput_OSEvents = 0,\n\tControllerEventOutput_VREvents = 1,\n};\n\n\n\n/** Collision Bounds Style */\nenum ECollisionBoundsStyle\n{\n\tCOLLISION_BOUNDS_STYLE_BEGINNER = 0,\n\tCOLLISION_BOUNDS_STYLE_INTERMEDIATE,\n\tCOLLISION_BOUNDS_STYLE_SQUARES,\n\tCOLLISION_BOUNDS_STYLE_ADVANCED,\n\tCOLLISION_BOUNDS_STYLE_NONE,\n\n\tCOLLISION_BOUNDS_STYLE_COUNT\n};\n\n/** Allows the application to customize how the overlay appears in the compositor */\nstruct Compositor_OverlaySettings\n{\n\tuint32_t size; // sizeof(Compositor_OverlaySettings)\n\tbool curved, antialias;\n\tfloat scale, distance, alpha;\n\tfloat uOffset, vOffset, uScale, vScale;\n\tfloat gridDivs, gridWidth, gridScale;\n\tHmdMatrix44_t transform;\n};\n\n/** used to refer to a single VR overlay */\ntypedef uint64_t VROverlayHandle_t;\n\nstatic const VROverlayHandle_t k_ulOverlayHandleInvalid = 0;\n\n/** Errors that can occur around VR overlays */\nenum EVROverlayError\n{\n\tVROverlayError_None\t\t\t\t\t\t= 0,\n\n\tVROverlayError_UnknownOverlay\t\t\t= 10,\n\tVROverlayError_InvalidHandle\t\t\t= 11,\n\tVROverlayError_PermissionDenied\t\t\t= 12,\n\tVROverlayError_OverlayLimitExceeded\t\t= 13, // No more overlays could be created because the maximum number already exist\n\tVROverlayError_WrongVisibilityType\t\t= 14,\n\tVROverlayError_KeyTooLong\t\t\t\t= 15,\n\tVROverlayError_NameTooLong\t\t\t\t= 16,\n\tVROverlayError_KeyInUse\t\t\t\t\t= 17,\n\tVROverlayError_WrongTransformType\t\t= 18,\n\tVROverlayError_InvalidTrackedDevice\t\t= 19,\n\tVROverlayError_InvalidParameter\t\t\t= 20,\n\tVROverlayError_ThumbnailCantBeDestroyed\t= 21,\n\tVROverlayError_ArrayTooSmall\t\t\t= 22,\n\tVROverlayError_RequestFailed\t\t\t= 23,\n\tVROverlayError_InvalidTexture\t\t\t= 24,\n\tVROverlayError_UnableToLoadFile\t\t\t= 25,\n\tVROverlayError_KeyboardAlreadyInUse\t\t= 26,\n\tVROverlayError_NoNeighbor\t\t\t\t= 27,\n\tVROverlayError_TooManyMaskPrimitives\t= 29,\n\tVROverlayError_BadMaskPrimitive\t\t\t= 30,\n\tVROverlayError_TextureAlreadyLocked\t\t= 31,\n\tVROverlayError_TextureLockCapacityReached = 32,\n\tVROverlayError_TextureNotLocked\t\t\t= 33,\n};\n\n/** enum values to pass in to VR_Init to identify whether the application will \n* draw a 3D scene. */\nenum EVRApplicationType\n{\n\tVRApplication_Other = 0,\t\t// Some other kind of application that isn't covered by the other entries \n\tVRApplication_Scene\t= 1,\t\t// Application will submit 3D frames \n\tVRApplication_Overlay = 2,\t\t// Application only interacts with overlays\n\tVRApplication_Background = 3,\t// Application should not start SteamVR if it's not already running, and should not\n\t\t\t\t\t\t\t\t\t// keep it running if everything else quits.\n\tVRApplication_Utility = 4,\t\t// Init should not try to load any drivers. The application needs access to utility\n\t\t\t\t\t\t\t\t\t// interfaces (like IVRSettings and IVRApplications) but not hardware.\n\tVRApplication_VRMonitor = 5,\t// Reserved for vrmonitor\n\tVRApplication_SteamWatchdog = 6,// Reserved for Steam\n\tVRApplication_Bootstrapper = 7, // Start up SteamVR\n\n\tVRApplication_Max\n};\n\n\n/** error codes for firmware */\nenum EVRFirmwareError\n{\n\tVRFirmwareError_None = 0,\n\tVRFirmwareError_Success = 1,\n\tVRFirmwareError_Fail = 2,\n};\n\n\n/** error codes for notifications */\nenum EVRNotificationError\n{\n\tVRNotificationError_OK = 0,\n\tVRNotificationError_InvalidNotificationId = 100,\n\tVRNotificationError_NotificationQueueFull = 101,\n\tVRNotificationError_InvalidOverlayHandle = 102,\n\tVRNotificationError_SystemWithUserValueAlreadyExists = 103,\n};\n\n\n/** Holds the transform for a single bone */\nstruct VRBoneTransform_t\n{\n\tHmdVector4_t position;\n\tHmdQuaternionf_t orientation;\n};\n\n\n/** error codes returned by Vr_Init */\n\n// Please add adequate error description to https://developer.valvesoftware.com/w/index.php?title=Category:SteamVRHelp\nenum EVRInitError\n{\n\tVRInitError_None\t= 0,\n\tVRInitError_Unknown = 1,\n\n\tVRInitError_Init_InstallationNotFound\t\t\t= 100,\n\tVRInitError_Init_InstallationCorrupt\t\t\t= 101,\n\tVRInitError_Init_VRClientDLLNotFound\t\t\t= 102,\n\tVRInitError_Init_FileNotFound\t\t\t\t\t= 103,\n\tVRInitError_Init_FactoryNotFound\t\t\t\t= 104,\n\tVRInitError_Init_InterfaceNotFound\t\t\t\t= 105,\n\tVRInitError_Init_InvalidInterface\t\t\t\t= 106,\n\tVRInitError_Init_UserConfigDirectoryInvalid\t\t= 107,\n\tVRInitError_Init_HmdNotFound\t\t\t\t\t= 108,\n\tVRInitError_Init_NotInitialized\t\t\t\t\t= 109,\n\tVRInitError_Init_PathRegistryNotFound\t\t\t= 110,\n\tVRInitError_Init_NoConfigPath\t\t\t\t\t= 111,\n\tVRInitError_Init_NoLogPath\t\t\t\t\t\t= 112,\n\tVRInitError_Init_PathRegistryNotWritable\t\t= 113,\n\tVRInitError_Init_AppInfoInitFailed\t\t\t\t= 114,\n\tVRInitError_Init_Retry\t\t\t\t\t\t\t= 115, // Used internally to cause retries to vrserver\n\tVRInitError_Init_InitCanceledByUser\t\t\t\t= 116, // The calling application should silently exit. The user canceled app startup\n\tVRInitError_Init_AnotherAppLaunching\t\t\t= 117, \n\tVRInitError_Init_SettingsInitFailed\t\t\t\t= 118, \n\tVRInitError_Init_ShuttingDown\t\t\t\t\t= 119,\n\tVRInitError_Init_TooManyObjects\t\t\t\t\t= 120,\n\tVRInitError_Init_NoServerForBackgroundApp\t\t= 121,\n\tVRInitError_Init_NotSupportedWithCompositor\t\t= 122,\n\tVRInitError_Init_NotAvailableToUtilityApps\t\t= 123,\n\tVRInitError_Init_Internal\t\t\t\t \t\t= 124,\n\tVRInitError_Init_HmdDriverIdIsNone\t\t \t\t= 125,\n\tVRInitError_Init_HmdNotFoundPresenceFailed \t\t= 126,\n\tVRInitError_Init_VRMonitorNotFound\t\t\t\t= 127,\n\tVRInitError_Init_VRMonitorStartupFailed\t\t\t= 128,\n\tVRInitError_Init_LowPowerWatchdogNotSupported\t= 129, \n\tVRInitError_Init_InvalidApplicationType\t\t\t= 130,\n\tVRInitError_Init_NotAvailableToWatchdogApps\t\t= 131,\n\tVRInitError_Init_WatchdogDisabledInSettings\t\t= 132,\n\tVRInitError_Init_VRDashboardNotFound\t\t\t= 133,\n\tVRInitError_Init_VRDashboardStartupFailed\t\t= 134,\n\tVRInitError_Init_VRHomeNotFound\t\t\t\t\t= 135,\n\tVRInitError_Init_VRHomeStartupFailed\t\t\t= 136,\n\tVRInitError_Init_RebootingBusy\t\t\t\t\t= 137,\n\tVRInitError_Init_FirmwareUpdateBusy\t\t\t\t= 138,\n\tVRInitError_Init_FirmwareRecoveryBusy\t\t\t= 139,\n\tVRInitError_Init_USBServiceBusy\t\t\t\t\t= 140,\n\tVRInitError_Init_VRWebHelperStartupFailed\t\t= 141,\n\n\tVRInitError_Driver_Failed\t\t\t\t\t\t= 200,\n\tVRInitError_Driver_Unknown\t\t\t\t\t\t= 201,\n\tVRInitError_Driver_HmdUnknown\t\t\t\t\t= 202,\n\tVRInitError_Driver_NotLoaded\t\t\t\t\t= 203,\n\tVRInitError_Driver_RuntimeOutOfDate\t\t\t\t= 204,\n\tVRInitError_Driver_HmdInUse\t\t\t\t\t\t= 205,\n\tVRInitError_Driver_NotCalibrated\t\t\t\t= 206,\n\tVRInitError_Driver_CalibrationInvalid\t\t\t= 207,\n\tVRInitError_Driver_HmdDisplayNotFound\t\t\t= 208,\n\tVRInitError_Driver_TrackedDeviceInterfaceUnknown = 209,\n\t// VRInitError_Driver_HmdDisplayNotFoundAfterFix = 210, // not needed: here for historic reasons\n\tVRInitError_Driver_HmdDriverIdOutOfBounds\t\t= 211,\n\tVRInitError_Driver_HmdDisplayMirrored\t\t\t= 212,\n\n\tVRInitError_IPC_ServerInitFailed\t\t\t\t= 300,\n\tVRInitError_IPC_ConnectFailed\t\t\t\t\t= 301,\n\tVRInitError_IPC_SharedStateInitFailed\t\t\t= 302,\n\tVRInitError_IPC_CompositorInitFailed\t\t\t= 303,\n\tVRInitError_IPC_MutexInitFailed\t\t\t\t\t= 304,\n\tVRInitError_IPC_Failed\t\t\t\t\t\t\t= 305,\n\tVRInitError_IPC_CompositorConnectFailed\t\t\t= 306,\n\tVRInitError_IPC_CompositorInvalidConnectResponse = 307,\n\tVRInitError_IPC_ConnectFailedAfterMultipleAttempts = 308,\n\n\tVRInitError_Compositor_Failed\t\t\t\t\t= 400,\n\tVRInitError_Compositor_D3D11HardwareRequired\t= 401,\n\tVRInitError_Compositor_FirmwareRequiresUpdate\t= 402,\n\tVRInitError_Compositor_OverlayInitFailed\t\t= 403,\n\tVRInitError_Compositor_ScreenshotsInitFailed\t= 404,\n\tVRInitError_Compositor_UnableToCreateDevice\t\t= 405,\n\n\tVRInitError_VendorSpecific_UnableToConnectToOculusRuntime\t\t= 1000,\n\tVRInitError_VendorSpecific_WindowsNotInDevMode\t\t\t\t\t= 1001,\n\n\tVRInitError_VendorSpecific_HmdFound_CantOpenDevice \t\t\t\t= 1101,\n\tVRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart\t= 1102,\n\tVRInitError_VendorSpecific_HmdFound_NoStoredConfig \t\t\t\t= 1103,\n\tVRInitError_VendorSpecific_HmdFound_ConfigTooBig \t\t\t\t= 1104,\n\tVRInitError_VendorSpecific_HmdFound_ConfigTooSmall \t\t\t\t= 1105,\n\tVRInitError_VendorSpecific_HmdFound_UnableToInitZLib \t\t\t= 1106,\n\tVRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion \t= 1107,\n\tVRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart\t= 1108,\n\tVRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart\t= 1109,\n\tVRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext\t\t= 1110,\n\tVRInitError_VendorSpecific_HmdFound_UserDataAddressRange\t\t= 1111,\n\tVRInitError_VendorSpecific_HmdFound_UserDataError\t\t\t\t= 1112,\n\tVRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck\t\t= 1113,\n\n\tVRInitError_Steam_SteamInstallationNotFound = 2000,\n};\n\nenum EVRScreenshotType\n{\n\tVRScreenshotType_None = 0,\n\tVRScreenshotType_Mono = 1, // left eye only\n\tVRScreenshotType_Stereo = 2,\n\tVRScreenshotType_Cubemap = 3,\n\tVRScreenshotType_MonoPanorama = 4,\n\tVRScreenshotType_StereoPanorama = 5\n};\n\nenum EVRScreenshotPropertyFilenames\n{\n\tVRScreenshotPropertyFilenames_Preview = 0,\n\tVRScreenshotPropertyFilenames_VR = 1,\n};\n\nenum EVRTrackedCameraError\n{\n\tVRTrackedCameraError_None                       = 0,\n\tVRTrackedCameraError_OperationFailed            = 100,\n\tVRTrackedCameraError_InvalidHandle              = 101,\t\n\tVRTrackedCameraError_InvalidFrameHeaderVersion  = 102,\n\tVRTrackedCameraError_OutOfHandles               = 103,\n\tVRTrackedCameraError_IPCFailure                 = 104,\n\tVRTrackedCameraError_NotSupportedForThisDevice  = 105,\n\tVRTrackedCameraError_SharedMemoryFailure        = 106,\n\tVRTrackedCameraError_FrameBufferingFailure      = 107,\n\tVRTrackedCameraError_StreamSetupFailure         = 108,\n\tVRTrackedCameraError_InvalidGLTextureId         = 109,\n\tVRTrackedCameraError_InvalidSharedTextureHandle = 110,\n\tVRTrackedCameraError_FailedToGetGLTextureId     = 111,\n\tVRTrackedCameraError_SharedTextureFailure       = 112,\n\tVRTrackedCameraError_NoFrameAvailable           = 113,\n\tVRTrackedCameraError_InvalidArgument            = 114,\n\tVRTrackedCameraError_InvalidFrameBufferSize     = 115,\n};\n\nenum EVRTrackedCameraFrameLayout\n{\n\tEVRTrackedCameraFrameLayout_Mono\t\t\t\t= 0x0001,\n\tEVRTrackedCameraFrameLayout_Stereo\t\t\t\t= 0x0002,\n\tEVRTrackedCameraFrameLayout_VerticalLayout\t\t= 0x0010,\t// Stereo frames are Top/Bottom (left/right)\n\tEVRTrackedCameraFrameLayout_HorizontalLayout\t= 0x0020,\t// Stereo frames are Left/Right\n};\n\t\nenum EVRTrackedCameraFrameType\n{\n\tVRTrackedCameraFrameType_Distorted = 0,\t\t\t// This is the camera video frame size in pixels, still distorted.\n\tVRTrackedCameraFrameType_Undistorted,\t\t\t// In pixels, an undistorted inscribed rectangle region without invalid regions. This size is subject to changes shortly.\n\tVRTrackedCameraFrameType_MaximumUndistorted,\t// In pixels, maximum undistorted with invalid regions. Non zero alpha component identifies valid regions.\n\tMAX_CAMERA_FRAME_TYPES\n};\n\ntypedef uint64_t TrackedCameraHandle_t;\n#define INVALID_TRACKED_CAMERA_HANDLE\t((vr::TrackedCameraHandle_t)0)\n\nstruct CameraVideoStreamFrameHeader_t\n{\n\tEVRTrackedCameraFrameType eFrameType;\n\n\tuint32_t nWidth;\n\tuint32_t nHeight;\n\tuint32_t nBytesPerPixel;\n\n\tuint32_t nFrameSequence;\n\n\tTrackedDevicePose_t standingTrackedDevicePose;\n};\n\n// Screenshot types\ntypedef uint32_t ScreenshotHandle_t;\n\nstatic const uint32_t k_unScreenshotHandleInvalid = 0;\n\n/** Frame timing data provided by direct mode drivers. */\nstruct DriverDirectMode_FrameTiming\n{\n\tuint32_t m_nSize; // Set to sizeof( DriverDirectMode_FrameTiming )\n\tuint32_t m_nNumFramePresents; // number of times frame was presented\n\tuint32_t m_nNumMisPresented; // number of times frame was presented on a vsync other than it was originally predicted to\n\tuint32_t m_nNumDroppedFrames; // number of additional times previous frame was scanned out (i.e. compositor missed vsync)\n\tuint32_t m_nReprojectionFlags;\n};\n\nenum EVSync\n{\n\tVSync_None,\n\tVSync_WaitRender,\t// block following render work until vsync\n\tVSync_NoWaitRender,\t// do not block following render work (allow to get started early)\n};\n\n/** raw IMU data provided by IVRIOBuffer from paths to tracked devices with IMUs */\nenum Imu_OffScaleFlags\n{\n\tOffScale_AccelX\t= 0x01,\n\tOffScale_AccelY\t= 0x02,\n\tOffScale_AccelZ\t= 0x04,\n\tOffScale_GyroX\t= 0x08,\n\tOffScale_GyroY\t= 0x10,\n\tOffScale_GyroZ\t= 0x20,\n};\n\t\nstruct ImuSample_t\n{\n\tdouble fSampleTime;\n\tHmdVector3d_t vAccel;\n\tHmdVector3d_t vGyro;\n\tuint32_t unOffScaleFlags;\n};\n\n#pragma pack( pop )\n\n// figure out how to import from the VR API dll\n#if defined(_WIN32)\n\n#ifdef VR_API_EXPORT\n#define VR_INTERFACE extern \"C\" __declspec( dllexport )\n#else\n#define VR_INTERFACE extern \"C\" __declspec( dllimport )\n#endif\n\n#elif defined(__GNUC__) || defined(COMPILER_GCC) || defined(__APPLE__)\n\n#ifdef VR_API_EXPORT\n#define VR_INTERFACE extern \"C\" __attribute__((visibility(\"default\")))\n#else\n#define VR_INTERFACE extern \"C\" \n#endif\n\n#else\n#error \"Unsupported Platform.\"\n#endif\n\n\n#if defined( _WIN32 )\n#define VR_CALLTYPE __cdecl\n#else\n#define VR_CALLTYPE \n#endif\n\n} // namespace vr\n\n#endif // _INCLUDE_VRTYPES_H\n\n\n// vrannotation.h\n#ifdef API_GEN\n# define VR_CLANG_ATTR(ATTR) __attribute__((annotate( ATTR )))\n#else\n# define VR_CLANG_ATTR(ATTR)\n#endif\n\n#define VR_METHOD_DESC(DESC) VR_CLANG_ATTR( \"desc:\" #DESC \";\" )\n#define VR_IGNOREATTR() VR_CLANG_ATTR( \"ignore\" )\n#define VR_OUT_STRUCT() VR_CLANG_ATTR( \"out_struct: ;\" )\n#define VR_OUT_STRING() VR_CLANG_ATTR( \"out_string: ;\" )\n#define VR_OUT_ARRAY_CALL(COUNTER,FUNCTION,PARAMS) VR_CLANG_ATTR( \"out_array_call:\" #COUNTER \",\" #FUNCTION \",\" #PARAMS \";\" )\n#define VR_OUT_ARRAY_COUNT(COUNTER) VR_CLANG_ATTR( \"out_array_count:\" #COUNTER \";\" )\n#define VR_ARRAY_COUNT(COUNTER) VR_CLANG_ATTR( \"array_count:\" #COUNTER \";\" )\n#define VR_ARRAY_COUNT_D(COUNTER, DESC) VR_CLANG_ATTR( \"array_count:\" #COUNTER \";desc:\" #DESC )\n#define VR_BUFFER_COUNT(COUNTER) VR_CLANG_ATTR( \"buffer_count:\" #COUNTER \";\" )\n#define VR_OUT_BUFFER_COUNT(COUNTER) VR_CLANG_ATTR( \"out_buffer_count:\" #COUNTER \";\" )\n#define VR_OUT_STRING_COUNT(COUNTER) VR_CLANG_ATTR( \"out_string_count:\" #COUNTER \";\" )\n\n// ivrsystem.h\nnamespace vr\n{\n\nclass IVRSystem\n{\npublic:\n\n\n\t// ------------------------------------\n\t// Display Methods\n\t// ------------------------------------\n\n\t/** Suggested size for the intermediate render target that the distortion pulls from. */\n\tvirtual void GetRecommendedRenderTargetSize( uint32_t *pnWidth, uint32_t *pnHeight ) = 0;\n\n\t/** The projection matrix for the specified eye */\n\tvirtual HmdMatrix44_t GetProjectionMatrix( EVREye eEye, float fNearZ, float fFarZ ) = 0;\n\n\t/** The components necessary to build your own projection matrix in case your\n\t* application is doing something fancy like infinite Z */\n\tvirtual void GetProjectionRaw( EVREye eEye, float *pfLeft, float *pfRight, float *pfTop, float *pfBottom ) = 0;\n\n\t/** Gets the result of the distortion function for the specified eye and input UVs. UVs go from 0,0 in \n\t* the upper left of that eye's viewport and 1,1 in the lower right of that eye's viewport.\n\t* Returns true for success. Otherwise, returns false, and distortion coordinates are not suitable. */\n\tvirtual bool ComputeDistortion( EVREye eEye, float fU, float fV, DistortionCoordinates_t *pDistortionCoordinates ) = 0;\n\n\t/** Returns the transform from eye space to the head space. Eye space is the per-eye flavor of head\n\t* space that provides stereo disparity. Instead of Model * View * Projection the sequence is Model * View * Eye^-1 * Projection. \n\t* Normally View and Eye^-1 will be multiplied together and treated as View in your application. \n\t*/\n\tvirtual HmdMatrix34_t GetEyeToHeadTransform( EVREye eEye ) = 0;\n\n\t/** Returns the number of elapsed seconds since the last recorded vsync event. This \n\t*\twill come from a vsync timer event in the timer if possible or from the application-reported\n\t*   time if that is not available. If no vsync times are available the function will \n\t*   return zero for vsync time and frame counter and return false from the method. */\n\tvirtual bool GetTimeSinceLastVsync( float *pfSecondsSinceLastVsync, uint64_t *pulFrameCounter ) = 0;\n\n\t/** [D3D9 Only]\n\t* Returns the adapter index that the user should pass into CreateDevice to set up D3D9 in such\n\t* a way that it can go full screen exclusive on the HMD. Returns -1 if there was an error.\n\t*/\n\tvirtual int32_t GetD3D9AdapterIndex() = 0;\n\n\t/** [D3D10/11 Only]\n\t* Returns the adapter index that the user should pass into EnumAdapters to create the device \n\t* and swap chain in DX10 and DX11. If an error occurs the index will be set to -1.\n\t*/\n\tvirtual void GetDXGIOutputInfo( int32_t *pnAdapterIndex ) = 0;\n\t\n\t/**\n\t * Returns platform- and texture-type specific adapter identification so that applications and the\n\t * compositor are creating textures and swap chains on the same GPU. If an error occurs the device\n\t * will be set to 0.\n\t * pInstance is an optional parameter that is required only when textureType is TextureType_Vulkan.\n\t * [D3D10/11/12 Only (D3D9 Not Supported)]\n\t *  Returns the adapter LUID that identifies the GPU attached to the HMD. The user should\n\t *  enumerate all adapters using IDXGIFactory::EnumAdapters and IDXGIAdapter::GetDesc to find\n\t *  the adapter with the matching LUID, or use IDXGIFactory4::EnumAdapterByLuid.\n\t *  The discovered IDXGIAdapter should be used to create the device and swap chain.\n\t * [Vulkan Only]\n\t *  Returns the VkPhysicalDevice that should be used by the application.\n\t *  pInstance must be the instance the application will use to query for the VkPhysicalDevice.  The application\n\t *  must create the VkInstance with extensions returned by IVRCompositor::GetVulkanInstanceExtensionsRequired enabled.\n\t * [macOS Only]\n\t *  For TextureType_IOSurface returns the id<MTLDevice> that should be used by the application.\n\t *  On 10.13+ for TextureType_OpenGL returns the 'registryId' of the renderer which should be used\n\t *   by the application. See Apple Technical Q&A QA1168 for information on enumerating GL Renderers, and the\n\t *   new kCGLRPRegistryIDLow and kCGLRPRegistryIDHigh CGLRendererProperty values in the 10.13 SDK.\n\t *  Pre 10.13 for TextureType_OpenGL returns 0, as there is no dependable way to correlate the HMDs MTLDevice\n\t *   with a GL Renderer.\n\t */\n\tvirtual void GetOutputDevice( uint64_t *pnDevice, ETextureType textureType, VkInstance_T *pInstance = nullptr ) = 0;\n\n\t// ------------------------------------\n\t// Display Mode methods\n\t// ------------------------------------\n\n\t/** Use to determine if the headset display is part of the desktop (i.e. extended) or hidden (i.e. direct mode). */\n\tvirtual bool IsDisplayOnDesktop() = 0;\n\n\t/** Set the display visibility (true = extended, false = direct mode).  Return value of true indicates that the change was successful. */\n\tvirtual bool SetDisplayVisibility( bool bIsVisibleOnDesktop ) = 0;\n\n\t// ------------------------------------\n\t// Tracking Methods\n\t// ------------------------------------\n\n\t/** The pose that the tracker thinks that the HMD will be in at the specified number of seconds into the \n\t* future. Pass 0 to get the state at the instant the method is called. Most of the time the application should\n\t* calculate the time until the photons will be emitted from the display and pass that time into the method.\n\t*\n\t* This is roughly analogous to the inverse of the view matrix in most applications, though \n\t* many games will need to do some additional rotation or translation on top of the rotation\n\t* and translation provided by the head pose.\n\t*\n\t* For devices where bPoseIsValid is true the application can use the pose to position the device\n\t* in question. The provided array can be any size up to k_unMaxTrackedDeviceCount. \n\t*\n\t* Seated experiences should call this method with TrackingUniverseSeated and receive poses relative\n\t* to the seated zero pose. Standing experiences should call this method with TrackingUniverseStanding \n\t* and receive poses relative to the Chaperone Play Area. TrackingUniverseRawAndUncalibrated should \n\t* probably not be used unless the application is the Chaperone calibration tool itself, but will provide\n\t* poses relative to the hardware-specific coordinate system in the driver.\n\t*/\n\tvirtual void GetDeviceToAbsoluteTrackingPose( ETrackingUniverseOrigin eOrigin, float fPredictedSecondsToPhotonsFromNow, VR_ARRAY_COUNT(unTrackedDevicePoseArrayCount) TrackedDevicePose_t *pTrackedDevicePoseArray, uint32_t unTrackedDevicePoseArrayCount ) = 0;\n\n\t/** Sets the zero pose for the seated tracker coordinate system to the current position and yaw of the HMD. After \n\t* ResetSeatedZeroPose all GetDeviceToAbsoluteTrackingPose calls that pass TrackingUniverseSeated as the origin \n\t* will be relative to this new zero pose. The new zero coordinate system will not change the fact that the Y axis \n\t* is up in the real world, so the next pose returned from GetDeviceToAbsoluteTrackingPose after a call to \n\t* ResetSeatedZeroPose may not be exactly an identity matrix.\n\t*\n\t* NOTE: This function overrides the user's previously saved seated zero pose and should only be called as the result of a user action. \n\t* Users are also able to set their seated zero pose via the OpenVR Dashboard.\n\t**/\n\tvirtual void ResetSeatedZeroPose() = 0;\n\n\t/** Returns the transform from the seated zero pose to the standing absolute tracking system. This allows \n\t* applications to represent the seated origin to used or transform object positions from one coordinate\n\t* system to the other. \n\t*\n\t* The seated origin may or may not be inside the Play Area or Collision Bounds returned by IVRChaperone. Its position \n\t* depends on what the user has set from the Dashboard settings and previous calls to ResetSeatedZeroPose. */\n\tvirtual HmdMatrix34_t GetSeatedZeroPoseToStandingAbsoluteTrackingPose() = 0;\n\n\t/** Returns the transform from the tracking origin to the standing absolute tracking system. This allows\n\t* applications to convert from raw tracking space to the calibrated standing coordinate system. */\n\tvirtual HmdMatrix34_t GetRawZeroPoseToStandingAbsoluteTrackingPose() = 0;\n\n\t/** Get a sorted array of device indices of a given class of tracked devices (e.g. controllers).  Devices are sorted right to left\n\t* relative to the specified tracked device (default: hmd -- pass in -1 for absolute tracking space).  Returns the number of devices\n\t* in the list, or the size of the array needed if not large enough. */\n\tvirtual uint32_t GetSortedTrackedDeviceIndicesOfClass( ETrackedDeviceClass eTrackedDeviceClass, VR_ARRAY_COUNT(unTrackedDeviceIndexArrayCount) vr::TrackedDeviceIndex_t *punTrackedDeviceIndexArray, uint32_t unTrackedDeviceIndexArrayCount, vr::TrackedDeviceIndex_t unRelativeToTrackedDeviceIndex = k_unTrackedDeviceIndex_Hmd ) = 0;\n\n\t/** Returns the level of activity on the device. */\n\tvirtual EDeviceActivityLevel GetTrackedDeviceActivityLevel( vr::TrackedDeviceIndex_t unDeviceId ) = 0;\n\n\t/** Convenience utility to apply the specified transform to the specified pose.\n\t*   This properly transforms all pose components, including velocity and angular velocity\n\t*/\n\tvirtual void ApplyTransform( TrackedDevicePose_t *pOutputPose, const TrackedDevicePose_t *pTrackedDevicePose, const HmdMatrix34_t *pTransform ) = 0;\n\n\t/** Returns the device index associated with a specific role, for example the left hand or the right hand. */\n\tvirtual vr::TrackedDeviceIndex_t GetTrackedDeviceIndexForControllerRole( vr::ETrackedControllerRole unDeviceType ) = 0;\n\n\t/** Returns the controller type associated with a device index. */\n\tvirtual vr::ETrackedControllerRole GetControllerRoleForTrackedDeviceIndex( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0;\n\n\t// ------------------------------------\n\t// Property methods\n\t// ------------------------------------\n\n\t/** Returns the device class of a tracked device. If there has not been a device connected in this slot\n\t* since the application started this function will return TrackedDevice_Invalid. For previous detected\n\t* devices the function will return the previously observed device class. \n\t*\n\t* To determine which devices exist on the system, just loop from 0 to k_unMaxTrackedDeviceCount and check\n\t* the device class. Every device with something other than TrackedDevice_Invalid is associated with an \n\t* actual tracked device. */\n\tvirtual ETrackedDeviceClass GetTrackedDeviceClass( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0;\n\n\t/** Returns true if there is a device connected in this slot. */\n\tvirtual bool IsTrackedDeviceConnected( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0;\n\n\t/** Returns a bool property. If the device index is not valid or the property is not a bool type this function will return false. */\n\tvirtual bool GetBoolTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;\n\n\t/** Returns a float property. If the device index is not valid or the property is not a float type this function will return 0. */\n\tvirtual float GetFloatTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;\n\n\t/** Returns an int property. If the device index is not valid or the property is not a int type this function will return 0. */\n\tvirtual int32_t GetInt32TrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;\n\n\t/** Returns a uint64 property. If the device index is not valid or the property is not a uint64 type this function will return 0. */\n\tvirtual uint64_t GetUint64TrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;\n\n\t/** Returns a matrix property. If the device index is not valid or the property is not a matrix type, this function will return identity. */\n\tvirtual HmdMatrix34_t GetMatrix34TrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, ETrackedPropertyError *pError = 0L ) = 0;\n\t\n\t/** Returns an array of one type of property. If the device index is not valid or the property is not a single value or an array of the specified type,\n\t* this function will return 0. Otherwise it returns the number of bytes necessary to hold the array of properties. If unBufferSize is\n\t* greater than the returned size and pBuffer is non-NULL, pBuffer is filled with the contents of array of properties. */\n\tvirtual uint32_t GetArrayTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, PropertyTypeTag_t propType, void *pBuffer, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0;\n\n\t/** Returns a string property. If the device index is not valid or the property is not a string type this function will \n\t* return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing\n\t* null. Strings will always fit in buffers of k_unMaxPropertyStringSize characters. */\n\tvirtual uint32_t GetStringTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0;\n\n\t/** returns a string that corresponds with the specified property error. The string will be the name \n\t* of the error enum value for all valid error codes */\n\tvirtual const char *GetPropErrorNameFromEnum( ETrackedPropertyError error ) = 0;\n\n\t// ------------------------------------\n\t// Event methods\n\t// ------------------------------------\n\n\t/** Returns true and fills the event with the next event on the queue if there is one. If there are no events\n\t* this method returns false. uncbVREvent should be the size in bytes of the VREvent_t struct */\n\tvirtual bool PollNextEvent( VREvent_t *pEvent, uint32_t uncbVREvent ) = 0;\n\n\t/** Returns true and fills the event with the next event on the queue if there is one. If there are no events\n\t* this method returns false. Fills in the pose of the associated tracked device in the provided pose struct. \n\t* This pose will always be older than the call to this function and should not be used to render the device. \n\tuncbVREvent should be the size in bytes of the VREvent_t struct */\n\tvirtual bool PollNextEventWithPose( ETrackingUniverseOrigin eOrigin, VREvent_t *pEvent, uint32_t uncbVREvent, vr::TrackedDevicePose_t *pTrackedDevicePose ) = 0;\n\n\t/** returns the name of an EVREvent enum value */\n\tvirtual const char *GetEventTypeNameFromEnum( EVREventType eType ) = 0;\n\n\t// ------------------------------------\n\t// Rendering helper methods\n\t// ------------------------------------\n\n\t/** Returns the hidden area mesh for the current HMD. The pixels covered by this mesh will never be seen by the user after the lens distortion is\n\t* applied based on visibility to the panels. If this HMD does not have a hidden area mesh, the vertex data and count will be NULL and 0 respectively.\n\t* This mesh is meant to be rendered into the stencil buffer (or into the depth buffer setting nearz) before rendering each eye's view. \n\t* This will improve performance by letting the GPU early-reject pixels the user will never see before running the pixel shader.\n\t* NOTE: Render this mesh with backface culling disabled since the winding order of the vertices can be different per-HMD or per-eye.\n\t* Setting the bInverse argument to true will produce the visible area mesh that is commonly used in place of full-screen quads. The visible area mesh covers all of the pixels the hidden area mesh does not cover.\n\t* Setting the bLineLoop argument will return a line loop of vertices in HiddenAreaMesh_t->pVertexData with HiddenAreaMesh_t->unTriangleCount set to the number of vertices.\n\t*/\n\tvirtual HiddenAreaMesh_t GetHiddenAreaMesh( EVREye eEye, EHiddenAreaMeshType type = k_eHiddenAreaMesh_Standard ) = 0;\n\n\t// ------------------------------------\n\t// Controller methods\n\t// ------------------------------------\n\n\t/** Fills the supplied struct with the current state of the controller. Returns false if the controller index\n\t* is invalid. */\n\tvirtual bool GetControllerState( vr::TrackedDeviceIndex_t unControllerDeviceIndex, vr::VRControllerState_t *pControllerState, uint32_t unControllerStateSize ) = 0;\n\n\t/** fills the supplied struct with the current state of the controller and the provided pose with the pose of \n\t* the controller when the controller state was updated most recently. Use this form if you need a precise controller\n\t* pose as input to your application when the user presses or releases a button. */\n\tvirtual bool GetControllerStateWithPose( ETrackingUniverseOrigin eOrigin, vr::TrackedDeviceIndex_t unControllerDeviceIndex, vr::VRControllerState_t *pControllerState, uint32_t unControllerStateSize, TrackedDevicePose_t *pTrackedDevicePose ) = 0;\n\n\t/** Trigger a single haptic pulse on a controller. After this call the application may not trigger another haptic pulse on this controller\n\t* and axis combination for 5ms. */\n\tvirtual void TriggerHapticPulse( vr::TrackedDeviceIndex_t unControllerDeviceIndex, uint32_t unAxisId, unsigned short usDurationMicroSec ) = 0;\n\n\t/** returns the name of an EVRButtonId enum value */\n\tvirtual const char *GetButtonIdNameFromEnum( EVRButtonId eButtonId ) = 0;\n\n\t/** returns the name of an EVRControllerAxisType enum value */\n\tvirtual const char *GetControllerAxisTypeNameFromEnum( EVRControllerAxisType eAxisType ) = 0;\n\n\t/** Returns true if this application is receiving input from the system. This would return false if \n\t* system-related functionality is consuming the input stream. */\n\tvirtual bool IsInputAvailable() = 0;\n\n\t/** Returns true SteamVR is drawing controllers on top of the application. Applications should consider\n\t* not drawing anything attached to the user's hands in this case. */\n\tvirtual bool IsSteamVRDrawingControllers() = 0;\n\n\t/** Returns true if the user has put SteamVR into a mode that is distracting them from the application.\n\t* For applications where this is appropriate, the application should pause ongoing activity. */\n\tvirtual bool ShouldApplicationPause() = 0;\n\n\t/** Returns true if SteamVR is doing significant rendering work and the game should do what it can to reduce\n\t* its own workload. One common way to do this is to reduce the size of the render target provided for each eye. */\n\tvirtual bool ShouldApplicationReduceRenderingWork() = 0;\n\n\t// ------------------------------------\n\t// Debug Methods\n\t// ------------------------------------\n\n\t/** Sends a request to the driver for the specified device and returns the response. The maximum response size is 32k,\n\t* but this method can be called with a smaller buffer. If the response exceeds the size of the buffer, it is truncated. \n\t* The size of the response including its terminating null is returned. */\n\tvirtual uint32_t DriverDebugRequest( vr::TrackedDeviceIndex_t unDeviceIndex, const char *pchRequest, VR_OUT_STRING() char *pchResponseBuffer, uint32_t unResponseBufferSize ) = 0;\n\n\t// ------------------------------------\n\t// Firmware methods\n\t// ------------------------------------\n\t\n\t/** Performs the actual firmware update if applicable. \n\t * The following events will be sent, if VRFirmwareError_None was returned: VREvent_FirmwareUpdateStarted, VREvent_FirmwareUpdateFinished \n\t * Use the properties Prop_Firmware_UpdateAvailable_Bool, Prop_Firmware_ManualUpdate_Bool, and Prop_Firmware_ManualUpdateURL_String\n\t * to figure our whether a firmware update is available, and to figure out whether its a manual update \n\t * Prop_Firmware_ManualUpdateURL_String should point to an URL describing the manual update process */\n\tvirtual vr::EVRFirmwareError PerformFirmwareUpdate( vr::TrackedDeviceIndex_t unDeviceIndex ) = 0;\n\n\t// ------------------------------------\n\t// Application life cycle methods\n\t// ------------------------------------\n\n\t/** Call this to acknowledge to the system that VREvent_Quit has been received and that the process is exiting.\n\t* This extends the timeout until the process is killed. */\n\tvirtual void AcknowledgeQuit_Exiting() = 0;\n\n\t/** Call this to tell the system that the user is being prompted to save data. This\n\t* halts the timeout and dismisses the dashboard (if it was up). Applications should be sure to actually \n\t* prompt the user to save and then exit afterward, otherwise the user will be left in a confusing state. */\n\tvirtual void AcknowledgeQuit_UserPrompt() = 0;\n\n};\n\nstatic const char * const IVRSystem_Version = \"IVRSystem_019\";\n\n}\n\n\n// ivrapplications.h\nnamespace vr\n{\n\n\t/** Used for all errors reported by the IVRApplications interface */\n\tenum EVRApplicationError\n\t{\n\t\tVRApplicationError_None = 0,\n\n\t\tVRApplicationError_AppKeyAlreadyExists = 100,\t// Only one application can use any given key\n\t\tVRApplicationError_NoManifest = 101,\t\t\t// the running application does not have a manifest\n\t\tVRApplicationError_NoApplication = 102,\t\t\t// No application is running\n\t\tVRApplicationError_InvalidIndex = 103,\n\t\tVRApplicationError_UnknownApplication = 104,\t// the application could not be found\n\t\tVRApplicationError_IPCFailed = 105,\t\t\t\t// An IPC failure caused the request to fail\n\t\tVRApplicationError_ApplicationAlreadyRunning = 106, \n\t\tVRApplicationError_InvalidManifest = 107,\n\t\tVRApplicationError_InvalidApplication = 108,\n\t\tVRApplicationError_LaunchFailed = 109,\t\t\t// the process didn't start\n\t\tVRApplicationError_ApplicationAlreadyStarting = 110, // the system was already starting the same application\n\t\tVRApplicationError_LaunchInProgress = 111,\t\t// The system was already starting a different application\n\t\tVRApplicationError_OldApplicationQuitting = 112, \n\t\tVRApplicationError_TransitionAborted = 113,\n\t\tVRApplicationError_IsTemplate = 114, // error when you try to call LaunchApplication() on a template type app (use LaunchTemplateApplication)\n\t\tVRApplicationError_SteamVRIsExiting = 115,\n\n\t\tVRApplicationError_BufferTooSmall = 200,\t\t// The provided buffer was too small to fit the requested data\n\t\tVRApplicationError_PropertyNotSet = 201,\t\t// The requested property was not set\n\t\tVRApplicationError_UnknownProperty = 202,\n\t\tVRApplicationError_InvalidParameter = 203,\n\t};\n\n\t/** The maximum length of an application key */\n\tstatic const uint32_t k_unMaxApplicationKeyLength = 128;\n\n\t/** these are the properties available on applications. */\n\tenum EVRApplicationProperty\n\t{\n\t\tVRApplicationProperty_Name_String\t\t\t\t= 0,\n\n\t\tVRApplicationProperty_LaunchType_String\t\t\t= 11,\n\t\tVRApplicationProperty_WorkingDirectory_String\t= 12,\n\t\tVRApplicationProperty_BinaryPath_String\t\t\t= 13,\n\t\tVRApplicationProperty_Arguments_String\t\t\t= 14,\n\t\tVRApplicationProperty_URL_String\t\t\t\t= 15,\n\n\t\tVRApplicationProperty_Description_String\t\t= 50,\n\t\tVRApplicationProperty_NewsURL_String\t\t\t= 51,\n\t\tVRApplicationProperty_ImagePath_String\t\t\t= 52,\n\t\tVRApplicationProperty_Source_String\t\t\t\t= 53,\n\t\tVRApplicationProperty_ActionManifestURL_String\t= 54,\n\n\t\tVRApplicationProperty_IsDashboardOverlay_Bool\t= 60,\n\t\tVRApplicationProperty_IsTemplate_Bool\t\t\t= 61,\n\t\tVRApplicationProperty_IsInstanced_Bool\t\t\t= 62,\n\t\tVRApplicationProperty_IsInternal_Bool\t\t\t= 63,\n\t\tVRApplicationProperty_WantsCompositorPauseInStandby_Bool = 64,\n\n\t\tVRApplicationProperty_LastLaunchTime_Uint64\t\t= 70,\n\t};\n\n\t/** These are states the scene application startup process will go through. */\n\tenum EVRApplicationTransitionState\n\t{\n\t\tVRApplicationTransition_None = 0,\n\n\t\tVRApplicationTransition_OldAppQuitSent = 10,\n\t\tVRApplicationTransition_WaitingForExternalLaunch = 11,\n\t\t\n\t\tVRApplicationTransition_NewAppLaunched = 20,\n\t};\n\n\tstruct AppOverrideKeys_t\n\t{\n\t\tconst char *pchKey;\n\t\tconst char *pchValue;\n\t};\n\n\t/** Currently recognized mime types */\n\tstatic const char * const k_pch_MimeType_HomeApp\t\t= \"vr/home\";\n\tstatic const char * const k_pch_MimeType_GameTheater\t= \"vr/game_theater\";\n\n\tclass IVRApplications\n\t{\n\tpublic:\n\n\t\t// ---------------  Application management  --------------- //\n\n\t\t/** Adds an application manifest to the list to load when building the list of installed applications. \n\t\t* Temporary manifests are not automatically loaded */\n\t\tvirtual EVRApplicationError AddApplicationManifest( const char *pchApplicationManifestFullPath, bool bTemporary = false ) = 0;\n\n\t\t/** Removes an application manifest from the list to load when building the list of installed applications. */\n\t\tvirtual EVRApplicationError RemoveApplicationManifest( const char *pchApplicationManifestFullPath ) = 0;\n\n\t\t/** Returns true if an application is installed */\n\t\tvirtual bool IsApplicationInstalled( const char *pchAppKey ) = 0;\n\n\t\t/** Returns the number of applications available in the list */\n\t\tvirtual uint32_t GetApplicationCount() = 0;\n\n\t\t/** Returns the key of the specified application. The index is at least 0 and is less than the return \n\t\t* value of GetApplicationCount(). The buffer should be at least k_unMaxApplicationKeyLength in order to \n\t\t* fit the key. */\n\t\tvirtual EVRApplicationError GetApplicationKeyByIndex( uint32_t unApplicationIndex, VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0;\n\n\t\t/** Returns the key of the application for the specified Process Id. The buffer should be at least \n\t\t* k_unMaxApplicationKeyLength in order to fit the key. */\n\t\tvirtual EVRApplicationError GetApplicationKeyByProcessId( uint32_t unProcessId, VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0;\n\n\t\t/** Launches the application. The existing scene application will exit and then the new application will start.\n\t\t* This call is not valid for dashboard overlay applications. */\n\t\tvirtual EVRApplicationError LaunchApplication( const char *pchAppKey ) = 0;\n\n\t\t/** Launches an instance of an application of type template, with its app key being pchNewAppKey (which must be unique) and optionally override sections\n\t\t* from the manifest file via AppOverrideKeys_t\n\t\t*/\n\t\tvirtual EVRApplicationError LaunchTemplateApplication( const char *pchTemplateAppKey, const char *pchNewAppKey, VR_ARRAY_COUNT( unKeys ) const AppOverrideKeys_t *pKeys, uint32_t unKeys ) = 0;\n\n\t\t/** launches the application currently associated with this mime type and passes it the option args, typically the filename or object name of the item being launched */\n\t\tvirtual vr::EVRApplicationError LaunchApplicationFromMimeType( const char *pchMimeType, const char *pchArgs ) = 0;\n\n\t\t/** Launches the dashboard overlay application if it is not already running. This call is only valid for \n\t\t* dashboard overlay applications. */\n\t\tvirtual EVRApplicationError LaunchDashboardOverlay( const char *pchAppKey ) = 0;\n\n\t\t/** Cancel a pending launch for an application */\n\t\tvirtual bool CancelApplicationLaunch( const char *pchAppKey ) = 0;\n\n\t\t/** Identifies a running application. OpenVR can't always tell which process started in response\n\t\t* to a URL. This function allows a URL handler (or the process itself) to identify the app key \n\t\t* for the now running application. Passing a process ID of 0 identifies the calling process. \n\t\t* The application must be one that's known to the system via a call to AddApplicationManifest. */\n\t\tvirtual EVRApplicationError IdentifyApplication( uint32_t unProcessId, const char *pchAppKey ) = 0;\n\n\t\t/** Returns the process ID for an application. Return 0 if the application was not found or is not running. */\n\t\tvirtual uint32_t GetApplicationProcessId( const char *pchAppKey ) = 0;\n\n\t\t/** Returns a string for an applications error */\n\t\tvirtual const char *GetApplicationsErrorNameFromEnum( EVRApplicationError error ) = 0;\n\n\t\t// ---------------  Application properties  --------------- //\n\n\t\t/** Returns a value for an application property. The required buffer size to fit this value will be returned. */\n\t\tvirtual uint32_t GetApplicationPropertyString( const char *pchAppKey, EVRApplicationProperty eProperty, VR_OUT_STRING() char *pchPropertyValueBuffer, uint32_t unPropertyValueBufferLen, EVRApplicationError *peError = nullptr ) = 0;\n\n\t\t/** Returns a bool value for an application property. Returns false in all error cases. */\n\t\tvirtual bool GetApplicationPropertyBool( const char *pchAppKey, EVRApplicationProperty eProperty, EVRApplicationError *peError = nullptr ) = 0;\n\n\t\t/** Returns a uint64 value for an application property. Returns 0 in all error cases. */\n\t\tvirtual uint64_t GetApplicationPropertyUint64( const char *pchAppKey, EVRApplicationProperty eProperty, EVRApplicationError *peError = nullptr ) = 0;\n\n\t\t/** Sets the application auto-launch flag. This is only valid for applications which return true for VRApplicationProperty_IsDashboardOverlay_Bool. */\n\t\tvirtual EVRApplicationError SetApplicationAutoLaunch( const char *pchAppKey, bool bAutoLaunch ) = 0;\n\n\t\t/** Gets the application auto-launch flag. This is only valid for applications which return true for VRApplicationProperty_IsDashboardOverlay_Bool. */\n\t\tvirtual bool GetApplicationAutoLaunch( const char *pchAppKey ) = 0;\n\n\t\t/** Adds this mime-type to the list of supported mime types for this application*/\n\t\tvirtual EVRApplicationError SetDefaultApplicationForMimeType( const char *pchAppKey, const char *pchMimeType ) = 0;\n\n\t\t/** return the app key that will open this mime type */\n\t\tvirtual bool GetDefaultApplicationForMimeType( const char *pchMimeType, VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0;\n\n\t\t/** Get the list of supported mime types for this application, comma-delimited */\n\t\tvirtual bool GetApplicationSupportedMimeTypes( const char *pchAppKey, VR_OUT_STRING() char *pchMimeTypesBuffer, uint32_t unMimeTypesBuffer ) = 0;\n\n\t\t/** Get the list of app-keys that support this mime type, comma-delimited, the return value is number of bytes you need to return the full string */\n\t\tvirtual uint32_t GetApplicationsThatSupportMimeType( const char *pchMimeType, VR_OUT_STRING() char *pchAppKeysThatSupportBuffer, uint32_t unAppKeysThatSupportBuffer ) = 0;\n\n\t\t/** Get the args list from an app launch that had the process already running, you call this when you get a VREvent_ApplicationMimeTypeLoad */\n\t\tvirtual uint32_t GetApplicationLaunchArguments( uint32_t unHandle, VR_OUT_STRING() char *pchArgs, uint32_t unArgs ) = 0;\n\n\t\t// ---------------  Transition methods --------------- //\n\n\t\t/** Returns the app key for the application that is starting up */\n\t\tvirtual EVRApplicationError GetStartingApplication( VR_OUT_STRING() char *pchAppKeyBuffer, uint32_t unAppKeyBufferLen ) = 0;\n\n\t\t/** Returns the application transition state */\n\t\tvirtual EVRApplicationTransitionState GetTransitionState() = 0;\n\n\t\t/** Returns errors that would prevent the specified application from launching immediately. Calling this function will\n\t\t* cause the current scene application to quit, so only call it when you are actually about to launch something else.\n\t\t* What the caller should do about these failures depends on the failure:\n\t\t*   VRApplicationError_OldApplicationQuitting - An existing application has been told to quit. Wait for a VREvent_ProcessQuit\n\t\t*                                               and try again.\n\t\t*   VRApplicationError_ApplicationAlreadyStarting - This application is already starting. This is a permanent failure.\n\t\t*   VRApplicationError_LaunchInProgress\t      - A different application is already starting. This is a permanent failure.\n\t\t*   VRApplicationError_None                   - Go ahead and launch. Everything is clear.\n\t\t*/\n\t\tvirtual EVRApplicationError PerformApplicationPrelaunchCheck( const char *pchAppKey ) = 0;\n\n\t\t/** Returns a string for an application transition state */\n\t\tvirtual const char *GetApplicationsTransitionStateNameFromEnum( EVRApplicationTransitionState state ) = 0;\n\n\t\t/** Returns true if the outgoing scene app has requested a save prompt before exiting */\n\t\tvirtual bool IsQuitUserPromptRequested() = 0;\n\n\t\t/** Starts a subprocess within the calling application. This\n\t\t* suppresses all application transition UI and automatically identifies the new executable \n\t\t* as part of the same application. On success the calling process should exit immediately. \n\t\t* If working directory is NULL or \"\" the directory portion of the binary path will be \n\t\t* the working directory. */\n\t\tvirtual EVRApplicationError LaunchInternalProcess( const char *pchBinaryPath, const char *pchArguments, const char *pchWorkingDirectory ) = 0;\n\n\t\t/** Returns the current scene process ID according to the application system. A scene process will get scene\n\t\t* focus once it starts rendering, but it will appear here once it calls VR_Init with the Scene application\n\t\t* type. */\n\t\tvirtual uint32_t GetCurrentSceneProcessId() = 0;\n\t};\n\n\tstatic const char * const IVRApplications_Version = \"IVRApplications_006\";\n\n} // namespace vr\n\n// ivrsettings.h\nnamespace vr\n{\n\tenum EVRSettingsError\n\t{\n\t\tVRSettingsError_None = 0,\n\t\tVRSettingsError_IPCFailed = 1,\n\t\tVRSettingsError_WriteFailed = 2,\n\t\tVRSettingsError_ReadFailed = 3,\n\t\tVRSettingsError_JsonParseFailed = 4,\n\t\tVRSettingsError_UnsetSettingHasNoDefault = 5, // This will be returned if the setting does not appear in the appropriate default file and has not been set\n\t};\n\n\t// The maximum length of a settings key\n\tstatic const uint32_t k_unMaxSettingsKeyLength = 128;\n\n\tclass IVRSettings\n\t{\n\tpublic:\n\t\tvirtual const char *GetSettingsErrorNameFromEnum( EVRSettingsError eError ) = 0;\n\n\t\t// Returns true if file sync occurred (force or settings dirty)\n\t\tvirtual bool Sync( bool bForce = false, EVRSettingsError *peError = nullptr ) = 0;\n\n\t\tvirtual void SetBool( const char *pchSection, const char *pchSettingsKey, bool bValue, EVRSettingsError *peError = nullptr ) = 0;\n\t\tvirtual void SetInt32( const char *pchSection, const char *pchSettingsKey, int32_t nValue, EVRSettingsError *peError = nullptr ) = 0;\n\t\tvirtual void SetFloat( const char *pchSection, const char *pchSettingsKey, float flValue, EVRSettingsError *peError = nullptr ) = 0;\n\t\tvirtual void SetString( const char *pchSection, const char *pchSettingsKey, const char *pchValue, EVRSettingsError *peError = nullptr ) = 0;\n\n\t\t// Users of the system need to provide a proper default in default.vrsettings in the resources/settings/ directory\n\t\t// of either the runtime or the driver_xxx directory. Otherwise the default will be false, 0, 0.0 or \"\"\n\t\tvirtual bool GetBool( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0;\n\t\tvirtual int32_t GetInt32( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0;\n\t\tvirtual float GetFloat( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0;\n\t\tvirtual void GetString( const char *pchSection, const char *pchSettingsKey, VR_OUT_STRING() char *pchValue, uint32_t unValueLen, EVRSettingsError *peError = nullptr ) = 0;\n\n\t\tvirtual void RemoveSection( const char *pchSection, EVRSettingsError *peError = nullptr ) = 0;\n\t\tvirtual void RemoveKeyInSection( const char *pchSection, const char *pchSettingsKey, EVRSettingsError *peError = nullptr ) = 0;\n\t};\n\n\t//-----------------------------------------------------------------------------\n\tstatic const char * const IVRSettings_Version = \"IVRSettings_002\";\n\n\t//-----------------------------------------------------------------------------\n\t// steamvr keys\n\tstatic const char * const k_pch_SteamVR_Section = \"steamvr\";\n\tstatic const char * const k_pch_SteamVR_RequireHmd_String = \"requireHmd\";\n\tstatic const char * const k_pch_SteamVR_ForcedDriverKey_String = \"forcedDriver\";\n\tstatic const char * const k_pch_SteamVR_ForcedHmdKey_String = \"forcedHmd\";\n\tstatic const char * const k_pch_SteamVR_DisplayDebug_Bool = \"displayDebug\";\n\tstatic const char * const k_pch_SteamVR_DebugProcessPipe_String = \"debugProcessPipe\";\n\tstatic const char * const k_pch_SteamVR_DisplayDebugX_Int32 = \"displayDebugX\";\n\tstatic const char * const k_pch_SteamVR_DisplayDebugY_Int32 = \"displayDebugY\";\n\tstatic const char * const k_pch_SteamVR_SendSystemButtonToAllApps_Bool= \"sendSystemButtonToAllApps\";\n\tstatic const char * const k_pch_SteamVR_LogLevel_Int32 = \"loglevel\";\n\tstatic const char * const k_pch_SteamVR_IPD_Float = \"ipd\";\n\tstatic const char * const k_pch_SteamVR_Background_String = \"background\";\n\tstatic const char * const k_pch_SteamVR_BackgroundUseDomeProjection_Bool = \"backgroundUseDomeProjection\";\n\tstatic const char * const k_pch_SteamVR_BackgroundCameraHeight_Float = \"backgroundCameraHeight\";\n\tstatic const char * const k_pch_SteamVR_BackgroundDomeRadius_Float = \"backgroundDomeRadius\";\n\tstatic const char * const k_pch_SteamVR_GridColor_String = \"gridColor\";\n\tstatic const char * const k_pch_SteamVR_PlayAreaColor_String = \"playAreaColor\";\n\tstatic const char * const k_pch_SteamVR_ShowStage_Bool = \"showStage\";\n\tstatic const char * const k_pch_SteamVR_ActivateMultipleDrivers_Bool = \"activateMultipleDrivers\";\n\tstatic const char * const k_pch_SteamVR_DirectMode_Bool = \"directMode\";\n\tstatic const char * const k_pch_SteamVR_DirectModeEdidVid_Int32 = \"directModeEdidVid\";\n\tstatic const char * const k_pch_SteamVR_DirectModeEdidPid_Int32 = \"directModeEdidPid\";\n\tstatic const char * const k_pch_SteamVR_UsingSpeakers_Bool = \"usingSpeakers\";\n\tstatic const char * const k_pch_SteamVR_SpeakersForwardYawOffsetDegrees_Float = \"speakersForwardYawOffsetDegrees\";\n\tstatic const char * const k_pch_SteamVR_BaseStationPowerManagement_Bool = \"basestationPowerManagement\";\n\tstatic const char * const k_pch_SteamVR_NeverKillProcesses_Bool = \"neverKillProcesses\";\n\tstatic const char * const k_pch_SteamVR_SupersampleScale_Float = \"supersampleScale\";\n\tstatic const char * const k_pch_SteamVR_AllowAsyncReprojection_Bool = \"allowAsyncReprojection\";\n\tstatic const char * const k_pch_SteamVR_AllowReprojection_Bool = \"allowInterleavedReprojection\";\n\tstatic const char * const k_pch_SteamVR_ForceReprojection_Bool = \"forceReprojection\";\n\tstatic const char * const k_pch_SteamVR_ForceFadeOnBadTracking_Bool = \"forceFadeOnBadTracking\";\n\tstatic const char * const k_pch_SteamVR_DefaultMirrorView_Int32 = \"defaultMirrorView\";\n\tstatic const char * const k_pch_SteamVR_ShowMirrorView_Bool = \"showMirrorView\";\n\tstatic const char * const k_pch_SteamVR_MirrorViewGeometry_String = \"mirrorViewGeometry\";\n\tstatic const char * const k_pch_SteamVR_StartMonitorFromAppLaunch = \"startMonitorFromAppLaunch\";\n\tstatic const char * const k_pch_SteamVR_StartCompositorFromAppLaunch_Bool = \"startCompositorFromAppLaunch\";\n\tstatic const char * const k_pch_SteamVR_StartDashboardFromAppLaunch_Bool = \"startDashboardFromAppLaunch\";\n\tstatic const char * const k_pch_SteamVR_StartOverlayAppsFromDashboard_Bool = \"startOverlayAppsFromDashboard\";\n\tstatic const char * const k_pch_SteamVR_EnableHomeApp = \"enableHomeApp\";\n\tstatic const char * const k_pch_SteamVR_CycleBackgroundImageTimeSec_Int32 = \"CycleBackgroundImageTimeSec\";\n\tstatic const char * const k_pch_SteamVR_RetailDemo_Bool = \"retailDemo\";\n\tstatic const char * const k_pch_SteamVR_IpdOffset_Float = \"ipdOffset\";\n\tstatic const char * const k_pch_SteamVR_AllowSupersampleFiltering_Bool = \"allowSupersampleFiltering\";\n\tstatic const char * const k_pch_SteamVR_SupersampleManualOverride_Bool = \"supersampleManualOverride\";\n\tstatic const char * const k_pch_SteamVR_EnableLinuxVulkanAsync_Bool = \"enableLinuxVulkanAsync\";\n\tstatic const char * const k_pch_SteamVR_AllowDisplayLockedMode_Bool = \"allowDisplayLockedMode\";\n\tstatic const char * const k_pch_SteamVR_HaveStartedTutorialForNativeChaperoneDriver_Bool = \"haveStartedTutorialForNativeChaperoneDriver\";\n\tstatic const char * const k_pch_SteamVR_ForceWindows32bitVRMonitor = \"forceWindows32BitVRMonitor\";\n\tstatic const char * const k_pch_SteamVR_DebugInput = \"debugInput\";\n\tstatic const char * const k_pch_SteamVR_LegacyInputRebinding = \"legacyInputRebinding\";\n\tstatic const char * const k_pch_SteamVR_DebugInputBinding = \"debugInputBinding\";\n\n\t//-----------------------------------------------------------------------------\n\t// lighthouse keys\n\tstatic const char * const k_pch_Lighthouse_Section = \"driver_lighthouse\";\n\tstatic const char * const k_pch_Lighthouse_DisableIMU_Bool = \"disableimu\";\n\tstatic const char * const k_pch_Lighthouse_DisableIMUExceptHMD_Bool = \"disableimuexcepthmd\";\n\tstatic const char * const k_pch_Lighthouse_UseDisambiguation_String = \"usedisambiguation\";\n\tstatic const char * const k_pch_Lighthouse_DisambiguationDebug_Int32 = \"disambiguationdebug\";\n\tstatic const char * const k_pch_Lighthouse_PrimaryBasestation_Int32 = \"primarybasestation\";\n\tstatic const char * const k_pch_Lighthouse_DBHistory_Bool = \"dbhistory\";\n\tstatic const char * const k_pch_Lighthouse_EnableBluetooth_Bool = \"enableBluetooth\";\n\tstatic const char * const k_pch_Lighthouse_PowerManagedBaseStations_String = \"PowerManagedBaseStations\";\n\n\t//-----------------------------------------------------------------------------\n\t// null keys\n\tstatic const char * const k_pch_Null_Section = \"driver_null\";\n\tstatic const char * const k_pch_Null_SerialNumber_String = \"serialNumber\";\n\tstatic const char * const k_pch_Null_ModelNumber_String = \"modelNumber\";\n\tstatic const char * const k_pch_Null_WindowX_Int32 = \"windowX\";\n\tstatic const char * const k_pch_Null_WindowY_Int32 = \"windowY\";\n\tstatic const char * const k_pch_Null_WindowWidth_Int32 = \"windowWidth\";\n\tstatic const char * const k_pch_Null_WindowHeight_Int32 = \"windowHeight\";\n\tstatic const char * const k_pch_Null_RenderWidth_Int32 = \"renderWidth\";\n\tstatic const char * const k_pch_Null_RenderHeight_Int32 = \"renderHeight\";\n\tstatic const char * const k_pch_Null_SecondsFromVsyncToPhotons_Float = \"secondsFromVsyncToPhotons\";\n\tstatic const char * const k_pch_Null_DisplayFrequency_Float = \"displayFrequency\";\n\n\t//-----------------------------------------------------------------------------\n\t// user interface keys\n\tstatic const char * const k_pch_UserInterface_Section = \"userinterface\";\n\tstatic const char * const k_pch_UserInterface_StatusAlwaysOnTop_Bool = \"StatusAlwaysOnTop\";\n\tstatic const char * const k_pch_UserInterface_MinimizeToTray_Bool = \"MinimizeToTray\";\n\tstatic const char * const k_pch_UserInterface_Screenshots_Bool = \"screenshots\";\n\tstatic const char * const k_pch_UserInterface_ScreenshotType_Int = \"screenshotType\";\n\n\t//-----------------------------------------------------------------------------\n\t// notification keys\n\tstatic const char * const k_pch_Notifications_Section = \"notifications\";\n\tstatic const char * const k_pch_Notifications_DoNotDisturb_Bool = \"DoNotDisturb\";\n\n\t//-----------------------------------------------------------------------------\n\t// keyboard keys\n\tstatic const char * const k_pch_Keyboard_Section = \"keyboard\";\n\tstatic const char * const k_pch_Keyboard_TutorialCompletions = \"TutorialCompletions\";\n\tstatic const char * const k_pch_Keyboard_ScaleX = \"ScaleX\";\n\tstatic const char * const k_pch_Keyboard_ScaleY = \"ScaleY\";\n\tstatic const char * const k_pch_Keyboard_OffsetLeftX = \"OffsetLeftX\";\n\tstatic const char * const k_pch_Keyboard_OffsetRightX = \"OffsetRightX\";\n\tstatic const char * const k_pch_Keyboard_OffsetY = \"OffsetY\";\n\tstatic const char * const k_pch_Keyboard_Smoothing = \"Smoothing\";\n\n\t//-----------------------------------------------------------------------------\n\t// perf keys\n\tstatic const char * const k_pch_Perf_Section = \"perfcheck\";\n\tstatic const char * const k_pch_Perf_HeuristicActive_Bool = \"heuristicActive\";\n\tstatic const char * const k_pch_Perf_NotifyInHMD_Bool = \"warnInHMD\";\n\tstatic const char * const k_pch_Perf_NotifyOnlyOnce_Bool = \"warnOnlyOnce\";\n\tstatic const char * const k_pch_Perf_AllowTimingStore_Bool = \"allowTimingStore\";\n\tstatic const char * const k_pch_Perf_SaveTimingsOnExit_Bool = \"saveTimingsOnExit\";\n\tstatic const char * const k_pch_Perf_TestData_Float = \"perfTestData\";\n\tstatic const char * const k_pch_Perf_LinuxGPUProfiling_Bool = \"linuxGPUProfiling\";\n\n\t//-----------------------------------------------------------------------------\n\t// collision bounds keys\n\tstatic const char * const k_pch_CollisionBounds_Section = \"collisionBounds\";\n\tstatic const char * const k_pch_CollisionBounds_Style_Int32 = \"CollisionBoundsStyle\";\n\tstatic const char * const k_pch_CollisionBounds_GroundPerimeterOn_Bool = \"CollisionBoundsGroundPerimeterOn\";\n\tstatic const char * const k_pch_CollisionBounds_CenterMarkerOn_Bool = \"CollisionBoundsCenterMarkerOn\";\n\tstatic const char * const k_pch_CollisionBounds_PlaySpaceOn_Bool = \"CollisionBoundsPlaySpaceOn\";\n\tstatic const char * const k_pch_CollisionBounds_FadeDistance_Float = \"CollisionBoundsFadeDistance\";\n\tstatic const char * const k_pch_CollisionBounds_ColorGammaR_Int32 = \"CollisionBoundsColorGammaR\";\n\tstatic const char * const k_pch_CollisionBounds_ColorGammaG_Int32 = \"CollisionBoundsColorGammaG\";\n\tstatic const char * const k_pch_CollisionBounds_ColorGammaB_Int32 = \"CollisionBoundsColorGammaB\";\n\tstatic const char * const k_pch_CollisionBounds_ColorGammaA_Int32 = \"CollisionBoundsColorGammaA\";\n\n\t//-----------------------------------------------------------------------------\n\t// camera keys\n\tstatic const char * const k_pch_Camera_Section = \"camera\";\n\tstatic const char * const k_pch_Camera_EnableCamera_Bool = \"enableCamera\";\n\tstatic const char * const k_pch_Camera_EnableCameraInDashboard_Bool = \"enableCameraInDashboard\";\n\tstatic const char * const k_pch_Camera_EnableCameraForCollisionBounds_Bool = \"enableCameraForCollisionBounds\";\n\tstatic const char * const k_pch_Camera_EnableCameraForRoomView_Bool = \"enableCameraForRoomView\";\n\tstatic const char * const k_pch_Camera_BoundsColorGammaR_Int32 = \"cameraBoundsColorGammaR\";\n\tstatic const char * const k_pch_Camera_BoundsColorGammaG_Int32 = \"cameraBoundsColorGammaG\";\n\tstatic const char * const k_pch_Camera_BoundsColorGammaB_Int32 = \"cameraBoundsColorGammaB\";\n\tstatic const char * const k_pch_Camera_BoundsColorGammaA_Int32 = \"cameraBoundsColorGammaA\";\n\tstatic const char * const k_pch_Camera_BoundsStrength_Int32 = \"cameraBoundsStrength\";\n\tstatic const char * const k_pch_Camera_RoomViewMode_Int32 = \"cameraRoomViewMode\";\n\n\t//-----------------------------------------------------------------------------\n\t// audio keys\n\tstatic const char * const k_pch_audio_Section = \"audio\";\n\tstatic const char * const k_pch_audio_OnPlaybackDevice_String = \"onPlaybackDevice\";\n\tstatic const char * const k_pch_audio_OnRecordDevice_String = \"onRecordDevice\";\n\tstatic const char * const k_pch_audio_OnPlaybackMirrorDevice_String = \"onPlaybackMirrorDevice\";\n\tstatic const char * const k_pch_audio_OffPlaybackDevice_String = \"offPlaybackDevice\";\n\tstatic const char * const k_pch_audio_OffRecordDevice_String = \"offRecordDevice\";\n\tstatic const char * const k_pch_audio_VIVEHDMIGain = \"viveHDMIGain\";\n\n\t//-----------------------------------------------------------------------------\n\t// power management keys\n\tstatic const char * const k_pch_Power_Section = \"power\";\n\tstatic const char * const k_pch_Power_PowerOffOnExit_Bool = \"powerOffOnExit\";\n\tstatic const char * const k_pch_Power_TurnOffScreensTimeout_Float = \"turnOffScreensTimeout\";\n\tstatic const char * const k_pch_Power_TurnOffControllersTimeout_Float = \"turnOffControllersTimeout\";\n\tstatic const char * const k_pch_Power_ReturnToWatchdogTimeout_Float = \"returnToWatchdogTimeout\";\n\tstatic const char * const k_pch_Power_AutoLaunchSteamVROnButtonPress = \"autoLaunchSteamVROnButtonPress\";\n\tstatic const char * const k_pch_Power_PauseCompositorOnStandby_Bool = \"pauseCompositorOnStandby\";\n\n\t//-----------------------------------------------------------------------------\n\t// dashboard keys\n\tstatic const char * const k_pch_Dashboard_Section = \"dashboard\";\n\tstatic const char * const k_pch_Dashboard_EnableDashboard_Bool = \"enableDashboard\";\n\tstatic const char * const k_pch_Dashboard_ArcadeMode_Bool = \"arcadeMode\";\n\tstatic const char * const k_pch_Dashboard_EnableWebUI = \"webUI\";\n\tstatic const char * const k_pch_Dashboard_EnableWebUIDevTools = \"webUIDevTools\";\n\n\t//-----------------------------------------------------------------------------\n\t// model skin keys\n\tstatic const char * const k_pch_modelskin_Section = \"modelskins\";\n\n\t//-----------------------------------------------------------------------------\n\t// driver keys - These could be checked in any driver_<name> section\n\tstatic const char * const k_pch_Driver_Enable_Bool = \"enable\";\n\n\t//-----------------------------------------------------------------------------\n\t// web interface keys\n\tstatic const char* const k_pch_WebInterface_Section = \"WebInterface\";\n\tstatic const char* const k_pch_WebInterface_WebPort_String = \"WebPort\";\n\n\t//-----------------------------------------------------------------------------\n\t// tracking overrides - keys are device paths, values are the device paths their\n\t//  tracking/pose information overrides\n\tstatic const char* const k_pch_TrackingOverride_Section = \"TrackingOverrides\";\n\n\t//-----------------------------------------------------------------------------\n\t// per-app keys - the section name for these is the app key itself. Some of these are prefixed by the controller type\n\tstatic const char* const k_pch_App_BindingAutosaveURLSuffix_String = \"AutosaveURL\";\n\tstatic const char* const k_pch_App_BindingCurrentURLSuffix_String = \"CurrentURL\";\n\tstatic const char* const k_pch_App_NeedToUpdateAutosaveSuffix_Bool = \"NeedToUpdateAutosave\";\n\tstatic const char* const k_pch_App_ActionManifestURL_String = \"ActionManifestURL\";\n\n} // namespace vr\n\n// ivrchaperone.h\nnamespace vr\n{\n\n#pragma pack( push, 8 )\n\nenum ChaperoneCalibrationState\n{\n\t// OK!\n\tChaperoneCalibrationState_OK = 1,\t\t\t\t\t\t\t\t\t// Chaperone is fully calibrated and working correctly\n\n\t// Warnings\n\tChaperoneCalibrationState_Warning = 100,\n\tChaperoneCalibrationState_Warning_BaseStationMayHaveMoved = 101,\t// A base station thinks that it might have moved\n\tChaperoneCalibrationState_Warning_BaseStationRemoved = 102,\t\t\t// There are less base stations than when calibrated\n\tChaperoneCalibrationState_Warning_SeatedBoundsInvalid = 103,\t\t// Seated bounds haven't been calibrated for the current tracking center\n\n\t// Errors\n\tChaperoneCalibrationState_Error = 200,\t\t\t\t\t\t\t\t// The UniverseID is invalid\n\tChaperoneCalibrationState_Error_BaseStationUninitialized = 201,\t\t// Tracking center hasn't be calibrated for at least one of the base stations\n\tChaperoneCalibrationState_Error_BaseStationConflict = 202,\t\t\t// Tracking center is calibrated, but base stations disagree on the tracking space\n\tChaperoneCalibrationState_Error_PlayAreaInvalid = 203,\t\t\t\t// Play Area hasn't been calibrated for the current tracking center\n\tChaperoneCalibrationState_Error_CollisionBoundsInvalid = 204,\t\t// Collision Bounds haven't been calibrated for the current tracking center\n};\n\n\n/** HIGH LEVEL TRACKING SPACE ASSUMPTIONS:\n* 0,0,0 is the preferred standing area center.\n* 0Y is the floor height.\n* -Z is the preferred forward facing direction. */\nclass IVRChaperone\n{\npublic:\n\n\t/** Get the current state of Chaperone calibration. This state can change at any time during a session due to physical base station changes. **/\n\tvirtual ChaperoneCalibrationState GetCalibrationState() = 0;\n\n\t/** Returns the width and depth of the Play Area (formerly named Soft Bounds) in X and Z. \n\t* Tracking space center (0,0,0) is the center of the Play Area. **/\n\tvirtual bool GetPlayAreaSize( float *pSizeX, float *pSizeZ ) = 0;\n\n\t/** Returns the 4 corner positions of the Play Area (formerly named Soft Bounds).\n\t* Corners are in counter-clockwise order.\n\t* Standing center (0,0,0) is the center of the Play Area.\n\t* It's a rectangle.\n\t* 2 sides are parallel to the X axis and 2 sides are parallel to the Z axis.\n\t* Height of every corner is 0Y (on the floor). **/\n\tvirtual bool GetPlayAreaRect( HmdQuad_t *rect ) = 0;\n\n\t/** Reload Chaperone data from the .vrchap file on disk. */\n\tvirtual void ReloadInfo( void ) = 0;\n\n\t/** Optionally give the chaperone system a hit about the color and brightness in the scene **/\n\tvirtual void SetSceneColor( HmdColor_t color ) = 0;\n\n\t/** Get the current chaperone bounds draw color and brightness **/\n\tvirtual void GetBoundsColor( HmdColor_t *pOutputColorArray, int nNumOutputColors, float flCollisionBoundsFadeDistance, HmdColor_t *pOutputCameraColor ) = 0;\n\n\t/** Determine whether the bounds are showing right now **/\n\tvirtual bool AreBoundsVisible() = 0;\n\n\t/** Force the bounds to show, mostly for utilities **/\n\tvirtual void ForceBoundsVisible( bool bForce ) = 0;\n};\n\nstatic const char * const IVRChaperone_Version = \"IVRChaperone_003\";\n\n#pragma pack( pop )\n\n}\n\n// ivrchaperonesetup.h\nnamespace vr\n{\n\nenum EChaperoneConfigFile\n{\n\tEChaperoneConfigFile_Live = 1,\t\t// The live chaperone config, used by most applications and games\n\tEChaperoneConfigFile_Temp = 2,\t\t// The temporary chaperone config, used to live-preview collision bounds in room setup\n};\n\nenum EChaperoneImportFlags\n{\n\tEChaperoneImport_BoundsOnly = 0x0001,\n};\n\n/** Manages the working copy of the chaperone info. By default this will be the same as the \n* live copy. Any changes made with this interface will stay in the working copy until \n* CommitWorkingCopy() is called, at which point the working copy and the live copy will be \n* the same again. */\nclass IVRChaperoneSetup\n{\npublic:\n\n\t/** Saves the current working copy to disk */\n\tvirtual bool CommitWorkingCopy( EChaperoneConfigFile configFile ) = 0;\n\n\t/** Reverts the working copy to match the live chaperone calibration.\n\t* To modify existing data this MUST be do WHILE getting a non-error ChaperoneCalibrationStatus.\n\t* Only after this should you do gets and sets on the existing data. */\n\tvirtual void RevertWorkingCopy() = 0;\n\n\t/** Returns the width and depth of the Play Area (formerly named Soft Bounds) in X and Z from the working copy.\n\t* Tracking space center (0,0,0) is the center of the Play Area. */\n\tvirtual bool GetWorkingPlayAreaSize( float *pSizeX, float *pSizeZ ) = 0;\n\n\t/** Returns the 4 corner positions of the Play Area (formerly named Soft Bounds) from the working copy.\n\t* Corners are in clockwise order.\n\t* Tracking space center (0,0,0) is the center of the Play Area.\n\t* It's a rectangle.\n\t* 2 sides are parallel to the X axis and 2 sides are parallel to the Z axis.\n\t* Height of every corner is 0Y (on the floor). **/\n\tvirtual bool GetWorkingPlayAreaRect( HmdQuad_t *rect ) = 0;\n\n\t/** Returns the number of Quads if the buffer points to null. Otherwise it returns Quads \n\t* into the buffer up to the max specified from the working copy. */\n\tvirtual bool GetWorkingCollisionBoundsInfo( VR_OUT_ARRAY_COUNT(punQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t* punQuadsCount ) = 0;\n\n\t/** Returns the number of Quads if the buffer points to null. Otherwise it returns Quads \n\t* into the buffer up to the max specified. */\n\tvirtual bool GetLiveCollisionBoundsInfo( VR_OUT_ARRAY_COUNT(punQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t* punQuadsCount ) = 0;\n\n\t/** Returns the preferred seated position from the working copy. */\n\tvirtual bool GetWorkingSeatedZeroPoseToRawTrackingPose( HmdMatrix34_t *pmatSeatedZeroPoseToRawTrackingPose ) = 0;\n\n\t/** Returns the standing origin from the working copy. */\n\tvirtual bool GetWorkingStandingZeroPoseToRawTrackingPose( HmdMatrix34_t *pmatStandingZeroPoseToRawTrackingPose ) = 0;\n\n\t/** Sets the Play Area in the working copy. */\n\tvirtual void SetWorkingPlayAreaSize( float sizeX, float sizeZ ) = 0;\n\n\t/** Sets the Collision Bounds in the working copy. */\n\tvirtual void SetWorkingCollisionBoundsInfo( VR_ARRAY_COUNT(unQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t unQuadsCount ) = 0;\n\n\t/** Sets the preferred seated position in the working copy. */\n\tvirtual void SetWorkingSeatedZeroPoseToRawTrackingPose( const HmdMatrix34_t *pMatSeatedZeroPoseToRawTrackingPose ) = 0;\n\n\t/** Sets the preferred standing position in the working copy. */\n\tvirtual void SetWorkingStandingZeroPoseToRawTrackingPose( const HmdMatrix34_t *pMatStandingZeroPoseToRawTrackingPose ) = 0;\n\n\t/** Tear everything down and reload it from the file on disk */\n\tvirtual void ReloadFromDisk( EChaperoneConfigFile configFile ) = 0;\n\n\t/** Returns the preferred seated position. */\n\tvirtual bool GetLiveSeatedZeroPoseToRawTrackingPose( HmdMatrix34_t *pmatSeatedZeroPoseToRawTrackingPose ) = 0;\n\n\tvirtual void SetWorkingCollisionBoundsTagsInfo( VR_ARRAY_COUNT(unTagCount) uint8_t *pTagsBuffer, uint32_t unTagCount ) = 0;\n\tvirtual bool GetLiveCollisionBoundsTagsInfo( VR_OUT_ARRAY_COUNT(punTagCount) uint8_t *pTagsBuffer, uint32_t *punTagCount ) = 0;\n\n\tvirtual bool SetWorkingPhysicalBoundsInfo( VR_ARRAY_COUNT(unQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t unQuadsCount ) = 0;\n\tvirtual bool GetLivePhysicalBoundsInfo( VR_OUT_ARRAY_COUNT(punQuadsCount) HmdQuad_t *pQuadsBuffer, uint32_t* punQuadsCount ) = 0;\n\n\tvirtual bool ExportLiveToBuffer( VR_OUT_STRING() char *pBuffer, uint32_t *pnBufferLength ) = 0;\n\tvirtual bool ImportFromBufferToWorking( const char *pBuffer, uint32_t nImportFlags ) = 0;\n};\n\nstatic const char * const IVRChaperoneSetup_Version = \"IVRChaperoneSetup_005\";\n\n\n}\n\n// ivrcompositor.h\nnamespace vr\n{\n\n#pragma pack( push, 8 )\n\n/** Errors that can occur with the VR compositor */\nenum EVRCompositorError\n{\n\tVRCompositorError_None\t\t\t\t\t\t= 0,\n\tVRCompositorError_RequestFailed\t\t\t\t= 1,\n\tVRCompositorError_IncompatibleVersion\t\t= 100,\n\tVRCompositorError_DoNotHaveFocus\t\t\t= 101,\n\tVRCompositorError_InvalidTexture\t\t\t= 102,\n\tVRCompositorError_IsNotSceneApplication\t\t= 103,\n\tVRCompositorError_TextureIsOnWrongDevice\t= 104,\n\tVRCompositorError_TextureUsesUnsupportedFormat = 105,\n\tVRCompositorError_SharedTexturesNotSupported = 106,\n\tVRCompositorError_IndexOutOfRange\t\t\t= 107,\n\tVRCompositorError_AlreadySubmitted\t\t\t= 108,\n\tVRCompositorError_InvalidBounds\t\t\t\t= 109,\n};\n\n/** Timing mode passed to SetExplicitTimingMode(); see that function for documentation */\nenum EVRCompositorTimingMode\n{\n\tVRCompositorTimingMode_Implicit\t\t\t\t\t\t\t\t\t\t\t= 0,\n\tVRCompositorTimingMode_Explicit_RuntimePerformsPostPresentHandoff\t\t= 1,\n\tVRCompositorTimingMode_Explicit_ApplicationPerformsPostPresentHandoff\t= 2,\n};\n\nconst uint32_t VRCompositor_ReprojectionReason_Cpu = 0x01;\nconst uint32_t VRCompositor_ReprojectionReason_Gpu = 0x02;\nconst uint32_t VRCompositor_ReprojectionAsync      = 0x04;\t// This flag indicates the async reprojection mode is active,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// but does not indicate if reprojection actually happened or not.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// Use the ReprojectionReason flags above to check if reprojection\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// was actually applied (i.e. scene texture was reused).\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// NumFramePresents > 1 also indicates the scene texture was reused,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// and also the number of times that it was presented in total.\n\n/** Provides a single frame's timing information to the app */\nstruct Compositor_FrameTiming\n{\n\tuint32_t m_nSize; // Set to sizeof( Compositor_FrameTiming )\n\tuint32_t m_nFrameIndex;\n\tuint32_t m_nNumFramePresents; // number of times this frame was presented\n\tuint32_t m_nNumMisPresented; // number of times this frame was presented on a vsync other than it was originally predicted to\n\tuint32_t m_nNumDroppedFrames; // number of additional times previous frame was scanned out\n\tuint32_t m_nReprojectionFlags;\n\n\t/** Absolute time reference for comparing frames.  This aligns with the vsync that running start is relative to. */\n\tdouble m_flSystemTimeInSeconds;\n\n\t/** These times may include work from other processes due to OS scheduling.\n\t* The fewer packets of work these are broken up into, the less likely this will happen.\n\t* GPU work can be broken up by calling Flush.  This can sometimes be useful to get the GPU started\n\t* processing that work earlier in the frame. */\n\tfloat m_flPreSubmitGpuMs; // time spent rendering the scene (gpu work submitted between WaitGetPoses and second Submit)\n\tfloat m_flPostSubmitGpuMs; // additional time spent rendering by application (e.g. companion window)\n\tfloat m_flTotalRenderGpuMs; // time between work submitted immediately after present (ideally vsync) until the end of compositor submitted work\n\tfloat m_flCompositorRenderGpuMs; // time spend performing distortion correction, rendering chaperone, overlays, etc.\n\tfloat m_flCompositorRenderCpuMs; // time spent on cpu submitting the above work for this frame\n\tfloat m_flCompositorIdleCpuMs; // time spent waiting for running start (application could have used this much more time)\n\n\t/** Miscellaneous measured intervals. */\n\tfloat m_flClientFrameIntervalMs; // time between calls to WaitGetPoses\n\tfloat m_flPresentCallCpuMs; // time blocked on call to present (usually 0.0, but can go long)\n\tfloat m_flWaitForPresentCpuMs; // time spent spin-waiting for frame index to change (not near-zero indicates wait object failure)\n\tfloat m_flSubmitFrameMs; // time spent in IVRCompositor::Submit (not near-zero indicates driver issue)\n\n\t/** The following are all relative to this frame's SystemTimeInSeconds */\n\tfloat m_flWaitGetPosesCalledMs;\n\tfloat m_flNewPosesReadyMs;\n\tfloat m_flNewFrameReadyMs; // second call to IVRCompositor::Submit\n\tfloat m_flCompositorUpdateStartMs;\n\tfloat m_flCompositorUpdateEndMs;\n\tfloat m_flCompositorRenderStartMs;\n\n\tvr::TrackedDevicePose_t m_HmdPose; // pose used by app to render this frame\n};\n\n/** Cumulative stats for current application.  These are not cleared until a new app connects,\n* but they do stop accumulating once the associated app disconnects. */\nstruct Compositor_CumulativeStats\n{\n\tuint32_t m_nPid; // Process id associated with these stats (may no longer be running).\n\tuint32_t m_nNumFramePresents; // total number of times we called present (includes reprojected frames)\n\tuint32_t m_nNumDroppedFrames; // total number of times an old frame was re-scanned out (without reprojection)\n\tuint32_t m_nNumReprojectedFrames; // total number of times a frame was scanned out a second time (with reprojection)\n\n\t/** Values recorded at startup before application has fully faded in the first time. */\n\tuint32_t m_nNumFramePresentsOnStartup;\n\tuint32_t m_nNumDroppedFramesOnStartup;\n\tuint32_t m_nNumReprojectedFramesOnStartup;\n\n\t/** Applications may explicitly fade to the compositor.  This is usually to handle level transitions, and loading often causes\n\t* system wide hitches.  The following stats are collected during this period.  Does not include values recorded during startup. */\n\tuint32_t m_nNumLoading;\n\tuint32_t m_nNumFramePresentsLoading;\n\tuint32_t m_nNumDroppedFramesLoading;\n\tuint32_t m_nNumReprojectedFramesLoading;\n\n\t/** If we don't get a new frame from the app in less than 2.5 frames, then we assume the app has hung and start\n\t* fading back to the compositor.  The following stats are a result of this, and are a subset of those recorded above.\n\t* Does not include values recorded during start up or loading. */\n\tuint32_t m_nNumTimedOut;\n\tuint32_t m_nNumFramePresentsTimedOut;\n\tuint32_t m_nNumDroppedFramesTimedOut;\n\tuint32_t m_nNumReprojectedFramesTimedOut;\n};\n\n#pragma pack( pop )\n\n/** Allows the application to interact with the compositor */\nclass IVRCompositor\n{\npublic:\n\t/** Sets tracking space returned by WaitGetPoses */\n\tvirtual void SetTrackingSpace( ETrackingUniverseOrigin eOrigin ) = 0;\n\n\t/** Gets current tracking space returned by WaitGetPoses */\n\tvirtual ETrackingUniverseOrigin GetTrackingSpace() = 0;\n\n\t/** Scene applications should call this function to get poses to render with (and optionally poses predicted an additional frame out to use for gameplay).\n\t* This function will block until \"running start\" milliseconds before the start of the frame, and should be called at the last moment before needing to\n\t* start rendering.\n\t*\n\t* Return codes:\n\t*\t- IsNotSceneApplication (make sure to call VR_Init with VRApplicaiton_Scene)\n\t*\t- DoNotHaveFocus (some other app has taken focus - this will throttle the call to 10hz to reduce the impact on that app)\n\t*/\n\tvirtual EVRCompositorError WaitGetPoses( VR_ARRAY_COUNT(unRenderPoseArrayCount) TrackedDevicePose_t* pRenderPoseArray, uint32_t unRenderPoseArrayCount,\n\t\tVR_ARRAY_COUNT(unGamePoseArrayCount) TrackedDevicePose_t* pGamePoseArray, uint32_t unGamePoseArrayCount ) = 0;\n\n\t/** Get the last set of poses returned by WaitGetPoses. */\n\tvirtual EVRCompositorError GetLastPoses( VR_ARRAY_COUNT( unRenderPoseArrayCount ) TrackedDevicePose_t* pRenderPoseArray, uint32_t unRenderPoseArrayCount,\n\t\tVR_ARRAY_COUNT( unGamePoseArrayCount ) TrackedDevicePose_t* pGamePoseArray, uint32_t unGamePoseArrayCount ) = 0;\n\n\t/** Interface for accessing last set of poses returned by WaitGetPoses one at a time.\n\t* Returns VRCompositorError_IndexOutOfRange if unDeviceIndex not less than k_unMaxTrackedDeviceCount otherwise VRCompositorError_None.\n\t* It is okay to pass NULL for either pose if you only want one of the values. */\n\tvirtual EVRCompositorError GetLastPoseForTrackedDeviceIndex( TrackedDeviceIndex_t unDeviceIndex, TrackedDevicePose_t *pOutputPose, TrackedDevicePose_t *pOutputGamePose ) = 0;\n\n\t/** Updated scene texture to display. If pBounds is NULL the entire texture will be used.  If called from an OpenGL app, consider adding a glFlush after\n\t* Submitting both frames to signal the driver to start processing, otherwise it may wait until the command buffer fills up, causing the app to miss frames.\n\t*\n\t* OpenGL dirty state:\n\t*\tglBindTexture\n\t*\n\t* Return codes:\n\t*\t- IsNotSceneApplication (make sure to call VR_Init with VRApplicaiton_Scene)\n\t*\t- DoNotHaveFocus (some other app has taken focus)\n\t*\t- TextureIsOnWrongDevice (application did not use proper AdapterIndex - see IVRSystem.GetDXGIOutputInfo)\n\t*\t- SharedTexturesNotSupported (application needs to call CreateDXGIFactory1 or later before creating DX device)\n\t*\t- TextureUsesUnsupportedFormat (scene textures must be compatible with DXGI sharing rules - e.g. uncompressed, no mips, etc.)\n\t*\t- InvalidTexture (usually means bad arguments passed in)\n\t*\t- AlreadySubmitted (app has submitted two left textures or two right textures in a single frame - i.e. before calling WaitGetPoses again)\n\t*/\n\tvirtual EVRCompositorError Submit( EVREye eEye, const Texture_t *pTexture, const VRTextureBounds_t* pBounds = 0, EVRSubmitFlags nSubmitFlags = Submit_Default ) = 0;\n\n\t/** Clears the frame that was sent with the last call to Submit. This will cause the \n\t* compositor to show the grid until Submit is called again. */\n\tvirtual void ClearLastSubmittedFrame() = 0;\n\n\t/** Call immediately after presenting your app's window (i.e. companion window) to unblock the compositor.\n\t* This is an optional call, which only needs to be used if you can't instead call WaitGetPoses immediately after Present.\n\t* For example, if your engine's render and game loop are not on separate threads, or blocking the render thread until 3ms before the next vsync would\n\t* introduce a deadlock of some sort.  This function tells the compositor that you have finished all rendering after having Submitted buffers for both\n\t* eyes, and it is free to start its rendering work.  This should only be called from the same thread you are rendering on. */\n\tvirtual void PostPresentHandoff() = 0;\n\n\t/** Returns true if timing data is filled it.  Sets oldest timing info if nFramesAgo is larger than the stored history.\n\t* Be sure to set timing.size = sizeof(Compositor_FrameTiming) on struct passed in before calling this function. */\n\tvirtual bool GetFrameTiming( Compositor_FrameTiming *pTiming, uint32_t unFramesAgo = 0 ) = 0;\n\n\t/** Interface for copying a range of timing data.  Frames are returned in ascending order (oldest to newest) with the last being the most recent frame.\n\t* Only the first entry's m_nSize needs to be set, as the rest will be inferred from that.  Returns total number of entries filled out. */\n\tvirtual uint32_t GetFrameTimings( Compositor_FrameTiming *pTiming, uint32_t nFrames ) = 0;\n\n\t/** Returns the time in seconds left in the current (as identified by FrameTiming's frameIndex) frame.\n\t* Due to \"running start\", this value may roll over to the next frame before ever reaching 0.0. */\n\tvirtual float GetFrameTimeRemaining() = 0;\n\n\t/** Fills out stats accumulated for the last connected application.  Pass in sizeof( Compositor_CumulativeStats ) as second parameter. */\n\tvirtual void GetCumulativeStats( Compositor_CumulativeStats *pStats, uint32_t nStatsSizeInBytes ) = 0;\n\n\t/** Fades the view on the HMD to the specified color. The fade will take fSeconds, and the color values are between\n\t* 0.0 and 1.0. This color is faded on top of the scene based on the alpha parameter. Removing the fade color instantly \n\t* would be FadeToColor( 0.0, 0.0, 0.0, 0.0, 0.0 ).  Values are in un-premultiplied alpha space. */\n\tvirtual void FadeToColor( float fSeconds, float fRed, float fGreen, float fBlue, float fAlpha, bool bBackground = false ) = 0;\n\n\t/** Get current fade color value. */\n\tvirtual HmdColor_t GetCurrentFadeColor( bool bBackground = false ) = 0;\n\n\t/** Fading the Grid in or out in fSeconds */\n\tvirtual void FadeGrid( float fSeconds, bool bFadeIn ) = 0;\n\n\t/** Get current alpha value of grid. */\n\tvirtual float GetCurrentGridAlpha() = 0;\n\n\t/** Override the skybox used in the compositor (e.g. for during level loads when the app can't feed scene images fast enough)\n\t* Order is Front, Back, Left, Right, Top, Bottom.  If only a single texture is passed, it is assumed in lat-long format.\n\t* If two are passed, it is assumed a lat-long stereo pair. */\n\tvirtual EVRCompositorError SetSkyboxOverride( VR_ARRAY_COUNT( unTextureCount ) const Texture_t *pTextures, uint32_t unTextureCount ) = 0;\n\n\t/** Resets compositor skybox back to defaults. */\n\tvirtual void ClearSkyboxOverride() = 0;\n\n\t/** Brings the compositor window to the front. This is useful for covering any other window that may be on the HMD\n\t* and is obscuring the compositor window. */\n\tvirtual void CompositorBringToFront() = 0;\n\n\t/** Pushes the compositor window to the back. This is useful for allowing other applications to draw directly to the HMD. */\n\tvirtual void CompositorGoToBack() = 0;\n\n\t/** Tells the compositor process to clean up and exit. You do not need to call this function at shutdown. Under normal \n\t* circumstances the compositor will manage its own life cycle based on what applications are running. */\n\tvirtual void CompositorQuit() = 0;\n\t\n\t/** Return whether the compositor is fullscreen */\n\tvirtual bool IsFullscreen() = 0;\n\n\t/** Returns the process ID of the process that is currently rendering the scene */\n\tvirtual uint32_t GetCurrentSceneFocusProcess() = 0;\n\n\t/** Returns the process ID of the process that rendered the last frame (or 0 if the compositor itself rendered the frame.)\n\t* Returns 0 when fading out from an app and the app's process Id when fading into an app. */\n\tvirtual uint32_t GetLastFrameRenderer() = 0;\n\n\t/** Returns true if the current process has the scene focus */\n\tvirtual bool CanRenderScene() = 0;\n\n\t/** Creates a window on the primary monitor to display what is being shown in the headset. */\n\tvirtual void ShowMirrorWindow() = 0;\n\n\t/** Closes the mirror window. */\n\tvirtual void HideMirrorWindow() = 0;\n\n\t/** Returns true if the mirror window is shown. */\n\tvirtual bool IsMirrorWindowVisible() = 0;\n\n\t/** Writes all images that the compositor knows about (including overlays) to a 'screenshots' folder in the SteamVR runtime root. */\n\tvirtual void CompositorDumpImages() = 0;\n\n\t/** Let an app know it should be rendering with low resources. */\n\tvirtual bool ShouldAppRenderWithLowResources() = 0;\n\n\t/** Override interleaved reprojection logic to force on. */\n\tvirtual void ForceInterleavedReprojectionOn( bool bOverride ) = 0;\n\n\t/** Force reconnecting to the compositor process. */\n\tvirtual void ForceReconnectProcess() = 0;\n\n\t/** Temporarily suspends rendering (useful for finer control over scene transitions). */\n\tvirtual void SuspendRendering( bool bSuspend ) = 0;\n\n\t/** Opens a shared D3D11 texture with the undistorted composited image for each eye.  Use ReleaseMirrorTextureD3D11 when finished\n\t* instead of calling Release on the resource itself. */\n\tvirtual vr::EVRCompositorError GetMirrorTextureD3D11( vr::EVREye eEye, void *pD3D11DeviceOrResource, void **ppD3D11ShaderResourceView ) = 0;\n\tvirtual void ReleaseMirrorTextureD3D11( void *pD3D11ShaderResourceView ) = 0;\n\n\t/** Access to mirror textures from OpenGL. */\n\tvirtual vr::EVRCompositorError GetMirrorTextureGL( vr::EVREye eEye, vr::glUInt_t *pglTextureId, vr::glSharedTextureHandle_t *pglSharedTextureHandle ) = 0;\n\tvirtual bool ReleaseSharedGLTexture( vr::glUInt_t glTextureId, vr::glSharedTextureHandle_t glSharedTextureHandle ) = 0;\n\tvirtual void LockGLSharedTextureForAccess( vr::glSharedTextureHandle_t glSharedTextureHandle ) = 0;\n\tvirtual void UnlockGLSharedTextureForAccess( vr::glSharedTextureHandle_t glSharedTextureHandle ) = 0;\n\n\t/** [Vulkan Only]\n\t* return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing\n\t* null.  The string will be a space separated list of-required instance extensions to enable in VkCreateInstance */\n\tvirtual uint32_t GetVulkanInstanceExtensionsRequired( VR_OUT_STRING() char *pchValue, uint32_t unBufferSize ) = 0;\n\n\t/** [Vulkan only]\n\t* return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing\n\t* null.  The string will be a space separated list of required device extensions to enable in VkCreateDevice */\n\tvirtual uint32_t GetVulkanDeviceExtensionsRequired( VkPhysicalDevice_T *pPhysicalDevice, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize ) = 0;\n\n\t/** [ Vulkan/D3D12 Only ]\n\t* There are two purposes for SetExplicitTimingMode:\n\t*\t1. To get a more accurate GPU timestamp for when the frame begins in Vulkan/D3D12 applications.\n\t*\t2. (Optional) To avoid having WaitGetPoses access the Vulkan queue so that the queue can be accessed from\n\t*\tanother thread while WaitGetPoses is executing.\n\t*\n\t* More accurate GPU timestamp for the start of the frame is achieved by the application calling\n\t* SubmitExplicitTimingData immediately before its first submission to the Vulkan/D3D12 queue.\n\t* This is more accurate because normally this GPU timestamp is recorded during WaitGetPoses.  In D3D11, \n\t* WaitGetPoses queues a GPU timestamp write, but it does not actually get submitted to the GPU until the \n\t* application flushes.  By using SubmitExplicitTimingData, the timestamp is recorded at the same place for \n\t* Vulkan/D3D12 as it is for D3D11, resulting in a more accurate GPU time measurement for the frame.\n\t*\n\t* Avoiding WaitGetPoses accessing the Vulkan queue can be achieved using SetExplicitTimingMode as well.  If this is desired,\n\t* the application should set the timing mode to Explicit_ApplicationPerformsPostPresentHandoff and *MUST* call PostPresentHandoff\n\t* itself. If these conditions are met, then WaitGetPoses is guaranteed not to access the queue.  Note that PostPresentHandoff\n\t* and SubmitExplicitTimingData will access the queue, so only WaitGetPoses becomes safe for accessing the queue from another\n\t* thread. */\n\tvirtual void SetExplicitTimingMode( EVRCompositorTimingMode eTimingMode ) = 0;\n\n\t/** [ Vulkan/D3D12 Only ]\n\t* Submit explicit timing data.  When SetExplicitTimingMode is true, this must be called immediately before\n\t* the application's first vkQueueSubmit (Vulkan) or ID3D12CommandQueue::ExecuteCommandLists (D3D12) of each frame.\n\t* This function will insert a GPU timestamp write just before the application starts its rendering.  This function\n\t* will perform a vkQueueSubmit on Vulkan so must not be done simultaneously with VkQueue operations on another thread.\n\t* Returns VRCompositorError_RequestFailed if SetExplicitTimingMode is not enabled. */\n\tvirtual EVRCompositorError SubmitExplicitTimingData() = 0;\n};\n\nstatic const char * const IVRCompositor_Version = \"IVRCompositor_022\";\n\n} // namespace vr\n\n\n\n// ivrnotifications.h\nnamespace vr\n{\n\n#pragma pack( push, 8 )\n\n// Used for passing graphic data\nstruct NotificationBitmap_t\n{\n\tNotificationBitmap_t()\n\t\t: m_pImageData( nullptr )\n\t\t, m_nWidth( 0 )\n\t\t, m_nHeight( 0 )\n\t\t, m_nBytesPerPixel( 0 )\n\t{\n\t};\n\n\tvoid *m_pImageData;\n\tint32_t m_nWidth;\n\tint32_t m_nHeight;\n\tint32_t m_nBytesPerPixel;\n};\n\n\n/** Be aware that the notification type is used as 'priority' to pick the next notification */\nenum EVRNotificationType\n{\n\t/** Transient notifications are automatically hidden after a period of time set by the user. \n\t* They are used for things like information and chat messages that do not require user interaction. */\n\tEVRNotificationType_Transient = 0,\n\n\t/** Persistent notifications are shown to the user until they are hidden by calling RemoveNotification().\n\t* They are used for things like phone calls and alarms that require user interaction. */\n\tEVRNotificationType_Persistent = 1,\n\n\t/** System notifications are shown no matter what. It is expected, that the ulUserValue is used as ID.\n\t * If there is already a system notification in the queue with that ID it is not accepted into the queue\n\t * to prevent spamming with system notification */\n\tEVRNotificationType_Transient_SystemWithUserValue = 2,\n};\n\nenum EVRNotificationStyle\n{\n\t/** Creates a notification with minimal external styling. */\n\tEVRNotificationStyle_None = 0,\n\n\t/** Used for notifications about overlay-level status. In Steam this is used for events like downloads completing. */\n\tEVRNotificationStyle_Application = 100,\n\n\t/** Used for notifications about contacts that are unknown or not available. In Steam this is used for friend invitations and offline friends. */\n\tEVRNotificationStyle_Contact_Disabled = 200,\n\n\t/** Used for notifications about contacts that are available but inactive. In Steam this is used for friends that are online but not playing a game. */\n\tEVRNotificationStyle_Contact_Enabled = 201,\n\n\t/** Used for notifications about contacts that are available and active. In Steam this is used for friends that are online and currently running a game. */\n\tEVRNotificationStyle_Contact_Active = 202,\n};\n\nstatic const uint32_t k_unNotificationTextMaxSize = 256;\n\ntypedef uint32_t VRNotificationId;\n\n\n\n#pragma pack( pop )\n\n/** Allows notification sources to interact with the VR system\n\tThis current interface is not yet implemented. Do not use yet. */\nclass IVRNotifications\n{\npublic:\n\t/** Create a notification and enqueue it to be shown to the user.\n\t* An overlay handle is required to create a notification, as otherwise it would be impossible for a user to act on it.\n\t* To create a two-line notification, use a line break ('\\n') to split the text into two lines.\n\t* The pImage argument may be NULL, in which case the specified overlay's icon will be used instead. */\n\tvirtual EVRNotificationError CreateNotification( VROverlayHandle_t ulOverlayHandle, uint64_t ulUserValue, EVRNotificationType type, const char *pchText, EVRNotificationStyle style, const NotificationBitmap_t *pImage, /* out */ VRNotificationId *pNotificationId ) = 0;\n\n\t/** Destroy a notification, hiding it first if it currently shown to the user. */\n\tvirtual EVRNotificationError RemoveNotification( VRNotificationId notificationId ) = 0;\n\n};\n\nstatic const char * const IVRNotifications_Version = \"IVRNotifications_002\";\n\n} // namespace vr\n\n\n\n// ivroverlay.h\nnamespace vr\n{\n\n\t/** The maximum length of an overlay key in bytes, counting the terminating null character. */\n\tstatic const uint32_t k_unVROverlayMaxKeyLength = 128;\n\n\t/** The maximum length of an overlay name in bytes, counting the terminating null character. */\n\tstatic const uint32_t k_unVROverlayMaxNameLength = 128;\n\n\t/** The maximum number of overlays that can exist in the system at one time. */\n\tstatic const uint32_t k_unMaxOverlayCount = 64;\n\n\t/** The maximum number of overlay intersection mask primitives per overlay */\n\tstatic const uint32_t k_unMaxOverlayIntersectionMaskPrimitivesCount = 32;\n\n\t/** Types of input supported by VR Overlays */\n\tenum VROverlayInputMethod\n\t{\n\t\tVROverlayInputMethod_None\t\t= 0, // No input events will be generated automatically for this overlay\n\t\tVROverlayInputMethod_Mouse\t\t= 1, // Tracked controllers will get mouse events automatically\n\t\tVROverlayInputMethod_DualAnalog = 2, // Analog inputs from tracked controllers are turned into DualAnalog events\n\t};\n\n\t/** Allows the caller to figure out which overlay transform getter to call. */\n\tenum VROverlayTransformType\n\t{\n\t\tVROverlayTransform_Absolute\t\t\t\t\t= 0,\n\t\tVROverlayTransform_TrackedDeviceRelative\t= 1,\n\t\tVROverlayTransform_SystemOverlay\t\t\t= 2,\n\t\tVROverlayTransform_TrackedComponent \t\t= 3,\n\t};\n\n\t/** Overlay control settings */\n\tenum VROverlayFlags\n\t{\n\t\tVROverlayFlags_None\t\t\t= 0,\n\n\t\t// The following only take effect when rendered using the high quality render path (see SetHighQualityOverlay).\n\t\tVROverlayFlags_Curved\t\t= 1,\n\t\tVROverlayFlags_RGSS4X\t\t= 2,\n\n\t\t// Set this flag on a dashboard overlay to prevent a tab from showing up for that overlay\n\t\tVROverlayFlags_NoDashboardTab = 3,\n\n\t\t// Set this flag on a dashboard that is able to deal with gamepad focus events\n\t\tVROverlayFlags_AcceptsGamepadEvents = 4,\n\n\t\t// Indicates that the overlay should dim/brighten to show gamepad focus\n\t\tVROverlayFlags_ShowGamepadFocus = 5,\n\n\t\t// When in VROverlayInputMethod_Mouse you can optionally enable sending VRScroll_t \n\t\tVROverlayFlags_SendVRScrollEvents = 6,\n\t\tVROverlayFlags_SendVRTouchpadEvents = 7,\n\n\t\t// If set this will render a vertical scroll wheel on the primary controller, \n\t\t//  only needed if not using VROverlayFlags_SendVRScrollEvents but you still want to represent a scroll wheel\n\t\tVROverlayFlags_ShowTouchPadScrollWheel = 8,\n\n\t\t// If this is set ownership and render access to the overlay are transferred \n\t\t// to the new scene process on a call to IVRApplications::LaunchInternalProcess\n\t\tVROverlayFlags_TransferOwnershipToInternalProcess = 9,\n\n\t\t// If set, renders 50% of the texture in each eye, side by side\n\t\tVROverlayFlags_SideBySide_Parallel = 10, // Texture is left/right\n\t\tVROverlayFlags_SideBySide_Crossed = 11, // Texture is crossed and right/left\n\n\t\tVROverlayFlags_Panorama = 12, // Texture is a panorama\n\t\tVROverlayFlags_StereoPanorama = 13, // Texture is a stereo panorama\n\n\t\t// If this is set on an overlay owned by the scene application that overlay\n\t\t// will be sorted with the \"Other\" overlays on top of all other scene overlays\n\t\tVROverlayFlags_SortWithNonSceneOverlays = 14,\n\n\t\t// If set, the overlay will be shown in the dashboard, otherwise it will be hidden.\n\t\tVROverlayFlags_VisibleInDashboard = 15,\n\t};\n\n\tenum VRMessageOverlayResponse\n\t{\n\t\tVRMessageOverlayResponse_ButtonPress_0 = 0,\n\t\tVRMessageOverlayResponse_ButtonPress_1 = 1,\n\t\tVRMessageOverlayResponse_ButtonPress_2 = 2,\n\t\tVRMessageOverlayResponse_ButtonPress_3 = 3,\n\t\tVRMessageOverlayResponse_CouldntFindSystemOverlay = 4,\n\t\tVRMessageOverlayResponse_CouldntFindOrCreateClientOverlay= 5,\n\t\tVRMessageOverlayResponse_ApplicationQuit = 6\n\t};\n\n\tstruct VROverlayIntersectionParams_t\n\t{\n\t\tHmdVector3_t vSource;\n\t\tHmdVector3_t vDirection;\n\t\tETrackingUniverseOrigin eOrigin;\n\t};\n\n\tstruct VROverlayIntersectionResults_t\n\t{\n\t\tHmdVector3_t vPoint;\n\t\tHmdVector3_t vNormal;\n\t\tHmdVector2_t vUVs;\n\t\tfloat fDistance;\n\t};\n\n\t// Input modes for the Big Picture gamepad text entry\n\tenum EGamepadTextInputMode\n\t{\n\t\tk_EGamepadTextInputModeNormal = 0,\n\t\tk_EGamepadTextInputModePassword = 1,\n\t\tk_EGamepadTextInputModeSubmit = 2,\n\t};\n\n\t// Controls number of allowed lines for the Big Picture gamepad text entry\n\tenum EGamepadTextInputLineMode\n\t{\n\t\tk_EGamepadTextInputLineModeSingleLine = 0,\n\t\tk_EGamepadTextInputLineModeMultipleLines = 1\n\t};\n\n\t/** Directions for changing focus between overlays with the gamepad */\n\tenum EOverlayDirection\n\t{\n\t\tOverlayDirection_Up = 0,\n\t\tOverlayDirection_Down = 1,\n\t\tOverlayDirection_Left = 2,\n\t\tOverlayDirection_Right = 3,\n\t\t\n\t\tOverlayDirection_Count = 4,\n\t};\n\n\tenum EVROverlayIntersectionMaskPrimitiveType\n\t{\n\t\tOverlayIntersectionPrimitiveType_Rectangle,\n\t\tOverlayIntersectionPrimitiveType_Circle,\n\t};\n\n\tstruct IntersectionMaskRectangle_t\n\t{\n\t\tfloat m_flTopLeftX;\n\t\tfloat m_flTopLeftY;\n\t\tfloat m_flWidth;\n\t\tfloat m_flHeight;\n\t};\n\n\tstruct IntersectionMaskCircle_t\n\t{\n\t\tfloat m_flCenterX;\n\t\tfloat m_flCenterY;\n\t\tfloat m_flRadius;\n\t};\n\n\t/** NOTE!!! If you change this you MUST manually update openvr_interop.cs.py and openvr_api_flat.h.py */\n\ttypedef union\n\t{\n\t\tIntersectionMaskRectangle_t m_Rectangle;\n\t\tIntersectionMaskCircle_t m_Circle;\n\t} VROverlayIntersectionMaskPrimitive_Data_t;\n\n\tstruct VROverlayIntersectionMaskPrimitive_t\n\t{\n\t\tEVROverlayIntersectionMaskPrimitiveType m_nPrimitiveType;\n\t\tVROverlayIntersectionMaskPrimitive_Data_t m_Primitive;\n\t};\n\n\tclass IVROverlay\n\t{\n\tpublic:\n\n\t\t// ---------------------------------------------\n\t\t// Overlay management methods\n\t\t// ---------------------------------------------\n\n\t\t/** Finds an existing overlay with the specified key. */\n\t\tvirtual EVROverlayError FindOverlay( const char *pchOverlayKey, VROverlayHandle_t * pOverlayHandle ) = 0;\n\n\t\t/** Creates a new named overlay. All overlays start hidden and with default settings. */\n\t\tvirtual EVROverlayError CreateOverlay( const char *pchOverlayKey, const char *pchOverlayName, VROverlayHandle_t * pOverlayHandle ) = 0;\n\n\t\t/** Destroys the specified overlay. When an application calls VR_Shutdown all overlays created by that app are\n\t\t* automatically destroyed. */\n\t\tvirtual EVROverlayError DestroyOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;\n\n\t\t/** Specify which overlay to use the high quality render path.  This overlay will be composited in during the distortion pass which\n\t\t* results in it drawing on top of everything else, but also at a higher quality as it samples the source texture directly rather than\n\t\t* rasterizing into each eye's render texture first.  Because if this, only one of these is supported at any given time.  It is most useful\n\t\t* for overlays that are expected to take up most of the user's view (e.g. streaming video).\n\t\t* This mode does not support mouse input to your overlay. */\n\t\tvirtual EVROverlayError SetHighQualityOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;\n\n\t\t/** Returns the overlay handle of the current overlay being rendered using the single high quality overlay render path.\n\t\t* Otherwise it will return k_ulOverlayHandleInvalid. */\n\t\tvirtual vr::VROverlayHandle_t GetHighQualityOverlay() = 0;\n\n\t\t/** Fills the provided buffer with the string key of the overlay. Returns the size of buffer required to store the key, including\n\t\t* the terminating null character. k_unVROverlayMaxKeyLength will be enough bytes to fit the string. */\n\t\tvirtual uint32_t GetOverlayKey( VROverlayHandle_t ulOverlayHandle, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, EVROverlayError *pError = 0L ) = 0;\n\n\t\t/** Fills the provided buffer with the friendly name of the overlay. Returns the size of buffer required to store the key, including\n\t\t* the terminating null character. k_unVROverlayMaxNameLength will be enough bytes to fit the string. */\n\t\tvirtual uint32_t GetOverlayName( VROverlayHandle_t ulOverlayHandle, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, EVROverlayError *pError = 0L ) = 0;\n\n\t\t/** set the name to use for this overlay */\n\t\tvirtual EVROverlayError SetOverlayName( VROverlayHandle_t ulOverlayHandle, const char *pchName ) = 0;\n\n\t\t/** Gets the raw image data from an overlay. Overlay image data is always returned as RGBA data, 4 bytes per pixel. If the buffer is not large enough, width and height \n\t\t* will be set and VROverlayError_ArrayTooSmall is returned. */\n\t\tvirtual EVROverlayError GetOverlayImageData( VROverlayHandle_t ulOverlayHandle, void *pvBuffer, uint32_t unBufferSize, uint32_t *punWidth, uint32_t *punHeight ) = 0;\n\n\t\t/** returns a string that corresponds with the specified overlay error. The string will be the name \n\t\t* of the error enum value for all valid error codes */\n\t\tvirtual const char *GetOverlayErrorNameFromEnum( EVROverlayError error ) = 0;\n\n\t\t// ---------------------------------------------\n\t\t// Overlay rendering methods\n\t\t// ---------------------------------------------\n\n\t\t/** Sets the pid that is allowed to render to this overlay (the creator pid is always allow to render),\n\t\t*\tby default this is the pid of the process that made the overlay */\n\t\tvirtual EVROverlayError SetOverlayRenderingPid( VROverlayHandle_t ulOverlayHandle, uint32_t unPID ) = 0;\n\n\t\t/** Gets the pid that is allowed to render to this overlay */\n\t\tvirtual uint32_t GetOverlayRenderingPid( VROverlayHandle_t ulOverlayHandle ) = 0;\n\n\t\t/** Specify flag setting for a given overlay */\n\t\tvirtual EVROverlayError SetOverlayFlag( VROverlayHandle_t ulOverlayHandle, VROverlayFlags eOverlayFlag, bool bEnabled ) = 0;\n\n\t\t/** Sets flag setting for a given overlay */\n\t\tvirtual EVROverlayError GetOverlayFlag( VROverlayHandle_t ulOverlayHandle, VROverlayFlags eOverlayFlag, bool *pbEnabled ) = 0;\n\n\t\t/** Sets the color tint of the overlay quad. Use 0.0 to 1.0 per channel. */\n\t\tvirtual EVROverlayError SetOverlayColor( VROverlayHandle_t ulOverlayHandle, float fRed, float fGreen, float fBlue ) = 0;\n\n\t\t/** Gets the color tint of the overlay quad. */\n\t\tvirtual EVROverlayError GetOverlayColor( VROverlayHandle_t ulOverlayHandle, float *pfRed, float *pfGreen, float *pfBlue ) = 0;\n\n\t\t/** Sets the alpha of the overlay quad. Use 1.0 for 100 percent opacity to 0.0 for 0 percent opacity. */\n\t\tvirtual EVROverlayError SetOverlayAlpha( VROverlayHandle_t ulOverlayHandle, float fAlpha ) = 0;\n\n\t\t/** Gets the alpha of the overlay quad. By default overlays are rendering at 100 percent alpha (1.0). */\n\t\tvirtual EVROverlayError GetOverlayAlpha( VROverlayHandle_t ulOverlayHandle, float *pfAlpha ) = 0;\n\n\t\t/** Sets the aspect ratio of the texels in the overlay. 1.0 means the texels are square. 2.0 means the texels\n\t\t* are twice as wide as they are tall. Defaults to 1.0. */\n\t\tvirtual EVROverlayError SetOverlayTexelAspect( VROverlayHandle_t ulOverlayHandle, float fTexelAspect ) = 0;\n\n\t\t/** Gets the aspect ratio of the texels in the overlay. Defaults to 1.0 */\n\t\tvirtual EVROverlayError GetOverlayTexelAspect( VROverlayHandle_t ulOverlayHandle, float *pfTexelAspect ) = 0;\n\n\t\t/** Sets the rendering sort order for the overlay. Overlays are rendered this order:\n\t\t*      Overlays owned by the scene application\n\t\t*      Overlays owned by some other application\n\t\t*\n\t\t*\tWithin a category overlays are rendered lowest sort order to highest sort order. Overlays with the same \n\t\t*\tsort order are rendered back to front base on distance from the HMD.\n\t\t*\n\t\t*\tSort order defaults to 0. */\n\t\tvirtual EVROverlayError SetOverlaySortOrder( VROverlayHandle_t ulOverlayHandle, uint32_t unSortOrder ) = 0;\n\n\t\t/** Gets the sort order of the overlay. See SetOverlaySortOrder for how this works. */\n\t\tvirtual EVROverlayError GetOverlaySortOrder( VROverlayHandle_t ulOverlayHandle, uint32_t *punSortOrder ) = 0;\n\n\t\t/** Sets the width of the overlay quad in meters. By default overlays are rendered on a quad that is 1 meter across */\n\t\tvirtual EVROverlayError SetOverlayWidthInMeters( VROverlayHandle_t ulOverlayHandle, float fWidthInMeters ) = 0;\n\n\t\t/** Returns the width of the overlay quad in meters. By default overlays are rendered on a quad that is 1 meter across */\n\t\tvirtual EVROverlayError GetOverlayWidthInMeters( VROverlayHandle_t ulOverlayHandle, float *pfWidthInMeters ) = 0;\n\n\t\t/** For high-quality curved overlays only, sets the distance range in meters from the overlay used to automatically curve\n\t\t* the surface around the viewer.  Min is distance is when the surface will be most curved.  Max is when least curved. */\n\t\tvirtual EVROverlayError SetOverlayAutoCurveDistanceRangeInMeters( VROverlayHandle_t ulOverlayHandle, float fMinDistanceInMeters, float fMaxDistanceInMeters ) = 0;\n\n\t\t/** For high-quality curved overlays only, gets the distance range in meters from the overlay used to automatically curve\n\t\t* the surface around the viewer.  Min is distance is when the surface will be most curved.  Max is when least curved. */\n\t\tvirtual EVROverlayError GetOverlayAutoCurveDistanceRangeInMeters( VROverlayHandle_t ulOverlayHandle, float *pfMinDistanceInMeters, float *pfMaxDistanceInMeters ) = 0;\n\n\t\t/** Sets the colorspace the overlay texture's data is in.  Defaults to 'auto'.\n\t\t* If the texture needs to be resolved, you should call SetOverlayTexture with the appropriate colorspace instead. */\n\t\tvirtual EVROverlayError SetOverlayTextureColorSpace( VROverlayHandle_t ulOverlayHandle, EColorSpace eTextureColorSpace ) = 0;\n\n\t\t/** Gets the overlay's current colorspace setting. */\n\t\tvirtual EVROverlayError GetOverlayTextureColorSpace( VROverlayHandle_t ulOverlayHandle, EColorSpace *peTextureColorSpace ) = 0;\n\n\t\t/** Sets the part of the texture to use for the overlay. UV Min is the upper left corner and UV Max is the lower right corner. */\n\t\tvirtual EVROverlayError SetOverlayTextureBounds( VROverlayHandle_t ulOverlayHandle, const VRTextureBounds_t *pOverlayTextureBounds ) = 0;\n\n\t\t/** Gets the part of the texture to use for the overlay. UV Min is the upper left corner and UV Max is the lower right corner. */\n\t\tvirtual EVROverlayError GetOverlayTextureBounds( VROverlayHandle_t ulOverlayHandle, VRTextureBounds_t *pOverlayTextureBounds ) = 0;\n\n\t\t/** Gets render model to draw behind this overlay */\n\t\tvirtual uint32_t GetOverlayRenderModel( vr::VROverlayHandle_t ulOverlayHandle, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, HmdColor_t *pColor, vr::EVROverlayError *pError ) = 0;\n\n\t\t/** Sets render model to draw behind this overlay and the vertex color to use, pass null for pColor to match the overlays vertex color. \n\t\t\tThe model is scaled by the same amount as the overlay, with a default of 1m. */\n\t\tvirtual vr::EVROverlayError SetOverlayRenderModel( vr::VROverlayHandle_t ulOverlayHandle, const char *pchRenderModel, const HmdColor_t *pColor ) = 0;\n\n\t\t/** Returns the transform type of this overlay. */\n\t\tvirtual EVROverlayError GetOverlayTransformType( VROverlayHandle_t ulOverlayHandle, VROverlayTransformType *peTransformType ) = 0;\n\n\t\t/** Sets the transform to absolute tracking origin. */\n\t\tvirtual EVROverlayError SetOverlayTransformAbsolute( VROverlayHandle_t ulOverlayHandle, ETrackingUniverseOrigin eTrackingOrigin, const HmdMatrix34_t *pmatTrackingOriginToOverlayTransform ) = 0;\n\n\t\t/** Gets the transform if it is absolute. Returns an error if the transform is some other type. */\n\t\tvirtual EVROverlayError GetOverlayTransformAbsolute( VROverlayHandle_t ulOverlayHandle, ETrackingUniverseOrigin *peTrackingOrigin, HmdMatrix34_t *pmatTrackingOriginToOverlayTransform ) = 0;\n\n\t\t/** Sets the transform to relative to the transform of the specified tracked device. */\n\t\tvirtual EVROverlayError SetOverlayTransformTrackedDeviceRelative( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t unTrackedDevice, const HmdMatrix34_t *pmatTrackedDeviceToOverlayTransform ) = 0;\n\n\t\t/** Gets the transform if it is relative to a tracked device. Returns an error if the transform is some other type. */\n\t\tvirtual EVROverlayError GetOverlayTransformTrackedDeviceRelative( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t *punTrackedDevice, HmdMatrix34_t *pmatTrackedDeviceToOverlayTransform ) = 0;\n\n\t\t/** Sets the transform to draw the overlay on a rendermodel component mesh instead of a quad. This will only draw when the system is\n\t\t* drawing the device. Overlays with this transform type cannot receive mouse events. */\n\t\tvirtual EVROverlayError SetOverlayTransformTrackedDeviceComponent( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t unDeviceIndex, const char *pchComponentName ) = 0;\n\n\t\t/** Gets the transform information when the overlay is rendering on a component. */\n\t\tvirtual EVROverlayError GetOverlayTransformTrackedDeviceComponent( VROverlayHandle_t ulOverlayHandle, TrackedDeviceIndex_t *punDeviceIndex, VR_OUT_STRING() char *pchComponentName, uint32_t unComponentNameSize ) = 0;\n\n\t\t/** Gets the transform if it is relative to another overlay. Returns an error if the transform is some other type. */\n\t\tvirtual vr::EVROverlayError GetOverlayTransformOverlayRelative( VROverlayHandle_t ulOverlayHandle, VROverlayHandle_t *ulOverlayHandleParent, HmdMatrix34_t *pmatParentOverlayToOverlayTransform ) = 0;\n\t\t\n\t\t/** Sets the transform to relative to the transform of the specified overlay. This overlays visibility will also track the parents visibility */\n\t\tvirtual vr::EVROverlayError SetOverlayTransformOverlayRelative( VROverlayHandle_t ulOverlayHandle, VROverlayHandle_t ulOverlayHandleParent, const HmdMatrix34_t *pmatParentOverlayToOverlayTransform ) = 0;\n\n\t\t/** Shows the VR overlay.  For dashboard overlays, only the Dashboard Manager is allowed to call this. */\n\t\tvirtual EVROverlayError ShowOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;\n\n\t\t/** Hides the VR overlay.  For dashboard overlays, only the Dashboard Manager is allowed to call this. */\n\t\tvirtual EVROverlayError HideOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;\n\n\t\t/** Returns true if the overlay is visible. */\n\t\tvirtual bool IsOverlayVisible( VROverlayHandle_t ulOverlayHandle ) = 0;\n\n\t\t/** Get the transform in 3d space associated with a specific 2d point in the overlay's coordinate space (where 0,0 is the lower left). -Z points out of the overlay */\n\t\tvirtual EVROverlayError GetTransformForOverlayCoordinates( VROverlayHandle_t ulOverlayHandle, ETrackingUniverseOrigin eTrackingOrigin, HmdVector2_t coordinatesInOverlay, HmdMatrix34_t *pmatTransform ) = 0;\n\n\t\t// ---------------------------------------------\n\t\t// Overlay input methods\n\t\t// ---------------------------------------------\n\n\t\t/** Returns true and fills the event with the next event on the overlay's event queue, if there is one. \n\t\t* If there are no events this method returns false. uncbVREvent should be the size in bytes of the VREvent_t struct */\n\t\tvirtual bool PollNextOverlayEvent( VROverlayHandle_t ulOverlayHandle, VREvent_t *pEvent, uint32_t uncbVREvent ) = 0;\n\n\t\t/** Returns the current input settings for the specified overlay. */\n\t\tvirtual EVROverlayError GetOverlayInputMethod( VROverlayHandle_t ulOverlayHandle, VROverlayInputMethod *peInputMethod ) = 0;\n\n\t\t/** Sets the input settings for the specified overlay. */\n\t\tvirtual EVROverlayError SetOverlayInputMethod( VROverlayHandle_t ulOverlayHandle, VROverlayInputMethod eInputMethod ) = 0;\n\n\t\t/** Gets the mouse scaling factor that is used for mouse events. The actual texture may be a different size, but this is\n\t\t* typically the size of the underlying UI in pixels. */\n\t\tvirtual EVROverlayError GetOverlayMouseScale( VROverlayHandle_t ulOverlayHandle, HmdVector2_t *pvecMouseScale ) = 0;\n\n\t\t/** Sets the mouse scaling factor that is used for mouse events. The actual texture may be a different size, but this is\n\t\t* typically the size of the underlying UI in pixels (not in world space). */\n\t\tvirtual EVROverlayError SetOverlayMouseScale( VROverlayHandle_t ulOverlayHandle, const HmdVector2_t *pvecMouseScale ) = 0;\n\n\t\t/** Computes the overlay-space pixel coordinates of where the ray intersects the overlay with the\n\t\t* specified settings. Returns false if there is no intersection. */\n\t\tvirtual bool ComputeOverlayIntersection( VROverlayHandle_t ulOverlayHandle, const VROverlayIntersectionParams_t *pParams, VROverlayIntersectionResults_t *pResults ) = 0;\n\n\t\t/** Returns true if the specified overlay is the hover target. An overlay is the hover target when it is the last overlay \"moused over\" \n\t\t* by the virtual mouse pointer */\n\t\tvirtual bool IsHoverTargetOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;\n\n\t\t/** Returns the current Gamepad focus overlay */\n\t\tvirtual vr::VROverlayHandle_t GetGamepadFocusOverlay() = 0;\n\n\t\t/** Sets the current Gamepad focus overlay */\n\t\tvirtual EVROverlayError SetGamepadFocusOverlay( VROverlayHandle_t ulNewFocusOverlay ) = 0;\n\n\t\t/** Sets an overlay's neighbor. This will also set the neighbor of the \"to\" overlay\n\t\t* to point back to the \"from\" overlay. If an overlay's neighbor is set to invalid both\n\t\t* ends will be cleared */\n\t\tvirtual EVROverlayError SetOverlayNeighbor( EOverlayDirection eDirection, VROverlayHandle_t ulFrom, VROverlayHandle_t ulTo ) = 0;\n\n\t\t/** Changes the Gamepad focus from one overlay to one of its neighbors. Returns VROverlayError_NoNeighbor if there is no\n\t\t* neighbor in that direction */\n\t\tvirtual EVROverlayError MoveGamepadFocusToNeighbor( EOverlayDirection eDirection, VROverlayHandle_t ulFrom ) = 0;\n\n\t\t/** Sets the analog input to Dual Analog coordinate scale for the specified overlay. */\n\t\tvirtual EVROverlayError SetOverlayDualAnalogTransform( VROverlayHandle_t ulOverlay, EDualAnalogWhich eWhich, const HmdVector2_t & vCenter, float fRadius ) = 0;\n\n\t\t/** Gets the analog input to Dual Analog coordinate scale for the specified overlay. */\n\t\tvirtual EVROverlayError GetOverlayDualAnalogTransform( VROverlayHandle_t ulOverlay, EDualAnalogWhich eWhich, HmdVector2_t *pvCenter, float *pfRadius ) = 0;\n\n\t\t// ---------------------------------------------\n\t\t// Overlay texture methods\n\t\t// ---------------------------------------------\n\n\t\t/** Texture to draw for the overlay. This function can only be called by the overlay's creator or renderer process (see SetOverlayRenderingPid) .\n\t\t*\n\t\t* OpenGL dirty state:\n\t\t*\tglBindTexture\n\t\t*/\n\t\tvirtual EVROverlayError SetOverlayTexture( VROverlayHandle_t ulOverlayHandle, const Texture_t *pTexture ) = 0;\n\n\t\t/** Use this to tell the overlay system to release the texture set for this overlay. */\n\t\tvirtual EVROverlayError ClearOverlayTexture( VROverlayHandle_t ulOverlayHandle ) = 0;\n\n\t\t/** Separate interface for providing the data as a stream of bytes, but there is an upper bound on data \n\t\t* that can be sent. This function can only be called by the overlay's renderer process. */\n\t\tvirtual EVROverlayError SetOverlayRaw( VROverlayHandle_t ulOverlayHandle, void *pvBuffer, uint32_t unWidth, uint32_t unHeight, uint32_t unDepth ) = 0;\n\n\t\t/** Separate interface for providing the image through a filename: can be png or jpg, and should not be bigger than 1920x1080.\n\t\t* This function can only be called by the overlay's renderer process */\n\t\tvirtual EVROverlayError SetOverlayFromFile( VROverlayHandle_t ulOverlayHandle, const char *pchFilePath ) = 0;\n\n\t\t/** Get the native texture handle/device for an overlay you have created.\n\t\t* On windows this handle will be a ID3D11ShaderResourceView with a ID3D11Texture2D bound.\n\t\t*\n\t\t* The texture will always be sized to match the backing texture you supplied in SetOverlayTexture above.\n\t\t*\n\t\t* You MUST call ReleaseNativeOverlayHandle() with pNativeTextureHandle once you are done with this texture.\n\t\t*\n\t\t* pNativeTextureHandle is an OUTPUT, it will be a pointer to a ID3D11ShaderResourceView *.\n\t\t* pNativeTextureRef is an INPUT and should be a ID3D11Resource *. The device used by pNativeTextureRef will be used to bind pNativeTextureHandle.\n\t\t*/\n\t\tvirtual EVROverlayError GetOverlayTexture( VROverlayHandle_t ulOverlayHandle, void **pNativeTextureHandle, void *pNativeTextureRef, uint32_t *pWidth, uint32_t *pHeight, uint32_t *pNativeFormat, ETextureType *pAPIType, EColorSpace *pColorSpace, VRTextureBounds_t *pTextureBounds ) = 0;\n\n\t\t/** Release the pNativeTextureHandle provided from the GetOverlayTexture call, this allows the system to free the underlying GPU resources for this object,\n\t\t* so only do it once you stop rendering this texture.\n\t\t*/\n\t\tvirtual EVROverlayError ReleaseNativeOverlayHandle( VROverlayHandle_t ulOverlayHandle, void *pNativeTextureHandle ) = 0;\n\n\t\t/** Get the size of the overlay texture */\n\t\tvirtual EVROverlayError GetOverlayTextureSize( VROverlayHandle_t ulOverlayHandle, uint32_t *pWidth, uint32_t *pHeight ) = 0;\n\n\t\t// ----------------------------------------------\n\t\t// Dashboard Overlay Methods\n\t\t// ----------------------------------------------\n\n\t\t/** Creates a dashboard overlay and returns its handle */\n\t\tvirtual EVROverlayError CreateDashboardOverlay( const char *pchOverlayKey, const char *pchOverlayFriendlyName, VROverlayHandle_t * pMainHandle, VROverlayHandle_t *pThumbnailHandle ) = 0;\n\n\t\t/** Returns true if the dashboard is visible */\n\t\tvirtual bool IsDashboardVisible() = 0;\n\n\t\t/** returns true if the dashboard is visible and the specified overlay is the active system Overlay */\n\t\tvirtual bool IsActiveDashboardOverlay( VROverlayHandle_t ulOverlayHandle ) = 0;\n\n\t\t/** Sets the dashboard overlay to only appear when the specified process ID has scene focus */\n\t\tvirtual EVROverlayError SetDashboardOverlaySceneProcess( VROverlayHandle_t ulOverlayHandle, uint32_t unProcessId ) = 0;\n\n\t\t/** Gets the process ID that this dashboard overlay requires to have scene focus */\n\t\tvirtual EVROverlayError GetDashboardOverlaySceneProcess( VROverlayHandle_t ulOverlayHandle, uint32_t *punProcessId ) = 0;\n\n\t\t/** Shows the dashboard. */\n\t\tvirtual void ShowDashboard( const char *pchOverlayToShow ) = 0;\n\n\t\t/** Returns the tracked device that has the laser pointer in the dashboard */\n\t\tvirtual vr::TrackedDeviceIndex_t GetPrimaryDashboardDevice() = 0;\n\n\t\t// ---------------------------------------------\n\t\t// Keyboard methods\n\t\t// ---------------------------------------------\n\t\t\n\t\t/** Show the virtual keyboard to accept input **/\n\t\tvirtual EVROverlayError ShowKeyboard( EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32_t unCharMax, const char *pchExistingText, bool bUseMinimalMode, uint64_t uUserValue ) = 0;\n\n\t\tvirtual EVROverlayError ShowKeyboardForOverlay( VROverlayHandle_t ulOverlayHandle, EGamepadTextInputMode eInputMode, EGamepadTextInputLineMode eLineInputMode, const char *pchDescription, uint32_t unCharMax, const char *pchExistingText, bool bUseMinimalMode, uint64_t uUserValue ) = 0;\n\n\t\t/** Get the text that was entered into the text input **/\n\t\tvirtual uint32_t GetKeyboardText( VR_OUT_STRING() char *pchText, uint32_t cchText ) = 0;\n\n\t\t/** Hide the virtual keyboard **/\n\t\tvirtual void HideKeyboard() = 0;\n\n\t\t/** Set the position of the keyboard in world space **/\n\t\tvirtual void SetKeyboardTransformAbsolute( ETrackingUniverseOrigin eTrackingOrigin, const HmdMatrix34_t *pmatTrackingOriginToKeyboardTransform ) = 0;\n\n\t\t/** Set the position of the keyboard in overlay space by telling it to avoid a rectangle in the overlay. Rectangle coords have (0,0) in the bottom left **/\n\t\tvirtual void SetKeyboardPositionForOverlay( VROverlayHandle_t ulOverlayHandle, HmdRect2_t avoidRect ) = 0;\n\n\t\t// ---------------------------------------------\n\t\t// Overlay input methods\n\t\t// ---------------------------------------------\n\n\t\t/** Sets a list of primitives to be used for controller ray intersection\n\t\t* typically the size of the underlying UI in pixels (not in world space). */\n\t\tvirtual EVROverlayError SetOverlayIntersectionMask( VROverlayHandle_t ulOverlayHandle, VROverlayIntersectionMaskPrimitive_t *pMaskPrimitives, uint32_t unNumMaskPrimitives, uint32_t unPrimitiveSize = sizeof( VROverlayIntersectionMaskPrimitive_t ) ) = 0;\n\n\t\tvirtual EVROverlayError GetOverlayFlags( VROverlayHandle_t ulOverlayHandle, uint32_t *pFlags ) = 0;\n\n\t\t// ---------------------------------------------\n\t\t// Message box methods\n\t\t// ---------------------------------------------\n\n\t\t/** Show the message overlay. This will block and return you a result. **/\n\t\tvirtual VRMessageOverlayResponse ShowMessageOverlay( const char* pchText, const char* pchCaption, const char* pchButton0Text, const char* pchButton1Text = nullptr, const char* pchButton2Text = nullptr, const char* pchButton3Text = nullptr ) = 0;\n\n\t\t/** If the calling process owns the overlay and it's open, this will close it. **/\n\t\tvirtual void CloseMessageOverlay() = 0;\n\t};\n\n\tstatic const char * const IVROverlay_Version = \"IVROverlay_018\";\n\n} // namespace vr\n\n// ivrrendermodels.h\nnamespace vr\n{\n\nstatic const char * const k_pch_Controller_Component_GDC2015 = \"gdc2015\";   // Canonical coordinate system of the gdc 2015 wired controller, provided for backwards compatibility\nstatic const char * const k_pch_Controller_Component_Base = \"base\";         // For controllers with an unambiguous 'base'.\nstatic const char * const k_pch_Controller_Component_Tip = \"tip\";           // For controllers with an unambiguous 'tip' (used for 'laser-pointing')\nstatic const char * const k_pch_Controller_Component_HandGrip = \"handgrip\"; // Neutral, ambidextrous hand-pose when holding controller. On plane between neutrally posed index finger and thumb\nstatic const char * const k_pch_Controller_Component_Status = \"status\";\t\t// 1:1 aspect ratio status area, with canonical [0,1] uv mapping\n\n#pragma pack( push, 8 )\n\n/** Errors that can occur with the VR compositor */\nenum EVRRenderModelError\n{\n\tVRRenderModelError_None = 0,\n\tVRRenderModelError_Loading = 100,\n\tVRRenderModelError_NotSupported = 200,\n\tVRRenderModelError_InvalidArg = 300,\n\tVRRenderModelError_InvalidModel = 301,\n\tVRRenderModelError_NoShapes = 302,\n\tVRRenderModelError_MultipleShapes = 303,\n\tVRRenderModelError_TooManyVertices = 304,\n\tVRRenderModelError_MultipleTextures = 305,\n\tVRRenderModelError_BufferTooSmall = 306,\n\tVRRenderModelError_NotEnoughNormals = 307,\n\tVRRenderModelError_NotEnoughTexCoords = 308,\n\n\tVRRenderModelError_InvalidTexture = 400,\n};\n\ntypedef uint32_t VRComponentProperties;\n\nenum EVRComponentProperty\n{\n\tVRComponentProperty_IsStatic = (1 << 0),\n\tVRComponentProperty_IsVisible = (1 << 1),\n\tVRComponentProperty_IsTouched = (1 << 2),\n\tVRComponentProperty_IsPressed = (1 << 3),\n\tVRComponentProperty_IsScrolled = (1 << 4),\n};\n\n/** Describes state information about a render-model component, including transforms and other dynamic properties */\nstruct RenderModel_ComponentState_t\n{\n\tHmdMatrix34_t mTrackingToComponentRenderModel;  // Transform required when drawing the component render model\n\tHmdMatrix34_t mTrackingToComponentLocal;        // Transform available for attaching to a local component coordinate system (-Z out from surface )\n\tVRComponentProperties uProperties;\n};\n\n/** A single vertex in a render model */\nstruct RenderModel_Vertex_t\n{\n\tHmdVector3_t vPosition;\t\t// position in meters in device space\n\tHmdVector3_t vNormal;\n\tfloat rfTextureCoord[2];\n};\n\n/** A texture map for use on a render model */\n#if defined(__linux__) || defined(__APPLE__) \n// This structure was originally defined mis-packed on Linux, preserved for \n// compatibility. \n#pragma pack( push, 4 )\n#endif\n\nstruct RenderModel_TextureMap_t\n{\n\tuint16_t unWidth, unHeight; // width and height of the texture map in pixels\n\tconst uint8_t *rubTextureMapData;\t// Map texture data. All textures are RGBA with 8 bits per channel per pixel. Data size is width * height * 4ub\n};\n#if defined(__linux__) || defined(__APPLE__) \n#pragma pack( pop )\n#endif\n\n/**  Session unique texture identifier. Rendermodels which share the same texture will have the same id.\nIDs <0 denote the texture is not present */\n\ntypedef int32_t TextureID_t;\n\nconst TextureID_t INVALID_TEXTURE_ID = -1;\n\n#if defined(__linux__) || defined(__APPLE__) \n// This structure was originally defined mis-packed on Linux, preserved for \n// compatibility. \n#pragma pack( push, 4 )\n#endif\n\nstruct RenderModel_t\n{\n\tconst RenderModel_Vertex_t *rVertexData;\t// Vertex data for the mesh\n\tuint32_t unVertexCount;\t\t\t\t\t\t// Number of vertices in the vertex data\n\tconst uint16_t *rIndexData;\t\t\t\t\t// Indices into the vertex data for each triangle\n\tuint32_t unTriangleCount;\t\t\t\t\t// Number of triangles in the mesh. Index count is 3 * TriangleCount\n\tTextureID_t diffuseTextureId;\t\t\t\t// Session unique texture identifier. Rendermodels which share the same texture will have the same id. <0 == texture not present\n};\n#if defined(__linux__) || defined(__APPLE__) \n#pragma pack( pop )\n#endif\n\n\nstruct RenderModel_ControllerMode_State_t\n{\n\tbool bScrollWheelVisible; // is this controller currently set to be in a scroll wheel mode\n};\n\n#pragma pack( pop )\n\nclass IVRRenderModels\n{\npublic:\n\n\t/** Loads and returns a render model for use in the application. pchRenderModelName should be a render model name\n\t* from the Prop_RenderModelName_String property or an absolute path name to a render model on disk. \n\t*\n\t* The resulting render model is valid until VR_Shutdown() is called or until FreeRenderModel() is called. When the \n\t* application is finished with the render model it should call FreeRenderModel() to free the memory associated\n\t* with the model.\n\t*\n\t* The method returns VRRenderModelError_Loading while the render model is still being loaded.\n\t* The method returns VRRenderModelError_None once loaded successfully, otherwise will return an error. */\n\tvirtual EVRRenderModelError LoadRenderModel_Async( const char *pchRenderModelName, RenderModel_t **ppRenderModel ) = 0;\n\n\t/** Frees a previously returned render model\n\t*   It is safe to call this on a null ptr. */\n\tvirtual void FreeRenderModel( RenderModel_t *pRenderModel ) = 0;\n\n\t/** Loads and returns a texture for use in the application. */\n\tvirtual EVRRenderModelError LoadTexture_Async( TextureID_t textureId, RenderModel_TextureMap_t **ppTexture ) = 0;\n\n\t/** Frees a previously returned texture\n\t*   It is safe to call this on a null ptr. */\n\tvirtual void FreeTexture( RenderModel_TextureMap_t *pTexture ) = 0;\n\n\t/** Creates a D3D11 texture and loads data into it. */\n\tvirtual EVRRenderModelError LoadTextureD3D11_Async( TextureID_t textureId, void *pD3D11Device, void **ppD3D11Texture2D ) = 0;\n\n\t/** Helper function to copy the bits into an existing texture. */\n\tvirtual EVRRenderModelError LoadIntoTextureD3D11_Async( TextureID_t textureId, void *pDstTexture ) = 0;\n\n\t/** Use this to free textures created with LoadTextureD3D11_Async instead of calling Release on them. */\n\tvirtual void FreeTextureD3D11( void *pD3D11Texture2D ) = 0;\n\n\t/** Use this to get the names of available render models.  Index does not correlate to a tracked device index, but\n\t* is only used for iterating over all available render models.  If the index is out of range, this function will return 0.\n\t* Otherwise, it will return the size of the buffer required for the name. */\n\tvirtual uint32_t GetRenderModelName( uint32_t unRenderModelIndex, VR_OUT_STRING() char *pchRenderModelName, uint32_t unRenderModelNameLen ) = 0;\n\n\t/** Returns the number of available render models. */\n\tvirtual uint32_t GetRenderModelCount() = 0;\n\n\n\t/** Returns the number of components of the specified render model.\n\t*  Components are useful when client application wish to draw, label, or otherwise interact with components of tracked objects.\n\t*  Examples controller components:\n\t*   renderable things such as triggers, buttons\n\t*   non-renderable things which include coordinate systems such as 'tip', 'base', a neutral controller agnostic hand-pose\n\t*   If all controller components are enumerated and rendered, it will be equivalent to drawing the traditional render model\n\t*   Returns 0 if components not supported, >0 otherwise */\n\tvirtual uint32_t GetComponentCount( const char *pchRenderModelName ) = 0;\n\n\t/** Use this to get the names of available components.  Index does not correlate to a tracked device index, but\n\t* is only used for iterating over all available components.  If the index is out of range, this function will return 0.\n\t* Otherwise, it will return the size of the buffer required for the name. */\n\tvirtual uint32_t GetComponentName( const char *pchRenderModelName, uint32_t unComponentIndex, VR_OUT_STRING( ) char *pchComponentName, uint32_t unComponentNameLen ) = 0;\n\n\t/** Get the button mask for all buttons associated with this component\n\t*   If no buttons (or axes) are associated with this component, return 0\n\t*   Note: multiple components may be associated with the same button. Ex: two grip buttons on a single controller.\n\t*   Note: A single component may be associated with multiple buttons. Ex: A trackpad which also provides \"D-pad\" functionality */\n\tvirtual uint64_t GetComponentButtonMask( const char *pchRenderModelName, const char *pchComponentName ) = 0;\n\n\t/** Use this to get the render model name for the specified rendermode/component combination, to be passed to LoadRenderModel.\n\t* If the component name is out of range, this function will return 0.\n\t* Otherwise, it will return the size of the buffer required for the name. */\n\tvirtual uint32_t GetComponentRenderModelName( const char *pchRenderModelName, const char *pchComponentName, VR_OUT_STRING( ) char *pchComponentRenderModelName, uint32_t unComponentRenderModelNameLen ) = 0;\n\n\t/** Use this to query information about the component, as a function of the controller state.\n\t*\n\t* For dynamic controller components (ex: trigger) values will reflect component motions\n\t* For static components this will return a consistent value independent of the VRControllerState_t\n\t*\n\t* If the pchRenderModelName or pchComponentName is invalid, this will return false (and transforms will be set to identity).\n\t* Otherwise, return true\n\t* Note: For dynamic objects, visibility may be dynamic. (I.e., true/false will be returned based on controller state and controller mode state ) */\n\tvirtual bool GetComponentState( const char *pchRenderModelName, const char *pchComponentName, const vr::VRControllerState_t *pControllerState, const RenderModel_ControllerMode_State_t *pState, RenderModel_ComponentState_t *pComponentState ) = 0;\n\n\t/** Returns true if the render model has a component with the specified name */\n\tvirtual bool RenderModelHasComponent( const char *pchRenderModelName, const char *pchComponentName ) = 0;\n\n\t/** Returns the URL of the thumbnail image for this rendermodel */\n\tvirtual uint32_t GetRenderModelThumbnailURL( const char *pchRenderModelName, VR_OUT_STRING() char *pchThumbnailURL, uint32_t unThumbnailURLLen, vr::EVRRenderModelError *peError ) = 0;\n\n\t/** Provides a render model path that will load the unskinned model if the model name provided has been replace by the user. If the model\n\t* hasn't been replaced the path value will still be a valid path to load the model. Pass this to LoadRenderModel_Async, etc. to load the\n\t* model. */\n\tvirtual uint32_t GetRenderModelOriginalPath( const char *pchRenderModelName, VR_OUT_STRING() char *pchOriginalPath, uint32_t unOriginalPathLen, vr::EVRRenderModelError *peError ) = 0;\n\n\t/** Returns a string for a render model error */\n\tvirtual const char *GetRenderModelErrorNameFromEnum( vr::EVRRenderModelError error ) = 0;\n};\n\nstatic const char * const IVRRenderModels_Version = \"IVRRenderModels_005\";\n\n}\n\n\n// ivrextendeddisplay.h\nnamespace vr\n{\n\n\t/** NOTE: Use of this interface is not recommended in production applications. It will not work for displays which use\n\t* direct-to-display mode. Creating our own window is also incompatible with the VR compositor and is not available when the compositor is running. */\n\tclass IVRExtendedDisplay\n\t{\n\tpublic:\n\n\t\t/** Size and position that the window needs to be on the VR display. */\n\t\tvirtual void GetWindowBounds( int32_t *pnX, int32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) = 0;\n\n\t\t/** Gets the viewport in the frame buffer to draw the output of the distortion into */\n\t\tvirtual void GetEyeOutputViewport( EVREye eEye, uint32_t *pnX, uint32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) = 0;\n\n\t\t/** [D3D10/11 Only]\n\t\t* Returns the adapter index and output index that the user should pass into EnumAdapters and EnumOutputs\n\t\t* to create the device and swap chain in DX10 and DX11. If an error occurs both indices will be set to -1.\n\t\t*/\n\t\tvirtual void GetDXGIOutputInfo( int32_t *pnAdapterIndex, int32_t *pnAdapterOutputIndex ) = 0;\n\n\t};\n\n\tstatic const char * const IVRExtendedDisplay_Version = \"IVRExtendedDisplay_001\";\n\n}\n\n\n// ivrtrackedcamera.h\nnamespace vr\n{\n\nclass IVRTrackedCamera\n{\npublic:\n\t/** Returns a string for an error */\n\tvirtual const char *GetCameraErrorNameFromEnum( vr::EVRTrackedCameraError eCameraError ) = 0;\n\n\t/** For convenience, same as tracked property request Prop_HasCamera_Bool */\n\tvirtual vr::EVRTrackedCameraError HasCamera( vr::TrackedDeviceIndex_t nDeviceIndex, bool *pHasCamera ) = 0;\n\n\t/** Gets size of the image frame. */\n\tvirtual vr::EVRTrackedCameraError GetCameraFrameSize( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, uint32_t *pnWidth, uint32_t *pnHeight, uint32_t *pnFrameBufferSize ) = 0;\n\n\tvirtual vr::EVRTrackedCameraError GetCameraIntrinsics( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, vr::HmdVector2_t *pFocalLength, vr::HmdVector2_t *pCenter ) = 0;\n\n\tvirtual vr::EVRTrackedCameraError GetCameraProjection( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, float flZNear, float flZFar, vr::HmdMatrix44_t *pProjection ) = 0;\n\n\t/** Acquiring streaming service permits video streaming for the caller. Releasing hints the system that video services do not need to be maintained for this client.\n\t* If the camera has not already been activated, a one time spin up may incur some auto exposure as well as initial streaming frame delays.\n\t* The camera should be considered a global resource accessible for shared consumption but not exclusive to any caller.\n\t* The camera may go inactive due to lack of active consumers or headset idleness. */\n\tvirtual vr::EVRTrackedCameraError AcquireVideoStreamingService( vr::TrackedDeviceIndex_t nDeviceIndex, vr::TrackedCameraHandle_t *pHandle ) = 0;\n\tvirtual vr::EVRTrackedCameraError ReleaseVideoStreamingService( vr::TrackedCameraHandle_t hTrackedCamera ) = 0;\n\n\t/** Copies the image frame into a caller's provided buffer. The image data is currently provided as RGBA data, 4 bytes per pixel.\n\t* A caller can provide null for the framebuffer or frameheader if not desired. Requesting the frame header first, followed by the frame buffer allows\n\t* the caller to determine if the frame as advanced per the frame header sequence. \n\t* If there is no frame available yet, due to initial camera spinup or re-activation, the error will be VRTrackedCameraError_NoFrameAvailable.\n\t* Ideally a caller should be polling at ~16ms intervals */\n\tvirtual vr::EVRTrackedCameraError GetVideoStreamFrameBuffer( vr::TrackedCameraHandle_t hTrackedCamera, vr::EVRTrackedCameraFrameType eFrameType, void *pFrameBuffer, uint32_t nFrameBufferSize, vr::CameraVideoStreamFrameHeader_t *pFrameHeader, uint32_t nFrameHeaderSize ) = 0;\n\n\t/** Gets size of the image frame. */\n\tvirtual vr::EVRTrackedCameraError GetVideoStreamTextureSize( vr::TrackedDeviceIndex_t nDeviceIndex, vr::EVRTrackedCameraFrameType eFrameType, vr::VRTextureBounds_t *pTextureBounds, uint32_t *pnWidth, uint32_t *pnHeight ) = 0; \n\n\t/** Access a shared D3D11 texture for the specified tracked camera stream.\n\t* The camera frame type VRTrackedCameraFrameType_Undistorted is not supported directly as a shared texture. It is an interior subregion of the shared texture VRTrackedCameraFrameType_MaximumUndistorted.\n\t* Instead, use GetVideoStreamTextureSize() with VRTrackedCameraFrameType_Undistorted to determine the proper interior subregion bounds along with GetVideoStreamTextureD3D11() with\n\t* VRTrackedCameraFrameType_MaximumUndistorted to provide the texture. The VRTrackedCameraFrameType_MaximumUndistorted will yield an image where the invalid regions are decoded\n\t* by the alpha channel having a zero component. The valid regions all have a non-zero alpha component. The subregion as described by VRTrackedCameraFrameType_Undistorted \n\t* guarantees a rectangle where all pixels are valid. */\n\tvirtual vr::EVRTrackedCameraError GetVideoStreamTextureD3D11( vr::TrackedCameraHandle_t hTrackedCamera, vr::EVRTrackedCameraFrameType eFrameType, void *pD3D11DeviceOrResource, void **ppD3D11ShaderResourceView, vr::CameraVideoStreamFrameHeader_t *pFrameHeader, uint32_t nFrameHeaderSize ) = 0;\n\n\t/** Access a shared GL texture for the specified tracked camera stream */\n\tvirtual vr::EVRTrackedCameraError GetVideoStreamTextureGL( vr::TrackedCameraHandle_t hTrackedCamera, vr::EVRTrackedCameraFrameType eFrameType, vr::glUInt_t *pglTextureId, vr::CameraVideoStreamFrameHeader_t *pFrameHeader, uint32_t nFrameHeaderSize ) = 0;\n\tvirtual vr::EVRTrackedCameraError ReleaseVideoStreamTextureGL( vr::TrackedCameraHandle_t hTrackedCamera, vr::glUInt_t glTextureId ) = 0;\n};\n\nstatic const char * const IVRTrackedCamera_Version = \"IVRTrackedCamera_003\";\n\n} // namespace vr\n\n\n// ivrscreenshots.h\nnamespace vr\n{\n\n/** Errors that can occur with the VR compositor */\nenum EVRScreenshotError\n{\n\tVRScreenshotError_None\t\t\t\t\t\t\t= 0,\n\tVRScreenshotError_RequestFailed\t\t\t\t\t= 1,\n\tVRScreenshotError_IncompatibleVersion\t\t\t= 100,\n\tVRScreenshotError_NotFound\t\t\t\t\t\t= 101,\n\tVRScreenshotError_BufferTooSmall\t\t\t\t= 102,\n\tVRScreenshotError_ScreenshotAlreadyInProgress\t= 108,\n};\n\n/** Allows the application to generate screenshots */\nclass IVRScreenshots\n{\npublic:\n\t/** Request a screenshot of the requested type.\n\t *  A request of the VRScreenshotType_Stereo type will always\n\t *  work. Other types will depend on the underlying application\n\t *  support.\n\t *  The first file name is for the preview image and should be a\n\t *  regular screenshot (ideally from the left eye). The second\n\t *  is the VR screenshot in the correct format. They should be\n\t *  in the same aspect ratio.  Formats per type:\n\t *  VRScreenshotType_Mono: the VR filename is ignored (can be\n\t *  nullptr), this is a normal flat single shot.\n\t *  VRScreenshotType_Stereo:  The VR image should be a\n\t *  side-by-side with the left eye image on the left.\n\t *  VRScreenshotType_Cubemap: The VR image should be six square\n\t *  images composited horizontally.\n\t *  VRScreenshotType_StereoPanorama: above/below with left eye\n\t *  panorama being the above image.  Image is typically square\n\t *  with the panorama being 2x horizontal.\n\t *  \n\t *  Note that the VR dashboard will call this function when\n\t *  the user presses the screenshot binding (currently System\n\t *  Button + Trigger).  If Steam is running, the destination\n\t *  file names will be in %TEMP% and will be copied into\n\t *  Steam's screenshot library for the running application\n\t *  once SubmitScreenshot() is called.\n\t *  If Steam is not running, the paths will be in the user's\n\t *  documents folder under Documents\\SteamVR\\Screenshots.\n\t *  Other VR applications can call this to initiate a\n\t *  screenshot outside of user control.\n\t *  The destination file names do not need an extension,\n\t *  will be replaced with the correct one for the format\n\t *  which is currently .png. */\n\tvirtual vr::EVRScreenshotError RequestScreenshot( vr::ScreenshotHandle_t *pOutScreenshotHandle, vr::EVRScreenshotType type, const char *pchPreviewFilename, const char *pchVRFilename ) = 0;\n\n\t/** Called by the running VR application to indicate that it\n\t *  wishes to be in charge of screenshots.  If the\n\t *  application does not call this, the Compositor will only\n\t *  support VRScreenshotType_Stereo screenshots that will be\n\t *  captured without notification to the running app.\n\t *  Once hooked your application will receive a\n\t *  VREvent_RequestScreenshot event when the user presses the\n\t *  buttons to take a screenshot. */\n\tvirtual vr::EVRScreenshotError HookScreenshot( VR_ARRAY_COUNT( numTypes ) const vr::EVRScreenshotType *pSupportedTypes, int numTypes ) = 0;\n\n\t/** When your application receives a\n\t *  VREvent_RequestScreenshot event, call these functions to get\n\t *  the details of the screenshot request. */\n\tvirtual vr::EVRScreenshotType GetScreenshotPropertyType( vr::ScreenshotHandle_t screenshotHandle, vr::EVRScreenshotError *pError ) = 0;\n\n\t/** Get the filename for the preview or vr image (see\n\t *  vr::EScreenshotPropertyFilenames).  The return value is\n\t *  the size of the string.   */\n \tvirtual uint32_t GetScreenshotPropertyFilename( vr::ScreenshotHandle_t screenshotHandle, vr::EVRScreenshotPropertyFilenames filenameType, VR_OUT_STRING() char *pchFilename, uint32_t cchFilename, vr::EVRScreenshotError *pError ) = 0;\n\n\t/** Call this if the application is taking the screen shot\n\t *  will take more than a few ms processing. This will result\n\t *  in an overlay being presented that shows a completion\n\t *  bar. */\n\tvirtual vr::EVRScreenshotError UpdateScreenshotProgress( vr::ScreenshotHandle_t screenshotHandle, float flProgress ) = 0;\n\n\t/** Tells the compositor to take an internal screenshot of\n\t *  type VRScreenshotType_Stereo. It will take the current\n\t *  submitted scene textures of the running application and\n\t *  write them into the preview image and a side-by-side file\n\t *  for the VR image.\n\t *  This is similar to request screenshot, but doesn't ever\n\t *  talk to the application, just takes the shot and submits. */\n\tvirtual vr::EVRScreenshotError TakeStereoScreenshot( vr::ScreenshotHandle_t *pOutScreenshotHandle, const char *pchPreviewFilename, const char *pchVRFilename ) = 0;\n\n\t/** Submit the completed screenshot.  If Steam is running\n\t *  this will call into the Steam client and upload the\n\t *  screenshot to the screenshots section of the library for\n\t *  the running application.  If Steam is not running, this\n\t *  function will display a notification to the user that the\n\t *  screenshot was taken. The paths should be full paths with\n\t *  extensions.\n\t *  File paths should be absolute including extensions.\n\t *  screenshotHandle can be k_unScreenshotHandleInvalid if this\n\t *  was a new shot taking by the app to be saved and not\n\t *  initiated by a user (achievement earned or something) */\n\tvirtual vr::EVRScreenshotError SubmitScreenshot( vr::ScreenshotHandle_t screenshotHandle, vr::EVRScreenshotType type, const char *pchSourcePreviewFilename, const char *pchSourceVRFilename ) = 0;\n};\n\nstatic const char * const IVRScreenshots_Version = \"IVRScreenshots_001\";\n\n} // namespace vr\n\n\n\n// ivrresources.h\nnamespace vr\n{\n\nclass IVRResources\n{\npublic:\n\n\t// ------------------------------------\n\t// Shared Resource Methods\n\t// ------------------------------------\n\n\t/** Loads the specified resource into the provided buffer if large enough.\n\t* Returns the size in bytes of the buffer required to hold the specified resource. */\n\tvirtual uint32_t LoadSharedResource( const char *pchResourceName, char *pchBuffer, uint32_t unBufferLen ) = 0;\n\n\t/** Provides the full path to the specified resource. Resource names can include named directories for\n\t* drivers and other things, and this resolves all of those and returns the actual physical path. \n\t* pchResourceTypeDirectory is the subdirectory of resources to look in. */\n\tvirtual uint32_t GetResourceFullPath( const char *pchResourceName, const char *pchResourceTypeDirectory, VR_OUT_STRING() char *pchPathBuffer, uint32_t unBufferLen ) = 0;\n};\n\nstatic const char * const IVRResources_Version = \"IVRResources_001\";\n\n\n}\n// ivrdrivermanager.h\nnamespace vr\n{\n\nclass IVRDriverManager\n{\npublic:\n\tvirtual uint32_t GetDriverCount() const = 0;\n\n\t/** Returns the length of the number of bytes necessary to hold this string including the trailing null. */\n\tvirtual uint32_t GetDriverName( vr::DriverId_t nDriver, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize ) = 0;\n\n\tvirtual DriverHandle_t GetDriverHandle( const char *pchDriverName ) = 0;\n};\n\nstatic const char * const IVRDriverManager_Version = \"IVRDriverManager_001\";\n\n} // namespace vr\n\n\n\n// ivrinput.h\nnamespace vr\n{\n\n\ttypedef uint64_t VRActionHandle_t;\n\ttypedef uint64_t VRActionSetHandle_t;\n\ttypedef uint64_t VRInputValueHandle_t;\n\n\tstatic const VRActionHandle_t k_ulInvalidActionHandle = 0;\n\tstatic const VRActionSetHandle_t k_ulInvalidActionSetHandle = 0;\n\tstatic const VRInputValueHandle_t k_ulInvalidInputValueHandle = 0;\n\n\tstatic const uint32_t k_unMaxActionNameLength = 64;\n\tstatic const uint32_t k_unMaxActionSetNameLength = 64;\n\tstatic const uint32_t k_unMaxActionOriginCount = 16;\n\n\tstruct InputAnalogActionData_t\n\t{\n\t\t// Whether or not this action is currently available to be bound in the active action set\n\t\tbool bActive;\n\n\t\t// The origin that caused this action's current state\n\t\tVRInputValueHandle_t activeOrigin;\n\n\t\t// The current state of this action; will be delta updates for mouse actions\n\t\tfloat x, y, z;\n\n\t\t// Deltas since the previous call to UpdateActionState()\n\t\tfloat deltaX, deltaY, deltaZ;\n\t\n\t\t// Time relative to now when this event happened. Will be negative to indicate a past time.\n\t\tfloat fUpdateTime;\n\t};\n\n\tstruct InputDigitalActionData_t\n\t{\n\t\t// Whether or not this action is currently available to be bound in the active action set\n\t\tbool bActive;\n\n\t\t// The origin that caused this action's current state\n\t\tVRInputValueHandle_t activeOrigin;\n\n\t\t// The current state of this action; will be true if currently pressed\n\t\tbool bState;\n\n\t\t// This is true if the state has changed since the last frame\n\t\tbool bChanged;\n\n\t\t// Time relative to now when this event happened. Will be negative to indicate a past time.\n\t\tfloat fUpdateTime;\n\t};\n\n\tstruct InputPoseActionData_t\n\t{\n\t\t// Whether or not this action is currently available to be bound in the active action set\n\t\tbool bActive;\n\n\t\t// The origin that caused this action's current state\n\t\tVRInputValueHandle_t activeOrigin;\n\n\t\t// The current state of this action\n\t\tTrackedDevicePose_t pose;\n\t};\n\n\tenum EVRSkeletalTransformSpace\n\t{\n\t\tVRSkeletalTransformSpace_Action = 0,\n\t\tVRSkeletalTransformSpace_Parent = 1,\n\t\tVRSkeletalTransformSpace_Additive = 2,\n\t};\n\n\n\tstruct InputSkeletonActionData_t\n\t{\n\t\t// Whether or not this action is currently available to be bound in the active action set\n\t\tbool bActive;\n\n\t\t// The origin that caused this action's current state\n\t\tVRInputValueHandle_t activeOrigin;\n\t};\n\n\tenum EVRInputFilterCancelType\n\t{\n\t\tVRInputFilterCancel_Timers = 0,\n\t\tVRInputFilterCancel_Momentum = 1,\n\t};\n\n\tstruct InputOriginInfo_t\n\t{\n\t\tVRInputValueHandle_t devicePath;\n\t\tTrackedDeviceIndex_t trackedDeviceIndex;\n\t\tchar rchRenderModelComponentName[128];\n\t};\n\n\tstruct VRActiveActionSet_t\n\t{\n\t\t/** This is the handle of the action set to activate for this frame. */\n\t\tVRActionSetHandle_t ulActionSet;\n\n\t\t/** This is the handle of a device path that this action set should be active for. To\n\t\t* activate for all devices, set this to k_ulInvalidInputValueHandle. */\n\t\tVRInputValueHandle_t ulRestrictedToDevice;\n\n\t\t/** The action set to activate for all devices other than ulRestrictedDevice. If \n\t\t* ulRestrictedToDevice is set to k_ulInvalidInputValueHandle, this parameter is \n\t\t* ignored. */\n\t\tVRActionSetHandle_t ulSecondaryActionSet;\n\t};\n\n\n\tclass IVRInput\n\t{\n\tpublic:\n\n\t\t// ---------------  Handle management   --------------- //\n\n\t\t/** Sets the path to the action manifest JSON file that is used by this application. If this information\n\t\t* was set on the Steam partner site, calls to this function are ignored. If the Steam partner site\n\t\t* setting and the path provided by this call are different, VRInputError_MismatchedActionManifest is returned. \n\t\t* This call must be made before the first call to UpdateActionState or IVRSystem::PollNextEvent. */\n\t\tvirtual EVRInputError SetActionManifestPath( const char *pchActionManifestPath ) = 0;\n\n\t\t/** Returns a handle for an action set. This handle is used for all performance-sensitive calls. */\n\t\tvirtual EVRInputError GetActionSetHandle( const char *pchActionSetName, VRActionSetHandle_t *pHandle ) = 0;\n\n\t\t/** Returns a handle for an action. This handle is used for all performance-sensitive calls. */\n\t\tvirtual EVRInputError GetActionHandle( const char *pchActionName, VRActionHandle_t *pHandle ) = 0;\n\n\t\t/** Returns a handle for any path in the input system. E.g. /user/hand/right */\n\t\tvirtual EVRInputError GetInputSourceHandle( const char *pchInputSourcePath, VRInputValueHandle_t  *pHandle ) = 0;\n\n\t\t// --------------- Reading action state ------------------- //\n\n\t\t/** Reads the current state into all actions. After this call, the results of Get*Action calls \n\t\t* will be the same until the next call to UpdateActionState. */\n\t\tvirtual EVRInputError UpdateActionState( VR_ARRAY_COUNT( unSetCount ) VRActiveActionSet_t *pSets, uint32_t unSizeOfVRSelectedActionSet_t, uint32_t unSetCount ) = 0;\n\n\t\t/** Reads the state of a digital action given its handle. This will return VRInputError_WrongType if the type of\n\t\t* action is something other than digital */\n\t\tvirtual EVRInputError GetDigitalActionData( VRActionHandle_t action, InputDigitalActionData_t *pActionData, uint32_t unActionDataSize ) = 0;\n\n\t\t/** Reads the state of an analog action given its handle. This will return VRInputError_WrongType if the type of\n\t\t* action is something other than analog */\n\t\tvirtual EVRInputError GetAnalogActionData( VRActionHandle_t action, InputAnalogActionData_t *pActionData, uint32_t unActionDataSize ) = 0;\n\n\t\t/** Reads the state of a pose action given its handle. */\n\t\tvirtual EVRInputError GetPoseActionData( VRActionHandle_t action, ETrackingUniverseOrigin eOrigin, float fPredictedSecondsFromNow, InputPoseActionData_t *pActionData, uint32_t unActionDataSize ) = 0;\n\n\t\t/** Reads the state of a skeletal action given its handle. */\n\t\tvirtual EVRInputError GetSkeletalActionData( VRActionHandle_t action, EVRSkeletalTransformSpace eBoneParent, float fPredictedSecondsFromNow, InputSkeletonActionData_t *pActionData, uint32_t unActionDataSize, VR_ARRAY_COUNT( unTransformArrayCount ) VRBoneTransform_t *pTransformArray, uint32_t unTransformArrayCount ) = 0;\n\n\t\t/** Reads the state of a skeletal action given its handle in a compressed form that is suitable for\n\t\t* sending over the network. The required buffer size will never exceed ( sizeof(VR_BoneTransform_t)*boneCount + 2).\n\t\t* Usually the size will be much smaller. */\n\t\tvirtual EVRInputError GetSkeletalActionDataCompressed( VRActionHandle_t action, EVRSkeletalTransformSpace eBoneParent, float fPredictedSecondsFromNow, VR_OUT_BUFFER_COUNT( unCompressedSize ) void *pvCompressedData, uint32_t unCompressedSize, uint32_t *punRequiredCompressedSize ) = 0;\n\n\t\t/** Turns a compressed buffer from GetSkeletalActionDataCompressed and turns it back into a bone transform array. */\n\t\tvirtual EVRInputError UncompressSkeletalActionData( void *pvCompressedBuffer, uint32_t unCompressedBufferSize, EVRSkeletalTransformSpace *peBoneParent, VR_ARRAY_COUNT( unTransformArrayCount ) VRBoneTransform_t *pTransformArray, uint32_t unTransformArrayCount ) = 0;\n\n\t\t// --------------- Haptics ------------------- //\n\n\t\t/** Triggers a haptic event as described by the specified action */\n\t\tvirtual EVRInputError TriggerHapticVibrationAction( VRActionHandle_t action, float fStartSecondsFromNow, float fDurationSeconds, float fFrequency, float fAmplitude ) = 0;\n\n\t\t// --------------- Action Origins ---------------- //\n\n\t\t/** Retrieve origin handles for an action */\n\t\tvirtual EVRInputError GetActionOrigins( VRActionSetHandle_t actionSetHandle, VRActionHandle_t digitalActionHandle, VR_ARRAY_COUNT( originOutCount ) VRInputValueHandle_t *originsOut, uint32_t originOutCount ) = 0;\n\n\t\t/** Retrieves the name of the origin in the current language */\n\t\tvirtual EVRInputError GetOriginLocalizedName( VRInputValueHandle_t origin, VR_OUT_STRING() char *pchNameArray, uint32_t unNameArraySize ) = 0;\n\n\t\t/** Retrieves useful information for the origin of this action */\n\t\tvirtual EVRInputError GetOriginTrackedDeviceInfo( VRInputValueHandle_t origin, InputOriginInfo_t *pOriginInfo, uint32_t unOriginInfoSize ) = 0;\n\n\t\t/** Shows the current binding for the action in-headset */\n\t\tvirtual EVRInputError ShowActionOrigins( VRActionSetHandle_t actionSetHandle, VRActionHandle_t ulActionHandle ) = 0;\n\n\t\t/** Shows the current binding all the actions in the specified action sets */\n\t\tvirtual EVRInputError ShowBindingsForActionSet( VR_ARRAY_COUNT( unSetCount ) VRActiveActionSet_t *pSets, uint32_t unSizeOfVRSelectedActionSet_t, uint32_t unSetCount, VRInputValueHandle_t originToHighlight ) = 0;\n\t};\n\n\tstatic const char * const IVRInput_Version = \"IVRInput_003\";\n\n} // namespace vr\n\n// ivriobuffer.h\nnamespace vr\n{\n\ntypedef uint64_t IOBufferHandle_t;\nstatic const uint64_t k_ulInvalidIOBufferHandle = 0;\n\t\n\tenum EIOBufferError\n\t{\n\t\tIOBuffer_Success                  = 0,\n\t\tIOBuffer_OperationFailed          = 100,\n\t\tIOBuffer_InvalidHandle            = 101,\n\t\tIOBuffer_InvalidArgument          = 102,\n\t\tIOBuffer_PathExists               = 103,\n\t\tIOBuffer_PathDoesNotExist         = 104,\n\t\tIOBuffer_Permission               = 105,\n\t};\n\n\tenum EIOBufferMode\n\t{\n\t\tIOBufferMode_Read                 = 0x0001,\n\t\tIOBufferMode_Write                = 0x0002,\n\t\tIOBufferMode_Create               = 0x0200,\n\t};\n\n\t// ----------------------------------------------------------------------------------------------\n\t// Purpose:\n\t// ----------------------------------------------------------------------------------------------\n\tclass IVRIOBuffer\n\t{\n\tpublic:\n\t\t/** opens an existing or creates a new IOBuffer of unSize bytes */\n\t\tvirtual vr::EIOBufferError Open( const char *pchPath, vr::EIOBufferMode mode, uint32_t unElementSize, uint32_t unElements, vr::IOBufferHandle_t *pulBuffer ) = 0;\n\t\t\n\t\t/** closes a previously opened or created buffer */\n\t\tvirtual vr::EIOBufferError Close( vr::IOBufferHandle_t ulBuffer ) = 0;\n\t\t\n\t\t/** reads up to unBytes from buffer into *pDst, returning number of bytes read in *punRead */\n\t\tvirtual vr::EIOBufferError Read( vr::IOBufferHandle_t ulBuffer, void *pDst, uint32_t unBytes, uint32_t *punRead ) = 0;\n\t\t\n\t\t/** writes unBytes of data from *pSrc into a buffer. */\n\t\tvirtual vr::EIOBufferError Write( vr::IOBufferHandle_t ulBuffer, void *pSrc, uint32_t unBytes ) = 0;\n\t\t\n\t\t/** retrieves the property container of an buffer. */\n\t\tvirtual vr::PropertyContainerHandle_t PropertyContainer( vr::IOBufferHandle_t ulBuffer ) = 0;\n\t};\n\n\tstatic const char *IVRIOBuffer_Version = \"IVRIOBuffer_001\";\n}\n// End\n\n#endif // _OPENVR_API\n\n\nnamespace vr\n{\n\t/** Finds the active installation of the VR API and initializes it. The provided path must be absolute\n\t* or relative to the current working directory. These are the local install versions of the equivalent\n\t* functions in steamvr.h and will work without a local Steam install.\n\t*\n\t* This path is to the \"root\" of the VR API install. That's the directory with\n\t* the \"drivers\" directory and a platform (i.e. \"win32\") directory in it, not the directory with the DLL itself.\n\t*\n\t* pStartupInfo is reserved for future use.\n\t*/\n\tinline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType, const char *pStartupInfo = nullptr );\n\n\t/** unloads vrclient.dll. Any interface pointers from the interface are\n\t* invalid after this point */\n\tinline void VR_Shutdown();\n\n\t/** Returns true if there is an HMD attached. This check is as lightweight as possible and\n\t* can be called outside of VR_Init/VR_Shutdown. It should be used when an application wants\n\t* to know if initializing VR is a possibility but isn't ready to take that step yet.\n\t*/\n\tVR_INTERFACE bool VR_CALLTYPE VR_IsHmdPresent();\n\n\t/** Returns true if the OpenVR runtime is installed. */\n\tVR_INTERFACE bool VR_CALLTYPE VR_IsRuntimeInstalled();\n\n\t/** Returns where the OpenVR runtime is installed. */\n\tVR_INTERFACE const char *VR_CALLTYPE VR_RuntimePath();\n\n\t/** Returns the name of the enum value for an EVRInitError. This function may be called outside of VR_Init()/VR_Shutdown(). */\n\tVR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsSymbol( EVRInitError error );\n\n\t/** Returns an English string for an EVRInitError. Applications should call VR_GetVRInitErrorAsSymbol instead and\n\t* use that as a key to look up their own localized error message. This function may be called outside of VR_Init()/VR_Shutdown(). */\n\tVR_INTERFACE const char *VR_CALLTYPE VR_GetVRInitErrorAsEnglishDescription( EVRInitError error );\n\n\t/** Returns the interface of the specified version. This method must be called after VR_Init. The\n\t* pointer returned is valid until VR_Shutdown is called.\n\t*/\n\tVR_INTERFACE void *VR_CALLTYPE VR_GetGenericInterface( const char *pchInterfaceVersion, EVRInitError *peError );\n\n\t/** Returns whether the interface of the specified version exists.\n\t*/\n\tVR_INTERFACE bool VR_CALLTYPE VR_IsInterfaceVersionValid( const char *pchInterfaceVersion );\n\n\t/** Returns a token that represents whether the VR interface handles need to be reloaded */\n\tVR_INTERFACE uint32_t VR_CALLTYPE VR_GetInitToken();\n\n\t// These typedefs allow old enum names from SDK 0.9.11 to be used in applications.\n\t// They will go away in the future.\n\ttypedef EVRInitError HmdError;\n\ttypedef EVREye Hmd_Eye;\n\ttypedef EColorSpace ColorSpace;\n\ttypedef ETrackingResult HmdTrackingResult;\n\ttypedef ETrackedDeviceClass TrackedDeviceClass;\n\ttypedef ETrackingUniverseOrigin TrackingUniverseOrigin;\n\ttypedef ETrackedDeviceProperty TrackedDeviceProperty;\n\ttypedef ETrackedPropertyError TrackedPropertyError;\n\ttypedef EVRSubmitFlags VRSubmitFlags_t;\n\ttypedef EVRState VRState_t;\n\ttypedef ECollisionBoundsStyle CollisionBoundsStyle_t;\n\ttypedef EVROverlayError VROverlayError;\n\ttypedef EVRFirmwareError VRFirmwareError;\n\ttypedef EVRCompositorError VRCompositorError;\n\ttypedef EVRScreenshotError VRScreenshotsError;\n\n\tinline uint32_t &VRToken()\n\t{\n\t\tstatic uint32_t token;\n\t\treturn token;\n\t}\n\n\tclass COpenVRContext\n\t{\n\tpublic:\n\t\tCOpenVRContext() { Clear(); }\n\t\tvoid Clear();\n\n\t\tinline void CheckClear()\n\t\t{\n\t\t\tif ( VRToken() != VR_GetInitToken() )\n\t\t\t{\n\t\t\t\tClear();\n\t\t\t\tVRToken() = VR_GetInitToken();\n\t\t\t}\n\t\t}\n\n\t\tIVRSystem *VRSystem()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRSystem == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRSystem = ( IVRSystem * )VR_GetGenericInterface( IVRSystem_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRSystem;\n\t\t}\n\t\tIVRChaperone *VRChaperone()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRChaperone == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRChaperone = ( IVRChaperone * )VR_GetGenericInterface( IVRChaperone_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRChaperone;\n\t\t}\n\n\t\tIVRChaperoneSetup *VRChaperoneSetup()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRChaperoneSetup == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRChaperoneSetup = ( IVRChaperoneSetup * )VR_GetGenericInterface( IVRChaperoneSetup_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRChaperoneSetup;\n\t\t}\n\n\t\tIVRCompositor *VRCompositor()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRCompositor == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRCompositor = ( IVRCompositor * )VR_GetGenericInterface( IVRCompositor_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRCompositor;\n\t\t}\n\n\t\tIVROverlay *VROverlay()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVROverlay == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVROverlay = ( IVROverlay * )VR_GetGenericInterface( IVROverlay_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVROverlay;\n\t\t}\n\n\t\tIVRResources *VRResources()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRResources == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRResources = (IVRResources *)VR_GetGenericInterface( IVRResources_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRResources;\n\t\t}\n\n\t\tIVRScreenshots *VRScreenshots()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRScreenshots == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRScreenshots = ( IVRScreenshots * )VR_GetGenericInterface( IVRScreenshots_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRScreenshots;\n\t\t}\n\n\t\tIVRRenderModels *VRRenderModels()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRRenderModels == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRRenderModels = ( IVRRenderModels * )VR_GetGenericInterface( IVRRenderModels_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRRenderModels;\n\t\t}\n\n\t\tIVRExtendedDisplay *VRExtendedDisplay()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRExtendedDisplay == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRExtendedDisplay = ( IVRExtendedDisplay * )VR_GetGenericInterface( IVRExtendedDisplay_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRExtendedDisplay;\n\t\t}\n\n\t\tIVRSettings *VRSettings()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRSettings == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRSettings = ( IVRSettings * )VR_GetGenericInterface( IVRSettings_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRSettings;\n\t\t}\n\n\t\tIVRApplications *VRApplications()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRApplications == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRApplications = ( IVRApplications * )VR_GetGenericInterface( IVRApplications_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRApplications;\n\t\t}\n\n\t\tIVRTrackedCamera *VRTrackedCamera()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( m_pVRTrackedCamera == nullptr )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRTrackedCamera = ( IVRTrackedCamera * )VR_GetGenericInterface( IVRTrackedCamera_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRTrackedCamera;\n\t\t}\n\n\t\tIVRDriverManager *VRDriverManager()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( !m_pVRDriverManager )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRDriverManager = ( IVRDriverManager * )VR_GetGenericInterface( IVRDriverManager_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRDriverManager;\n\t\t}\n\n\t\tIVRInput *VRInput()\n\t\t{\n\t\t\tCheckClear();\n\t\t\tif ( !m_pVRInput )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRInput = (IVRInput *)VR_GetGenericInterface( IVRInput_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRInput;\n\t\t}\n\n\t\tIVRIOBuffer *VRIOBuffer()\n\t\t{\n\t\t\tif ( !m_pVRIOBuffer )\n\t\t\t{\n\t\t\t\tEVRInitError eError;\n\t\t\t\tm_pVRIOBuffer = ( IVRIOBuffer * )VR_GetGenericInterface( IVRIOBuffer_Version, &eError );\n\t\t\t}\n\t\t\treturn m_pVRIOBuffer;\n\t\t}\n\t\t\n\tprivate:\n\t\tIVRSystem\t\t\t*m_pVRSystem;\n\t\tIVRChaperone\t\t*m_pVRChaperone;\n\t\tIVRChaperoneSetup\t*m_pVRChaperoneSetup;\n\t\tIVRCompositor\t\t*m_pVRCompositor;\n\t\tIVROverlay\t\t\t*m_pVROverlay;\n\t\tIVRResources\t\t*m_pVRResources;\n\t\tIVRRenderModels\t\t*m_pVRRenderModels;\n\t\tIVRExtendedDisplay\t*m_pVRExtendedDisplay;\n\t\tIVRSettings\t\t\t*m_pVRSettings;\n\t\tIVRApplications\t\t*m_pVRApplications;\n\t\tIVRTrackedCamera\t*m_pVRTrackedCamera;\n\t\tIVRScreenshots\t\t*m_pVRScreenshots;\n\t\tIVRDriverManager\t*m_pVRDriverManager;\n\t\tIVRInput\t\t\t*m_pVRInput;\n\t\tIVRIOBuffer\t\t\t*m_pVRIOBuffer;\n\t};\n\n\tinline COpenVRContext &OpenVRInternal_ModuleContext()\n\t{\n\t\tstatic void *ctx[ sizeof( COpenVRContext ) / sizeof( void * ) ];\n\t\treturn *( COpenVRContext * )ctx; // bypass zero-init constructor\n\t}\n\n\tinline IVRSystem *VR_CALLTYPE VRSystem() { return OpenVRInternal_ModuleContext().VRSystem(); }\n\tinline IVRChaperone *VR_CALLTYPE VRChaperone() { return OpenVRInternal_ModuleContext().VRChaperone(); }\n\tinline IVRChaperoneSetup *VR_CALLTYPE VRChaperoneSetup() { return OpenVRInternal_ModuleContext().VRChaperoneSetup(); }\n\tinline IVRCompositor *VR_CALLTYPE VRCompositor() { return OpenVRInternal_ModuleContext().VRCompositor(); }\n\tinline IVROverlay *VR_CALLTYPE VROverlay() { return OpenVRInternal_ModuleContext().VROverlay(); }\n\tinline IVRScreenshots *VR_CALLTYPE VRScreenshots() { return OpenVRInternal_ModuleContext().VRScreenshots(); }\n\tinline IVRRenderModels *VR_CALLTYPE VRRenderModels() { return OpenVRInternal_ModuleContext().VRRenderModels(); }\n\tinline IVRApplications *VR_CALLTYPE VRApplications() { return OpenVRInternal_ModuleContext().VRApplications(); }\n\tinline IVRSettings *VR_CALLTYPE VRSettings() { return OpenVRInternal_ModuleContext().VRSettings(); }\n\tinline IVRResources *VR_CALLTYPE VRResources() { return OpenVRInternal_ModuleContext().VRResources(); }\n\tinline IVRExtendedDisplay *VR_CALLTYPE VRExtendedDisplay() { return OpenVRInternal_ModuleContext().VRExtendedDisplay(); }\n\tinline IVRTrackedCamera *VR_CALLTYPE VRTrackedCamera() { return OpenVRInternal_ModuleContext().VRTrackedCamera(); }\n\tinline IVRDriverManager *VR_CALLTYPE VRDriverManager() { return OpenVRInternal_ModuleContext().VRDriverManager(); }\n\tinline IVRInput *VR_CALLTYPE VRInput() { return OpenVRInternal_ModuleContext().VRInput(); }\n\tinline IVRIOBuffer *VR_CALLTYPE VRIOBuffer() { return OpenVRInternal_ModuleContext().VRIOBuffer(); }\n\n\tinline void COpenVRContext::Clear()\n\t{\n\t\tm_pVRSystem = nullptr;\n\t\tm_pVRChaperone = nullptr;\n\t\tm_pVRChaperoneSetup = nullptr;\n\t\tm_pVRCompositor = nullptr;\n\t\tm_pVROverlay = nullptr;\n\t\tm_pVRRenderModels = nullptr;\n\t\tm_pVRExtendedDisplay = nullptr;\n\t\tm_pVRSettings = nullptr;\n\t\tm_pVRApplications = nullptr;\n\t\tm_pVRTrackedCamera = nullptr;\n\t\tm_pVRResources = nullptr;\n\t\tm_pVRScreenshots = nullptr;\n\t\tm_pVRDriverManager = nullptr;\n\t\tm_pVRInput = nullptr;\n\t\tm_pVRIOBuffer = nullptr;\n\t}\n\t\n\tVR_INTERFACE uint32_t VR_CALLTYPE VR_InitInternal2( EVRInitError *peError, EVRApplicationType eApplicationType, const char *pStartupInfo );\n\tVR_INTERFACE void VR_CALLTYPE VR_ShutdownInternal();\n\n\t/** Finds the active installation of vrclient.dll and initializes it */\n\tinline IVRSystem *VR_Init( EVRInitError *peError, EVRApplicationType eApplicationType, const char *pStartupInfo )\n\t{\n\t\tIVRSystem *pVRSystem = nullptr;\n\n\t\tEVRInitError eError;\n\t\tVRToken() = VR_InitInternal2( &eError, eApplicationType, pStartupInfo );\n\t\tCOpenVRContext &ctx = OpenVRInternal_ModuleContext();\n\t\tctx.Clear();\n\n\t\tif ( eError == VRInitError_None )\n\t\t{\n\t\t\tif ( VR_IsInterfaceVersionValid( IVRSystem_Version ) )\n\t\t\t{\n\t\t\t\tpVRSystem = VRSystem();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVR_ShutdownInternal();\n\t\t\t\teError = VRInitError_Init_InterfaceNotFound;\n\t\t\t}\n\t\t}\n\n\t\tif ( peError )\n\t\t\t*peError = eError;\n\t\treturn pVRSystem;\n\t}\n\n\t/** unloads vrclient.dll. Any interface pointers from the interface are\n\t* invalid after this point */\n\tinline void VR_Shutdown()\n\t{\n\t\tVR_ShutdownInternal();\n\t}\n}"
  },
  {
    "path": "meson.build",
    "content": "project('dxvk', ['c', 'cpp'], version : '2.7.1', meson_version : '>= 0.58', default_options : [ 'cpp_std=c++17', 'b_vscrt=static_from_buildtype', 'warning_level=2' ])\n\npkg = import('pkgconfig')\ncpu_family = target_machine.cpu_family()\nplatform   = target_machine.system()\nfs = import('fs')\n\ncpp = meson.get_compiler('cpp')\ncc = meson.get_compiler('c')\ndxvk_is_msvc = cpp.get_argument_syntax() == 'msvc'\n\ncompiler_args = [\n  '-msse',\n  '-msse2',\n  '-msse3',\n  '-mfpmath=sse',\n  '-Wimplicit-fallthrough',\n  # gcc\n  '-Wno-missing-field-initializers',\n  '-Wno-unused-parameter',\n  '-Wno-misleading-indentation',\n  '-Wno-cast-function-type', # Needed for GetProcAddress.\n  # clang\n  '-Wno-unused-private-field',\n  '-Wno-microsoft-exception-spec',\n  '-Wno-extern-c-compat',\n  '-Wno-unused-const-variable',\n  '-Wno-missing-braces',\n]\n\nlink_args = []\n\nif get_option('build_id')\n  link_args += [\n    '-Wl,--build-id',\n  ]\nendif\n\ndxvk_include_dirs = ['./include']\nif fs.is_dir('./include/vulkan/include')\n  dxvk_include_dirs += ['./include/vulkan/include']\nelif not cpp.check_header('vulkan/vulkan.h')\n  error('Missing Vulkan-Headers')\nendif\nif fs.is_dir('./include/spirv/include')\n  dxvk_include_dirs += ['./include/spirv/include']\nelif not cpp.check_header('spirv/unified1/spirv.hpp')\n  error('Missing SPIRV-Headers')\nendif\n\ndep_displayinfo = dependency(\n  'libdisplay-info',\n  version: ['>= 0.0.0', '< 0.2.0'],\n  fallback: ['libdisplay-info', 'di_dep'],\n  default_options: ['default_library=static'],\n)\n\nif platform == 'windows'\n  dxvk_so_version = {'name_prefix': ''}\n\n  compiler_args += [\n    '-DNOMINMAX',\n    '-D_WIN32_WINNT=0xa00',\n  ]\n\n  if not dxvk_is_msvc\n    link_args += [\n      '-static',\n      '-static-libgcc',\n      '-static-libstdc++',\n      # We need to set the section alignment for debug symbols to\n      # work properly as well as avoiding a memcpy from the Wine loader.\n      '-Wl,--file-alignment=4096',\n    ]\n\n    # Wine's built-in back traces only work with dwarf4 symbols\n    if get_option('debug')\n      compiler_args += [\n        '-gdwarf-4',\n      ]\n    endif\n\n    if cpu_family == 'x86'\n      # Enable stdcall fixup on 32-bit\n      link_args += [\n        '-Wl,--enable-stdcall-fixup',\n        '-Wl,--kill-at',\n      ]\n      # Fix stack alignment issues with mingw on 32-bit\n      compiler_args += [\n        '-mpreferred-stack-boundary=2'\n      ]\n    endif\n  else\n    # setup file alignment + enable PDB output for MSVC builds\n    # PDBs are useful for Windows consumers of DXVK \n    compiler_args += [\n      '/Z7'\n    ]\n    link_args += [\n      '/FILEALIGN:4096',\n      '/DEBUG:FULL'\n    ]\n  endif\n\n  lib_d3d9    = cpp.find_library('d3d9')\n  lib_d3d11   = cpp.find_library('d3d11')\n  lib_dxgi    = cpp.find_library('dxgi')\n  lib_gdi32   = cpp.find_library('gdi32')\n\n  if dxvk_is_msvc\n    res_ext = '.res'\n    wrc = find_program('rc')\n    wrc_generator = generator(wrc,\n      output    : [ '@BASENAME@' + res_ext ],\n      arguments : [ '/fo', '@OUTPUT@', '@INPUT@' ],\n    )\n  else\n    res_ext = '.o'\n    wrc = find_program('windres')\n    wrc_generator = generator(wrc,\n      output    : [ '@BASENAME@' + res_ext ],\n      arguments : [ '-i', '@INPUT@', '-o', '@OUTPUT@' ],\n    )\n  endif\n\n  dxvk_name_prefix = ''\n  compiler_args += ['-DDXVK_WSI_WIN32']\nelse\n  dxvk_abi_version = '0'\n  dxvk_version_raw = meson.project_version().strip('v').split('.')\n  dxvk_version = [ dxvk_version_raw[0] ]\n  foreach i : [ 1, 2 ]\n    padded = dxvk_version_raw[i]\n    if padded.to_int() < 10\n      padded = '0' + padded\n    endif\n    dxvk_version += [ padded ]\n  endforeach\n  dxvk_so_version = {'version': dxvk_abi_version + '.' + dxvk_version[0] + dxvk_version[1] + dxvk_version[2]}\n\n  wrc           = find_program('touch')\n  wrc_generator = generator(wrc, output : [ '@BASENAME@_ignored.h' ], arguments : [ '@OUTPUT@' ] )\n\n  dxvk_include_dirs += [\n    './include/native',\n    './include/native/windows',\n    './include/native/directx'\n  ]\n\n  lib_sdl3 = dependency('sdl3', required: get_option('native_sdl3'))\n  lib_sdl2 = dependency('sdl2', required: get_option('native_sdl2'))\n  lib_glfw = dependency('glfw3', required: get_option('native_glfw'))\n  if lib_sdl3.found()\n    compiler_args += ['-DDXVK_WSI_SDL3']\n  endif\n  if lib_sdl2.found()\n    compiler_args += ['-DDXVK_WSI_SDL2']\n  endif\n  if lib_glfw.found()\n    compiler_args += ['-DDXVK_WSI_GLFW']\n  endif\n  if (not lib_sdl3.found() and not lib_sdl2.found() and not lib_glfw.found())\n    error('SDL3, SDL2, or GLFW are required to build dxvk-native')\n  endif\n  \n  dxvk_name_prefix = 'dxvk_'\n  dxvk_pkg_prefix = 'dxvk-'\n\n  link_args += [\n    '-static-libgcc',\n    '-static-libstdc++',\n  ]\nendif\n\ndxbc_spirv = subproject('dxbc-spirv')\ndxbc_spirv_dep = dxbc_spirv.get_variable('dxbc_spv_dep')\n\ndxvk_include_path = include_directories(dxvk_include_dirs)\n\nadd_project_arguments(cpp.get_supported_arguments(compiler_args), language: 'cpp')\nadd_project_arguments(cc.get_supported_arguments(compiler_args), language: 'c')\nadd_project_link_arguments(cpp.get_supported_link_arguments(link_args), language: 'cpp')\nadd_project_link_arguments(cc.get_supported_link_arguments(link_args), language: 'c')\n\nexe_ext = ''\ndef_spec_ext = '.def'\n\nglsl_compiler = find_program('glslang', 'glslangValidator')\nglsl_args = [\n  '--quiet',\n  '--target-env', 'vulkan1.3',\n  '--vn', '@BASENAME@',\n  '--depfile', '@DEPFILE@',\n  '@INPUT@',\n  '-o', '@OUTPUT@',\n]\nif get_option('debug')\n  glsl_args += ['-gVS']\nendif\nglsl_generator = generator(\n  glsl_compiler,\n  output    : [ '@BASENAME@.h' ],\n  depfile   : '@BASENAME@.h.d',\n  arguments : glsl_args,\n)\n\ndxvk_version = vcs_tag(\n  command: ['git', 'describe', '--dirty=+'],\n  input:  'version.h.in',\n  output: 'version.h',\n)\n\nconf_data = configuration_data()\nconf_data.set('BUILD_COMPILER', cpp.get_id())\nconf_data.set('BUILD_COMPILER_VERSION', cpp.version())\nconf_data.set('BUILD_TARGET', cpu_family)\ndxvk_buildenv = configure_file(\n  configuration : conf_data,\n  input:  'buildenv.h.in',\n  output: 'buildenv.h',\n)\n\nif platform != 'windows'\n  subdir('include/native')\nendif\n\nsubdir('src')\n"
  },
  {
    "path": "meson_options.txt",
    "content": "option('enable_dxgi',  type : 'boolean', value : true, description: 'Build DXGI')\noption('enable_d3d8',  type : 'boolean', value : true, description: 'Build D3D8')\noption('enable_d3d9',  type : 'boolean', value : true, description: 'Build D3D9')\noption('enable_d3d10', type : 'boolean', value : true, description: 'Build D3D10')\noption('enable_d3d11', type : 'boolean', value : true, description: 'Build D3D11')\noption('build_id',     type : 'boolean', value : false)\noption('native_glfw',  type : 'feature', value : 'auto', description: 'Enable GLFW WSI for DXVK Native')\noption('native_sdl2',  type : 'feature', value : 'auto', description: 'Enable SDL2 WSI for DXVK Native')\noption('native_sdl3',  type : 'feature', value : 'auto', description: 'Enable SDL3 WSI for DXVK Native')\n"
  },
  {
    "path": "package-native.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\nshopt -s extglob\n\nif [ -z \"$1\" ] || [ -z \"$2\" ]; then\n  echo \"Usage: $0 version destdir [--no-package] [--dev-build]\"\n  exit 1\nfi\n\nDXVK_VERSION=\"$1\"\nDXVK_SRC_DIR=$(readlink -f \"$0\")\nDXVK_SRC_DIR=$(dirname \"$DXVK_SRC_DIR\")\nDXVK_BUILD_DIR=$(realpath \"$2\")\"/dxvk-native-$DXVK_VERSION\"\nDXVK_ARCHIVE_PATH=$(realpath \"$2\")\"/dxvk-native-$DXVK_VERSION.tar.gz\"\n\nif [ -e \"$DXVK_BUILD_DIR\" ]; then\n  echo \"Build directory $DXVK_BUILD_DIR already exists\"\n  exit 1\nfi\n\nshift 2\n\nopt_nopackage=0\nopt_devbuild=0\nopt_buildid=false\nopt_64_only=0\nopt_32_only=0\n\nCC=${CC:=\"gcc\"}\nCXX=${CXX:=\"g++\"}\n\nwhile [ $# -gt 0 ]; do\n  case \"$1\" in\n  \"--no-package\")\n    opt_nopackage=1\n    ;;\n  \"--dev-build\")\n    opt_nopackage=1\n    opt_devbuild=1\n    ;;\n  \"--build-id\")\n    opt_buildid=true\n    ;;\n  \"--64-only\")\n    opt_64_only=1\n    ;;\n  \"--32-only\")\n    opt_32_only=1\n    ;;\n  *)\n    echo \"Unrecognized option: $1\" >&2\n    exit 1\n  esac\n  shift\ndone\n\nfunction build_arch {  \n  cd \"$DXVK_SRC_DIR\"\n\n  opt_strip=\n  if [ $opt_devbuild -eq 0 ]; then\n    opt_strip=--strip\n  fi\n\n  CC=\"$CC -m$1\" CXX=\"$CXX -m$1\" meson setup  \\\n        --buildtype \"release\"                \\\n        --prefix \"$DXVK_BUILD_DIR/usr\"       \\\n        $opt_strip                           \\\n        --bindir \"$2\"                        \\\n        --libdir \"$2\"                        \\\n        -Dbuild_id=$opt_buildid              \\\n        --force-fallback-for=libdisplay-info \\\n        \"$DXVK_BUILD_DIR/build.$1\"\n\n  cd \"$DXVK_BUILD_DIR/build.$1\"\n  ninja install\n\n  if [ $opt_devbuild -eq 0 ]; then\n    rm -r \"$DXVK_BUILD_DIR/build.$1\"\n  fi\n}\n\nfunction package {\n  cd \"$DXVK_BUILD_DIR\"\n  tar -czf \"$DXVK_ARCHIVE_PATH\" \"usr\"\n  cd \"..\"\n  rm -R \"dxvk-native-$DXVK_VERSION\"\n}\n\nif [ $opt_32_only -eq 0 ]; then\n  build_arch 64 lib\nfi\nif [ $opt_64_only -eq 0 ]; then\n  build_arch 32 lib32\nfi\n\nif [ $opt_nopackage -eq 0 ]; then\n  package\nfi\n"
  },
  {
    "path": "package-release.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\nshopt -s extglob\n\nif [ -z \"$1\" ] || [ -z \"$2\" ]; then\n  echo \"Usage: $0 version destdir [--no-package] [--dev-build]\"\n  exit 1\nfi\n\nDXVK_VERSION=\"$1\"\nDXVK_SRC_DIR=$(readlink -f \"$0\")\nDXVK_SRC_DIR=$(dirname \"$DXVK_SRC_DIR\")\nDXVK_BUILD_DIR=$(realpath \"$2\")\"/dxvk-$DXVK_VERSION\"\nDXVK_ARCHIVE_PATH=$(realpath \"$2\")\"/dxvk-$DXVK_VERSION.tar.gz\"\n\nif [ -e \"$DXVK_BUILD_DIR\" ]; then\n  echo \"Build directory $DXVK_BUILD_DIR already exists\"\n  exit 1\nfi\n\nshift 2\n\nopt_nopackage=0\nopt_devbuild=0\nopt_buildid=false\nopt_64_only=0\nopt_32_only=0\n\ncrossfile=\"build-win\"\n\nwhile [ $# -gt 0 ]; do\n  case \"$1\" in\n  \"--no-package\")\n    opt_nopackage=1\n    ;;\n  \"--dev-build\")\n    opt_nopackage=1\n    opt_devbuild=1\n    ;;\n  \"--build-id\")\n    opt_buildid=true\n    ;;\n  \"--64-only\")\n    opt_64_only=1\n    ;;\n  \"--32-only\")\n    opt_32_only=1\n    ;;\n  *)\n    echo \"Unrecognized option: $1\" >&2\n    exit 1\n  esac\n  shift\ndone\n\nfunction build_arch {\n  export WINEARCH=\"win$1\"\n  export WINEPREFIX=\"$DXVK_BUILD_DIR/wine.$1\"\n  \n  cd \"$DXVK_SRC_DIR\"\n\n  opt_strip=\n  if [ $opt_devbuild -eq 0 ]; then\n    opt_strip=--strip\n  fi\n\n  meson setup --cross-file \"$DXVK_SRC_DIR/$crossfile$1.txt\" \\\n        --buildtype \"release\"                               \\\n        --prefix \"$DXVK_BUILD_DIR\"                          \\\n        $opt_strip                                          \\\n        --bindir \"x$1\"                                      \\\n        --libdir \"x$1\"                                      \\\n        -Db_ndebug=if-release                               \\\n        -Dbuild_id=$opt_buildid                             \\\n        \"$DXVK_BUILD_DIR/build.$1\"\n\n  cd \"$DXVK_BUILD_DIR/build.$1\"\n  ninja install\n\n  if [ $opt_devbuild -eq 0 ]; then\n    # get rid of some useless .a files\n    rm \"$DXVK_BUILD_DIR/x$1/\"*.!(dll)\n    rm -R \"$DXVK_BUILD_DIR/build.$1\"\n  fi\n}\n\nfunction package {\n  cd \"$DXVK_BUILD_DIR/..\"\n  tar -czf \"$DXVK_ARCHIVE_PATH\" \"dxvk-$DXVK_VERSION\"\n  rm -R \"dxvk-$DXVK_VERSION\"\n}\n\nif [ $opt_32_only -eq 0 ]; then\n  build_arch 64\nfi\nif [ $opt_64_only -eq 0 ]; then\n  build_arch 32\nfi\n\nif [ $opt_nopackage -eq 0 ]; then\n  package\nfi\n"
  },
  {
    "path": "src/d3d10/d3d10_blend.cpp",
    "content": "#include \"d3d10_blend.h\"\n\n#include \"../d3d11/d3d11_blend.h\"\n#include \"../d3d11/d3d11_device.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10BlendState::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10BlendState::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10BlendState::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10BlendState::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10BlendState::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10BlendState::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10BlendState::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10BlendState::GetDesc(\n          D3D10_BLEND_DESC*         pDesc) {\n    D3D11_BLEND_DESC d3d11Desc;\n    m_d3d11->GetDesc(&d3d11Desc);\n\n    pDesc->AlphaToCoverageEnable  = d3d11Desc.AlphaToCoverageEnable;\n    pDesc->SrcBlend               = D3D10_BLEND   (d3d11Desc.RenderTarget[0].SrcBlend);\n    pDesc->DestBlend              = D3D10_BLEND   (d3d11Desc.RenderTarget[0].DestBlend);\n    pDesc->BlendOp                = D3D10_BLEND_OP(d3d11Desc.RenderTarget[0].BlendOp);\n    pDesc->SrcBlendAlpha          = D3D10_BLEND   (d3d11Desc.RenderTarget[0].SrcBlendAlpha);\n    pDesc->DestBlendAlpha         = D3D10_BLEND   (d3d11Desc.RenderTarget[0].DestBlendAlpha);\n    pDesc->BlendOpAlpha           = D3D10_BLEND_OP(d3d11Desc.RenderTarget[0].BlendOpAlpha);\n\n    for (uint32_t i = 0; i < 8; i++) {\n      uint32_t srcId = d3d11Desc.IndependentBlendEnable ? i : 0;\n      pDesc->BlendEnable[i]           = d3d11Desc.RenderTarget[srcId].BlendEnable;\n      pDesc->RenderTargetWriteMask[i] = d3d11Desc.RenderTarget[srcId].RenderTargetWriteMask;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10BlendState::GetDesc1(\n          D3D10_BLEND_DESC1*        pDesc) {\n    static_assert(sizeof(D3D10_BLEND_DESC1) == sizeof(D3D11_BLEND_DESC));\n    m_d3d11->GetDesc(reinterpret_cast<D3D11_BLEND_DESC*>(pDesc));\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_blend.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D11BlendState;\n  class D3D11Device;\n\n  class D3D10BlendState : public ID3D10BlendState1 {\n\n  public:\n\n    D3D10BlendState(D3D11BlendState* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_BLEND_DESC*         pDesc);\n\n    void STDMETHODCALLTYPE GetDesc1(\n            D3D10_BLEND_DESC1*        pDesc);\n\n    D3D11BlendState* GetD3D11Iface() {\n      return m_d3d11;\n    }\n    \n  private:\n\n    D3D11BlendState* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_buffer.cpp",
    "content": "#include \"d3d10_buffer.h\"\n\n#include \"../d3d11/d3d11_buffer.h\"\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_context.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10Buffer::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Buffer::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Buffer::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Buffer::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D10Buffer::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Buffer::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Buffer::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Buffer::GetType(\n          D3D10_RESOURCE_DIMENSION* rType) {\n    *rType = D3D10_RESOURCE_DIMENSION_BUFFER;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Buffer::SetEvictionPriority(\n          UINT                      EvictionPriority) {\n    m_d3d11->SetEvictionPriority(EvictionPriority);\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D10Buffer::GetEvictionPriority() {\n    return m_d3d11->GetEvictionPriority();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Buffer::Map(\n          D3D10_MAP                 MapType,\n          UINT                      MapFlags,\n          void**                    ppData) {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    D3D11_MAPPED_SUBRESOURCE sr;\n    HRESULT hr = ctx->Map(m_d3d11, 0,\n      D3D11_MAP(MapType), MapFlags, &sr);\n    \n    if (FAILED(hr))\n      return hr;\n    \n    if (ppData != nullptr) {\n      *ppData = sr.pData;\n      return S_OK;\n    } return S_FALSE;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Buffer::Unmap() {    \n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    ctx->Unmap(m_d3d11, 0);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Buffer::GetDesc(\n          D3D10_BUFFER_DESC*        pDesc) {\n    D3D11_BUFFER_DESC d3d11Desc;\n    m_d3d11->GetDesc(&d3d11Desc);\n    \n    pDesc->ByteWidth       = d3d11Desc.ByteWidth;\n    pDesc->Usage           = D3D10_USAGE(d3d11Desc.Usage);\n    pDesc->BindFlags       = d3d11Desc.BindFlags;\n    pDesc->CPUAccessFlags  = d3d11Desc.CPUAccessFlags;\n    pDesc->MiscFlags       = ConvertD3D11ResourceFlags(d3d11Desc.MiscFlags);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d10/d3d10_buffer.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D11Buffer;\n  class D3D11Device;\n  class D3D10Device;\n\n  class D3D10Buffer : public ID3D10Buffer {\n\n  public:\n\n    D3D10Buffer(D3D11Buffer* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    void STDMETHODCALLTYPE GetType(\n            D3D10_RESOURCE_DIMENSION* rType);\n\n    void STDMETHODCALLTYPE SetEvictionPriority(\n            UINT                      EvictionPriority);\n\n    UINT STDMETHODCALLTYPE GetEvictionPriority();\n\n    HRESULT STDMETHODCALLTYPE Map(\n            D3D10_MAP                 MapType,\n            UINT                      MapFlags,\n            void**                    ppData);\n\n    void STDMETHODCALLTYPE Unmap();\n\n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_BUFFER_DESC*        pDesc);\n    \n    D3D11Buffer* GetD3D11Iface() {\n      return m_d3d11;\n    }\n\n  private:\n\n    D3D11Buffer* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_core.cpp",
    "content": "#include <d3d11.h>\n#include <d3d10_1.h>\n\n#include \"../dxgi/dxgi_interfaces.h\"\n\nextern \"C\" {\n  using namespace dxvk;\n\n  HRESULT __stdcall D3D11CoreCreateDevice(\n          IDXGIFactory*       pFactory,\n          IDXGIAdapter*       pAdapter,\n          D3D_DRIVER_TYPE     DriverType,\n          HMODULE             Software,\n          UINT                Flags,\n    const D3D_FEATURE_LEVEL*  pFeatureLevels,\n          UINT                FeatureLevels,\n          UINT                SDKVersion,\n          ID3D11Device**      ppDevice,\n          D3D_FEATURE_LEVEL*  pFeatureLevel);\n\n\n  DLLEXPORT HRESULT __stdcall D3D10CoreCreateDevice(\n          IDXGIFactory*           pFactory,\n          IDXGIAdapter*           pAdapter,\n          UINT                    Flags,\n          D3D_FEATURE_LEVEL       FeatureLevel,\n          ID3D10Device**          ppDevice) {\n    InitReturnPtr(ppDevice);\n\n    Com<ID3D11Device> d3d11Device;\n\n    HRESULT hr = pAdapter->CheckInterfaceSupport(\n      __uuidof(ID3D10Device), nullptr);\n    \n    if (FAILED(hr))\n      return hr;\n\n    hr = D3D11CoreCreateDevice(pFactory, pAdapter, D3D_DRIVER_TYPE_UNKNOWN,\n      nullptr, Flags, &FeatureLevel, 1, D3D11_SDK_VERSION, &d3d11Device, nullptr);\n\n    if (FAILED(hr))\n      return hr;\n    \n    Com<ID3D10Multithread> multithread;\n    d3d11Device->QueryInterface(__uuidof(ID3D10Multithread), reinterpret_cast<void**>(&multithread));\n    multithread->SetMultithreadProtected(!(Flags & D3D10_CREATE_DEVICE_SINGLETHREADED));\n\n    Com<IDXGIDXVKDevice> dxvkDevice;\n    d3d11Device->QueryInterface(__uuidof(IDXGIDXVKDevice), reinterpret_cast<void**>(&dxvkDevice));\n    dxvkDevice->SetAPIVersion(10);\n\n    if (FAILED(d3d11Device->QueryInterface(\n        __uuidof(ID3D10Device), reinterpret_cast<void**>(ppDevice))))\n      return E_FAIL;\n    \n    return S_OK;\n  }\n\n\n  UINT64 STDMETHODCALLTYPE D3D10CoreGetVersion() {\n    // Match the Windows 10 return value, but we\n    // don't know the exact function signature\n    return 0xa000100041770ull;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10CoreRegisterLayers() {\n    return E_NOTIMPL;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d10/d3d10_depth_stencil.cpp",
    "content": "#include \"d3d10_depth_stencil.h\"\n\n#include \"../d3d11/d3d11_depth_stencil.h\"\n#include \"../d3d11/d3d11_device.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10DepthStencilState::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10DepthStencilState::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10DepthStencilState::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10DepthStencilState::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10DepthStencilState::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10DepthStencilState::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10DepthStencilState::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10DepthStencilState::GetDesc(\n          D3D10_DEPTH_STENCIL_DESC* pDesc) {\n    static_assert(sizeof(D3D10_DEPTH_STENCIL_DESC) == sizeof(D3D11_DEPTH_STENCIL_DESC));\n    m_d3d11->GetDesc(reinterpret_cast<D3D11_DEPTH_STENCIL_DESC*>(pDesc));\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_depth_stencil.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D11DepthStencilState;\n  class D3D11Device;\n\n  class D3D10DepthStencilState : public ID3D10DepthStencilState {\n\n  public:\n\n    D3D10DepthStencilState(D3D11DepthStencilState* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_DEPTH_STENCIL_DESC* pDesc);\n\n    D3D11DepthStencilState* GetD3D11Iface() {\n      return m_d3d11;\n    }\n    \n  private:\n\n    D3D11DepthStencilState* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_device.cpp",
    "content": "#include \"d3d10_device.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_context_imm.h\"\n\nnamespace dxvk {\n  \n  D3D10Device::D3D10Device(\n          D3D11Device*                      pDevice,\n          D3D11ImmediateContext*            pContext)\n  : m_device(pDevice), m_context(pContext) {\n  }\n\n  \n  D3D10Device::~D3D10Device() {\n\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D10Device::QueryInterface(\n          REFIID                            riid,\n          void**                            ppvObject) {\n    return m_device->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Device::AddRef() {\n    return m_device->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Device::Release() {\n    return m_device->Release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::GetPrivateData(\n          REFGUID                           guid,\n          UINT*                             pDataSize,\n          void*                             pData) {\n    return m_device->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::SetPrivateData(\n          REFGUID                           guid,\n          UINT                              DataSize,\n    const void*                             pData) {\n    return m_device->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::SetPrivateDataInterface(\n          REFGUID                           guid,\n    const IUnknown*                         pData) {\n    return m_device->SetPrivateDataInterface(guid, pData);\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D10Device::GetDeviceRemovedReason() {\n    return m_device->GetDeviceRemovedReason();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::SetExceptionMode(\n          UINT                              RaiseFlags) {\n    return m_device->SetExceptionMode(RaiseFlags);\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D10Device::GetExceptionMode() {\n    return m_device->GetExceptionMode();\n  }\n\n\n  D3D10_FEATURE_LEVEL1 STDMETHODCALLTYPE D3D10Device::GetFeatureLevel() {\n    return D3D10_FEATURE_LEVEL1(m_device->GetFeatureLevel());\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::ClearState() {\n    m_context->ClearState();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::Flush() {\n    m_context->Flush();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateBuffer(\n    const D3D10_BUFFER_DESC*                pDesc,\n    const D3D10_SUBRESOURCE_DATA*           pInitialData,\n          ID3D10Buffer**                    ppBuffer) {\n    InitReturnPtr(ppBuffer);\n\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n\n    D3D11_BUFFER_DESC d3d11Desc;\n    d3d11Desc.ByteWidth       = pDesc->ByteWidth;\n    d3d11Desc.Usage           = D3D11_USAGE(pDesc->Usage);\n    d3d11Desc.BindFlags       = pDesc->BindFlags;\n    d3d11Desc.CPUAccessFlags  = pDesc->CPUAccessFlags;\n    d3d11Desc.MiscFlags       = ConvertD3D10ResourceFlags(pDesc->MiscFlags);\n    d3d11Desc.StructureByteStride = 0;\n\n    ID3D11Buffer* d3d11Buffer = nullptr;\n    HRESULT hr = m_device->CreateBuffer(&d3d11Desc,\n      reinterpret_cast<const D3D11_SUBRESOURCE_DATA*>(pInitialData),\n      ppBuffer != nullptr ? &d3d11Buffer : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppBuffer = static_cast<D3D11Buffer*>(d3d11Buffer)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateTexture1D(\n    const D3D10_TEXTURE1D_DESC*             pDesc,\n    const D3D10_SUBRESOURCE_DATA*           pInitialData,\n          ID3D10Texture1D**                 ppTexture1D) {\n    InitReturnPtr(ppTexture1D);\n\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n\n    D3D11_TEXTURE1D_DESC d3d11Desc;\n    d3d11Desc.Width           = pDesc->Width;\n    d3d11Desc.MipLevels       = pDesc->MipLevels;\n    d3d11Desc.ArraySize       = pDesc->ArraySize;\n    d3d11Desc.Format          = pDesc->Format;\n    d3d11Desc.Usage           = D3D11_USAGE(pDesc->Usage);\n    d3d11Desc.BindFlags       = pDesc->BindFlags;\n    d3d11Desc.CPUAccessFlags  = pDesc->CPUAccessFlags;\n    d3d11Desc.MiscFlags       = ConvertD3D10ResourceFlags(pDesc->MiscFlags);\n\n    ID3D11Texture1D* d3d11Texture1D = nullptr;\n    HRESULT hr = m_device->CreateTexture1D(&d3d11Desc,\n      reinterpret_cast<const D3D11_SUBRESOURCE_DATA*>(pInitialData),\n      ppTexture1D != nullptr ? &d3d11Texture1D : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n\n    *ppTexture1D = static_cast<D3D11Texture1D*>(d3d11Texture1D)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateTexture2D(\n    const D3D10_TEXTURE2D_DESC*             pDesc,\n    const D3D10_SUBRESOURCE_DATA*           pInitialData,\n          ID3D10Texture2D**                 ppTexture2D) {\n    InitReturnPtr(ppTexture2D);\n\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n\n    D3D11_TEXTURE2D_DESC d3d11Desc;\n    d3d11Desc.Width           = pDesc->Width;\n    d3d11Desc.Height          = pDesc->Height;\n    d3d11Desc.MipLevels       = pDesc->MipLevels;\n    d3d11Desc.ArraySize       = pDesc->ArraySize;\n    d3d11Desc.Format          = pDesc->Format;\n    d3d11Desc.SampleDesc      = pDesc->SampleDesc;\n    d3d11Desc.Usage           = D3D11_USAGE(pDesc->Usage);\n    d3d11Desc.BindFlags       = pDesc->BindFlags;\n    d3d11Desc.CPUAccessFlags  = pDesc->CPUAccessFlags;\n    d3d11Desc.MiscFlags       = ConvertD3D10ResourceFlags(pDesc->MiscFlags);\n\n    ID3D11Texture2D* d3d11Texture2D = nullptr;\n    HRESULT hr = m_device->CreateTexture2D(&d3d11Desc,\n      reinterpret_cast<const D3D11_SUBRESOURCE_DATA*>(pInitialData),\n      ppTexture2D != nullptr ? &d3d11Texture2D : nullptr);\n\n    if (hr != S_OK)\n      return hr;\n\n    *ppTexture2D = static_cast<D3D11Texture2D*>(d3d11Texture2D)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateTexture3D(\n    const D3D10_TEXTURE3D_DESC*             pDesc,\n    const D3D10_SUBRESOURCE_DATA*           pInitialData,\n          ID3D10Texture3D**                 ppTexture3D) {\n    InitReturnPtr(ppTexture3D);\n\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n\n    D3D11_TEXTURE3D_DESC d3d11Desc;\n    d3d11Desc.Width           = pDesc->Width;\n    d3d11Desc.Height          = pDesc->Height;\n    d3d11Desc.Depth           = pDesc->Depth;\n    d3d11Desc.MipLevels       = pDesc->MipLevels;\n    d3d11Desc.Format          = pDesc->Format;\n    d3d11Desc.Usage           = D3D11_USAGE(pDesc->Usage);\n    d3d11Desc.BindFlags       = pDesc->BindFlags;\n    d3d11Desc.CPUAccessFlags  = pDesc->CPUAccessFlags;\n    d3d11Desc.MiscFlags       = ConvertD3D10ResourceFlags(pDesc->MiscFlags);\n\n    ID3D11Texture3D* d3d11Texture3D = nullptr;\n    HRESULT hr = m_device->CreateTexture3D(&d3d11Desc,\n      reinterpret_cast<const D3D11_SUBRESOURCE_DATA*>(pInitialData),\n      ppTexture3D != nullptr ? &d3d11Texture3D : nullptr);\n\n    if (hr != S_OK)\n      return hr;\n\n    *ppTexture3D = static_cast<D3D11Texture3D*>(d3d11Texture3D)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateShaderResourceView(\n          ID3D10Resource*                   pResource,\n    const D3D10_SHADER_RESOURCE_VIEW_DESC*  pDesc,\n          ID3D10ShaderResourceView**        ppSRView) {\n    InitReturnPtr(ppSRView);\n\n    if (!pResource)\n      return E_INVALIDARG;\n\n    Com<ID3D11Resource> d3d11Resource;\n    GetD3D11Resource(pResource, &d3d11Resource);\n\n    ID3D11ShaderResourceView* d3d11Srv = nullptr;\n    HRESULT hr = m_device->CreateShaderResourceView(d3d11Resource.ptr(),\n      reinterpret_cast<const D3D11_SHADER_RESOURCE_VIEW_DESC*>(pDesc),\n      ppSRView ? &d3d11Srv : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppSRView = static_cast<D3D11ShaderResourceView*>(d3d11Srv)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateShaderResourceView1(\n          ID3D10Resource*                   pResource,\n    const D3D10_SHADER_RESOURCE_VIEW_DESC1* pDesc,\n          ID3D10ShaderResourceView1**       ppSRView) {\n    InitReturnPtr(ppSRView);\n\n    if (!pResource)\n      return E_INVALIDARG;\n\n    Com<ID3D11Resource> d3d11Resource;\n    GetD3D11Resource(pResource, &d3d11Resource);\n\n    ID3D11ShaderResourceView* d3d11View = nullptr;\n    HRESULT hr = m_device->CreateShaderResourceView(d3d11Resource.ptr(),\n      reinterpret_cast<const D3D11_SHADER_RESOURCE_VIEW_DESC*>(pDesc),\n      ppSRView ? &d3d11View : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppSRView = static_cast<D3D11ShaderResourceView*>(d3d11View)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateRenderTargetView(\n          ID3D10Resource*                   pResource,\n    const D3D10_RENDER_TARGET_VIEW_DESC*    pDesc,\n          ID3D10RenderTargetView**          ppRTView) {\n    InitReturnPtr(ppRTView);\n\n    if (!pResource)\n      return E_INVALIDARG;\n\n    Com<ID3D11Resource> d3d11Resource;\n    GetD3D11Resource(pResource, &d3d11Resource);\n\n    ID3D11RenderTargetView* d3d11View = nullptr;\n    HRESULT hr = m_device->CreateRenderTargetView(d3d11Resource.ptr(),\n      reinterpret_cast<const D3D11_RENDER_TARGET_VIEW_DESC*>(pDesc),\n      ppRTView ? &d3d11View : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppRTView = static_cast<D3D11RenderTargetView*>(d3d11View)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateDepthStencilView(\n          ID3D10Resource*                   pResource,\n    const D3D10_DEPTH_STENCIL_VIEW_DESC*    pDesc,\n          ID3D10DepthStencilView**          ppDepthStencilView) {\n    InitReturnPtr(ppDepthStencilView);\n\n    if (!pResource)\n      return E_INVALIDARG;\n\n    Com<ID3D11Resource> d3d11Resource;\n    GetD3D11Resource(pResource, &d3d11Resource);\n\n    // D3D10 doesn't have the Flags member, so we have\n    // to convert the structure. pDesc can be nullptr.\n    D3D11_DEPTH_STENCIL_VIEW_DESC d3d11Desc;\n\n    if (pDesc != nullptr) {\n      d3d11Desc.ViewDimension         = D3D11_DSV_DIMENSION(pDesc->ViewDimension);\n      d3d11Desc.Format                = pDesc->Format;\n      d3d11Desc.Flags                 = 0;\n\n      switch (pDesc->ViewDimension) {\n        case D3D10_DSV_DIMENSION_UNKNOWN:\n          break;\n        \n        case D3D10_DSV_DIMENSION_TEXTURE1D:\n          d3d11Desc.Texture1D.MipSlice               = pDesc->Texture1D.MipSlice;\n          break;\n        \n        case D3D10_DSV_DIMENSION_TEXTURE1DARRAY:\n          d3d11Desc.Texture1DArray.MipSlice          = pDesc->Texture1DArray.MipSlice;\n          d3d11Desc.Texture1DArray.FirstArraySlice   = pDesc->Texture1DArray.FirstArraySlice;\n          d3d11Desc.Texture1DArray.ArraySize         = pDesc->Texture1DArray.ArraySize;\n          break;\n        \n        case D3D10_DSV_DIMENSION_TEXTURE2D:\n          d3d11Desc.Texture2D.MipSlice               = pDesc->Texture2D.MipSlice;\n          break;\n        \n        case D3D10_DSV_DIMENSION_TEXTURE2DARRAY:\n          d3d11Desc.Texture2DArray.MipSlice          = pDesc->Texture2DArray.MipSlice;\n          d3d11Desc.Texture2DArray.FirstArraySlice   = pDesc->Texture2DArray.FirstArraySlice;\n          d3d11Desc.Texture2DArray.ArraySize         = pDesc->Texture2DArray.ArraySize;\n          break;\n        \n        case D3D10_DSV_DIMENSION_TEXTURE2DMS:\n          break;\n\n        case D3D10_DSV_DIMENSION_TEXTURE2DMSARRAY:\n          d3d11Desc.Texture2DMSArray.FirstArraySlice = pDesc->Texture2DMSArray.FirstArraySlice;\n          d3d11Desc.Texture2DMSArray.ArraySize       = pDesc->Texture2DMSArray.ArraySize;\n          break;\n      }\n    }\n\n    ID3D11DepthStencilView* d3d11View = nullptr;\n    HRESULT hr = m_device->CreateDepthStencilView(\n      d3d11Resource.ptr(), pDesc ? &d3d11Desc : nullptr,\n      ppDepthStencilView ? &d3d11View : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppDepthStencilView = static_cast<D3D11DepthStencilView*>(d3d11View)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateInputLayout(\n    const D3D10_INPUT_ELEMENT_DESC*         pInputElementDescs,\n          UINT                              NumElements,\n    const void*                             pShaderBytecodeWithInputSignature,\n          SIZE_T                            BytecodeLength,\n          ID3D10InputLayout**               ppInputLayout) {\n    InitReturnPtr(ppInputLayout);\n\n    static_assert(sizeof(D3D10_INPUT_ELEMENT_DESC) ==\n                  sizeof(D3D11_INPUT_ELEMENT_DESC));\n\n    ID3D11InputLayout* d3d11InputLayout = nullptr;\n    HRESULT hr = m_device->CreateInputLayout(\n      reinterpret_cast<const D3D11_INPUT_ELEMENT_DESC*>(pInputElementDescs),\n      NumElements, pShaderBytecodeWithInputSignature, BytecodeLength,\n      ppInputLayout ? &d3d11InputLayout : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppInputLayout = static_cast<D3D11InputLayout*>(d3d11InputLayout)->GetD3D10Iface();\n    return hr;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateVertexShader(\n    const void*                             pShaderBytecode,\n          SIZE_T                            BytecodeLength,\n          ID3D10VertexShader**              ppVertexShader) {\n    InitReturnPtr(ppVertexShader);\n\n    ID3D11VertexShader* d3d11Shader = nullptr;\n    \n    HRESULT hr = m_device->CreateVertexShader(\n      pShaderBytecode, BytecodeLength, nullptr,\n      ppVertexShader ? &d3d11Shader : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppVertexShader = static_cast<D3D11VertexShader*>(d3d11Shader)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateGeometryShader(\n    const void*                             pShaderBytecode,\n          SIZE_T                            BytecodeLength,\n          ID3D10GeometryShader**            ppGeometryShader) {\n    InitReturnPtr(ppGeometryShader);\n\n    ID3D11GeometryShader* d3d11Shader = nullptr;\n\n    HRESULT hr = m_device->CreateGeometryShader(\n      pShaderBytecode, BytecodeLength, nullptr,\n      ppGeometryShader ? &d3d11Shader : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppGeometryShader = static_cast<D3D11GeometryShader*>(d3d11Shader)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateGeometryShaderWithStreamOutput(\n    const void*                             pShaderBytecode,\n          SIZE_T                            BytecodeLength,\n    const D3D10_SO_DECLARATION_ENTRY*       pSODeclaration,\n          UINT                              NumEntries,\n          UINT                              OutputStreamStride,\n          ID3D10GeometryShader**            ppGeometryShader) {\n    InitReturnPtr(ppGeometryShader);\n\n    std::vector<D3D11_SO_DECLARATION_ENTRY> d3d11Entries(NumEntries);\n\n    for (uint32_t i = 0; i < NumEntries; i++) {\n      d3d11Entries[i].Stream          = 0;\n      d3d11Entries[i].SemanticName    = pSODeclaration[i].SemanticName;\n      d3d11Entries[i].SemanticIndex   = pSODeclaration[i].SemanticIndex;\n      d3d11Entries[i].StartComponent  = pSODeclaration[i].StartComponent;\n      d3d11Entries[i].ComponentCount  = pSODeclaration[i].ComponentCount;\n      d3d11Entries[i].OutputSlot      = pSODeclaration[i].OutputSlot;\n    }\n\n    ID3D11GeometryShader* d3d11Shader = nullptr;\n\n    HRESULT hr = m_device->CreateGeometryShaderWithStreamOutput(\n      pShaderBytecode, BytecodeLength,\n      d3d11Entries.data(),\n      d3d11Entries.size(),\n      &OutputStreamStride, 1,\n      D3D11_SO_NO_RASTERIZED_STREAM, nullptr,\n      ppGeometryShader ? &d3d11Shader : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppGeometryShader = static_cast<D3D11GeometryShader*>(d3d11Shader)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreatePixelShader(\n    const void*                             pShaderBytecode,\n          SIZE_T                            BytecodeLength,\n          ID3D10PixelShader**               ppPixelShader) {\n    InitReturnPtr(ppPixelShader);\n\n    ID3D11PixelShader* d3d11Shader = nullptr;\n\n    HRESULT hr = m_device->CreatePixelShader(\n      pShaderBytecode, BytecodeLength, nullptr,\n      ppPixelShader ? &d3d11Shader : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppPixelShader = static_cast<D3D11PixelShader*>(d3d11Shader)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateBlendState(\n    const D3D10_BLEND_DESC*                 pBlendStateDesc,\n          ID3D10BlendState**                ppBlendState) {\n    InitReturnPtr(ppBlendState);\n\n    D3D11_BLEND_DESC d3d11Desc;\n\n    if (pBlendStateDesc != nullptr) {\n      d3d11Desc.AlphaToCoverageEnable   = pBlendStateDesc->AlphaToCoverageEnable;\n      d3d11Desc.IndependentBlendEnable  = TRUE;\n\n      for (uint32_t i = 0; i < 8; i++) {\n        d3d11Desc.RenderTarget[i].BlendEnable           = pBlendStateDesc->BlendEnable[i];\n        d3d11Desc.RenderTarget[i].SrcBlend              = D3D11_BLEND   (pBlendStateDesc->SrcBlend);\n        d3d11Desc.RenderTarget[i].DestBlend             = D3D11_BLEND   (pBlendStateDesc->DestBlend);\n        d3d11Desc.RenderTarget[i].BlendOp               = D3D11_BLEND_OP(pBlendStateDesc->BlendOp);\n        d3d11Desc.RenderTarget[i].SrcBlendAlpha         = D3D11_BLEND   (pBlendStateDesc->SrcBlendAlpha);\n        d3d11Desc.RenderTarget[i].DestBlendAlpha        = D3D11_BLEND   (pBlendStateDesc->DestBlendAlpha);\n        d3d11Desc.RenderTarget[i].BlendOpAlpha          = D3D11_BLEND_OP(pBlendStateDesc->BlendOpAlpha);\n        d3d11Desc.RenderTarget[i].RenderTargetWriteMask = pBlendStateDesc->RenderTargetWriteMask[i];\n      }\n    }\n\n    ID3D11BlendState* d3d11BlendState = nullptr;\n    HRESULT hr = m_device->CreateBlendState(&d3d11Desc,\n      ppBlendState ? &d3d11BlendState : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppBlendState = static_cast<D3D11BlendState*>(d3d11BlendState)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateBlendState1(\n    const D3D10_BLEND_DESC1*                pBlendStateDesc,\n          ID3D10BlendState1**               ppBlendState) {\n    InitReturnPtr(ppBlendState);\n\n    ID3D11BlendState* d3d11BlendState = nullptr;\n    HRESULT hr = m_device->CreateBlendState(\n      reinterpret_cast<const D3D11_BLEND_DESC*>(pBlendStateDesc),\n      ppBlendState ? &d3d11BlendState : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppBlendState = static_cast<D3D11BlendState*>(d3d11BlendState)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateDepthStencilState(\n    const D3D10_DEPTH_STENCIL_DESC*         pDepthStencilDesc,\n          ID3D10DepthStencilState**         ppDepthStencilState) {\n    InitReturnPtr(ppDepthStencilState);\n\n    ID3D11DepthStencilState* d3d11DepthStencilState = nullptr;\n    HRESULT hr = m_device->CreateDepthStencilState(\n      reinterpret_cast<const D3D11_DEPTH_STENCIL_DESC*>(pDepthStencilDesc),\n      ppDepthStencilState ? &d3d11DepthStencilState : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppDepthStencilState = static_cast<D3D11DepthStencilState*>(d3d11DepthStencilState)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateRasterizerState(\n    const D3D10_RASTERIZER_DESC*            pRasterizerDesc,\n          ID3D10RasterizerState**           ppRasterizerState) {\n    InitReturnPtr(ppRasterizerState);\n\n    ID3D11RasterizerState* d3d11RasterizerState = nullptr;\n    HRESULT hr = m_device->CreateRasterizerState(\n      reinterpret_cast<const D3D11_RASTERIZER_DESC*>(pRasterizerDesc),\n      ppRasterizerState ? &d3d11RasterizerState : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppRasterizerState = static_cast<D3D11RasterizerState*>(d3d11RasterizerState)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateSamplerState(\n    const D3D10_SAMPLER_DESC*               pSamplerDesc,\n          ID3D10SamplerState**              ppSamplerState) {\n    InitReturnPtr(ppSamplerState);\n\n    if (pSamplerDesc == nullptr)\n      return E_INVALIDARG;\n\n    D3D11_SAMPLER_DESC d3d11Desc;\n    d3d11Desc.Filter            = D3D11_FILTER(pSamplerDesc->Filter);\n    d3d11Desc.AddressU          = D3D11_TEXTURE_ADDRESS_MODE(pSamplerDesc->AddressU);\n    d3d11Desc.AddressV          = D3D11_TEXTURE_ADDRESS_MODE(pSamplerDesc->AddressV);\n    d3d11Desc.AddressW          = D3D11_TEXTURE_ADDRESS_MODE(pSamplerDesc->AddressW);\n    d3d11Desc.MipLODBias        = pSamplerDesc->MipLODBias;\n    d3d11Desc.MaxAnisotropy     = pSamplerDesc->MaxAnisotropy;\n    d3d11Desc.ComparisonFunc    = D3D11_COMPARISON_FUNC(pSamplerDesc->ComparisonFunc);\n    d3d11Desc.MinLOD            = pSamplerDesc->MinLOD;\n    d3d11Desc.MaxLOD            = pSamplerDesc->MaxLOD;\n\n    for (uint32_t i = 0; i < 4; i++)\n      d3d11Desc.BorderColor[i] = pSamplerDesc->BorderColor[i];\n\n    ID3D11SamplerState* d3d11SamplerState = nullptr;\n    HRESULT hr = m_device->CreateSamplerState(&d3d11Desc,\n      ppSamplerState ? &d3d11SamplerState : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n\n    *ppSamplerState = static_cast<D3D11SamplerState*>(d3d11SamplerState)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateQuery(\n    const D3D10_QUERY_DESC*                 pQueryDesc,\n          ID3D10Query**                     ppQuery) {\n    InitReturnPtr(ppQuery);\n\n    if (pQueryDesc == nullptr)\n      return E_INVALIDARG;\n\n    D3D11_QUERY_DESC d3d11Desc;\n    d3d11Desc.Query      = D3D11_QUERY(pQueryDesc->Query);\n    d3d11Desc.MiscFlags  = pQueryDesc->MiscFlags;\n\n    ID3D11Query* d3d11Query = nullptr;\n    HRESULT hr = m_device->CreateQuery(&d3d11Desc,\n      ppQuery ? &d3d11Query : nullptr);\n\n    if (hr != S_OK)\n      return hr;\n\n    *ppQuery = static_cast<D3D11Query*>(d3d11Query)->GetD3D10Iface();\n     return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreatePredicate(\n    const D3D10_QUERY_DESC*                 pPredicateDesc,\n          ID3D10Predicate**                 ppPredicate) {\n    InitReturnPtr(ppPredicate);\n\n    D3D11_QUERY_DESC d3d11Desc;\n    d3d11Desc.Query      = D3D11_QUERY(pPredicateDesc->Query);\n    d3d11Desc.MiscFlags  = pPredicateDesc->MiscFlags;\n\n    ID3D11Predicate* d3d11Predicate = nullptr;\n    HRESULT hr = m_device->CreatePredicate(&d3d11Desc,\n      ppPredicate ? &d3d11Predicate : nullptr);\n\n    if (hr != S_OK)\n      return hr;\n    \n    *ppPredicate = D3D11Query::FromPredicate(d3d11Predicate)->GetD3D10Iface();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CreateCounter(\n    const D3D10_COUNTER_DESC*               pCounterDesc,\n          ID3D10Counter**                   ppCounter) {\n    Logger::err(\"D3D10Device::CreateCounter: Not implemented\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CheckFormatSupport(\n          DXGI_FORMAT                       Format,\n          UINT*                             pFormatSupport) {\n    return m_device->CheckFormatSupport(Format, pFormatSupport);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CheckMultisampleQualityLevels(\n          DXGI_FORMAT                       Format,\n          UINT                              SampleCount,\n          UINT*                             pNumQualityLevels) {\n    return m_device->CheckMultisampleQualityLevels(\n      Format, SampleCount, pNumQualityLevels);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::CheckCounterInfo(\n          D3D10_COUNTER_INFO*               pCounterInfo) {\n    Logger::err(\"D3D10Device::CheckCounterInfo: Not implemented\");\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::CheckCounter(\n    const D3D10_COUNTER_DESC*               pDesc,\n          D3D10_COUNTER_TYPE*               pType,\n          UINT*                             pActiveCounters,\n          char*                             name,\n          UINT*                             pNameLength,\n          char*                             units,\n          UINT*                             pUnitsLength,\n          char*                             description,\n          UINT*                             pDescriptionLength) {\n    Logger::err(\"D3D10Device::CheckCounter: Not implemented\");\n    return E_NOTIMPL;\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D10Device::GetCreationFlags() {\n    return m_device->GetCreationFlags();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Device::OpenSharedResource(\n          HANDLE                            hResource,\n          REFIID                            ReturnedInterface,\n          void**                            ppResource) {\n    return m_device->OpenSharedResource(hResource, ReturnedInterface, ppResource);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::ClearRenderTargetView(\n          ID3D10RenderTargetView*           pRenderTargetView,\n    const FLOAT                             ColorRGBA[4]) {\n    D3D10RenderTargetView* d3d10View = static_cast<D3D10RenderTargetView*>(pRenderTargetView);\n    D3D11RenderTargetView* d3d11View = d3d10View ? d3d10View->GetD3D11Iface() : nullptr;\n\n    m_context->ClearRenderTargetView(d3d11View, ColorRGBA);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::ClearDepthStencilView(\n          ID3D10DepthStencilView*           pDepthStencilView,\n          UINT                              ClearFlags,\n          FLOAT                             Depth,\n          UINT8                             Stencil) {\n    D3D10DepthStencilView* d3d10View = static_cast<D3D10DepthStencilView*>(pDepthStencilView);\n    D3D11DepthStencilView* d3d11View = d3d10View ? d3d10View->GetD3D11Iface() : nullptr;\n\n    m_context->ClearDepthStencilView(d3d11View, ClearFlags, Depth, Stencil);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::SetPredication(\n          ID3D10Predicate*                  pPredicate,\n          BOOL                              PredicateValue) {\n    D3D10Query* d3d10Predicate = static_cast<D3D10Query*>(pPredicate);\n    D3D11Query* d3d11Predicate = d3d10Predicate ? d3d10Predicate->GetD3D11Iface() : nullptr;\n\n    m_context->SetPredication(D3D11Query::AsPredicate(d3d11Predicate), PredicateValue);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GetPredication(\n          ID3D10Predicate**                 ppPredicate,\n          BOOL*                             pPredicateValue) {\n    ID3D11Predicate* d3d11Predicate = nullptr;\n\n    m_context->GetPredication(\n      ppPredicate ? &d3d11Predicate : nullptr,\n      pPredicateValue);\n\n    if (ppPredicate)\n      *ppPredicate = d3d11Predicate ? D3D11Query::FromPredicate(d3d11Predicate)->GetD3D10Iface() : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::CopySubresourceRegion(\n          ID3D10Resource*                   pDstResource,\n          UINT                              DstSubresource,\n          UINT                              DstX,\n          UINT                              DstY,\n          UINT                              DstZ,\n          ID3D10Resource*                   pSrcResource,\n          UINT                              SrcSubresource,\n    const D3D10_BOX*                        pSrcBox) {\n    if (!pDstResource || !pSrcResource)\n      return;\n    \n    Com<ID3D11Resource> d3d11DstResource;\n    Com<ID3D11Resource> d3d11SrcResource;\n\n    GetD3D11Resource(pDstResource, &d3d11DstResource);\n    GetD3D11Resource(pSrcResource, &d3d11SrcResource);\n\n    m_context->CopySubresourceRegion(\n      d3d11DstResource.ptr(), DstSubresource, DstX, DstY, DstZ,\n      d3d11SrcResource.ptr(), SrcSubresource,\n      reinterpret_cast<const D3D11_BOX*>(pSrcBox));\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::CopyResource(\n          ID3D10Resource*                   pDstResource,\n          ID3D10Resource*                   pSrcResource) {\n    if (!pDstResource || !pSrcResource)\n      return;\n\n    Com<ID3D11Resource> d3d11DstResource;\n    Com<ID3D11Resource> d3d11SrcResource;\n    \n    GetD3D11Resource(pDstResource, &d3d11DstResource);\n    GetD3D11Resource(pSrcResource, &d3d11SrcResource);\n\n    m_context->CopyResource(\n      d3d11DstResource.ptr(),\n      d3d11SrcResource.ptr());\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::UpdateSubresource(\n          ID3D10Resource*                   pDstResource,\n          UINT                              DstSubresource,\n    const D3D10_BOX*                        pDstBox,\n    const void*                             pSrcData,\n          UINT                              SrcRowPitch,\n          UINT                              SrcDepthPitch) {\n    if (!pDstResource)\n      return;\n\n    Com<ID3D11Resource> d3d11DstResource;\n    GetD3D11Resource(pDstResource, &d3d11DstResource);\n\n    m_context->UpdateSubresource(\n      d3d11DstResource.ptr(), DstSubresource,\n      reinterpret_cast<const D3D11_BOX*>(pDstBox),\n      pSrcData, SrcRowPitch, SrcDepthPitch);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GenerateMips(\n          ID3D10ShaderResourceView*         pShaderResourceView) {\n    D3D10ShaderResourceView* d3d10View = static_cast<D3D10ShaderResourceView*>(pShaderResourceView);\n    D3D11ShaderResourceView* d3d11View = d3d10View ? d3d10View->GetD3D11Iface() : nullptr;\n\n    m_context->GenerateMips(d3d11View);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::ResolveSubresource(\n          ID3D10Resource*                   pDstResource,\n          UINT                              DstSubresource,\n          ID3D10Resource*                   pSrcResource,\n          UINT                              SrcSubresource,\n          DXGI_FORMAT                       Format) {\n    if (!pDstResource || !pSrcResource)\n      return;\n\n    Com<ID3D11Resource> d3d11DstResource;\n    Com<ID3D11Resource> d3d11SrcResource;\n    \n    GetD3D11Resource(pDstResource, &d3d11DstResource);\n    GetD3D11Resource(pSrcResource, &d3d11SrcResource);\n\n    m_context->ResolveSubresource(\n      d3d11DstResource.ptr(), DstSubresource,\n      d3d11SrcResource.ptr(), SrcSubresource,\n      Format);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::Draw(\n          UINT                              VertexCount,\n          UINT                              StartVertexLocation) {\n    m_context->Draw(VertexCount,\n      StartVertexLocation);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::DrawIndexed(\n          UINT                              IndexCount,\n          UINT                              StartIndexLocation,\n          INT                               BaseVertexLocation) {\n    m_context->DrawIndexed(IndexCount,\n      StartIndexLocation,\n      BaseVertexLocation);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::DrawInstanced(\n          UINT                              VertexCountPerInstance,\n          UINT                              InstanceCount,\n          UINT                              StartVertexLocation,\n          UINT                              StartInstanceLocation) {\n    m_context->DrawInstanced(\n      VertexCountPerInstance,\n      InstanceCount,\n      StartVertexLocation,\n      StartInstanceLocation);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::DrawIndexedInstanced(\n          UINT                              IndexCountPerInstance,\n          UINT                              InstanceCount,\n          UINT                              StartIndexLocation,\n          INT                               BaseVertexLocation,\n          UINT                              StartInstanceLocation) {\n    m_context->DrawIndexedInstanced(\n      IndexCountPerInstance,\n      InstanceCount,\n      StartIndexLocation,\n      BaseVertexLocation,\n      StartInstanceLocation);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::DrawAuto() {\n    m_context->DrawAuto();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::IASetInputLayout(\n          ID3D10InputLayout*                pInputLayout) {\n    D3D10InputLayout* d3d10InputLayout = static_cast<D3D10InputLayout*>(pInputLayout);\n    D3D11InputLayout* d3d11InputLayout = d3d10InputLayout ? d3d10InputLayout->GetD3D11Iface() : nullptr;\n\n    m_context->IASetInputLayout(d3d11InputLayout);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::IASetPrimitiveTopology(\n          D3D10_PRIMITIVE_TOPOLOGY          Topology) {\n    m_context->IASetPrimitiveTopology(\n      D3D11_PRIMITIVE_TOPOLOGY(Topology));\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::IASetVertexBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D10Buffer* const*              ppVertexBuffers,\n    const UINT*                             pStrides,\n    const UINT*                             pOffsets) {\n    ID3D11Buffer* d3d11Buffers[D3D10_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];\n\n    if (NumBuffers > D3D10_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      d3d11Buffers[i] = ppVertexBuffers[i]\n        ? static_cast<D3D10Buffer*>(ppVertexBuffers[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->IASetVertexBuffers(\n      StartSlot, NumBuffers, d3d11Buffers,\n      pStrides, pOffsets);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::IASetIndexBuffer(\n          ID3D10Buffer*                     pIndexBuffer,\n          DXGI_FORMAT                       Format,\n          UINT                              Offset) {\n    D3D10Buffer* d3d10Buffer = static_cast<D3D10Buffer*>(pIndexBuffer);\n    D3D11Buffer* d3d11Buffer = d3d10Buffer ? d3d10Buffer->GetD3D11Iface() : nullptr;\n\n    m_context->IASetIndexBuffer(d3d11Buffer, Format, Offset);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::IAGetInputLayout(\n          ID3D10InputLayout**               ppInputLayout) {\n    ID3D11InputLayout* d3d11InputLayout = nullptr;\n    m_context->IAGetInputLayout(&d3d11InputLayout);\n\n    *ppInputLayout = d3d11InputLayout\n      ? static_cast<D3D11InputLayout*>(d3d11InputLayout)->GetD3D10Iface()\n      : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::IAGetPrimitiveTopology(\n          D3D10_PRIMITIVE_TOPOLOGY*         pTopology) {\n    D3D11_PRIMITIVE_TOPOLOGY d3d11Topology;\n    m_context->IAGetPrimitiveTopology(&d3d11Topology);\n\n    *pTopology = d3d11Topology <= 32 /* begin patch list */\n      ? D3D10_PRIMITIVE_TOPOLOGY(d3d11Topology)\n      : D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::IAGetVertexBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D10Buffer**                    ppVertexBuffers,\n          UINT*                             pStrides,\n          UINT*                             pOffsets) {\n    ID3D11Buffer* d3d11Buffers[D3D10_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];\n\n    m_context->IAGetVertexBuffers(\n      StartSlot, NumBuffers,\n      ppVertexBuffers ? d3d11Buffers : nullptr,\n      pStrides, pOffsets);\n\n    if (ppVertexBuffers) {\n      for (uint32_t i = 0; i < NumBuffers; i++) {\n        ppVertexBuffers[i] = d3d11Buffers[i]\n          ? static_cast<D3D11Buffer*>(d3d11Buffers[i])->GetD3D10Iface()\n          : nullptr;\n      }\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::IAGetIndexBuffer(\n          ID3D10Buffer**                    pIndexBuffer,\n          DXGI_FORMAT*                      Format,\n          UINT*                             Offset) {\n    ID3D11Buffer* d3d11Buffer = nullptr;\n\n    m_context->IAGetIndexBuffer(\n      pIndexBuffer ? &d3d11Buffer : nullptr,\n      Format, Offset);\n\n    if (pIndexBuffer)\n      *pIndexBuffer = d3d11Buffer ? static_cast<D3D11Buffer*>(d3d11Buffer)->GetD3D10Iface() : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::VSSetShader(\n          ID3D10VertexShader*               pVertexShader) {\n    D3D10VertexShader* d3d10Shader = static_cast<D3D10VertexShader*>(pVertexShader);\n    D3D11VertexShader* d3d11Shader = d3d10Shader ? d3d10Shader->GetD3D11Iface() : nullptr;\n\n    m_context->VSSetShader(d3d11Shader, nullptr, 0);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::VSSetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D10Buffer* const*              ppConstantBuffers) {\n    ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];\n\n    if (NumBuffers > D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      d3d11Buffers[i] = ppConstantBuffers && ppConstantBuffers[i]\n        ? static_cast<D3D10Buffer*>(ppConstantBuffers[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->VSSetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::VSSetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D10ShaderResourceView* const*  ppShaderResourceViews) {\n    ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT];\n\n    if (NumViews > D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumViews; i++) {\n      d3d11Views[i] = ppShaderResourceViews && ppShaderResourceViews[i]\n        ? static_cast<D3D10ShaderResourceView*>(ppShaderResourceViews[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->VSSetShaderResources(StartSlot, NumViews, d3d11Views);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::VSSetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D10SamplerState* const*        ppSamplers) {\n    ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT];\n\n    if (NumSamplers > D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumSamplers; i++) {\n      d3d11Samplers[i] = ppSamplers && ppSamplers[i]\n        ? static_cast<D3D10SamplerState*>(ppSamplers[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->VSSetSamplers(StartSlot, NumSamplers, d3d11Samplers);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::VSGetShader(\n          ID3D10VertexShader**              ppVertexShader) {\n    ID3D11VertexShader* d3d11Shader = nullptr;\n    m_context->VSGetShader(&d3d11Shader, nullptr, nullptr);\n\n    *ppVertexShader = d3d11Shader ? static_cast<D3D11VertexShader*>(d3d11Shader)->GetD3D10Iface() : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::VSGetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D10Buffer**                    ppConstantBuffers) {\n    ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];\n    m_context->VSGetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers);\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      ppConstantBuffers[i] = d3d11Buffers[i]\n        ? static_cast<D3D11Buffer*>(d3d11Buffers[i])->GetD3D10Iface()\n        : nullptr;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::VSGetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D10ShaderResourceView**        ppShaderResourceViews) {\n    ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT];\n    m_context->VSGetShaderResources(StartSlot, NumViews, d3d11Views);\n\n    for (uint32_t i = 0; i < NumViews; i++) {\n      ppShaderResourceViews[i] = d3d11Views[i]\n        ? static_cast<D3D11ShaderResourceView*>(d3d11Views[i])->GetD3D10Iface()\n        : nullptr;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::VSGetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D10SamplerState**              ppSamplers) {\n    ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT];\n    m_context->VSGetSamplers(StartSlot, NumSamplers, d3d11Samplers);\n\n    for (uint32_t i = 0; i < NumSamplers; i++) {\n      ppSamplers[i] = d3d11Samplers[i]\n        ? static_cast<D3D11SamplerState*>(d3d11Samplers[i])->GetD3D10Iface()\n        : nullptr;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GSSetShader(\n          ID3D10GeometryShader*             pShader) {\n    D3D10GeometryShader* d3d10Shader = static_cast<D3D10GeometryShader*>(pShader);\n    D3D11GeometryShader* d3d11Shader = d3d10Shader ? d3d10Shader->GetD3D11Iface() : nullptr;\n\n    m_context->GSSetShader(d3d11Shader, nullptr, 0);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GSSetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D10Buffer* const*              ppConstantBuffers) {\n    ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];\n\n    if (NumBuffers > D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      d3d11Buffers[i] = ppConstantBuffers && ppConstantBuffers[i]\n        ? static_cast<D3D10Buffer*>(ppConstantBuffers[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->GSSetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GSSetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D10ShaderResourceView* const*  ppShaderResourceViews) {\n    ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT];\n\n    if (NumViews > D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumViews; i++) {\n      d3d11Views[i] = ppShaderResourceViews && ppShaderResourceViews[i]\n        ? static_cast<D3D10ShaderResourceView*>(ppShaderResourceViews[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->GSSetShaderResources(StartSlot, NumViews, d3d11Views);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GSSetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D10SamplerState* const*        ppSamplers) {\n    ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT];\n\n    if (NumSamplers > D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumSamplers; i++) {\n      d3d11Samplers[i] = ppSamplers && ppSamplers[i]\n        ? static_cast<D3D10SamplerState*>(ppSamplers[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->GSSetSamplers(StartSlot, NumSamplers, d3d11Samplers);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GSGetShader(\n          ID3D10GeometryShader**            ppGeometryShader) {\n    ID3D11GeometryShader* d3d11Shader = nullptr;\n    m_context->GSGetShader(&d3d11Shader, nullptr, nullptr);\n\n    *ppGeometryShader = d3d11Shader ? static_cast<D3D11GeometryShader*>(d3d11Shader)->GetD3D10Iface() : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GSGetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D10Buffer**                    ppConstantBuffers) {\n    ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];\n    m_context->GSGetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers);\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      ppConstantBuffers[i] = d3d11Buffers[i]\n        ? static_cast<D3D11Buffer*>(d3d11Buffers[i])->GetD3D10Iface()\n        : nullptr;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GSGetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D10ShaderResourceView**        ppShaderResourceViews) {\n    ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT];\n    m_context->GSGetShaderResources(StartSlot, NumViews, d3d11Views);\n\n    for (uint32_t i = 0; i < NumViews; i++) {\n      ppShaderResourceViews[i] = d3d11Views[i]\n        ? static_cast<D3D11ShaderResourceView*>(d3d11Views[i])->GetD3D10Iface()\n        : nullptr;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GSGetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D10SamplerState**              ppSamplers) {\n    ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT];\n    m_context->GSGetSamplers(StartSlot, NumSamplers, d3d11Samplers);\n\n    for (uint32_t i = 0; i < NumSamplers; i++) {\n      ppSamplers[i] = d3d11Samplers[i]\n        ? static_cast<D3D11SamplerState*>(d3d11Samplers[i])->GetD3D10Iface()\n        : nullptr;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::PSSetShader(\n          ID3D10PixelShader*                pPixelShader) {\n    D3D10PixelShader* d3d10Shader = static_cast<D3D10PixelShader*>(pPixelShader);\n    D3D11PixelShader* d3d11Shader = d3d10Shader ? d3d10Shader->GetD3D11Iface() : nullptr;\n\n    m_context->PSSetShader(d3d11Shader, nullptr, 0);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::PSSetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D10Buffer* const*              ppConstantBuffers) {\n    ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];\n\n    if (NumBuffers > D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      d3d11Buffers[i] = ppConstantBuffers && ppConstantBuffers[i]\n        ? static_cast<D3D10Buffer*>(ppConstantBuffers[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->PSSetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::PSSetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D10ShaderResourceView* const*  ppShaderResourceViews) {\n    ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT];\n\n    if (NumViews > D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumViews; i++) {\n      d3d11Views[i] = ppShaderResourceViews && ppShaderResourceViews[i]\n        ? static_cast<D3D10ShaderResourceView*>(ppShaderResourceViews[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->PSSetShaderResources(StartSlot, NumViews, d3d11Views);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::PSSetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D10SamplerState* const*        ppSamplers) {\n    ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT];\n\n    if (NumSamplers > D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumSamplers; i++) {\n      d3d11Samplers[i] = ppSamplers && ppSamplers[i]\n        ? static_cast<D3D10SamplerState*>(ppSamplers[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->PSSetSamplers(StartSlot, NumSamplers, d3d11Samplers);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::PSGetShader(\n          ID3D10PixelShader**               ppPixelShader) {\n    ID3D11PixelShader* d3d11Shader = nullptr;\n    m_context->PSGetShader(&d3d11Shader, nullptr, nullptr);\n\n    *ppPixelShader = d3d11Shader ? static_cast<D3D11PixelShader*>(d3d11Shader)->GetD3D10Iface() : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::PSGetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D10Buffer**                    ppConstantBuffers) {\n    ID3D11Buffer* d3d11Buffers[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT];\n    m_context->PSGetConstantBuffers(StartSlot, NumBuffers, d3d11Buffers);\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      ppConstantBuffers[i] = d3d11Buffers[i]\n        ? static_cast<D3D11Buffer*>(d3d11Buffers[i])->GetD3D10Iface()\n        : nullptr;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::PSGetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D10ShaderResourceView**        ppShaderResourceViews) {\n    ID3D11ShaderResourceView* d3d11Views[D3D10_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT];\n    m_context->PSGetShaderResources(StartSlot, NumViews, d3d11Views);\n\n    for (uint32_t i = 0; i < NumViews; i++) {\n      ppShaderResourceViews[i] = d3d11Views[i]\n        ? static_cast<D3D11ShaderResourceView*>(d3d11Views[i])->GetD3D10Iface()\n        : nullptr;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::PSGetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D10SamplerState**              ppSamplers) {\n    ID3D11SamplerState* d3d11Samplers[D3D10_COMMONSHADER_SAMPLER_SLOT_COUNT];\n    m_context->PSGetSamplers(StartSlot, NumSamplers, d3d11Samplers);\n\n    for (uint32_t i = 0; i < NumSamplers; i++) {\n      ppSamplers[i] = d3d11Samplers[i]\n        ? static_cast<D3D11SamplerState*>(d3d11Samplers[i])->GetD3D10Iface()\n        : nullptr;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::OMSetRenderTargets(\n          UINT                              NumViews,\n          ID3D10RenderTargetView* const*    ppRenderTargetViews,\n          ID3D10DepthStencilView*           pDepthStencilView) {\n    ID3D11RenderTargetView* d3d11Rtv[D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT];\n\n    if (NumViews > D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumViews; i++) {\n      d3d11Rtv[i] = ppRenderTargetViews && ppRenderTargetViews[i]\n        ? static_cast<D3D10RenderTargetView*>(ppRenderTargetViews[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    D3D10DepthStencilView* d3d10Dsv = static_cast<D3D10DepthStencilView*>(pDepthStencilView);\n    D3D11DepthStencilView* d3d11Dsv = d3d10Dsv ? d3d10Dsv->GetD3D11Iface() : nullptr;\n\n    m_context->OMSetRenderTargets(NumViews, d3d11Rtv, d3d11Dsv);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::OMSetBlendState(\n          ID3D10BlendState*                 pBlendState,\n    const FLOAT                             BlendFactor[4],\n          UINT                              SampleMask) {\n    D3D10BlendState* d3d10BlendState = static_cast<D3D10BlendState*>(pBlendState);\n    D3D11BlendState* d3d11BlendState = d3d10BlendState ? d3d10BlendState->GetD3D11Iface() : nullptr;\n\n    m_context->OMSetBlendState(d3d11BlendState, BlendFactor, SampleMask);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::OMSetDepthStencilState(\n          ID3D10DepthStencilState*          pDepthStencilState,\n          UINT                              StencilRef) {\n    D3D10DepthStencilState* d3d10DepthStencilState = static_cast<D3D10DepthStencilState*>(pDepthStencilState);\n    D3D11DepthStencilState* d3d11DepthStencilState = d3d10DepthStencilState ? d3d10DepthStencilState->GetD3D11Iface() : nullptr;\n\n    m_context->OMSetDepthStencilState(d3d11DepthStencilState, StencilRef);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::OMGetRenderTargets(\n          UINT                              NumViews,\n          ID3D10RenderTargetView**          ppRenderTargetViews,\n          ID3D10DepthStencilView**          ppDepthStencilView) {\n    ID3D11RenderTargetView* d3d11Rtv[D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT];\n    ID3D11DepthStencilView* d3d11Dsv = nullptr;\n\n    m_context->OMGetRenderTargets(NumViews,\n      ppRenderTargetViews ? d3d11Rtv : nullptr,\n      ppDepthStencilView ? &d3d11Dsv : nullptr);\n\n    if (ppRenderTargetViews != nullptr) {\n      for (uint32_t i = 0; i < NumViews; i++) {\n        ppRenderTargetViews[i] = d3d11Rtv[i]\n          ? static_cast<D3D11RenderTargetView*>(d3d11Rtv[i])->GetD3D10Iface()\n          : nullptr;\n      }\n    }\n\n    if (ppDepthStencilView)\n      *ppDepthStencilView = d3d11Dsv ? static_cast<D3D11DepthStencilView*>(d3d11Dsv)->GetD3D10Iface() : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::OMGetBlendState(\n          ID3D10BlendState**                ppBlendState,\n          FLOAT                             BlendFactor[4],\n          UINT*                             pSampleMask) {\n    ID3D11BlendState* d3d11BlendState = nullptr;\n\n    m_context->OMGetBlendState(\n      ppBlendState ? &d3d11BlendState : nullptr,\n      BlendFactor, pSampleMask);\n\n    if (ppBlendState != nullptr)\n      *ppBlendState = d3d11BlendState ? static_cast<D3D11BlendState*>(d3d11BlendState)->GetD3D10Iface() : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::OMGetDepthStencilState(\n          ID3D10DepthStencilState**         ppDepthStencilState,\n          UINT*                             pStencilRef) {\n    ID3D11DepthStencilState* d3d11DepthStencilState = nullptr;\n\n    m_context->OMGetDepthStencilState(\n      ppDepthStencilState ? &d3d11DepthStencilState : nullptr,\n      pStencilRef);\n\n    if (ppDepthStencilState != nullptr)\n      *ppDepthStencilState = d3d11DepthStencilState ? static_cast<D3D11DepthStencilState*>(d3d11DepthStencilState)->GetD3D10Iface() : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::RSSetState(\n          ID3D10RasterizerState*            pRasterizerState) {\n    D3D10RasterizerState* d3d10RasterizerState = static_cast<D3D10RasterizerState*>(pRasterizerState);\n    D3D11RasterizerState* d3d11RasterizerState = d3d10RasterizerState ? d3d10RasterizerState->GetD3D11Iface() : nullptr;\n\n    m_context->RSSetState(d3d11RasterizerState);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::RSSetViewports(\n          UINT                              NumViewports,\n    const D3D10_VIEWPORT*                   pViewports) {\n    D3D11_VIEWPORT vp[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];\n\n    if (NumViewports > D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE)\n      return;\n\n    for (uint32_t i = 0; i < NumViewports; i++) {\n      vp[i].TopLeftX = float(pViewports[i].TopLeftX);\n      vp[i].TopLeftY = float(pViewports[i].TopLeftY);\n      vp[i].Width    = float(pViewports[i].Width);\n      vp[i].Height   = float(pViewports[i].Height);\n      vp[i].MinDepth = pViewports[i].MinDepth;\n      vp[i].MaxDepth = pViewports[i].MaxDepth;\n    }\n\n    m_context->RSSetViewports(NumViewports, vp);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::RSSetScissorRects(\n          UINT                              NumRects,\n    const D3D10_RECT*                       pRects) {\n    m_context->RSSetScissorRects(NumRects, pRects);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::RSGetState(\n          ID3D10RasterizerState**           ppRasterizerState) {\n    ID3D11RasterizerState* d3d11RasterizerState = nullptr;\n    m_context->RSGetState(&d3d11RasterizerState);\n\n    *ppRasterizerState = d3d11RasterizerState ? static_cast<D3D11RasterizerState*>(d3d11RasterizerState)->GetD3D10Iface() : nullptr;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::RSGetViewports(\n          UINT*                             NumViewports,\n          D3D10_VIEWPORT*                   pViewports) {\n    D3D11_VIEWPORT vp[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];\n    m_context->RSGetViewports(NumViewports, pViewports != nullptr ? vp : nullptr);\n\n    if (pViewports != nullptr) {\n      for (uint32_t i = 0; i < *NumViewports; i++) {\n        pViewports[i].TopLeftX = int32_t(vp[i].TopLeftX);\n        pViewports[i].TopLeftY = int32_t(vp[i].TopLeftY);\n        pViewports[i].Width    = uint32_t(vp[i].Width);\n        pViewports[i].Height   = uint32_t(vp[i].Height);\n        pViewports[i].MinDepth = vp[i].MinDepth;\n        pViewports[i].MaxDepth = vp[i].MaxDepth;\n      }\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::RSGetScissorRects(\n          UINT*                             NumRects,\n          D3D10_RECT*                       pRects) {\n    m_context->RSGetScissorRects(NumRects, pRects);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::SOSetTargets(\n          UINT                              NumBuffers,\n          ID3D10Buffer* const*              ppSOTargets,\n    const UINT*                             pOffsets) {\n    ID3D11Buffer* d3d11Buffers[D3D10_SO_BUFFER_SLOT_COUNT];\n\n    if (NumBuffers > D3D10_SO_BUFFER_SLOT_COUNT)\n      return;\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      d3d11Buffers[i] = ppSOTargets && ppSOTargets[i]\n        ? static_cast<D3D10Buffer*>(ppSOTargets[i])->GetD3D11Iface()\n        : nullptr;\n    }\n\n    m_context->SOSetTargets(NumBuffers, d3d11Buffers, pOffsets);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::SOGetTargets(\n          UINT                              NumBuffers,\n          ID3D10Buffer**                    ppSOTargets,\n          UINT*                             pOffsets) {\n    ID3D11Buffer* d3d11Buffers[D3D10_SO_BUFFER_SLOT_COUNT];\n\n    m_context->SOGetTargetsWithOffsets(NumBuffers,\n      ppSOTargets ? d3d11Buffers : nullptr,\n      pOffsets);\n\n    if (ppSOTargets != nullptr) {\n      for (uint32_t i = 0; i < NumBuffers; i++) {\n        ppSOTargets[i] = d3d11Buffers[i]\n          ? static_cast<D3D11Buffer*>(d3d11Buffers[i])->GetD3D10Iface()\n          : nullptr;\n      }\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::SetTextFilterSize(\n          UINT                              Width,\n          UINT                              Height) {\n    // D3D10 doesn't seem to actually store or do anything with these values,\n    // as when calling GetTextFilterSize, it just makes the values 0.\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Device::GetTextFilterSize(\n          UINT*                             pWidth,\n          UINT*                             pHeight) {\n    if (pWidth)\n        *pWidth = 0;\n\n    if (pHeight)\n        *pHeight = 0;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d10/d3d10_device.h",
    "content": "#pragma once\n\n#include \"d3d10_multithread.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n  class D3D11ImmediateContext;\n\n  class D3D10Device final : public ID3D10Device1 {\n\n  public:\n\n    D3D10Device(\n            D3D11Device*                      pDevice,\n            D3D11ImmediateContext*            pContext);\n    \n    ~D3D10Device();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                            riid,\n            void**                            ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                           guid,\n            UINT*                             pDataSize,\n            void*                             pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                           guid,\n            UINT                              DataSize,\n      const void*                             pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                           guid,\n      const IUnknown*                         pData);\n\n    HRESULT STDMETHODCALLTYPE GetDeviceRemovedReason();\n\n    HRESULT STDMETHODCALLTYPE SetExceptionMode(\n            UINT                              RaiseFlags);\n\n    UINT STDMETHODCALLTYPE GetExceptionMode();\n\n    D3D10_FEATURE_LEVEL1 STDMETHODCALLTYPE GetFeatureLevel();\n\n    void STDMETHODCALLTYPE ClearState();\n\n    void STDMETHODCALLTYPE Flush();\n\n    HRESULT STDMETHODCALLTYPE CreateBuffer(\n      const D3D10_BUFFER_DESC*                pDesc,\n      const D3D10_SUBRESOURCE_DATA*           pInitialData,\n            ID3D10Buffer**                    ppBuffer);\n\n    HRESULT STDMETHODCALLTYPE CreateTexture1D(\n      const D3D10_TEXTURE1D_DESC*             pDesc,\n      const D3D10_SUBRESOURCE_DATA*           pInitialData,\n            ID3D10Texture1D**                 ppTexture1D);\n\n    HRESULT STDMETHODCALLTYPE CreateTexture2D(\n      const D3D10_TEXTURE2D_DESC*             pDesc,\n      const D3D10_SUBRESOURCE_DATA*           pInitialData,\n            ID3D10Texture2D**                 ppTexture2D);\n\n    HRESULT STDMETHODCALLTYPE CreateTexture3D(\n      const D3D10_TEXTURE3D_DESC*             pDesc,\n      const D3D10_SUBRESOURCE_DATA*           pInitialData,\n            ID3D10Texture3D**                 ppTexture3D);\n\n    HRESULT STDMETHODCALLTYPE CreateShaderResourceView(\n            ID3D10Resource*                   pResource,\n      const D3D10_SHADER_RESOURCE_VIEW_DESC*  pDesc,\n            ID3D10ShaderResourceView**        ppSRView);\n\n    HRESULT STDMETHODCALLTYPE CreateShaderResourceView1(\n            ID3D10Resource*                   pResource,\n      const D3D10_SHADER_RESOURCE_VIEW_DESC1* pDesc,\n            ID3D10ShaderResourceView1**       ppSRView);\n\n    HRESULT STDMETHODCALLTYPE CreateRenderTargetView(\n            ID3D10Resource*                   pResource,\n      const D3D10_RENDER_TARGET_VIEW_DESC*    pDesc,\n            ID3D10RenderTargetView**          ppRTView);\n\n    HRESULT STDMETHODCALLTYPE CreateDepthStencilView(\n            ID3D10Resource*                   pResource,\n      const D3D10_DEPTH_STENCIL_VIEW_DESC*    pDesc,\n            ID3D10DepthStencilView**          ppDepthStencilView);\n\n    HRESULT STDMETHODCALLTYPE CreateInputLayout(\n      const D3D10_INPUT_ELEMENT_DESC*         pInputElementDescs,\n            UINT                              NumElements,\n      const void*                             pShaderBytecodeWithInputSignature,\n            SIZE_T                            BytecodeLength,\n            ID3D10InputLayout**               ppInputLayout);\n\n    HRESULT STDMETHODCALLTYPE CreateVertexShader(\n      const void*                             pShaderBytecode,\n            SIZE_T                            BytecodeLength,\n            ID3D10VertexShader**              ppVertexShader);\n\n    HRESULT STDMETHODCALLTYPE CreateGeometryShader(\n      const void*                             pShaderBytecode,\n            SIZE_T                            BytecodeLength,\n            ID3D10GeometryShader**            ppGeometryShader);\n\n    HRESULT STDMETHODCALLTYPE CreateGeometryShaderWithStreamOutput(\n      const void*                             pShaderBytecode,\n            SIZE_T                            BytecodeLength,\n      const D3D10_SO_DECLARATION_ENTRY*       pSODeclaration,\n            UINT                              NumEntries,\n            UINT                              OutputStreamStride,\n            ID3D10GeometryShader**            ppGeometryShader);\n\n    HRESULT STDMETHODCALLTYPE CreatePixelShader(\n      const void*                             pShaderBytecode,\n            SIZE_T                            BytecodeLength,\n            ID3D10PixelShader**               ppPixelShader);\n\n    HRESULT STDMETHODCALLTYPE CreateBlendState(\n      const D3D10_BLEND_DESC*                 pBlendStateDesc,\n            ID3D10BlendState**                ppBlendState);\n\n    HRESULT STDMETHODCALLTYPE CreateBlendState1(\n      const D3D10_BLEND_DESC1*                pBlendStateDesc,\n            ID3D10BlendState1**               ppBlendState);\n\n    HRESULT STDMETHODCALLTYPE CreateDepthStencilState(\n      const D3D10_DEPTH_STENCIL_DESC*         pDepthStencilDesc,\n            ID3D10DepthStencilState**         ppDepthStencilState);\n\n    HRESULT STDMETHODCALLTYPE CreateRasterizerState(\n      const D3D10_RASTERIZER_DESC*            pRasterizerDesc,\n            ID3D10RasterizerState**           ppRasterizerState);\n\n    HRESULT STDMETHODCALLTYPE CreateSamplerState(\n      const D3D10_SAMPLER_DESC*               pSamplerDesc,\n            ID3D10SamplerState**              ppSamplerState);\n\n    HRESULT STDMETHODCALLTYPE CreateQuery(\n      const D3D10_QUERY_DESC*                 pQueryDesc,\n            ID3D10Query**                     ppQuery);\n\n    HRESULT STDMETHODCALLTYPE CreatePredicate(\n      const D3D10_QUERY_DESC*                 pPredicateDesc,\n            ID3D10Predicate**                 ppPredicate);\n\n    HRESULT STDMETHODCALLTYPE CreateCounter(\n      const D3D10_COUNTER_DESC*               pCounterDesc,\n            ID3D10Counter**                   ppCounter);\n\n    HRESULT STDMETHODCALLTYPE CheckFormatSupport(\n            DXGI_FORMAT                       Format,\n            UINT*                             pFormatSupport);\n\n    HRESULT STDMETHODCALLTYPE CheckMultisampleQualityLevels(\n            DXGI_FORMAT                       Format,\n            UINT                              SampleCount,\n            UINT*                             pNumQualityLevels);\n\n    void STDMETHODCALLTYPE CheckCounterInfo(\n            D3D10_COUNTER_INFO*               pCounterInfo);\n\n    HRESULT STDMETHODCALLTYPE CheckCounter(\n      const D3D10_COUNTER_DESC*               pDesc,\n            D3D10_COUNTER_TYPE*               pType,\n            UINT*                             pActiveCounters,\n            char*                             name,\n            UINT*                             pNameLength,\n            char*                             units,\n            UINT*                             pUnitsLength,\n            char*                             description,\n            UINT*                             pDescriptionLength);\n\n    UINT STDMETHODCALLTYPE GetCreationFlags();\n\n    HRESULT STDMETHODCALLTYPE OpenSharedResource(\n            HANDLE                            hResource,\n            REFIID                            ReturnedInterface,\n            void**                            ppResource);\n\n    void STDMETHODCALLTYPE ClearRenderTargetView(\n            ID3D10RenderTargetView*           pRenderTargetView,\n      const FLOAT                             ColorRGBA[4]);\n\n    void STDMETHODCALLTYPE ClearDepthStencilView(\n            ID3D10DepthStencilView*           pDepthStencilView,\n            UINT                              ClearFlags,\n            FLOAT                             Depth,\n            UINT8                             Stencil);\n\n    void STDMETHODCALLTYPE SetPredication(\n            ID3D10Predicate*                  pPredicate,\n            BOOL                              PredicateValue);\n\n    void STDMETHODCALLTYPE GetPredication(\n            ID3D10Predicate**                 ppPredicate,\n            BOOL*                             pPredicateValue);\n\n    void STDMETHODCALLTYPE CopySubresourceRegion(\n            ID3D10Resource*                   pDstResource,\n            UINT                              DstSubresource,\n            UINT                              DstX,\n            UINT                              DstY,\n            UINT                              DstZ,\n            ID3D10Resource*                   pSrcResource,\n            UINT                              SrcSubresource,\n      const D3D10_BOX*                        pSrcBox);\n\n    void STDMETHODCALLTYPE CopyResource(\n            ID3D10Resource*                   pDstResource,\n            ID3D10Resource*                   pSrcResource);\n\n    void STDMETHODCALLTYPE UpdateSubresource(\n            ID3D10Resource*                   pDstResource,\n            UINT                              DstSubresource,\n      const D3D10_BOX*                        pDstBox,\n      const void*                             pSrcData,\n            UINT                              SrcRowPitch,\n            UINT                              SrcDepthPitch);\n\n    void STDMETHODCALLTYPE GenerateMips(\n            ID3D10ShaderResourceView*         pShaderResourceView);\n\n    void STDMETHODCALLTYPE ResolveSubresource(\n            ID3D10Resource*                   pDstResource,\n            UINT                              DstSubresource,\n            ID3D10Resource*                   pSrcResource,\n            UINT                              SrcSubresource,\n            DXGI_FORMAT                       Format);\n\n    void STDMETHODCALLTYPE Draw(\n            UINT                              VertexCount,\n            UINT                              StartVertexLocation);\n\n    void STDMETHODCALLTYPE DrawIndexed(\n            UINT                              IndexCount,\n            UINT                              StartIndexLocation,\n            INT                               BaseVertexLocation);\n\n    void STDMETHODCALLTYPE DrawInstanced(\n            UINT                              VertexCountPerInstance,\n            UINT                              InstanceCount,\n            UINT                              StartVertexLocation,\n            UINT                              StartInstanceLocation);\n\n    void STDMETHODCALLTYPE DrawIndexedInstanced(\n            UINT                              IndexCountPerInstance,\n            UINT                              InstanceCount,\n            UINT                              StartIndexLocation,\n            INT                               BaseVertexLocation,\n            UINT                              StartInstanceLocation);\n\n    void STDMETHODCALLTYPE DrawAuto();\n\n    void STDMETHODCALLTYPE IASetInputLayout(\n            ID3D10InputLayout*                pInputLayout);\n\n    void STDMETHODCALLTYPE IASetPrimitiveTopology(\n            D3D10_PRIMITIVE_TOPOLOGY          Topology);\n\n    void STDMETHODCALLTYPE IASetVertexBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D10Buffer* const*              ppVertexBuffers,\n      const UINT*                             pStrides,\n      const UINT*                             pOffsets);\n\n    void STDMETHODCALLTYPE IASetIndexBuffer(\n            ID3D10Buffer*                     pIndexBuffer,\n            DXGI_FORMAT                       Format,\n            UINT                              Offset);\n\n    void STDMETHODCALLTYPE IAGetInputLayout(\n            ID3D10InputLayout**               ppInputLayout);\n\n    void STDMETHODCALLTYPE IAGetPrimitiveTopology(\n            D3D10_PRIMITIVE_TOPOLOGY*         pTopology);\n\n    void STDMETHODCALLTYPE IAGetVertexBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D10Buffer**                    ppVertexBuffers,\n            UINT*                             pStrides,\n            UINT*                             pOffsets);\n\n    void STDMETHODCALLTYPE IAGetIndexBuffer(\n            ID3D10Buffer**                    pIndexBuffer,\n            DXGI_FORMAT*                      Format,\n            UINT*                             Offset);\n\n    void STDMETHODCALLTYPE VSSetShader(\n            ID3D10VertexShader*               pVertexShader);\n\n    void STDMETHODCALLTYPE VSSetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D10Buffer* const*              ppConstantBuffers);\n\n    void STDMETHODCALLTYPE VSSetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D10ShaderResourceView* const*  ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE VSSetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D10SamplerState* const*        ppSamplers);\n\n    void STDMETHODCALLTYPE VSGetShader(\n            ID3D10VertexShader**              ppVertexShader);\n\n    void STDMETHODCALLTYPE VSGetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D10Buffer**                    ppConstantBuffers);\n\n    void STDMETHODCALLTYPE VSGetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D10ShaderResourceView**        ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE VSGetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D10SamplerState**              ppSamplers);\n\n    void STDMETHODCALLTYPE GSSetShader(\n            ID3D10GeometryShader*             pShader);\n\n    void STDMETHODCALLTYPE GSSetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D10Buffer* const*              ppConstantBuffers);\n\n    void STDMETHODCALLTYPE GSSetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D10ShaderResourceView* const*  ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE GSSetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D10SamplerState* const*        ppSamplers);\n\n    void STDMETHODCALLTYPE GSGetShader(\n            ID3D10GeometryShader**            ppGeometryShader);\n\n    void STDMETHODCALLTYPE GSGetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D10Buffer**                    ppConstantBuffers);\n\n    void STDMETHODCALLTYPE GSGetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D10ShaderResourceView**        ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE GSGetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D10SamplerState**              ppSamplers);\n\n    void STDMETHODCALLTYPE PSSetShader(\n            ID3D10PixelShader*                pPixelShader);\n\n    void STDMETHODCALLTYPE PSSetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D10Buffer* const*              ppConstantBuffers);\n\n    void STDMETHODCALLTYPE PSSetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D10ShaderResourceView* const*  ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE PSSetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D10SamplerState* const*        ppSamplers);\n\n    void STDMETHODCALLTYPE PSGetShader(\n            ID3D10PixelShader**               ppPixelShader);\n\n    void STDMETHODCALLTYPE PSGetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D10Buffer**                    ppConstantBuffers);\n\n    void STDMETHODCALLTYPE PSGetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D10ShaderResourceView**        ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE PSGetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D10SamplerState**              ppSamplers);\n\n    void STDMETHODCALLTYPE OMSetRenderTargets(\n            UINT                              NumViews,\n            ID3D10RenderTargetView* const*    ppRenderTargetViews,\n            ID3D10DepthStencilView*           pDepthStencilView);\n\n    void STDMETHODCALLTYPE OMSetBlendState(\n            ID3D10BlendState*                 pBlendState,\n      const FLOAT                             BlendFactor[4],\n            UINT                              SampleMask);\n\n    void STDMETHODCALLTYPE OMSetDepthStencilState(\n            ID3D10DepthStencilState*          pDepthStencilState,\n            UINT                              StencilRef);\n\n    void STDMETHODCALLTYPE OMGetRenderTargets(\n            UINT                              NumViews,\n            ID3D10RenderTargetView**          ppRenderTargetViews,\n            ID3D10DepthStencilView**          ppDepthStencilView);\n\n    void STDMETHODCALLTYPE OMGetBlendState(\n            ID3D10BlendState**                ppBlendState,\n            FLOAT                             BlendFactor[4],\n            UINT*                             pSampleMask);\n\n    void STDMETHODCALLTYPE OMGetDepthStencilState(\n            ID3D10DepthStencilState**         ppDepthStencilState,\n            UINT*                             pStencilRef);\n\n    void STDMETHODCALLTYPE RSSetState(\n            ID3D10RasterizerState*            pRasterizerState);\n\n    void STDMETHODCALLTYPE RSSetViewports(\n            UINT                              NumViewports,\n      const D3D10_VIEWPORT*                   pViewports);\n\n    void STDMETHODCALLTYPE RSSetScissorRects(\n            UINT                              NumRects,\n      const D3D10_RECT*                       pRects);\n\n    void STDMETHODCALLTYPE RSGetState(\n            ID3D10RasterizerState**           ppRasterizerState);\n\n    void STDMETHODCALLTYPE RSGetViewports(\n            UINT*                             NumViewports,\n            D3D10_VIEWPORT*                   pViewports);\n\n    void STDMETHODCALLTYPE RSGetScissorRects(\n            UINT*                             NumRects,\n            D3D10_RECT*                       pRects);\n\n    void STDMETHODCALLTYPE SOSetTargets(\n            UINT                              NumBuffers,\n            ID3D10Buffer* const*              ppSOTargets,\n      const UINT*                             pOffsets);\n\n    void STDMETHODCALLTYPE SOGetTargets(\n            UINT                              NumBuffers,\n            ID3D10Buffer**                    ppSOTargets,\n            UINT*                             pOffsets);\n\n    void STDMETHODCALLTYPE SetTextFilterSize(\n            UINT                              Width,\n            UINT                              Height);\n\n    void STDMETHODCALLTYPE GetTextFilterSize(\n            UINT*                             pWidth,\n            UINT*                             pHeight);\n\n  private:\n\n    D3D11Device*            m_device;\n    D3D11ImmediateContext*  m_context;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_include.h",
    "content": "#pragma once\n\n#include \"../dxgi/dxgi_include.h\"\n\n#include \"../util/sync/sync_spinlock.h\"\n#include \"../util/sync/sync_recursive.h\"\n\n#include <d3d10_1.h>\n#include <d3d11_1.h>\n"
  },
  {
    "path": "src/d3d10/d3d10_input_layout.cpp",
    "content": "#include \"d3d10_input_layout.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_input_layout.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10InputLayout::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10InputLayout::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10InputLayout::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10InputLayout::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10InputLayout::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10InputLayout::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10InputLayout::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_input_layout.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n  class D3D11InputLayout;\n\n  class D3D10InputLayout : public ID3D10InputLayout {\n\n  public:\n\n    D3D10InputLayout(D3D11InputLayout* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    D3D11InputLayout* GetD3D11Iface() {\n      return m_d3d11;\n    }\n    \n  private:\n\n    D3D11InputLayout* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_multithread.cpp",
    "content": "#include <utility>\n\n#include \"d3d10_device.h\"\n\nnamespace dxvk {\n\n  D3D10Multithread::D3D10Multithread(\n          IUnknown*             pParent,\n          BOOL                  Protected,\n          BOOL                  Force)\n  : m_parent    (pParent),\n    m_protected (Protected || Force),\n    m_enabled   (Protected),\n    m_forced    (Force) {\n    \n  }\n\n\n  D3D10Multithread::~D3D10Multithread() {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Multithread::AddRef() {\n    return m_parent->AddRef();\n  }\n\n  \n  ULONG STDMETHODCALLTYPE D3D10Multithread::Release() {\n    return m_parent->Release();\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D10Multithread::QueryInterface(\n          REFIID                riid,\n          void**                ppvObject) {\n    return m_parent->QueryInterface(riid, ppvObject);\n  }\n\n  \n  void STDMETHODCALLTYPE D3D10Multithread::Enter() {\n    if (m_protected)\n      m_mutex.lock();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Multithread::Leave() {\n    if (m_protected)\n      m_mutex.unlock();\n  }\n\n\n  BOOL STDMETHODCALLTYPE D3D10Multithread::SetMultithreadProtected(\n          BOOL                  bMTProtect) {\n    BOOL result = m_enabled;\n    m_enabled = bMTProtect;\n\n    if (!m_forced)\n      m_protected = m_enabled;\n\n    return result;\n  }\n\n\n  BOOL STDMETHODCALLTYPE D3D10Multithread::GetMultithreadProtected() {\n    return m_enabled;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d10/d3d10_multithread.h",
    "content": "#pragma once\n\n#include \"d3d10_include.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Device lock\n   * \n   * Lightweight RAII wrapper that implements\n   * a subset of the functionality provided by\n   * \\c std::unique_lock, with the goal of being\n   * cheaper to construct and destroy.\n   */  \n  class D3D10DeviceLock {\n\n  public:\n\n    D3D10DeviceLock()\n    : m_mutex(nullptr) { }\n\n    D3D10DeviceLock(sync::RecursiveSpinlock& mutex)\n    : m_mutex(&mutex) {\n      mutex.lock();\n    }\n\n    D3D10DeviceLock(D3D10DeviceLock&& other)\n    : m_mutex(other.m_mutex) {\n      other.m_mutex = nullptr;\n    }\n\n    D3D10DeviceLock& operator = (D3D10DeviceLock&& other) {\n      if (m_mutex)\n        m_mutex->unlock();\n      \n      m_mutex = other.m_mutex;\n      other.m_mutex = nullptr;\n      return *this;\n    }\n\n    ~D3D10DeviceLock() {\n      if (unlikely(m_mutex != nullptr))\n        m_mutex->unlock();\n    }\n\n  private:\n\n    sync::RecursiveSpinlock* m_mutex;\n    \n  };\n\n  \n  /**\n   * \\brief D3D10 device and D3D11 context lock\n   * \n   * Can be queried from the D3D10 device or from\n   * any D3D11 context in order to make individual\n   * calls thread-safe. Provides methods to lock\n   * the device or context explicitly.\n   */\n  class D3D10Multithread : public ID3D10Multithread {\n\n  public:\n\n    D3D10Multithread(\n            IUnknown*             pParent,\n            BOOL                  Protected,\n            BOOL                  Force);\n    \n    ~D3D10Multithread();\n\n    ULONG STDMETHODCALLTYPE AddRef() final;\n    \n    ULONG STDMETHODCALLTYPE Release() final;\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject) final;\n    \n    void STDMETHODCALLTYPE Enter() final;\n\n    void STDMETHODCALLTYPE Leave() final;\n\n    BOOL STDMETHODCALLTYPE SetMultithreadProtected(\n            BOOL                  bMTProtect) final;\n\n    BOOL STDMETHODCALLTYPE GetMultithreadProtected() final;\n\n    D3D10DeviceLock AcquireLock() {\n      return unlikely(m_protected)\n        ? D3D10DeviceLock(m_mutex)\n        : D3D10DeviceLock();\n    }\n    \n  private:\n\n    IUnknown* m_parent;\n    BOOL      m_protected;\n    BOOL      m_enabled;\n    BOOL      m_forced;\n\n    sync::RecursiveSpinlock m_mutex;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_query.cpp",
    "content": "#include \"d3d10_query.h\"\n#include \"d3d10_device.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_context.h\"\n#include \"../d3d11/d3d11_query.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10Query::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Query::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Query::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Query::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D10Query::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Query::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Query::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Query::Begin() {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    ctx->Begin(m_d3d11);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Query::End() {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    ctx->End(m_d3d11);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Query::GetData(\n          void*                     pData,\n          UINT                      DataSize,\n          UINT                      GetDataFlags) {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    return ctx->GetData(m_d3d11,\n      pData, DataSize, GetDataFlags);\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D10Query::GetDataSize() {\n    return m_d3d11->GetDataSize();\n  }\n\n  \n  void STDMETHODCALLTYPE D3D10Query::GetDesc(\n          D3D10_QUERY_DESC*         pDesc) {\n    D3D11_QUERY_DESC d3d11Desc;\n    m_d3d11->GetDesc(&d3d11Desc);\n\n    pDesc->Query      = D3D10_QUERY(d3d11Desc.Query);\n    pDesc->MiscFlags  = d3d11Desc.MiscFlags;\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_query.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D10Device;\n  class D3D11Device;\n  class D3D11Query;\n\n  class D3D10Query : public ID3D10Predicate {\n\n  public:\n\n    D3D10Query(D3D11Query* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    void STDMETHODCALLTYPE Begin();\n\n    void STDMETHODCALLTYPE End();\n\n    HRESULT STDMETHODCALLTYPE GetData(\n            void*                     pData,\n            UINT                      DataSize,\n            UINT                      GetDataFlags);\n\n    UINT STDMETHODCALLTYPE GetDataSize();\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_QUERY_DESC*         pDesc);\n    \n    D3D11Query* GetD3D11Iface() {\n      return m_d3d11;\n    }\n\n  private:\n\n    D3D11Query*  m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_rasterizer.cpp",
    "content": "#include \"d3d10_rasterizer.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_rasterizer.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10RasterizerState::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10RasterizerState::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10RasterizerState::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10RasterizerState::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10RasterizerState::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10RasterizerState::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10RasterizerState::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10RasterizerState::GetDesc(\n          D3D10_RASTERIZER_DESC*    pDesc) {\n    static_assert(sizeof(D3D10_RASTERIZER_DESC) == sizeof(D3D11_RASTERIZER_DESC));\n    m_d3d11->GetDesc(reinterpret_cast<D3D11_RASTERIZER_DESC*>(pDesc));\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_rasterizer.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D11RasterizerState;\n  class D3D11Device;\n\n  class D3D10RasterizerState : public ID3D10RasterizerState {\n\n  public:\n\n    D3D10RasterizerState(D3D11RasterizerState* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_RASTERIZER_DESC*    pDesc);\n\n    D3D11RasterizerState* GetD3D11Iface() {\n      return m_d3d11;\n    }\n    \n  private:\n\n    D3D11RasterizerState* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_sampler.cpp",
    "content": "#include \"d3d10_sampler.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_sampler.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10SamplerState::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10SamplerState::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10SamplerState::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10SamplerState::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10SamplerState::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10SamplerState::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10SamplerState::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10SamplerState::GetDesc(\n          D3D10_SAMPLER_DESC*       pDesc) {\n    D3D11_SAMPLER_DESC d3d11Desc;\n    m_d3d11->GetDesc(&d3d11Desc);\n\n    pDesc->Filter            = D3D10_FILTER(d3d11Desc.Filter);\n    pDesc->AddressU          = D3D10_TEXTURE_ADDRESS_MODE(d3d11Desc.AddressU);\n    pDesc->AddressV          = D3D10_TEXTURE_ADDRESS_MODE(d3d11Desc.AddressV);\n    pDesc->AddressW          = D3D10_TEXTURE_ADDRESS_MODE(d3d11Desc.AddressW);\n    pDesc->MipLODBias        = d3d11Desc.MipLODBias;\n    pDesc->MaxAnisotropy     = d3d11Desc.MaxAnisotropy;\n    pDesc->ComparisonFunc    = D3D10_COMPARISON_FUNC(d3d11Desc.ComparisonFunc);\n    pDesc->MinLOD            = d3d11Desc.MinLOD;\n    pDesc->MaxLOD            = d3d11Desc.MaxLOD;\n\n    for (uint32_t i = 0; i < 4; i++)\n      pDesc->BorderColor[i] = d3d11Desc.BorderColor[i];\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_sampler.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n  class D3D11SamplerState;\n\n  class D3D10SamplerState : public ID3D10SamplerState {\n\n  public:\n\n    D3D10SamplerState(D3D11SamplerState* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_SAMPLER_DESC*       pDesc);\n    \n    D3D11SamplerState* GetD3D11Iface() {\n      return m_d3d11;\n    }\n    \n  private:\n\n    D3D11SamplerState* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_shader.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  template<typename D3D11Interface, typename D3D10Interface>\n  class D3D11Shader;\n\n  template<typename D3D10Interface, typename D3D11Interface> \n  class D3D10Shader : public D3D10Interface {\n    using D3D11ShaderClass = D3D11Shader<D3D11Interface, D3D10Interface>;\n  public:\n\n    D3D10Shader(D3D11Shader<D3D11Interface, D3D10Interface>* pParent)\n    : m_d3d11(pParent) { }\n\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject) {\n      return m_d3d11->QueryInterface(riid, ppvObject);\n    }\n\n\n    ULONG STDMETHODCALLTYPE AddRef() {\n      return m_d3d11->AddRef();\n    }\n\n\n    ULONG STDMETHODCALLTYPE Release() {\n      return m_d3d11->Release();\n    }\n\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice) {\n      GetD3D10Device(m_d3d11, ppDevice);\n    }\n\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData) {\n      return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n    }\n\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData) {\n      return m_d3d11->SetPrivateData(guid, DataSize, pData);\n    }\n\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData) {\n      return m_d3d11->SetPrivateDataInterface(guid, pData);\n    }\n    \n    D3D11ShaderClass* GetD3D11Iface() {\n      return m_d3d11;\n    }\n\n  private:\n\n    D3D11ShaderClass* m_d3d11;\n\n  };\n\n  using D3D10VertexShader   = D3D10Shader<ID3D10VertexShader,   ID3D11VertexShader>;\n  using D3D10GeometryShader = D3D10Shader<ID3D10GeometryShader, ID3D11GeometryShader>;\n  using D3D10PixelShader    = D3D10Shader<ID3D10PixelShader,    ID3D11PixelShader>;\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_texture.cpp",
    "content": "#include \"d3d10_texture.h\"\n#include \"d3d10_device.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_context.h\"\n#include \"../d3d11/d3d11_texture.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture1D::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Texture1D::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Texture1D::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture1D::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D10Texture1D::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture1D::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture1D::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture1D::GetType(\n          D3D10_RESOURCE_DIMENSION* rType) {\n    *rType = D3D10_RESOURCE_DIMENSION_TEXTURE1D;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture1D::SetEvictionPriority(\n          UINT                      EvictionPriority) {\n    m_d3d11->SetEvictionPriority(EvictionPriority);\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D10Texture1D::GetEvictionPriority() {\n    return m_d3d11->GetEvictionPriority();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture1D::Map(\n          UINT                      Subresource,\n          D3D10_MAP                 MapType,\n          UINT                      MapFlags,\n          void**                    ppData) {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    D3D11_MAPPED_SUBRESOURCE sr;\n    HRESULT hr = ctx->Map(m_d3d11, Subresource,\n      D3D11_MAP(MapType), MapFlags, &sr);\n    \n    if (FAILED(hr))\n      return hr;\n    \n    if (ppData != nullptr) {\n      *ppData = sr.pData;\n      return S_OK;\n    } return S_FALSE;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture1D::Unmap(\n          UINT                      Subresource) {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    ctx->Unmap(m_d3d11, Subresource);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture1D::GetDesc(\n          D3D10_TEXTURE1D_DESC*     pDesc) {\n    D3D11_TEXTURE1D_DESC d3d11Desc;\n    m_d3d11->GetDesc(&d3d11Desc);\n\n    pDesc->Width           = d3d11Desc.Width;\n    pDesc->MipLevels       = d3d11Desc.MipLevels;\n    pDesc->ArraySize       = d3d11Desc.ArraySize;\n    pDesc->Format          = d3d11Desc.Format;\n    pDesc->Usage           = D3D10_USAGE(d3d11Desc.Usage);\n    pDesc->BindFlags       = d3d11Desc.BindFlags;\n    pDesc->CPUAccessFlags  = d3d11Desc.CPUAccessFlags;\n    pDesc->MiscFlags       = ConvertD3D11ResourceFlags(d3d11Desc.MiscFlags);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture2D::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Texture2D::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Texture2D::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture2D::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D10Texture2D::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture2D::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture2D::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture2D::GetType(\n          D3D10_RESOURCE_DIMENSION* rType) {\n    *rType = D3D10_RESOURCE_DIMENSION_TEXTURE2D;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture2D::SetEvictionPriority(\n          UINT                      EvictionPriority) {\n    m_d3d11->SetEvictionPriority(EvictionPriority);\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D10Texture2D::GetEvictionPriority() {\n    return m_d3d11->GetEvictionPriority();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture2D::Map(\n          UINT                      Subresource,\n          D3D10_MAP                 MapType,\n          UINT                      MapFlags,\n          D3D10_MAPPED_TEXTURE2D*   pMappedTex2D) {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    D3D11_MAPPED_SUBRESOURCE sr;\n    HRESULT hr = ctx->Map(m_d3d11, Subresource,\n      D3D11_MAP(MapType), MapFlags, &sr);\n    \n    if (FAILED(hr))\n      return hr;\n    \n    if (pMappedTex2D != nullptr) {\n      pMappedTex2D->pData    = sr.pData;\n      pMappedTex2D->RowPitch = sr.RowPitch;\n      return S_OK;\n    } return S_FALSE;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture2D::Unmap(\n          UINT                      Subresource) {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    ctx->Unmap(m_d3d11, Subresource);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture2D::GetDesc(\n          D3D10_TEXTURE2D_DESC*     pDesc) {\n    D3D11_TEXTURE2D_DESC d3d11Desc;\n    m_d3d11->GetDesc(&d3d11Desc);\n\n    pDesc->Width           = d3d11Desc.Width;\n    pDesc->Height          = d3d11Desc.Height;\n    pDesc->MipLevels       = d3d11Desc.MipLevels;\n    pDesc->ArraySize       = d3d11Desc.ArraySize;\n    pDesc->Format          = d3d11Desc.Format;\n    pDesc->SampleDesc      = d3d11Desc.SampleDesc;\n    pDesc->Usage           = D3D10_USAGE(d3d11Desc.Usage);\n    pDesc->BindFlags       = d3d11Desc.BindFlags;\n    pDesc->CPUAccessFlags  = d3d11Desc.CPUAccessFlags;\n    pDesc->MiscFlags       = ConvertD3D11ResourceFlags(d3d11Desc.MiscFlags);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture3D::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Texture3D::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10Texture3D::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture3D::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D10Texture3D::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture3D::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture3D::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture3D::GetType(\n          D3D10_RESOURCE_DIMENSION* rType) {\n    *rType = D3D10_RESOURCE_DIMENSION_TEXTURE3D;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture3D::SetEvictionPriority(\n          UINT                      EvictionPriority) {\n    m_d3d11->SetEvictionPriority(EvictionPriority);\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D10Texture3D::GetEvictionPriority() {\n    return m_d3d11->GetEvictionPriority();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10Texture3D::Map(\n          UINT                      Subresource,\n          D3D10_MAP                 MapType,\n          UINT                      MapFlags,\n          D3D10_MAPPED_TEXTURE3D*   pMappedTex3D) {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    D3D11_MAPPED_SUBRESOURCE sr;\n    HRESULT hr = ctx->Map(m_d3d11, Subresource,\n      D3D11_MAP(MapType), MapFlags, &sr);\n    \n    if (FAILED(hr))\n      return hr;\n    \n    if (pMappedTex3D != nullptr) {\n      pMappedTex3D->pData      = sr.pData;\n      pMappedTex3D->RowPitch   = sr.RowPitch;\n      pMappedTex3D->DepthPitch = sr.DepthPitch;\n      return S_OK;\n    } return S_FALSE;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture3D::Unmap(\n          UINT                      Subresource) {\n    Com<ID3D11DeviceContext> ctx;\n    GetD3D11Context(m_d3d11, &ctx);\n\n    ctx->Unmap(m_d3d11, Subresource);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10Texture3D::GetDesc(\n          D3D10_TEXTURE3D_DESC*     pDesc) {\n    D3D11_TEXTURE3D_DESC d3d11Desc;\n    m_d3d11->GetDesc(&d3d11Desc);\n\n    pDesc->Width           = d3d11Desc.Width;\n    pDesc->Height          = d3d11Desc.Height;\n    pDesc->Depth           = d3d11Desc.Depth;\n    pDesc->MipLevels       = d3d11Desc.MipLevels;\n    pDesc->Format          = d3d11Desc.Format;\n    pDesc->Usage           = D3D10_USAGE(d3d11Desc.Usage);\n    pDesc->BindFlags       = d3d11Desc.BindFlags;\n    pDesc->CPUAccessFlags  = d3d11Desc.CPUAccessFlags;\n    pDesc->MiscFlags       = ConvertD3D11ResourceFlags(d3d11Desc.MiscFlags);\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_texture.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D10Device;\n  class D3D11Device;\n  class D3D11Texture1D;\n  class D3D11Texture2D;\n  class D3D11Texture3D;\n\n  ///////////////////////////////////////////\n  //      D 3 D 1 0 T E X T U R E 1 D\n  class D3D10Texture1D : public ID3D10Texture1D {\n\n  public:\n\n    D3D10Texture1D(D3D11Texture1D* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    void STDMETHODCALLTYPE GetType(\n            D3D10_RESOURCE_DIMENSION* rType);\n\n    void STDMETHODCALLTYPE SetEvictionPriority(\n            UINT                      EvictionPriority);\n\n    UINT STDMETHODCALLTYPE GetEvictionPriority();\n\n    HRESULT STDMETHODCALLTYPE Map(\n            UINT                      Subresource,\n            D3D10_MAP                 MapType,\n            UINT                      MapFlags,\n            void**                    ppData);\n\n    void STDMETHODCALLTYPE Unmap(\n            UINT                      Subresource);\n\n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_TEXTURE1D_DESC*     pDesc);\n    \n    D3D11Texture1D* GetD3D11Iface() {\n      return m_d3d11;\n    }\n    \n  private:\n\n    D3D11Texture1D* m_d3d11;\n\n  };\n\n\n  ///////////////////////////////////////////\n  //      D 3 D 1 0 T E X T U R E 2 D\n  class D3D10Texture2D : public ID3D10Texture2D {\n\n  public:\n\n    D3D10Texture2D(D3D11Texture2D* pParent)\n    :  m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    void STDMETHODCALLTYPE GetType(\n            D3D10_RESOURCE_DIMENSION* rType);\n\n    void STDMETHODCALLTYPE SetEvictionPriority(\n            UINT                      EvictionPriority);\n\n    UINT STDMETHODCALLTYPE GetEvictionPriority();\n\n    HRESULT STDMETHODCALLTYPE Map(\n            UINT                      Subresource,\n            D3D10_MAP                 MapType,\n            UINT                      MapFlags,\n            D3D10_MAPPED_TEXTURE2D*   pMappedTex2D);\n\n    void STDMETHODCALLTYPE Unmap(\n            UINT                      Subresource);\n\n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_TEXTURE2D_DESC*     pDesc);\n    \n    D3D11Texture2D* GetD3D11Iface() {\n      return m_d3d11;\n    }\n\n  private:\n\n    D3D11Texture2D* m_d3d11;\n\n  };\n\n\n  ///////////////////////////////////////////\n  //      D 3 D 1 0 T E X T U R E 3 D\n  class D3D10Texture3D : public ID3D10Texture3D {\n\n  public:\n\n    D3D10Texture3D(D3D11Texture3D* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    void STDMETHODCALLTYPE GetType(\n            D3D10_RESOURCE_DIMENSION* rType);\n\n    void STDMETHODCALLTYPE SetEvictionPriority(\n            UINT                      EvictionPriority);\n\n    UINT STDMETHODCALLTYPE GetEvictionPriority();\n\n    HRESULT STDMETHODCALLTYPE Map(\n            UINT                      Subresource,\n            D3D10_MAP                 MapType,\n            UINT                      MapFlags,\n            D3D10_MAPPED_TEXTURE3D*   pMappedTex3D);\n\n    void STDMETHODCALLTYPE Unmap(\n            UINT                      Subresource);\n\n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_TEXTURE3D_DESC*     pDesc);\n    \n    D3D11Texture3D* GetD3D11Iface() {\n      return m_d3d11;\n    }\n\n  private:\n\n    D3D11Texture3D* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_util.cpp",
    "content": "#include \"d3d10_util.h\"\n#include \"d3d10_device.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n\nnamespace dxvk {\n\n  UINT ConvertD3D10ResourceFlags(UINT MiscFlags) {\n    UINT result = 0;\n    if (MiscFlags & D3D10_RESOURCE_MISC_GENERATE_MIPS)\n      result |= D3D11_RESOURCE_MISC_GENERATE_MIPS;\n    if (MiscFlags & D3D10_RESOURCE_MISC_SHARED)\n      result |= D3D11_RESOURCE_MISC_SHARED;\n    if (MiscFlags & D3D10_RESOURCE_MISC_TEXTURECUBE)\n      result |= D3D11_RESOURCE_MISC_TEXTURECUBE;\n    if (MiscFlags & D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX)\n      result |= D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;\n    if (MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE)\n      result |= D3D11_RESOURCE_MISC_GDI_COMPATIBLE;\n    return result;\n  }\n\n\n  UINT ConvertD3D11ResourceFlags(UINT MiscFlags) {\n    UINT result = 0;\n    if (MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS)\n      result |= D3D10_RESOURCE_MISC_GENERATE_MIPS;\n    if (MiscFlags & D3D11_RESOURCE_MISC_SHARED)\n      result |= D3D10_RESOURCE_MISC_SHARED;\n    if (MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE)\n      result |= D3D10_RESOURCE_MISC_TEXTURECUBE;\n    if (MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX)\n      result |= D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;\n    if (MiscFlags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE)\n      result |= D3D10_RESOURCE_MISC_GDI_COMPATIBLE;\n    return result;\n  }\n\n\n  void GetD3D10ResourceFromView(\n          ID3D11View*           pSrcView,\n          ID3D10Resource**      ppDstResource) {\n    Com<ID3D11Resource> d3d11Resource;\n    pSrcView->GetResource(&d3d11Resource);\n    GetD3D10Resource(d3d11Resource.ptr(), ppDstResource);\n  }\n\n\n  void GetD3D11ResourceFromView(\n          ID3D10View*           pSrcView,\n          ID3D11Resource**      ppDstResource) {\n    Com<ID3D10Resource> d3d10Resource;\n    pSrcView->GetResource(&d3d10Resource);\n    GetD3D11Resource(d3d10Resource.ptr(), ppDstResource);\n  }\n\n\n  void GetD3D10Resource(\n          ID3D11Resource*       pSrcResource,\n          ID3D10Resource**      ppDstResource) {\n    pSrcResource->QueryInterface(\n      __uuidof(ID3D10Resource),\n      reinterpret_cast<void**>(ppDstResource));\n  }\n\n\n  void GetD3D11Resource(\n          ID3D10Resource*       pSrcResource,\n          ID3D11Resource**      ppDstResource) {\n    pSrcResource->QueryInterface(\n      __uuidof(ID3D11Resource),\n      reinterpret_cast<void**>(ppDstResource));\n  }\n\n\n  void GetD3D10Device(\n          ID3D11DeviceChild*    pObject,\n          ID3D10Device**        ppDevice) {\n    ID3D11Device* d3d11Device = nullptr;\n    pObject->GetDevice(&d3d11Device);\n    *ppDevice = static_cast<D3D11Device*>(d3d11Device)->GetD3D10Interface();\n  }\n\n\n  void GetD3D11Device(\n          ID3D11DeviceChild*    pObject,\n          ID3D11Device**        ppDevice) {\n    pObject->GetDevice(ppDevice);\n  }\n\n  \n  void GetD3D11Context(\n          ID3D11DeviceChild*    pObject,\n          ID3D11DeviceContext** ppContext) {\n    Com<ID3D11Device> d3d11Device;\n    pObject->GetDevice(&d3d11Device);\n    d3d11Device->GetImmediateContext(ppContext);\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_util.h",
    "content": "#pragma once\n\n#include \"d3d10_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Converts misc. resource flags\n   * \n   * Converts the D3D11 misc. resource flags to\n   * their D3D10 equivalents and vice versa.\n   * \\param [in] MiscFlags Original bit mask\n   * \\returns Converted bit mask\n   */\n  UINT ConvertD3D10ResourceFlags(UINT MiscFlags);\n  UINT ConvertD3D11ResourceFlags(UINT MiscFlags);\n\n  /**\n   * \\brief Retrieves D3D10 resource from D3D11 view\n   * \n   * \\param [in] pSrcView The D3D11 resource view\n   * \\param [out] ppDstResource The D3D10 resource\n   */\n  void GetD3D10ResourceFromView(\n          ID3D11View*           pSrcView,\n          ID3D10Resource**      ppDstResource);\n\n  /**\n   * \\brief Retrieves D3D11 resource from D3D10 view\n   * \n   * \\param [in] pSrcView The D3D10 resource view\n   * \\param [out] ppDstResource The D3D11 resource\n   */\n  void GetD3D11ResourceFromView(\n          ID3D10View*           pSrcView,\n          ID3D11Resource**      ppDstResource);\n\n  /**\n   * \\brief Retrieves D3D10 resource from D3D11 resource\n   * \n   * \\param [in] pSrcResource The D3D11 resource\n   * \\param [out] ppDstResource The D3D10 resource\n   */\n  void GetD3D10Resource(\n          ID3D11Resource*       pSrcResource,\n          ID3D10Resource**      ppDstResource);\n\n  /**\n   * \\brief Retrieves D3D11 resource from D3D10 resource\n   * \n   * \\param [in] pSrcResource The D3D10 resource\n   * \\param [out] ppDstResource The D3D11 resource\n   */\n  void GetD3D11Resource(\n          ID3D10Resource*       pSrcResource,\n          ID3D11Resource**      ppDstResource);\n\n  /**\n   * \\brief Retrieves D3D10 device from D3D11 object\n   * \n   * \\param [in] pObject The D3D11 device child\n   * \\param [out] ppDevice The D3D10 device pointer\n   */\n  void GetD3D10Device(\n          ID3D11DeviceChild*    pObject,\n          ID3D10Device**        ppDevice);\n\n  /**\n   * \\brief Retrieves D3D11 device from D3D11 object\n   * \n   * \\param [in] pObject The D3D11 device child\n   * \\param [out] ppDevice The D3D11 device pointer\n   */\n  void GetD3D11Device(\n          ID3D11DeviceChild*    pObject,\n          ID3D11Device**        ppDevice);\n  \n  /**\n   * \\brief Retrieves D3D11 context from D3D11 object\n   * \n   * \\param [in] pObject The D3D11 device child\n   * \\param [out] ppContext The D3D11 immediate context\n   */\n  void GetD3D11Context(\n          ID3D11DeviceChild*    pObject,\n          ID3D11DeviceContext** ppContext);\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_view_dsv.cpp",
    "content": "#include \"d3d10_view_dsv.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_view_dsv.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10DepthStencilView::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10DepthStencilView::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10DepthStencilView::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10DepthStencilView::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10DepthStencilView::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10DepthStencilView::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10DepthStencilView::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10DepthStencilView::GetResource(\n          ID3D10Resource**          ppResource) {\n    GetD3D10ResourceFromView(m_d3d11, ppResource);\n  }\n\n  \n  void STDMETHODCALLTYPE D3D10DepthStencilView::GetDesc(\n          D3D10_DEPTH_STENCIL_VIEW_DESC* pDesc) {\n    D3D11_DEPTH_STENCIL_VIEW_DESC d3d11Desc;\n    m_d3d11->GetDesc(&d3d11Desc);\n\n    pDesc->ViewDimension = D3D10_DSV_DIMENSION(d3d11Desc.ViewDimension);\n    pDesc->Format        = d3d11Desc.Format;\n\n    switch (d3d11Desc.ViewDimension) {\n      case D3D11_DSV_DIMENSION_UNKNOWN:\n        break;\n      \n      case D3D11_DSV_DIMENSION_TEXTURE1D:\n        pDesc->Texture1D.MipSlice               = d3d11Desc.Texture1D.MipSlice;\n        break;\n      \n      case D3D11_DSV_DIMENSION_TEXTURE1DARRAY:\n        pDesc->Texture1DArray.MipSlice          = d3d11Desc.Texture1DArray.MipSlice;\n        pDesc->Texture1DArray.FirstArraySlice   = d3d11Desc.Texture1DArray.FirstArraySlice;\n        pDesc->Texture1DArray.ArraySize         = d3d11Desc.Texture1DArray.ArraySize;\n        break;\n      \n      case D3D11_DSV_DIMENSION_TEXTURE2D:\n        pDesc->Texture2D.MipSlice               = d3d11Desc.Texture2D.MipSlice;\n        break;\n      \n      case D3D11_DSV_DIMENSION_TEXTURE2DARRAY:\n        pDesc->Texture2DArray.MipSlice          = d3d11Desc.Texture2DArray.MipSlice;\n        pDesc->Texture2DArray.FirstArraySlice   = d3d11Desc.Texture2DArray.FirstArraySlice;\n        pDesc->Texture2DArray.ArraySize         = d3d11Desc.Texture2DArray.ArraySize;\n        break;\n      \n      case D3D11_DSV_DIMENSION_TEXTURE2DMS:\n        break;\n\n      case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY:\n        pDesc->Texture2DMSArray.FirstArraySlice = d3d11Desc.Texture2DMSArray.FirstArraySlice;\n        pDesc->Texture2DMSArray.ArraySize       = d3d11Desc.Texture2DMSArray.ArraySize;\n        break;\n    }\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_view_dsv.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n  class D3D11DepthStencilView;\n\n  class D3D10DepthStencilView : public ID3D10DepthStencilView {\n\n  public:\n\n    D3D10DepthStencilView(D3D11DepthStencilView* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    void STDMETHODCALLTYPE GetResource(\n            ID3D10Resource**          ppResource);\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_DEPTH_STENCIL_VIEW_DESC* pDesc);\n\n    D3D11DepthStencilView* GetD3D11Iface() {\n      return m_d3d11;\n    }\n    \n  private:\n\n    D3D11DepthStencilView* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_view_rtv.cpp",
    "content": "#include \"d3d10_view_rtv.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_view_rtv.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10RenderTargetView::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10RenderTargetView::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10RenderTargetView::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10RenderTargetView::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10RenderTargetView::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10RenderTargetView::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10RenderTargetView::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10RenderTargetView::GetResource(\n          ID3D10Resource**          ppResource) {\n    GetD3D10ResourceFromView(m_d3d11, ppResource);\n  }\n\n  \n  void STDMETHODCALLTYPE D3D10RenderTargetView::GetDesc(\n          D3D10_RENDER_TARGET_VIEW_DESC* pDesc) {\n    static_assert(sizeof(D3D10_RENDER_TARGET_VIEW_DESC) ==\n                  sizeof(D3D11_RENDER_TARGET_VIEW_DESC));\n    \n    m_d3d11->GetDesc(reinterpret_cast<D3D11_RENDER_TARGET_VIEW_DESC*>(pDesc));\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_view_rtv.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n  class D3D11RenderTargetView;\n\n  class D3D10RenderTargetView : public ID3D10RenderTargetView {\n\n  public:\n\n    D3D10RenderTargetView(D3D11RenderTargetView* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    void STDMETHODCALLTYPE GetResource(\n            ID3D10Resource**          ppResource);\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_RENDER_TARGET_VIEW_DESC* pDesc);\n\n    D3D11RenderTargetView* GetD3D11Iface() {\n      return m_d3d11;\n    }\n    \n  private:\n\n    D3D11RenderTargetView* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_view_srv.cpp",
    "content": "#include \"d3d10_view_srv.h\"\n\n#include \"../d3d11/d3d11_device.h\"\n#include \"../d3d11/d3d11_view_srv.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D10ShaderResourceView::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_d3d11->QueryInterface(riid, ppvObject);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10ShaderResourceView::AddRef() {\n    return m_d3d11->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D10ShaderResourceView::Release() {\n    return m_d3d11->Release();\n  }\n\n\n  void STDMETHODCALLTYPE D3D10ShaderResourceView::GetDevice(\n          ID3D10Device**            ppDevice) {\n    GetD3D10Device(m_d3d11, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10ShaderResourceView::GetPrivateData(\n          REFGUID                   guid,\n          UINT*                     pDataSize,\n          void*                     pData) {\n    return m_d3d11->GetPrivateData(guid, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10ShaderResourceView::SetPrivateData(\n          REFGUID                   guid,\n          UINT                      DataSize,\n    const void*                     pData) {\n    return m_d3d11->SetPrivateData(guid, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D10ShaderResourceView::SetPrivateDataInterface(\n          REFGUID                   guid,\n    const IUnknown*                 pData) {\n    return m_d3d11->SetPrivateDataInterface(guid, pData);\n  }\n\n\n  void STDMETHODCALLTYPE D3D10ShaderResourceView::GetResource(\n          ID3D10Resource**          ppResource) {\n    GetD3D10ResourceFromView(m_d3d11, ppResource);\n  }\n\n  \n  void STDMETHODCALLTYPE D3D10ShaderResourceView::GetDesc(\n          D3D10_SHADER_RESOURCE_VIEW_DESC* pDesc) {\n    static_assert(sizeof(D3D10_SHADER_RESOURCE_VIEW_DESC) ==\n                  sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));\n    \n    m_d3d11->GetDesc(reinterpret_cast<D3D11_SHADER_RESOURCE_VIEW_DESC*>(pDesc));\n\n    if (pDesc->ViewDimension > D3D10_SRV_DIMENSION_TEXTURECUBE)\n      pDesc->ViewDimension = D3D10_SRV_DIMENSION_UNKNOWN;\n  }\n\n\n  void STDMETHODCALLTYPE D3D10ShaderResourceView::GetDesc1(\n          D3D10_SHADER_RESOURCE_VIEW_DESC1* pDesc) {\n    static_assert(sizeof(D3D10_SHADER_RESOURCE_VIEW_DESC1) ==\n                  sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));\n    \n    m_d3d11->GetDesc(reinterpret_cast<D3D11_SHADER_RESOURCE_VIEW_DESC*>(pDesc));\n\n    if (pDesc->ViewDimension > D3D10_1_SRV_DIMENSION_TEXTURECUBEARRAY)\n      pDesc->ViewDimension = D3D10_1_SRV_DIMENSION_UNKNOWN;\n  }\n\n}"
  },
  {
    "path": "src/d3d10/d3d10_view_srv.h",
    "content": "#pragma once\n\n#include \"d3d10_util.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n  class D3D11ShaderResourceView;\n\n  class D3D10ShaderResourceView : public ID3D10ShaderResourceView1 {\n\n  public:\n\n    D3D10ShaderResourceView(D3D11ShaderResourceView* pParent)\n    : m_d3d11(pParent) { }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D10Device**            ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                   guid,\n            UINT*                     pDataSize,\n            void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                   guid,\n            UINT                      DataSize,\n      const void*                     pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                   guid,\n      const IUnknown*                 pData);\n\n    void STDMETHODCALLTYPE GetResource(\n            ID3D10Resource**          ppResource);\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D10_SHADER_RESOURCE_VIEW_DESC* pDesc);\n\n    void STDMETHODCALLTYPE GetDesc1(\n            D3D10_SHADER_RESOURCE_VIEW_DESC1* pDesc);\n\n    D3D11ShaderResourceView* GetD3D11Iface() {\n      return m_d3d11;\n    }\n    \n  private:\n\n    D3D11ShaderResourceView* m_d3d11;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d10/d3d10core.def",
    "content": "LIBRARY D3D10CORE.DLL\nEXPORTS  \n    D3D10CoreCreateDevice\n    D3D10CoreGetVersion\n    D3D10CoreRegisterLayers\n"
  },
  {
    "path": "src/d3d10/d3d10core.sym",
    "content": "{\n  global:\n    D3D10CoreCreateDevice;\n    D3D10CoreGetVersion;\n    D3D10CoreRegisterLayers;\n\n  local:\n    *;\n};\n"
  },
  {
    "path": "src/d3d10/meson.build",
    "content": "d3d10_core_res = wrc_generator.process('version10_core.rc')\n\nd3d10_core_src = [\n  'd3d10_core.cpp',\n]\n\nd3d10_core_ld_args      = []\nd3d10_core_link_depends = []\n\nif platform == 'windows'\n  d3d10_d3d11_dep = lib_d3d11\nelse\n  d3d10_core_ld_args      += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'd3d10core.sym') ]\n  d3d10_core_link_depends += files('d3d10core.sym')\n  d3d10_d3d11_dep = d3d11_dep\nendif\n\nd3d10_core_dll = shared_library(dxvk_name_prefix+'d3d10core', d3d10_core_src, d3d10_core_res,\n  dependencies        : [ d3d10_d3d11_dep ],\n  include_directories : dxvk_include_path,\n  install             : true,\n  vs_module_defs      : 'd3d10core'+def_spec_ext,\n  link_args           : d3d10_core_ld_args,\n  link_depends        : [ d3d10_core_link_depends ],\n  kwargs              : dxvk_so_version,\n)\n\nd3d10_core_dep = declare_dependency(\n  link_with           : [ d3d10_core_dll ],\n)\n\nif platform != 'windows'\n  pkg.generate(d3d10_core_dll,\n    filebase: dxvk_pkg_prefix + 'd3d10core',\n    subdirs:  'dxvk',\n  )\nendif\n"
  },
  {
    "path": "src/d3d10/version10_core.rc",
    "content": "#include <windows.h>\n\n// DLL version information.\nVS_VERSION_INFO    VERSIONINFO\nFILEVERSION        10,0,17763,1\nPRODUCTVERSION     10,0,17763,1\nFILEFLAGSMASK      VS_FFI_FILEFLAGSMASK\nFILEFLAGS          0\nFILEOS VOS_NT_WINDOWS32\nFILETYPE VFT_DLL\nFILESUBTYPE VFT2_UNKNOWN\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"080904b0\"\n    BEGIN\n      VALUE \"CompanyName\",      \"DXVK\"\n      VALUE \"FileDescription\",  \"Direct3D 10 Runtime\"\n      VALUE \"FileVersion\",      \"10.0.17763.1 (WinBuild.160101.0800)\"\n      VALUE \"InternalName\",     \"D3D10Core.dll\"\n      VALUE \"LegalCopyright\",   \"zlib/libpng license\"\n      VALUE \"OriginalFilename\", \"D3D10Core.dll\"\n      VALUE \"ProductName\",      \"DXVK\"\n      VALUE \"ProductVersion\",   \"10.0.17763.1\"\n    END\n  END\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0809, 1200\n  END\nEND\n\n"
  },
  {
    "path": "src/d3d11/d3d11.def",
    "content": "LIBRARY D3D11.DLL\nEXPORTS  \n    D3D11CoreCreateDevice @18\n    D3D11CreateDevice @22\n    D3D11CreateDeviceAndSwapChain @23\n    D3D11On12CreateDevice @24\n"
  },
  {
    "path": "src/d3d11/d3d11.sym",
    "content": "{\n  global:\n    D3D11CoreCreateDevice;\n    D3D11CreateDevice;\n    D3D11CreateDeviceAndSwapChain;\n    D3D11On12CreateDevice;\n\n  local:\n    *;\n};\n"
  },
  {
    "path": "src/d3d11/d3d11_annotation.cpp",
    "content": "#include \"d3d11_annotation.h\"\n#include \"d3d11_context_def.h\"\n#include \"d3d11_context_imm.h\"\n#include \"d3d11_device.h\"\n\n#include \"../util/util_misc.h\"\n#include \"../util/util_win32_compat.h\"\n\nnamespace dxvk {\n\n  template <bool Register>\n  static void RegisterUserDefinedAnnotation(IDXVKUserDefinedAnnotation* annotation) {\n    using RegistrationFunctionType = void(__stdcall *)(IDXVKUserDefinedAnnotation*);\n    static const int16_t RegisterOrdinal = 28257;\n    static const int16_t UnregisterOrdinal = 28258;\n\n    HMODULE d3d9Module = ::LoadLibraryA(\"d3d9.dll\");\n    if (!d3d9Module) {\n      Logger::info(\"Unable to find d3d9, some annotations may be missed.\");\n      return;\n    }\n\n    const int16_t ordinal = Register ? RegisterOrdinal : UnregisterOrdinal;\n    auto registrationFunction = reinterpret_cast<RegistrationFunctionType>(::GetProcAddress(d3d9Module,\n      reinterpret_cast<const char*>(static_cast<uintptr_t>(ordinal))));\n\n    if (!registrationFunction) {\n      Logger::info(\"Unable to find DXVK_RegisterAnnotation, some annotations may be missed.\");\n      return;\n    }\n\n    registrationFunction(annotation);\n  }\n\n\n  template<typename ContextType>\n  D3D11UserDefinedAnnotation<ContextType>::D3D11UserDefinedAnnotation(\n          ContextType*          container,\n    const Rc<DxvkDevice>&       dxvkDevice)\n  : m_container(container),\n    m_annotationsEnabled(dxvkDevice->debugFlags().test(DxvkDebugFlag::Markers)) {\n    if (!IsDeferred && m_annotationsEnabled)\n      RegisterUserDefinedAnnotation<true>(this);\n  }\n\n\n  template<typename ContextType>\n  D3D11UserDefinedAnnotation<ContextType>::~D3D11UserDefinedAnnotation() {\n    if (!IsDeferred && m_annotationsEnabled)\n      RegisterUserDefinedAnnotation<false>(this);\n  }\n\n\n  template<typename ContextType>\n  ULONG STDMETHODCALLTYPE D3D11UserDefinedAnnotation<ContextType>::AddRef() {\n    return m_container->AddRef();\n  }\n\n  \n  template<typename ContextType>\n  ULONG STDMETHODCALLTYPE D3D11UserDefinedAnnotation<ContextType>::Release() {\n    return m_container->Release();\n  }\n\n  \n  template<typename ContextType>\n  HRESULT STDMETHODCALLTYPE D3D11UserDefinedAnnotation<ContextType>::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_container->QueryInterface(riid, ppvObject);\n  }\n  \n\n  template<typename ContextType>\n  INT STDMETHODCALLTYPE D3D11UserDefinedAnnotation<ContextType>::BeginEvent(\n          D3DCOLOR                Color,\n          LPCWSTR                 Name) {\n    if (!m_annotationsEnabled || !Name)\n      return -1;\n\n    D3D10DeviceLock lock = m_container->LockContext();\n\n    m_container->EmitCs([\n      cColor = Color,\n      cLabel = dxvk::str::fromws(Name)\n    ] (DxvkContext* ctx) {\n      ctx->beginDebugLabel(vk::makeLabel(cColor, cLabel.c_str()));\n    });\n\n    return m_eventDepth++;\n  }\n\n\n  template<typename ContextType>\n  INT STDMETHODCALLTYPE D3D11UserDefinedAnnotation<ContextType>::EndEvent() {\n    if (!m_annotationsEnabled)\n      return -1;\n\n    D3D10DeviceLock lock = m_container->LockContext();\n\n    if (!m_eventDepth)\n      return 0;\n\n    m_container->EmitCs([] (DxvkContext* ctx) {\n      ctx->endDebugLabel();\n    });\n\n    return --m_eventDepth;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11UserDefinedAnnotation<ContextType>::SetMarker(\n          D3DCOLOR                Color,\n          LPCWSTR                 Name) {\n    if (!m_annotationsEnabled || !Name)\n      return;\n\n    D3D10DeviceLock lock = m_container->LockContext();\n\n    m_container->EmitCs([\n      cColor = Color,\n      cLabel = dxvk::str::fromws(Name)\n    ] (DxvkContext* ctx) {\n      ctx->insertDebugLabel(vk::makeLabel(cColor, cLabel.c_str()));\n    });\n  }\n\n\n  template<typename ContextType>\n  BOOL STDMETHODCALLTYPE D3D11UserDefinedAnnotation<ContextType>::GetStatus() {\n    return m_annotationsEnabled;\n  }\n\n\n  template class D3D11UserDefinedAnnotation<D3D11DeferredContext>;\n  template class D3D11UserDefinedAnnotation<D3D11ImmediateContext>;\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_annotation.h",
    "content": "#pragma once\n\n#include <type_traits>\n\n#include \"d3d11_include.h\"\n\n#include \"../dxvk/dxvk_annotation.h\"\n#include \"../dxvk/dxvk_device.h\"\n\nnamespace dxvk {\n\n  class D3D11DeferredContext;\n  class D3D11ImmediateContext;\n\n  template<typename ContextType>\n  class D3D11UserDefinedAnnotation final : public IDXVKUserDefinedAnnotation {\n    constexpr static bool IsDeferred = std::is_same_v<ContextType, D3D11DeferredContext>;\n  public:\n\n    D3D11UserDefinedAnnotation(\n            ContextType*          container,\n      const Rc<DxvkDevice>&       dxvkDevice);\n\n    ~D3D11UserDefinedAnnotation();\n\n    D3D11UserDefinedAnnotation             (const D3D11UserDefinedAnnotation&) = delete;\n    D3D11UserDefinedAnnotation& operator = (const D3D11UserDefinedAnnotation&) = delete;\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n    \n    INT STDMETHODCALLTYPE BeginEvent(\n            D3DCOLOR                Color,\n            LPCWSTR                 Name);\n\n    INT STDMETHODCALLTYPE EndEvent();\n\n    void STDMETHODCALLTYPE SetMarker(\n            D3DCOLOR                Color,\n            LPCWSTR                 Name);\n\n    BOOL STDMETHODCALLTYPE GetStatus();\n\n  private:\n\n    ContextType*  m_container           = nullptr;\n    int32_t       m_eventDepth          = 0u;\n    bool          m_annotationsEnabled  = false;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_blend.cpp",
    "content": "#include \"d3d11_blend.h\"\n#include \"d3d11_device.h\"\n\nnamespace dxvk {\n  \n  D3D11BlendState::D3D11BlendState(\n          D3D11Device*        device,\n    const D3D11_BLEND_DESC1&  desc,\n          Container*          container)\n  : D3D11StateObject<ID3D11BlendState1, D3D11BlendState>(device, container),\n    m_desc(desc), m_d3d10(this), m_destructionNotifier(this) {\n    // If Independent Blend is disabled, we must ignore the\n    // blend modes for render target 1 to 7. In Vulkan, all\n    // blend modes need to be identical in that case.\n    for (uint32_t i = 0; i < m_blendModes.size(); i++) {\n      m_blendModes[i] = DecodeBlendMode(\n        desc.IndependentBlendEnable\n          ? desc.RenderTarget[i]\n          : desc.RenderTarget[0]);\n    }\n    \n    // Multisample state is part of the blend state in D3D11\n    m_msState.setSampleMask(0u); // Set during bind\n    m_msState.setAlphaToCoverage(desc.AlphaToCoverageEnable);\n    \n    // Vulkan only supports a global logic op for the blend\n    // state, which might be problematic in some cases.\n    if (desc.IndependentBlendEnable && desc.RenderTarget[0].LogicOpEnable)\n      Logger::warn(\"D3D11: Per-target logic ops not supported\");\n    \n    m_loState.setLogicOp(desc.RenderTarget[0].LogicOpEnable,\n      DecodeLogicOp(desc.RenderTarget[0].LogicOp));\n  }\n  \n  \n  D3D11BlendState::~D3D11BlendState() {\n    \n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11BlendState::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11BlendState)\n     || riid == __uuidof(ID3D11BlendState1)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10BlendState)\n     || riid == __uuidof(ID3D10BlendState1)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11BlendState), riid)) {\n      Logger::warn(\"D3D11BlendState::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11BlendState::GetDesc(D3D11_BLEND_DESC* pDesc) {\n    pDesc->AlphaToCoverageEnable  = m_desc.AlphaToCoverageEnable;\n    pDesc->IndependentBlendEnable = m_desc.IndependentBlendEnable;\n    \n    for (uint32_t i = 0; i < 8; i++) {\n      pDesc->RenderTarget[i].BlendEnable           = m_desc.RenderTarget[i].BlendEnable;\n      pDesc->RenderTarget[i].SrcBlend              = m_desc.RenderTarget[i].SrcBlend;\n      pDesc->RenderTarget[i].DestBlend             = m_desc.RenderTarget[i].DestBlend;\n      pDesc->RenderTarget[i].BlendOp               = m_desc.RenderTarget[i].BlendOp;\n      pDesc->RenderTarget[i].SrcBlendAlpha         = m_desc.RenderTarget[i].SrcBlendAlpha;\n      pDesc->RenderTarget[i].DestBlendAlpha        = m_desc.RenderTarget[i].DestBlendAlpha;\n      pDesc->RenderTarget[i].BlendOpAlpha          = m_desc.RenderTarget[i].BlendOpAlpha;\n      pDesc->RenderTarget[i].RenderTargetWriteMask = m_desc.RenderTarget[i].RenderTargetWriteMask;\n    }\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11BlendState::GetDesc1(D3D11_BLEND_DESC1* pDesc) {\n    *pDesc = m_desc;\n  }\n  \n  \n  D3D11_BLEND_DESC1 D3D11BlendState::PromoteDesc(const D3D11_BLEND_DESC* pSrcDesc) {\n    D3D11_BLEND_DESC1 dstDesc;\n    dstDesc.AlphaToCoverageEnable  = pSrcDesc->AlphaToCoverageEnable;\n    dstDesc.IndependentBlendEnable = pSrcDesc->IndependentBlendEnable;\n    \n    for (uint32_t i = 0; i < 8; i++) {\n      dstDesc.RenderTarget[i].BlendEnable           = pSrcDesc->RenderTarget[i].BlendEnable;\n      dstDesc.RenderTarget[i].LogicOpEnable         = FALSE;\n      dstDesc.RenderTarget[i].SrcBlend              = pSrcDesc->RenderTarget[i].SrcBlend;\n      dstDesc.RenderTarget[i].DestBlend             = pSrcDesc->RenderTarget[i].DestBlend;\n      dstDesc.RenderTarget[i].BlendOp               = pSrcDesc->RenderTarget[i].BlendOp;\n      dstDesc.RenderTarget[i].SrcBlendAlpha         = pSrcDesc->RenderTarget[i].SrcBlendAlpha;\n      dstDesc.RenderTarget[i].DestBlendAlpha        = pSrcDesc->RenderTarget[i].DestBlendAlpha;\n      dstDesc.RenderTarget[i].BlendOpAlpha          = pSrcDesc->RenderTarget[i].BlendOpAlpha;\n      dstDesc.RenderTarget[i].LogicOp               = D3D11_LOGIC_OP_NOOP;\n      dstDesc.RenderTarget[i].RenderTargetWriteMask = pSrcDesc->RenderTarget[i].RenderTargetWriteMask;\n    }\n    \n    return dstDesc;\n  }\n  \n  \n  HRESULT D3D11BlendState::NormalizeDesc(D3D11_BLEND_DESC1* pDesc) {\n    if (pDesc->AlphaToCoverageEnable)\n      pDesc->AlphaToCoverageEnable = TRUE;\n    \n    if (pDesc->IndependentBlendEnable)\n      pDesc->IndependentBlendEnable = TRUE;\n    \n    const uint32_t numRenderTargets = pDesc->IndependentBlendEnable ? 8 : 1;\n    \n    for (uint32_t i = 0; i < numRenderTargets; i++) {\n      D3D11_RENDER_TARGET_BLEND_DESC1* rt = &pDesc->RenderTarget[i];\n      \n      if (rt->BlendEnable) {\n        rt->BlendEnable = TRUE;\n        \n        if (rt->LogicOpEnable)\n          return E_INVALIDARG;\n        \n        if (!ValidateBlendOperations(\n         rt->SrcBlend, rt->SrcBlendAlpha,\n         rt->DestBlend, rt->DestBlendAlpha,\n         rt->BlendOp, rt->BlendOpAlpha))\n          return E_INVALIDARG;\n      } else {\n        rt->SrcBlend       = D3D11_BLEND_ONE;\n        rt->DestBlend      = D3D11_BLEND_ZERO;\n        rt->BlendOp        = D3D11_BLEND_OP_ADD;\n        rt->SrcBlendAlpha  = D3D11_BLEND_ONE;\n        rt->DestBlendAlpha = D3D11_BLEND_ZERO;\n        rt->BlendOpAlpha   = D3D11_BLEND_OP_ADD;\n      }\n      \n      if (rt->LogicOpEnable) {\n        rt->LogicOpEnable = TRUE;\n        \n        // Blending must be disabled\n        // if the logic op is enabled\n        if (rt->BlendEnable\n         || pDesc->IndependentBlendEnable\n         || !ValidateLogicOp(rt->LogicOp))\n          return E_INVALIDARG;\n      } else {\n        rt->LogicOp = D3D11_LOGIC_OP_NOOP;\n      }\n      \n      if (rt->RenderTargetWriteMask > D3D11_COLOR_WRITE_ENABLE_ALL)\n        return E_INVALIDARG;\n    }\n    \n    for (uint32_t i = numRenderTargets; i < 8; i++) {\n      // Render targets blend operations are the same\n      // across all render targets when blend is enabled\n      // on rendertarget[0] with independent blend disabled\n      pDesc->RenderTarget[i] = pDesc->RenderTarget[0];\n    }\n    \n    return S_OK;\n  }\n  \n  \n  DxvkBlendMode D3D11BlendState::DecodeBlendMode(\n    const D3D11_RENDER_TARGET_BLEND_DESC1& BlendDesc) {\n    DxvkBlendMode mode = { };\n    mode.setBlendEnable(BlendDesc.BlendEnable);\n    mode.setColorOp(DecodeBlendFactor(BlendDesc.SrcBlend, false),\n                    DecodeBlendFactor(BlendDesc.DestBlend, false),\n                    DecodeBlendOp(BlendDesc.BlendOp));\n    mode.setAlphaOp(DecodeBlendFactor(BlendDesc.SrcBlendAlpha, true),\n                    DecodeBlendFactor(BlendDesc.DestBlendAlpha, true),\n                    DecodeBlendOp(BlendDesc.BlendOpAlpha));\n    mode.setWriteMask(BlendDesc.RenderTargetWriteMask);\n\n    mode.normalize();\n    return mode;\n  }\n  \n  \n  VkBlendFactor D3D11BlendState::DecodeBlendFactor(D3D11_BLEND BlendFactor, bool IsAlpha) {\n    switch (BlendFactor) {\n      case D3D11_BLEND_ZERO:              return VK_BLEND_FACTOR_ZERO;\n      case D3D11_BLEND_ONE:               return VK_BLEND_FACTOR_ONE;\n      case D3D11_BLEND_SRC_COLOR:         return VK_BLEND_FACTOR_SRC_COLOR;\n      case D3D11_BLEND_INV_SRC_COLOR:     return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;\n      case D3D11_BLEND_SRC_ALPHA:         return VK_BLEND_FACTOR_SRC_ALPHA;\n      case D3D11_BLEND_INV_SRC_ALPHA:     return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n      case D3D11_BLEND_DEST_ALPHA:        return VK_BLEND_FACTOR_DST_ALPHA;\n      case D3D11_BLEND_INV_DEST_ALPHA:    return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;\n      case D3D11_BLEND_DEST_COLOR:        return VK_BLEND_FACTOR_DST_COLOR;\n      case D3D11_BLEND_INV_DEST_COLOR:    return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;\n      case D3D11_BLEND_SRC_ALPHA_SAT:     return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;\n      case D3D11_BLEND_BLEND_FACTOR:      return IsAlpha ? VK_BLEND_FACTOR_CONSTANT_ALPHA : VK_BLEND_FACTOR_CONSTANT_COLOR;\n      case D3D11_BLEND_INV_BLEND_FACTOR:  return IsAlpha ? VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA : VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;\n      case D3D11_BLEND_SRC1_COLOR:        return VK_BLEND_FACTOR_SRC1_COLOR;\n      case D3D11_BLEND_INV_SRC1_COLOR:    return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;\n      case D3D11_BLEND_SRC1_ALPHA:        return VK_BLEND_FACTOR_SRC1_ALPHA;\n      case D3D11_BLEND_INV_SRC1_ALPHA:    return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;\n      default:                            return VK_BLEND_FACTOR_ZERO;\n    }\n  }\n  \n  \n  VkBlendOp D3D11BlendState::DecodeBlendOp(D3D11_BLEND_OP BlendOp) {\n    switch (BlendOp) {\n      case D3D11_BLEND_OP_ADD:            return VK_BLEND_OP_ADD;\n      case D3D11_BLEND_OP_SUBTRACT:       return VK_BLEND_OP_SUBTRACT;\n      case D3D11_BLEND_OP_REV_SUBTRACT:   return VK_BLEND_OP_REVERSE_SUBTRACT;\n      case D3D11_BLEND_OP_MIN:            return VK_BLEND_OP_MIN;\n      case D3D11_BLEND_OP_MAX:            return VK_BLEND_OP_MAX;\n      default:                            return VK_BLEND_OP_ADD;\n    }\n  }\n  \n  \n  VkLogicOp D3D11BlendState::DecodeLogicOp(D3D11_LOGIC_OP LogicOp) {\n    switch (LogicOp) {\n      case D3D11_LOGIC_OP_CLEAR:          return VK_LOGIC_OP_CLEAR;\n      case D3D11_LOGIC_OP_SET:            return VK_LOGIC_OP_SET;\n      case D3D11_LOGIC_OP_COPY:           return VK_LOGIC_OP_COPY;\n      case D3D11_LOGIC_OP_COPY_INVERTED:  return VK_LOGIC_OP_COPY_INVERTED;\n      case D3D11_LOGIC_OP_NOOP:           return VK_LOGIC_OP_NO_OP;\n      case D3D11_LOGIC_OP_INVERT:         return VK_LOGIC_OP_INVERT;\n      case D3D11_LOGIC_OP_AND:            return VK_LOGIC_OP_AND;\n      case D3D11_LOGIC_OP_NAND:           return VK_LOGIC_OP_NAND;\n      case D3D11_LOGIC_OP_OR:             return VK_LOGIC_OP_OR;\n      case D3D11_LOGIC_OP_NOR:            return VK_LOGIC_OP_NOR;\n      case D3D11_LOGIC_OP_XOR:            return VK_LOGIC_OP_XOR;\n      case D3D11_LOGIC_OP_EQUIV:          return VK_LOGIC_OP_EQUIVALENT;\n      case D3D11_LOGIC_OP_AND_REVERSE:    return VK_LOGIC_OP_AND_REVERSE;\n      case D3D11_LOGIC_OP_AND_INVERTED:   return VK_LOGIC_OP_AND_INVERTED;\n      case D3D11_LOGIC_OP_OR_REVERSE:     return VK_LOGIC_OP_OR_REVERSE;\n      case D3D11_LOGIC_OP_OR_INVERTED:    return VK_LOGIC_OP_OR_INVERTED;\n      default:                            return VK_LOGIC_OP_NO_OP;\n    }\n  }\n  \n  \n  bool D3D11BlendState::ValidateBlendFactor(D3D11_BLEND Blend) {\n    return Blend >= D3D11_BLEND_ZERO\n        && Blend <= D3D11_BLEND_INV_SRC1_ALPHA;\n  }\n  \n  \n  bool D3D11BlendState::ValidateBlendFactorAlpha(D3D11_BLEND BlendAlpha) {\n    return BlendAlpha >= D3D11_BLEND_ZERO\n        && BlendAlpha <= D3D11_BLEND_INV_SRC1_ALPHA\n        && BlendAlpha != D3D11_BLEND_SRC_COLOR\n        && BlendAlpha != D3D11_BLEND_INV_SRC_COLOR\n        && BlendAlpha != D3D11_BLEND_DEST_COLOR\n        && BlendAlpha != D3D11_BLEND_INV_DEST_COLOR\n        && BlendAlpha != D3D11_BLEND_SRC1_COLOR\n        && BlendAlpha != D3D11_BLEND_INV_SRC1_COLOR;\n  }\n  \n  \n  bool D3D11BlendState::ValidateBlendOp(D3D11_BLEND_OP BlendOp) {\n    return BlendOp >= D3D11_BLEND_OP_ADD\n        && BlendOp <= D3D11_BLEND_OP_MAX;\n  }\n  \n  \n  bool D3D11BlendState::ValidateLogicOp(D3D11_LOGIC_OP LogicOp) {\n    return LogicOp >= D3D11_LOGIC_OP_CLEAR\n        && LogicOp <= D3D11_LOGIC_OP_OR_INVERTED;\n  }\n  \n  \n  bool D3D11BlendState::ValidateBlendOperations(\n          D3D11_BLEND     SrcBlend, \n          D3D11_BLEND     SrcBlendAlpha, \n          D3D11_BLEND     DestBlend, \n          D3D11_BLEND     DestBlendAlpha, \n          D3D11_BLEND_OP  BlendOp, \n          D3D11_BLEND_OP  BlendOpAlpha) {\n    return ValidateBlendOp(BlendOp)\n        && ValidateBlendOp(BlendOpAlpha)\n        && ValidateBlendFactor(SrcBlend)\n        && ValidateBlendFactor(DestBlend)\n        && ValidateBlendFactorAlpha(SrcBlendAlpha)\n        && ValidateBlendFactorAlpha(DestBlendAlpha);\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_blend.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../d3d10/d3d10_blend.h\"\n\n#include \"d3d11_device_child.h\"\n#include \"d3d11_util.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  \n  class D3D11BlendState : public D3D11StateObject<ID3D11BlendState1, D3D11BlendState> {\n    using Container = D3D11StateObjectSet<D3D11BlendState>;\n  public:\n    \n    using DescType = D3D11_BLEND_DESC1;\n    \n    D3D11BlendState(\n            D3D11Device*       device,\n      const D3D11_BLEND_DESC1& desc,\n            Container*         container);\n    ~D3D11BlendState();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_BLEND_DESC* pDesc) final;\n    \n    void STDMETHODCALLTYPE GetDesc1(\n            D3D11_BLEND_DESC1* pDesc) final;\n    \n    const D3D11_BLEND_DESC1& Desc() const {\n      return m_desc;\n    }\n\n    DxvkMultisampleState GetMsState(uint32_t SampleMask) const {\n      DxvkMultisampleState msState = m_msState;\n      msState.setSampleMask(SampleMask);\n      return msState;\n    }\n\n    DxvkLogicOpState GetLoState() const {\n      return m_loState;\n    }\n\n    std::array<DxvkBlendMode, 8> GetBlendState() const {\n      return m_blendModes;\n    }\n\n    D3D10BlendState* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n    \n    static D3D11_BLEND_DESC1 PromoteDesc(\n      const D3D11_BLEND_DESC*   pSrcDesc);\n    \n    static HRESULT NormalizeDesc(\n            D3D11_BLEND_DESC1*  pDesc);\n\n  private:\n    \n    D3D11_BLEND_DESC1             m_desc;\n    \n    std::array<DxvkBlendMode, 8>  m_blendModes = { };\n    DxvkMultisampleState          m_msState = { };\n    DxvkLogicOpState              m_loState = { };\n\n    D3D10BlendState               m_d3d10;\n\n    D3DDestructionNotifier        m_destructionNotifier;\n\n    static DxvkBlendMode DecodeBlendMode(\n      const D3D11_RENDER_TARGET_BLEND_DESC1& BlendDesc);\n    \n    static VkBlendFactor DecodeBlendFactor(\n            D3D11_BLEND BlendFactor,\n            bool        IsAlpha);\n    \n    static VkBlendOp DecodeBlendOp(\n            D3D11_BLEND_OP BlendOp);\n    \n    static VkLogicOp DecodeLogicOp(\n            D3D11_LOGIC_OP LogicOp);\n\n    static bool ValidateBlendFactor(\n            D3D11_BLEND    Blend);\n\n    static bool ValidateBlendFactorAlpha(\n            D3D11_BLEND    BlendAlpha);\n\n    static bool ValidateBlendOp(\n            D3D11_BLEND_OP BlendOp);\n\n    static bool ValidateLogicOp(\n            D3D11_LOGIC_OP LogicOp);\n\n    static bool ValidateBlendOperations(\n            D3D11_BLEND    SrcBlend,\n            D3D11_BLEND    SrcBlendAlpha,\n            D3D11_BLEND    SestBlend,\n            D3D11_BLEND    DestBlendAlpha,\n            D3D11_BLEND_OP BlendOp,\n            D3D11_BLEND_OP BlendOpAlpha);\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_buffer.cpp",
    "content": "#include \"d3d11_buffer.h\"\n#include \"d3d11_context.h\"\n#include \"d3d11_context_imm.h\"\n#include \"d3d11_device.h\"\n\nnamespace dxvk {\n  \n  D3D11Buffer::D3D11Buffer(\n          D3D11Device*                pDevice,\n    const D3D11_BUFFER_DESC*          pDesc,\n    const D3D11_ON_12_RESOURCE_INFO*  p11on12Info)\n  : D3D11DeviceChild<ID3D11Buffer>(pDevice),\n    m_desc        (*pDesc),\n    m_resource    (this, pDevice),\n    m_d3d10       (this),\n    m_destructionNotifier(this) {\n    DxvkBufferCreateInfo info;\n    info.flags  = 0;\n    info.size   = pDesc->ByteWidth;\n    info.usage  = VK_BUFFER_USAGE_TRANSFER_SRC_BIT\n                | VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;\n    info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    info.access = VK_ACCESS_TRANSFER_READ_BIT\n                | VK_ACCESS_TRANSFER_WRITE_BIT;\n    \n    if (pDesc->BindFlags & D3D11_BIND_VERTEX_BUFFER) {\n      info.usage  |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;\n      info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;\n      info.access |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;\n    }\n    \n    if (pDesc->BindFlags & D3D11_BIND_INDEX_BUFFER) {\n      info.usage  |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;\n      info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;\n      info.access |= VK_ACCESS_INDEX_READ_BIT;\n    }\n    \n    if (pDesc->BindFlags & D3D11_BIND_CONSTANT_BUFFER) {\n      info.usage  |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;\n      info.stages |= m_parent->GetEnabledShaderStages();\n      info.access |= VK_ACCESS_UNIFORM_READ_BIT;\n    }\n    \n    if (pDesc->BindFlags & D3D11_BIND_SHADER_RESOURCE) {\n      info.usage  |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT\n                  |  VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n      info.stages |= m_parent->GetEnabledShaderStages();\n      info.access |= VK_ACCESS_SHADER_READ_BIT;\n    }\n    \n    if (pDesc->BindFlags & D3D11_BIND_STREAM_OUTPUT) {\n      info.usage  |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;\n      info.stages |= VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT;\n      info.access |= VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT;\n    }\n    \n    if (pDesc->BindFlags & D3D11_BIND_UNORDERED_ACCESS) {\n      info.usage  |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT\n                  |  VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n      info.stages |= m_parent->GetEnabledShaderStages();\n      info.access |= VK_ACCESS_SHADER_READ_BIT\n                  |  VK_ACCESS_SHADER_WRITE_BIT;\n    }\n    \n    if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS) {\n      info.usage  |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;\n      info.stages |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;\n      info.access |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT;\n    }\n\n    if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILED) {\n      info.flags  |= VK_BUFFER_CREATE_SPARSE_BINDING_BIT\n                  |  VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT\n                  |  VK_BUFFER_CREATE_SPARSE_ALIASED_BIT;\n    }\n\n    // Set host read bit as necessary. We may internally read staging\n    // buffer contents even if the buffer is not marked for reading.\n    if (pDesc->CPUAccessFlags && pDesc->Usage != D3D11_USAGE_DYNAMIC) {\n      info.stages |= VK_PIPELINE_STAGE_HOST_BIT;\n      info.access |= VK_ACCESS_HOST_READ_BIT;\n\n      if (pDesc->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)\n        info.access |= VK_ACCESS_HOST_WRITE_BIT;\n    }\n\n    if (p11on12Info) {\n      m_11on12 = *p11on12Info;\n\n      DxvkBufferImportInfo importInfo;\n      importInfo.buffer = VkBuffer(m_11on12.VulkanHandle);\n      importInfo.offset = m_11on12.VulkanOffset;\n\n      if (m_desc.CPUAccessFlags)\n        m_11on12.Resource->Map(0, nullptr, &importInfo.mapPtr);\n\n      m_buffer = m_parent->GetDXVKDevice()->importBuffer(info, importInfo, GetMemoryFlags());\n      m_cookie = m_buffer->cookie();\n      m_mapPtr = m_buffer->mapPtr(0);\n      m_mapMode = DetermineMapMode(m_buffer->memFlags());\n    } else if (!(pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL)) {\n      VkMemoryPropertyFlags memoryFlags = GetMemoryFlags();\n      m_mapMode = DetermineMapMode(memoryFlags);\n\n      // Create the buffer and set the entire buffer slice as mapped,\n      // so that we only have to update it when invalidating the buffer\n      m_buffer = m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags);\n      m_cookie = m_buffer->cookie();\n      m_mapPtr = m_buffer->mapPtr(0);\n    } else {\n      m_sparseAllocator = m_parent->GetDXVKDevice()->createSparsePageAllocator();\n      m_sparseAllocator->setCapacity(info.size / SparseMemoryPageSize);\n\n      m_cookie = 0u;\n      m_mapPtr = nullptr;\n      m_mapMode = D3D11_COMMON_BUFFER_MAP_MODE_NONE;\n    }\n\n    // For Stream Output buffers we need a counter\n    if (pDesc->BindFlags & D3D11_BIND_STREAM_OUTPUT)\n      m_soCounter = CreateSoCounterBuffer();\n  }\n  \n  \n  D3D11Buffer::~D3D11Buffer() {\n    if (m_desc.CPUAccessFlags && m_11on12.Resource != nullptr)\n      m_11on12.Resource->Unmap(0, nullptr);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Buffer::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11Resource)\n     || riid == __uuidof(ID3D11Buffer)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10Resource)\n     || riid == __uuidof(ID3D10Buffer)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(IDXGIObject)\n     || riid == __uuidof(IDXGIDeviceSubObject)\n     || riid == __uuidof(IDXGIResource)\n     || riid == __uuidof(IDXGIResource1)) {\n       *ppvObject = ref(&m_resource);\n       return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11Buffer), riid)) {\n      Logger::warn(\"D3D11Buffer::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  UINT STDMETHODCALLTYPE D3D11Buffer::GetEvictionPriority() {\n    return DXGI_RESOURCE_PRIORITY_NORMAL;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Buffer::SetEvictionPriority(UINT EvictionPriority) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11Buffer::SetEvictionPriority: Stub\");\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Buffer::GetType(D3D11_RESOURCE_DIMENSION* pResourceDimension) {\n    *pResourceDimension = D3D11_RESOURCE_DIMENSION_BUFFER;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Buffer::GetDesc(D3D11_BUFFER_DESC* pDesc) {\n    *pDesc = m_desc;\n  }\n  \n  \n  bool D3D11Buffer::CheckViewCompatibility(\n          UINT                BindFlags,\n          DXGI_FORMAT         Format) const {\n    // Check whether the given bind flags are supported\n    if ((m_desc.BindFlags & BindFlags) != BindFlags)\n      return false;\n\n    // Structured buffer views use no format\n    if (Format == DXGI_FORMAT_UNKNOWN)\n      return (m_desc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) != 0;\n\n    // Check whether the given combination of buffer view\n    // type and view format is supported by the device\n    DXGI_VK_FORMAT_INFO viewFormat = m_parent->LookupFormat(Format, DXGI_VK_FORMAT_MODE_ANY);\n    VkFormatFeatureFlags2 features = GetBufferFormatFeatures(BindFlags);\n\n    return CheckFormatFeatureSupport(viewFormat.Format, features);\n  }\n\n\n  void D3D11Buffer::SetDebugName(const char* pName) {\n    if (m_buffer) {\n      m_parent->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [\n        cBuffer = m_buffer,\n        cName   = std::string(pName ? pName : \"\")\n      ] (DxvkContext* ctx) {\n        ctx->setDebugName(cBuffer, cName.c_str());\n      });\n    }\n  }\n\n\n  HRESULT D3D11Buffer::NormalizeBufferProperties(D3D11_BUFFER_DESC* pDesc) {\n    // Zero-sized buffers are illegal\n    if (!pDesc->ByteWidth && !(pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL))\n      return E_INVALIDARG;\n\n    // Constant buffer size must be a multiple of 16\n    if ((pDesc->BindFlags & D3D11_BIND_CONSTANT_BUFFER)\n     && (pDesc->ByteWidth & 0xF))\n      return E_INVALIDARG;\n\n    // Basic validation for structured buffers\n    if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED)\n     && ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS)\n      || (pDesc->StructureByteStride == 0)\n      || (pDesc->StructureByteStride & 0x3)))\n      return E_INVALIDARG;\n    \n    // Basic validation for raw buffers\n    if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS)\n     && (!(pDesc->BindFlags & (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS))))\n      return E_INVALIDARG;\n\n    // Mip generation obviously doesn't work for buffers\n    if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS)\n      return E_INVALIDARG;\n\n    // Basic validation for tiled buffers\n    if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILED) {\n      if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL)\n       || (pDesc->Usage != D3D11_USAGE_DEFAULT)\n       || (pDesc->CPUAccessFlags))\n        return E_INVALIDARG;\n    }\n\n    // Basic validation for tile pools\n    if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL) {\n      if ((pDesc->MiscFlags & ~D3D11_RESOURCE_MISC_TILE_POOL)\n       || (pDesc->ByteWidth % SparseMemoryPageSize)\n       || (pDesc->Usage != D3D11_USAGE_DEFAULT)\n       || (pDesc->BindFlags)\n       || (pDesc->CPUAccessFlags))\n        return E_INVALIDARG;\n    }\n\n    if (!(pDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED))\n      pDesc->StructureByteStride = 0;\n    \n    return S_OK;\n  }\n\n\n  HRESULT D3D11Buffer::GetDescFromD3D12(\n          ID3D12Resource*         pResource,\n    const D3D11_RESOURCE_FLAGS*   pResourceFlags,\n          D3D11_BUFFER_DESC*      pBufferDesc) {\n    D3D12_RESOURCE_DESC desc12 = pResource->GetDesc();\n\n    pBufferDesc->ByteWidth = desc12.Width;\n    pBufferDesc->Usage = D3D11_USAGE_DEFAULT;\n    pBufferDesc->BindFlags = D3D11_BIND_SHADER_RESOURCE;\n    pBufferDesc->MiscFlags = 0;\n    pBufferDesc->CPUAccessFlags = 0;\n    pBufferDesc->StructureByteStride = 0;\n\n    if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)\n      pBufferDesc->BindFlags |= D3D11_BIND_RENDER_TARGET;\n\n    if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)\n      pBufferDesc->BindFlags |= D3D11_BIND_UNORDERED_ACCESS;\n\n    if (pResourceFlags) {\n      pBufferDesc->BindFlags = pResourceFlags->BindFlags;\n      pBufferDesc->MiscFlags |= pResourceFlags->MiscFlags;\n      pBufferDesc->CPUAccessFlags = pResourceFlags->CPUAccessFlags;\n      pBufferDesc->StructureByteStride = pResourceFlags->StructureByteStride;\n    }\n\n    return S_OK;\n  }\n\n\n  BOOL D3D11Buffer::CheckFormatFeatureSupport(\n          VkFormat              Format,\n          VkFormatFeatureFlags2 Features) const {\n    DxvkFormatFeatures support = m_parent->GetDXVKDevice()->getFormatFeatures(Format);\n    return (support.buffer & Features) == Features;\n  }\n\n\n  VkMemoryPropertyFlags D3D11Buffer::GetMemoryFlags() const {\n    VkMemoryPropertyFlags memoryFlags = 0;\n\n    if (m_desc.MiscFlags & (D3D11_RESOURCE_MISC_TILE_POOL | D3D11_RESOURCE_MISC_TILED))\n      return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n    switch (m_desc.Usage) {\n      case D3D11_USAGE_IMMUTABLE:\n        memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n        break;\n\n      case D3D11_USAGE_DEFAULT:\n        memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n        if ((m_desc.BindFlags & D3D11_BIND_CONSTANT_BUFFER) || m_desc.CPUAccessFlags) {\n          memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n                      |  VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;\n        }\n\n        if (m_desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) {\n          memoryFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;\n          memoryFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n        }\n        break;\n      \n      case D3D11_USAGE_DYNAMIC:\n        memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n                    |  VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;\n\n        if (m_desc.BindFlags)\n          memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n        break;\n      \n      case D3D11_USAGE_STAGING:\n        memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n                    |  VK_MEMORY_PROPERTY_HOST_COHERENT_BIT\n                    |  VK_MEMORY_PROPERTY_HOST_CACHED_BIT;\n        break;\n    }\n    \n    bool useCached = (m_parent->GetOptions()->cachedDynamicResources == ~0u)\n                  || (m_parent->GetOptions()->cachedDynamicResources & m_desc.BindFlags);\n\n    if ((memoryFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && useCached) {\n      memoryFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n      memoryFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT\n                  |  VK_MEMORY_PROPERTY_HOST_CACHED_BIT;\n    }\n\n    return memoryFlags;\n  }\n\n\n  Rc<DxvkBuffer> D3D11Buffer::CreateSoCounterBuffer() {\n    Rc<DxvkDevice> device = m_parent->GetDXVKDevice();\n\n    DxvkBufferCreateInfo info;\n    info.size   = sizeof(D3D11SOCounter);\n    info.usage  = VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                | VK_BUFFER_USAGE_TRANSFER_SRC_BIT\n                | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT\n                | VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT;\n    info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT\n                | VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT;\n    info.access = VK_ACCESS_TRANSFER_READ_BIT\n                | VK_ACCESS_TRANSFER_WRITE_BIT\n                | VK_ACCESS_INDIRECT_COMMAND_READ_BIT\n                | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT\n                | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;\n    info.debugName = \"SO counter\";\n\n    return device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n  }\n\n\n  D3D11_COMMON_BUFFER_MAP_MODE D3D11Buffer::DetermineMapMode(VkMemoryPropertyFlags MemFlags) {\n    return (MemFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)\n      ? D3D11_COMMON_BUFFER_MAP_MODE_DIRECT\n      : D3D11_COMMON_BUFFER_MAP_MODE_NONE;\n  }\n  \n\n  D3D11Buffer* GetCommonBuffer(ID3D11Resource* pResource) {\n    D3D11_RESOURCE_DIMENSION dimension = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&dimension);\n\n    return dimension == D3D11_RESOURCE_DIMENSION_BUFFER\n      ? static_cast<D3D11Buffer*>(pResource)\n      : nullptr;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_buffer.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_cs.h\"\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../d3d10/d3d10_buffer.h\"\n\n#include \"d3d11_device_child.h\"\n#include \"d3d11_interfaces.h\"\n#include \"d3d11_on_12.h\"\n#include \"d3d11_resource.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n\n\n  /**\n   * \\brief Buffer map mode\n   */\n  enum D3D11_COMMON_BUFFER_MAP_MODE {\n    D3D11_COMMON_BUFFER_MAP_MODE_NONE,\n    D3D11_COMMON_BUFFER_MAP_MODE_DIRECT,\n  };\n\n\n  /**\n   * \\brief Stream output buffer offset\n   *\n   * A byte offset into the buffer that\n   * stores the byte offset where new\n   * data will be written to.\n   */\n  struct D3D11SOCounter {\n    uint32_t byteOffset;\n  };\n  \n  \n  class D3D11Buffer : public D3D11DeviceChild<ID3D11Buffer> {\n    static constexpr VkDeviceSize BufferSliceAlignment = 64;\n  public:\n    \n    D3D11Buffer(\n            D3D11Device*                pDevice,\n      const D3D11_BUFFER_DESC*          pDesc,\n      const D3D11_ON_12_RESOURCE_INFO*  p11on12Info);\n\n    ~D3D11Buffer();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetType(\n            D3D11_RESOURCE_DIMENSION *pResourceDimension) final;\n    \n    UINT STDMETHODCALLTYPE GetEvictionPriority() final;\n    \n    void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final;\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_BUFFER_DESC *pDesc) final;\n    \n    void STDMETHODCALLTYPE SetDebugName(const char* pName) final;\n\n    bool CheckViewCompatibility(\n            UINT                BindFlags,\n            DXGI_FORMAT         Format) const;\n\n    const D3D11_BUFFER_DESC* Desc() const {\n      return &m_desc;\n    }\n\n    BOOL IsTilePool() const {\n      return bool(m_desc.MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL);\n    }\n\n    D3D11_COMMON_BUFFER_MAP_MODE GetMapMode() const {\n      return m_mapMode;\n    }\n\n    uint64_t GetCookie() const {\n      return m_cookie;\n    }\n\n    Rc<DxvkBuffer> GetBuffer() const {\n      return m_buffer;\n    }\n\n    Rc<DxvkSparsePageAllocator> GetSparseAllocator() const {\n      return m_sparseAllocator;\n    }\n    \n    DxvkBufferSlice GetBufferSlice() const {\n      return DxvkBufferSlice(m_buffer, 0, m_desc.ByteWidth);\n    }\n    \n    DxvkBufferSlice GetBufferSlice(VkDeviceSize offset) const {\n      VkDeviceSize size = m_desc.ByteWidth;\n      offset = std::min(offset, size);\n      return DxvkBufferSlice(m_buffer, offset, size - offset);\n    }\n    \n    DxvkBufferSlice GetBufferSlice(VkDeviceSize offset, VkDeviceSize length) const {\n      VkDeviceSize size = m_desc.ByteWidth;\n      offset = std::min(offset, size);\n      return DxvkBufferSlice(m_buffer, offset, std::min(length, size - offset));\n    }\n\n    VkDeviceSize GetRemainingSize(VkDeviceSize offset) const {\n      VkDeviceSize size = m_desc.ByteWidth;\n      offset = std::min(offset, size);\n      return size - offset;\n    }\n\n    DxvkBufferSlice GetSOCounter() {\n      return m_soCounter != nullptr\n        ? DxvkBufferSlice(m_soCounter)\n        : DxvkBufferSlice();\n    }\n    \n    Rc<DxvkResourceAllocation> AllocSlice(DxvkLocalAllocationCache* cache) {\n      return m_buffer->allocateStorage(cache);\n    }\n    \n    Rc<DxvkResourceAllocation> DiscardSlice(DxvkLocalAllocationCache* cache) {\n      auto allocation = m_buffer->allocateStorage(cache);\n      m_mapPtr = allocation->mapPtr();\n      return allocation;\n    }\n\n    void* GetMapPtr() const {\n      return m_mapPtr;\n    }\n\n    D3D10Buffer* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n\n    bool HasSequenceNumber() const {\n      return m_mapMode != D3D11_COMMON_BUFFER_MAP_MODE_NONE\n          && !(m_desc.MiscFlags & D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS)\n          && !(m_desc.BindFlags);\n    }\n\n    void TrackSequenceNumber(uint64_t Seq) {\n      m_seq = Seq;\n    }\n\n    uint64_t GetSequenceNumber() {\n      return HasSequenceNumber() ? m_seq\n        : DxvkCsThread::SynchronizeAll;\n    }\n\n    /**\n     * \\brief Retrieves D3D11on12 resource info\n     * \\returns 11on12 resource info\n     */\n    D3D11_ON_12_RESOURCE_INFO Get11on12Info() const {\n      return m_11on12;\n    }\n\n    /**\n     * \\brief Normalizes buffer description\n     * \n     * \\param [in] pDesc Buffer description\n     * \\returns \\c S_OK if the parameters are valid\n     */\n    static HRESULT NormalizeBufferProperties(\n            D3D11_BUFFER_DESC*      pDesc);\n\n    /**\n     * \\brief Initializes D3D11 buffer description from D3D12\n     *\n     * \\param [in] pResource D3D12 resource\n     * \\param [in] pResourceFlags D3D11 flag overrides\n     * \\param [out] pBufferDesc D3D11 buffer description\n     * \\returns \\c S_OK if the parameters are valid\n     */\n    static HRESULT GetDescFromD3D12(\n            ID3D12Resource*         pResource,\n      const D3D11_RESOURCE_FLAGS*   pResourceFlags,\n            D3D11_BUFFER_DESC*      pBufferDesc);\n\n  private:\n    \n    D3D11_BUFFER_DESC             m_desc;\n    D3D11_ON_12_RESOURCE_INFO     m_11on12;\n    D3D11_COMMON_BUFFER_MAP_MODE  m_mapMode;\n    \n    Rc<DxvkBuffer>                m_buffer;\n    uint64_t                      m_cookie = 0u;\n\n    Rc<DxvkBuffer>                m_soCounter;\n    Rc<DxvkSparsePageAllocator>   m_sparseAllocator;\n    uint64_t                      m_seq = 0ull;\n\n    void*                         m_mapPtr = nullptr;\n\n    D3D11DXGIResource             m_resource;\n    D3D10Buffer                   m_d3d10;\n\n    D3DDestructionNotifier        m_destructionNotifier;\n\n    BOOL CheckFormatFeatureSupport(\n            VkFormat              Format,\n            VkFormatFeatureFlags2 Features) const;\n    \n    VkMemoryPropertyFlags GetMemoryFlags() const;\n\n    Rc<DxvkBuffer> CreateSoCounterBuffer();\n\n    static D3D11_COMMON_BUFFER_MAP_MODE DetermineMapMode(\n            VkMemoryPropertyFlags MemFlags);\n\n  };\n\n\n  /**\n   * \\brief Retrieves buffer from resource pointer\n   * \n   * \\param [in] pResource The resource to query\n   * \\returns Pointer to buffer, or \\c nullptr\n   */\n  D3D11Buffer* GetCommonBuffer(\n          ID3D11Resource*       pResource);\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_class_linkage.cpp",
    "content": "#include <dxbc/dxbc_interface.h>\n\n#include \"d3d11_class_linkage.h\"\n#include \"d3d11_device.h\"\n\nnamespace dxvk {\n\n  D3D11InterfaceInfo::D3D11InterfaceInfo() {\n\n  }\n\n\n  D3D11InterfaceInfo::~D3D11InterfaceInfo() {\n\n  }\n\n\n  D3D11InstanceData D3D11InterfaceInfo::EncodeInstanceData(\n          uint32_t                    SlotId,\n          D3D11ClassInstance*         pInstance) const {\n    if (pInstance && SlotId < m_interfaceSlots.size()) {\n      uint32_t typeId = m_typeNames.size();\n\n      for (uint32_t i = 0u; i < typeId; i++) {\n        if (pInstance->MatchesTypeName(m_typeNames[i].c_str()))\n          typeId = i;\n      }\n\n      const auto& slot = m_interfaceSlots.at(SlotId);\n\n      for (const auto& e : slot.types) {\n        if (e.typeId == typeId) {\n          return pInstance->EncodeInstanceData(e.functionTable);\n        }\n      }\n\n      return pInstance->EncodeInstanceData(\n        dxbc_spv::dxbc::InstanceData::DefaultFunctionTable);\n    }\n\n    dxbc_spv::dxbc::InstanceData defaultData = { };\n\n    D3D11InstanceData result = {};\n    result.data = defaultData.data;\n    result.functionTable = defaultData.functionTable;\n    return result;\n  }\n\n\n  void D3D11InterfaceInfo::AddType(\n          uint32_t                    TypeId,\n    const char*                       pTypeName) {\n    if (TypeId >= m_typeNames.size())\n      m_typeNames.resize(TypeId + 1u);\n\n    m_typeNames.at(TypeId) = pTypeName;\n  }\n\n\n  void D3D11InterfaceInfo::AddSlotInfo(\n          uint32_t                    FirstSlot,\n          uint32_t                    SlotCount,\n          uint32_t                    TypeId,\n          uint32_t                    FunctionTable) {\n    uint32_t minSize = FirstSlot + SlotCount;\n\n    if (m_interfaceSlots.size() < minSize)\n      m_interfaceSlots.resize(minSize);\n\n    for (uint32_t i = 0u; i < SlotCount; i++) {\n      auto& e = m_interfaceSlots.at(FirstSlot + i).types.emplace_back();\n      e.typeId = TypeId;\n      e.functionTable = FunctionTable;\n    }\n  }\n\n\n  const char* D3D11InterfaceInfo::GetTypeName(\n          uint32_t                    TypeId) {\n    if (TypeId < m_typeNames.size())\n      return m_typeNames.at(TypeId).c_str();\n\n    return nullptr;\n  }\n\n\n  D3D11ClassInstance::D3D11ClassInstance(\n          D3D11Device*                pDevice,\n          D3D11ClassLinkage*          pLinkage,\n    const D3D11_CLASS_INSTANCE_DESC*  pDesc,\n    const char*                       pInstanceName,\n    const char*                       pTypeName,\n    const D3D11ClassTypeInfo*         pTypeInfo)\n  : D3D11DeviceObject<ID3D11ClassInstance>(pDevice),\n    m_linkage             (pLinkage),\n    m_destructionNotifier (this),\n    m_desc                (*pDesc) {\n    if (pInstanceName)\n      m_instanceName = pInstanceName;\n\n    if (pTypeName)\n      m_typeName = pTypeName;\n\n    if (pTypeInfo)\n      m_type = *pTypeInfo;\n  }\n\n\n  D3D11ClassInstance::~D3D11ClassInstance() {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11ClassInstance::AddRef() {\n    auto newCount = ++m_refCount;\n\n    if (newCount == 1u)\n      AddRefPrivate();\n\n    return newCount;\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11ClassInstance::Release() {\n    auto newCount = --m_refCount;\n\n    if (newCount == 0u)\n      ReleasePrivate();\n\n    return newCount;\n  }\n\n\n  void D3D11ClassInstance::AddRefPrivate() {\n    if (!(m_refPrivate++))\n      m_linkage->AddRefPrivate();\n  }\n\n\n  void D3D11ClassInstance::ReleasePrivate() {\n    if (!(--m_refPrivate))\n      m_linkage->ReleasePrivate();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11ClassInstance::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11ClassInstance)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11ClassInstance), riid)) {\n      Logger::warn(\"D3D11ClassLinkage::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11ClassInstance::GetClassLinkage(\n          ID3D11ClassLinkage**    ppLinkage) {\n    *ppLinkage = ref(m_linkage);\n  }\n\n\n  void STDMETHODCALLTYPE D3D11ClassInstance::GetDesc(\n          D3D11_CLASS_INSTANCE_DESC* pDesc) {\n    *pDesc = m_desc;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11ClassInstance::GetInstanceName(\n          LPSTR                   pInstanceName,\n          SIZE_T*                 pBufferLength) {\n    ReturnName(pInstanceName, pBufferLength,\n      m_desc.Created ? std::string() : m_instanceName);\n  }\n\n\n  void STDMETHODCALLTYPE D3D11ClassInstance::GetTypeName(\n          LPSTR                   pTypeName,\n          SIZE_T*                 pBufferLength) {\n    ReturnName(pTypeName, pBufferLength,\n      m_desc.Created ? m_typeName : std::string());\n  }\n\n\n  bool D3D11ClassInstance::MatchesTypeName(\n    const char*                   pName) const {\n    return !std::strncmp(pName, m_typeName.c_str(), m_typeName.size());\n  }\n\n\n  D3D11InstanceData D3D11ClassInstance::EncodeInstanceData(uint32_t Ft) const {\n    dxbc_spv::dxbc::InstanceData instanceInfo(m_desc.ConstantBuffer,\n      m_desc.BaseConstantBufferOffset + m_desc.InstanceIndex * m_type.CbvStride,\n      m_desc.BaseTexture + m_desc.InstanceIndex * m_type.SrvCount,\n      m_desc.BaseSampler + m_desc.InstanceIndex * m_type.SamplerCount,\n      Ft);\n\n    D3D11InstanceData result = { };\n    result.data = instanceInfo.data;\n    result.functionTable = instanceInfo.functionTable;\n    return result;\n  }\n\n\n  void D3D11ClassInstance::ReturnName(\n          LPSTR                   pName,\n          SIZE_T*                 pLength,\n    const std::string&            SrcName) {\n    if (pName)\n      str::strlcpy(pName, SrcName.c_str(), *pLength);\n\n    // Include null-terminator\n    *pLength = SrcName.size() + 1u;\n  }\n\n\n\n\n  D3D11ClassLinkage::D3D11ClassLinkage(\n          D3D11Device*                pDevice)\n  : D3D11DeviceChild<ID3D11ClassLinkage>(pDevice),\n    m_destructionNotifier(this) {\n    \n  }\n  \n  \n  D3D11ClassLinkage::~D3D11ClassLinkage() {\n    \n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11ClassLinkage::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11ClassLinkage)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11ClassLinkage), riid)) {\n      Logger::warn(\"D3D11ClassLinkage::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11ClassLinkage::CreateClassInstance(\n          LPCSTR              pClassTypeName,\n          UINT                ConstantBufferOffset,\n          UINT                ConstantVectorOffset,\n          UINT                TextureOffset,\n          UINT                SamplerOffset,\n          ID3D11ClassInstance **ppInstance) {\n    InitReturnPtr(ppInstance);\n\n    if (!ppInstance)\n      return S_FALSE;\n\n    // There is no deduplication or persistent storage for these going on\n    D3D11_CLASS_INSTANCE_DESC desc = { };\n    desc.TypeId = AddType(pClassTypeName, 0u, 0u, 0u).typeId;\n    desc.ConstantBuffer = ConstantBufferOffset;\n    desc.BaseConstantBufferOffset = ConstantVectorOffset;\n    desc.BaseTexture = TextureOffset;\n    desc.BaseSampler = SamplerOffset;\n    desc.Created = true;\n\n    *ppInstance = ref(new D3D11ClassInstance(m_parent,\n      this, &desc, nullptr, pClassTypeName, nullptr));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11ClassLinkage::GetClassInstance(\n          LPCSTR              pClassInstanceName,\n          UINT                InstanceIndex,\n          ID3D11ClassInstance **ppInstance) {\n    InitReturnPtr(ppInstance);\n\n    std::lock_guard lock(m_mutex);\n\n    // It is possible to \"get\" an instance before any shader containing\n    // an instance with the name is created. The resulting instance will\n    // have a default set of parameters.\n    D3D11ClassTypeInfo typeInfo = { };\n\n    InstanceInfo info = { };\n    info.desc.InstanceId = m_instances.size();\n    info.desc.InstanceIndex = InstanceIndex;\n\n    auto result = m_instances.emplace(std::piecewise_construct,\n      std::tuple(pClassInstanceName),\n      std::tuple(info));\n\n    if (!result.second) {\n      info = result.first->second;\n\n      typeInfo.CbvStride = info.typeInfo.cbvStride;\n      typeInfo.SrvCount = info.typeInfo.srvCount;\n      typeInfo.SamplerCount = info.typeInfo.samplerCount;\n    }\n\n    // Once again, no persistent storage here at all, the runtime\n    // will return different objects even if the parameters match.\n    *ppInstance = ref(new D3D11ClassInstance(m_parent,\n      this, &info.desc, pClassInstanceName, info.typeName.c_str(), &typeInfo));\n    return S_OK;\n  }\n\n\n  D3D11ClassLinkage::TypeInfo D3D11ClassLinkage::AddType(\n          LPCSTR              pTypeName,\n          UINT                CbvStride,\n          UINT                SrvCount,\n          UINT                SamplerCount) {\n    std::lock_guard lock(m_mutex);\n\n    TypeInfo result = { };\n    result.typeId = m_types.size();\n    result.cbvStride = CbvStride;\n    result.srvCount = SrvCount;\n    result.samplerCount = SamplerCount;\n\n    auto entry = m_types.emplace(std::piecewise_construct,\n      std::tuple(pTypeName),\n      std::tuple(result));\n\n    // Return existing type info if any\n    return entry.first->second;\n  }\n\n\n  void D3D11ClassLinkage::AddInstance(\n    const D3D11_CLASS_INSTANCE_DESC* pDesc,\n          LPCSTR              pTypeName,\n          LPCSTR              pInstanceName) {\n    std::lock_guard lock(m_mutex);\n\n    InstanceInfo info = { };\n    info.typeName = pTypeName;\n    info.desc = *pDesc;\n    info.desc.InstanceId = m_instances.size();\n\n    auto type = m_types.find(pTypeName);\n\n    if (type != m_types.end()) {\n      info.typeInfo = type->second;\n      info.desc.TypeId = info.typeInfo.typeId;\n    }\n\n    m_instances.emplace(std::piecewise_construct,\n      std::tuple(pInstanceName),\n      std::tuple(info));\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_class_linkage.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <unordered_map>\n\n#include \"../dxvk/dxvk_hash.h\"\n\n#include \"../util/thread.h\"\n#include \"../util/util_small_vector.h\"\n\n#include \"d3d11_device_child.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n  class D3D11ClassLinkage;\n  class D3D11ClassInstance;\n\n  struct D3D11ClassTypeInfo {\n    uint32_t CbvStride = 0u;\n    uint32_t SrvCount = 0u;\n    uint32_t SamplerCount = 0u;\n  };\n\n\n  struct D3D11InterfaceType {\n    uint32_t typeId = 0u;\n    uint32_t functionTable = 0u;\n  };\n\n\n  struct D3D11InterfaceSlot {\n    small_vector<D3D11InterfaceType, 16> types;\n  };\n\n\n  struct D3D11InstanceData {\n    uint32_t data = 0u;\n    uint32_t functionTable = 0u;\n  };\n\n\n  class D3D11InterfaceInfo {\n\n  public:\n\n    D3D11InterfaceInfo();\n\n    ~D3D11InterfaceInfo();\n\n    D3D11InstanceData EncodeInstanceData(\n            uint32_t                    SlotId,\n            D3D11ClassInstance*         pInstance) const;\n\n    void AddType(\n            uint32_t                    TypeId,\n      const char*                       pTypeName);\n\n    void AddSlotInfo(\n            uint32_t                    FirstSlot,\n            uint32_t                    SlotCount,\n            uint32_t                    TypeId,\n            uint32_t                    FunctionTable);\n\n    const char* GetTypeName(\n            uint32_t                    TypeId);\n\n  private:\n\n    std::vector<std::string>        m_typeNames;\n    std::vector<D3D11InterfaceSlot> m_interfaceSlots;\n\n  };\n\n  \n  class D3D11ClassInstance : public D3D11DeviceObject<ID3D11ClassInstance> {\n\n  public:\n\n    D3D11ClassInstance(\n            D3D11Device*                pDevice,\n            D3D11ClassLinkage*          pLinkage,\n      const D3D11_CLASS_INSTANCE_DESC*  pDesc,\n      const char*                       pInstanceName,\n      const char*                       pTypeName,\n      const D3D11ClassTypeInfo*         pTypeInfo);\n\n    ~D3D11ClassInstance();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    void STDMETHODCALLTYPE GetClassLinkage(\n            ID3D11ClassLinkage**    ppLinkage);\n\n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_CLASS_INSTANCE_DESC* pDesc);\n\n    void STDMETHODCALLTYPE GetInstanceName(\n            LPSTR                   pInstanceName,\n            SIZE_T*                 pBufferLength);\n\n    void STDMETHODCALLTYPE GetTypeName(\n            LPSTR                   pTypeName,\n            SIZE_T*                 pBufferLength);\n\n    bool MatchesTypeName(\n      const char*                   pName) const;\n\n    void AddRefPrivate();\n\n    void ReleasePrivate();\n\n    D3D11InstanceData EncodeInstanceData(uint32_t Ft) const;\n\n  private:\n\n    std::atomic<uint32_t> m_refCount = { 0u };\n    std::atomic<uint32_t> m_refPrivate = { 0u };\n\n    D3D11ClassLinkage* m_linkage = nullptr;\n    D3DDestructionNotifier m_destructionNotifier;\n\n    D3D11_CLASS_INSTANCE_DESC m_desc = { };\n    D3D11ClassTypeInfo m_type = { };\n\n    std::string m_instanceName;\n    std::string m_typeName;\n\n    void ReturnName(\n            LPSTR                   pName,\n            SIZE_T*                 pLength,\n      const std::string&            SrcName);\n\n  };\n\n\n  class D3D11ClassLinkage : public D3D11DeviceChild<ID3D11ClassLinkage> {\n    struct TypeInfo {\n      uint32_t typeId = 0u;\n      uint32_t cbvStride = 0u;\n      uint32_t srvCount = 0u;\n      uint32_t samplerCount = 0u;\n    };\n\n    struct InstanceInfo {\n      std::string typeName;\n      TypeInfo typeInfo = { };\n      D3D11_CLASS_INSTANCE_DESC desc = { };\n    };\n  public:\n    \n    D3D11ClassLinkage(\n            D3D11Device*                pDevice);\n    \n    ~D3D11ClassLinkage();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    HRESULT STDMETHODCALLTYPE CreateClassInstance(\n            LPCSTR              pTypeName,\n            UINT                ConstantBufferOffset,\n            UINT                ConstantVectorOffset,\n            UINT                TextureOffset,\n            UINT                SamplerOffset,\n            ID3D11ClassInstance **ppInstance);\n    \n    HRESULT STDMETHODCALLTYPE GetClassInstance(\n            LPCSTR              pInstanceName,\n            UINT                InstanceIndex,\n            ID3D11ClassInstance **ppInstance);\n\n    TypeInfo AddType(\n            LPCSTR              pTypeName,\n            UINT                CbvStride,\n            UINT                SrvCount,\n            UINT                SamplerCount);\n\n    void AddInstance(\n      const D3D11_CLASS_INSTANCE_DESC* pDesc,\n            LPCSTR              pTypeName,\n            LPCSTR              pInstanceName);\n\n  private:\n\n    D3DDestructionNotifier m_destructionNotifier;\n\n    dxvk::mutex m_mutex;\n\n    std::unordered_map<std::string, InstanceInfo> m_instances;\n    std::unordered_map<std::string, TypeInfo> m_types;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_cmd.h",
    "content": "#pragma once\n\n#include \"d3d11_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief D3D11 command type\n   * \n   * Used to identify the type of command\n   * data most recently added to a CS chunk.\n   */\n  enum class D3D11CmdType : uint32_t {\n    None,\n    DrawIndirect,\n    DrawIndirectIndexed,\n    Draw,\n    DrawIndexed,\n  };\n\n\n  /**\n   * \\brief Indirect draw command data\n   * \n   * Stores the offset into the draw buffer for\n   * the first draw, as well as the number of\n   * draws to execute.\n   */\n  struct D3D11CmdDrawIndirectData {\n    uint32_t            offset;\n    uint32_t            count;\n    uint32_t            stride;\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_cmdlist.cpp",
    "content": "#include \"d3d11_cmdlist.h\"\n#include \"d3d11_device.h\"\n#include \"d3d11_buffer.h\"\n#include \"d3d11_texture.h\"\n\nnamespace dxvk {\n    \n  D3D11CommandList::D3D11CommandList(\n          D3D11Device*  pDevice,\n          UINT          ContextFlags)\n  : D3D11DeviceChild<ID3D11CommandList>(pDevice),\n    m_contextFlags(ContextFlags), m_destructionNotifier(this) { }\n  \n  \n  D3D11CommandList::~D3D11CommandList() {\n    \n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11CommandList::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11CommandList)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11CommandList), riid)) {\n      Logger::warn(\"D3D11CommandList::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  UINT STDMETHODCALLTYPE D3D11CommandList::GetContextFlags() {\n    return m_contextFlags;\n  }\n  \n  \n  void D3D11CommandList::AddQuery(D3D11Query* pQuery) {\n    m_queries.emplace_back(pQuery);\n  }\n\n\n  uint64_t D3D11CommandList::AddChunk(DxvkCsChunkRef&& Chunk, uint64_t Cost) {\n    m_chunks.emplace_back(std::move(Chunk), Cost);\n    return m_chunks.size() - 1;\n  }\n  \n  \n  uint64_t D3D11CommandList::AddCommandList(\n          D3D11CommandList*   pCommandList) {\n    // This will be the chunk ID of the first chunk\n    // added, for the purpose of resource tracking.\n    uint64_t baseChunkId = m_chunks.size();\n    \n    for (const auto& chunk : pCommandList->m_chunks)\n      m_chunks.push_back(chunk);\n\n    for (const auto& query : pCommandList->m_queries)\n      m_queries.push_back(query);\n\n    for (const auto& resource : pCommandList->m_resources) {\n      TrackedResource entry = resource;\n      entry.chunkId += baseChunkId;\n\n      m_resources.push_back(std::move(entry));\n    }\n\n    // Return ID of the last chunk added. The command list\n    // added can never be empty, so do not handle zero.\n    return m_chunks.size() - 1;\n  }\n\n\n  void D3D11CommandList::EmitToCsThread(\n    const D3D11ChunkDispatchProc& DispatchProc) {\n    for (const auto& query : m_queries)\n      query->DoDeferredEnd();\n\n    for (size_t i = 0, j = 0; i < m_chunks.size(); i++) {\n      // If there are resources to track for the current chunk,\n      // use a strong flush hint to dispatch GPU work quickly.\n      GpuFlushType flushType = GpuFlushType::ImplicitWeakHint;\n\n      if (j < m_resources.size() && m_resources[j].chunkId == i)\n        flushType = GpuFlushType::ImplicitStrongHint;\n\n      // Dispatch the chunk and capture its sequence number\n      uint64_t seq = DispatchProc(DxvkCsChunkRef(m_chunks[i].chunk), m_chunks[i].cost, flushType);\n\n      // Track resource sequence numbers for the added chunk\n      while (j < m_resources.size() && m_resources[j].chunkId == i)\n        TrackResourceSequenceNumber(m_resources[j++].ref, seq);\n    }\n  }\n  \n  \n  void D3D11CommandList::TrackResourceUsage(\n          ID3D11Resource*     pResource,\n          D3D11_RESOURCE_DIMENSION ResourceType,\n          UINT                Subresource,\n          uint64_t            ChunkId) {\n    TrackedResource entry;\n    entry.ref = D3D11ResourceRef(pResource, Subresource, ResourceType);\n    entry.chunkId = ChunkId;\n\n    m_resources.push_back(std::move(entry));\n  }\n\n\n  void D3D11CommandList::TrackResourceSequenceNumber(\n    const D3D11ResourceRef&   Resource,\n          uint64_t            Seq) {\n    ID3D11Resource* iface = Resource.Get();\n\n    switch (Resource.GetType()) {\n      case D3D11_RESOURCE_DIMENSION_UNKNOWN:\n        break;\n\n      case D3D11_RESOURCE_DIMENSION_BUFFER: {\n        auto impl = static_cast<D3D11Buffer*>(iface);\n        impl->TrackSequenceNumber(Seq);\n      } break;\n\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        auto impl = static_cast<D3D11Texture1D*>(iface)->GetCommonTexture();\n        impl->TrackSequenceNumber(Resource.GetSubresource(), Seq);\n      } break;\n\n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        auto impl = static_cast<D3D11Texture2D*>(iface)->GetCommonTexture();\n        impl->TrackSequenceNumber(Resource.GetSubresource(), Seq);\n      } break;\n\n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {\n        auto impl = static_cast<D3D11Texture3D*>(iface)->GetCommonTexture();\n        impl->TrackSequenceNumber(Resource.GetSubresource(), Seq);\n      } break;\n    }\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_cmdlist.h",
    "content": "#pragma once\n\n#include <functional>\n\n#include \"d3d11_context.h\"\n\nnamespace dxvk {\n  \n  using D3D11ChunkDispatchProc = std::function<uint64_t (DxvkCsChunkRef&&, uint64_t, GpuFlushType)>;\n\n  class D3D11CommandList : public D3D11DeviceChild<ID3D11CommandList> {\n    \n  public:\n    \n    D3D11CommandList(\n            D3D11Device*  pDevice,\n            UINT          ContextFlags);\n    \n    ~D3D11CommandList();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    UINT STDMETHODCALLTYPE GetContextFlags() final;\n    \n    void AddQuery(\n            D3D11Query*         pQuery);\n    \n    uint64_t AddChunk(\n            DxvkCsChunkRef&&    Chunk,\n            uint64_t            Cost);\n\n    uint64_t AddCommandList(\n            D3D11CommandList*   pCommandList);\n\n    void EmitToCsThread(\n      const D3D11ChunkDispatchProc& DispatchProc);\n\n    void TrackResourceUsage(\n            ID3D11Resource*     pResource,\n            D3D11_RESOURCE_DIMENSION ResourceType,\n            UINT                Subresource,\n            uint64_t            ChunkId);\n\n  private:\n\n    struct ChunkEntry {\n      ChunkEntry() = default;\n      ChunkEntry(DxvkCsChunkRef&& c, uint64_t v)\n      : chunk(std::move(c)), cost(v) { }\n      DxvkCsChunkRef chunk = { };\n      uint64_t cost = 0u;\n    };\n\n    struct TrackedResource {\n      D3D11ResourceRef  ref;\n      uint64_t          chunkId;\n    };\n\n    UINT m_contextFlags = 0u;\n\n    std::vector<ChunkEntry>             m_chunks;\n    std::vector<Com<D3D11Query, false>> m_queries;\n    std::vector<TrackedResource>        m_resources;\n\n    D3DDestructionNotifier              m_destructionNotifier;\n\n    void TrackResourceSequenceNumber(\n      const D3D11ResourceRef&   Resource,\n            uint64_t            Seq);\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_context.cpp",
    "content": "#include <algorithm>\n\n#include \"d3d11_context.h\"\n#include \"d3d11_context_def.h\"\n#include \"d3d11_context_imm.h\"\n\nnamespace dxvk {\n\n  template<typename ContextType>\n  D3D11CommonContext<ContextType>::D3D11CommonContext(\n          D3D11Device*            pParent,\n    const Rc<DxvkDevice>&         Device,\n          UINT                    ContextFlags,\n          DxvkCsChunkFlags        CsFlags)\n  : D3D11DeviceChild<ID3D11DeviceContext4>(pParent),\n    m_contextExt(GetTypedContext()),\n    m_annotation(GetTypedContext(), Device),\n    m_device    (Device),\n    m_flags     (ContextFlags),\n    m_staging   (Device, StagingBufferSize),\n    m_csFlags   (CsFlags),\n    m_csChunk   (AllocCsChunk()) {\n    // Create local allocation cache with the same properties\n    // that we will use for common dynamic buffer types\n    uint32_t cachedDynamic = pParent->GetOptions()->cachedDynamicResources;\n\n    VkMemoryPropertyFlags memoryFlags =\n      VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |\n      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n      VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;\n\n    if (cachedDynamic & D3D11_BIND_CONSTANT_BUFFER) {\n      memoryFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n      cachedDynamic = 0;\n    }\n\n    VkBufferUsageFlags bufferUsage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;\n\n    if (!(cachedDynamic & D3D11_BIND_SHADER_RESOURCE)) {\n      bufferUsage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                  |  VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n    }\n\n    if (!(cachedDynamic & D3D11_BIND_VERTEX_BUFFER))\n      bufferUsage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;\n\n    if (!(cachedDynamic & D3D11_BIND_INDEX_BUFFER))\n      bufferUsage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;\n\n    m_allocationCache = m_device->createAllocationCache(bufferUsage, memoryFlags);\n\n    // Determine maximum tess factor based on device options\n    int32_t tessFactorOption = m_parent->GetOptions()->maxTessFactor;\n    m_maxTessFactor = std::min(m_device->properties().core.properties.limits.maxTessellationGenerationLevel, 64u);\n\n    if (tessFactorOption > 0 && tessFactorOption < int32_t(m_maxTessFactor))\n      m_maxTessFactor = tessFactorOption;\n  }\n\n\n  template<typename ContextType>\n  D3D11CommonContext<ContextType>::~D3D11CommonContext() {\n\n  }\n\n\n  template<typename ContextType>\n  HRESULT STDMETHODCALLTYPE D3D11CommonContext<ContextType>::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11DeviceContext)\n     || riid == __uuidof(ID3D11DeviceContext1)\n     || riid == __uuidof(ID3D11DeviceContext2)\n     || riid == __uuidof(ID3D11DeviceContext3)\n     || riid == __uuidof(ID3D11DeviceContext4)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D11VkExtContext)\n     || riid == __uuidof(ID3D11VkExtContext1)) {\n      *ppvObject = ref(&m_contextExt);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DUserDefinedAnnotation)\n     || riid == __uuidof(IDXVKUserDefinedAnnotation)) {\n      *ppvObject = ref(&m_annotation);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11DeviceContext), riid)) {\n      Logger::warn(\"D3D11DeviceContext::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  template<typename ContextType>\n  D3D11_DEVICE_CONTEXT_TYPE STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GetType() {\n    return IsDeferred\n      ? D3D11_DEVICE_CONTEXT_DEFERRED\n      : D3D11_DEVICE_CONTEXT_IMMEDIATE;\n  }\n\n\n  template<typename ContextType>\n  UINT STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GetContextFlags() {\n    return m_flags;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::ClearState() {\n    D3D10DeviceLock lock = LockContext();\n\n    ResetCommandListState();\n    ResetContextState();\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DiscardResource(ID3D11Resource* pResource) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (!pResource)\n      return;\n\n    D3D11_RESOURCE_DIMENSION resType = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resType);\n\n    if (resType == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      DiscardBuffer(pResource);\n    } else {\n      auto texture = GetCommonTexture(pResource);\n      auto image = texture->GetImage();\n\n      for (uint32_t i = 0; i < texture->CountSubresources(); i++)\n        DiscardTexture(pResource, i);\n\n      if (image) {\n        EmitCs([cImage = std::move(image)] (DxvkContext* ctx) {\n          ctx->discardImage(cImage);\n        });\n      }\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DiscardView(ID3D11View* pResourceView) {\n    DiscardViewBase(pResourceView, nullptr, 0);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DiscardView1(\n          ID3D11View*              pResourceView,\n    const D3D11_RECT*              pRects,\n          UINT                     NumRects) {\n    DiscardViewBase(pResourceView, pRects, NumRects);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DiscardViewBase(\n          ID3D11View*              pResourceView,\n    const D3D11_RECT*              pRects,\n          UINT                     NumRects) {\n    D3D10DeviceLock lock = LockContext();\n\n    // We don't support discarding individual rectangles\n    if (!pResourceView || (NumRects && pRects))\n      return;\n\n    // ID3D11View has no methods to query the exact type of\n    // the view, so we'll have to check each possible class\n    auto dsv = dynamic_cast<D3D11DepthStencilView*>(pResourceView);\n    auto rtv = dynamic_cast<D3D11RenderTargetView*>(pResourceView);\n    auto uav = dynamic_cast<D3D11UnorderedAccessView*>(pResourceView);\n\n    Rc<DxvkImageView> view;\n    if (dsv) view = dsv->GetImageView();\n    if (rtv) view = rtv->GetImageView();\n    if (uav) view = uav->GetImageView();\n\n    if (view == nullptr)\n      return;\n\n    // Get information about underlying resource\n    Com<ID3D11Resource> resource;\n    pResourceView->GetResource(&resource);\n\n    uint32_t mipCount = GetCommonTexture(resource.ptr())->Desc()->MipLevels;\n\n    // Discard mip levels one by one\n    VkImageSubresourceRange sr = view->subresources();\n\n    for (uint32_t layer = 0; layer < sr.layerCount; layer++) {\n      for (uint32_t mip = 0; mip < sr.levelCount; mip++) {\n        DiscardTexture(resource.ptr(), D3D11CalcSubresource(\n          sr.baseMipLevel + mip, sr.baseArrayLayer + layer, mipCount));\n      }\n    }\n\n    if (rtv || dsv) {\n      EmitCs([cView = view] (DxvkContext* ctx) {\n        ctx->clearRenderTarget(cView, 0, VkClearValue(), cView->info().aspects);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CopySubresourceRegion(\n          ID3D11Resource*                   pDstResource,\n          UINT                              DstSubresource,\n          UINT                              DstX,\n          UINT                              DstY,\n          UINT                              DstZ,\n          ID3D11Resource*                   pSrcResource,\n          UINT                              SrcSubresource,\n    const D3D11_BOX*                        pSrcBox) {\n    CopySubresourceRegionBase(\n      pDstResource, DstSubresource, DstX, DstY, DstZ,\n      pSrcResource, SrcSubresource, pSrcBox, 0);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CopySubresourceRegion1(\n          ID3D11Resource*                   pDstResource,\n          UINT                              DstSubresource,\n          UINT                              DstX,\n          UINT                              DstY,\n          UINT                              DstZ,\n          ID3D11Resource*                   pSrcResource,\n          UINT                              SrcSubresource,\n    const D3D11_BOX*                        pSrcBox,\n          UINT                              CopyFlags) {\n    CopySubresourceRegionBase(\n      pDstResource, DstSubresource, DstX, DstY, DstZ,\n      pSrcResource, SrcSubresource, pSrcBox, CopyFlags);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CopySubresourceRegionBase(\n          ID3D11Resource*                   pDstResource,\n          UINT                              DstSubresource,\n          UINT                              DstX,\n          UINT                              DstY,\n          UINT                              DstZ,\n          ID3D11Resource*                   pSrcResource,\n          UINT                              SrcSubresource,\n    const D3D11_BOX*                        pSrcBox,\n          UINT                              CopyFlags) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (!pDstResource || !pSrcResource)\n      return;\n\n    if (pSrcBox\n     && (pSrcBox->left  >= pSrcBox->right\n      || pSrcBox->top   >= pSrcBox->bottom\n      || pSrcBox->front >= pSrcBox->back))\n      return;\n\n    D3D11_RESOURCE_DIMENSION dstResourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    D3D11_RESOURCE_DIMENSION srcResourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n\n    pDstResource->GetType(&dstResourceDim);\n    pSrcResource->GetType(&srcResourceDim);\n\n    if (dstResourceDim == D3D11_RESOURCE_DIMENSION_BUFFER && srcResourceDim == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      auto dstBuffer = static_cast<D3D11Buffer*>(pDstResource);\n      auto srcBuffer = static_cast<D3D11Buffer*>(pSrcResource);\n\n      VkDeviceSize dstOffset = DstX;\n      VkDeviceSize srcOffset = 0;\n      VkDeviceSize byteCount = -1;\n\n      if (pSrcBox) {\n        srcOffset = pSrcBox->left;\n        byteCount = pSrcBox->right - pSrcBox->left;\n      }\n\n      CopyBuffer(dstBuffer, dstOffset, srcBuffer, srcOffset, byteCount);\n    } else if (dstResourceDim != D3D11_RESOURCE_DIMENSION_BUFFER && srcResourceDim != D3D11_RESOURCE_DIMENSION_BUFFER) {\n      auto dstTexture = GetCommonTexture(pDstResource);\n      auto srcTexture = GetCommonTexture(pSrcResource);\n\n      if (DstSubresource >= dstTexture->CountSubresources()\n       || SrcSubresource >= srcTexture->CountSubresources())\n        return;\n\n      auto dstFormatInfo = lookupFormatInfo(dstTexture->GetPackedFormat());\n      auto srcFormatInfo = lookupFormatInfo(srcTexture->GetPackedFormat());\n\n      auto dstLayers = vk::makeSubresourceLayers(dstTexture->GetSubresourceFromIndex(dstFormatInfo->aspectMask, DstSubresource));\n      auto srcLayers = vk::makeSubresourceLayers(srcTexture->GetSubresourceFromIndex(srcFormatInfo->aspectMask, SrcSubresource));\n\n      VkOffset3D srcOffset = { 0, 0, 0 };\n      VkOffset3D dstOffset = { int32_t(DstX), int32_t(DstY), int32_t(DstZ) };\n\n      VkExtent3D srcExtent = srcTexture->MipLevelExtent(srcLayers.mipLevel);\n\n      if (pSrcBox) {\n        srcOffset.x = pSrcBox->left;\n        srcOffset.y = pSrcBox->top;\n        srcOffset.z = pSrcBox->front;\n\n        srcExtent.width  = pSrcBox->right -  pSrcBox->left;\n        srcExtent.height = pSrcBox->bottom - pSrcBox->top;\n        srcExtent.depth  = pSrcBox->back -   pSrcBox->front;\n      }\n\n      CopyImage(\n        dstTexture, &dstLayers, dstOffset,\n        srcTexture, &srcLayers, srcOffset,\n        srcExtent);\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CopyResource(\n          ID3D11Resource*                   pDstResource,\n          ID3D11Resource*                   pSrcResource) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (!pDstResource || !pSrcResource || (pDstResource == pSrcResource))\n      return;\n\n    D3D11_RESOURCE_DIMENSION dstResourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    D3D11_RESOURCE_DIMENSION srcResourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n\n    pDstResource->GetType(&dstResourceDim);\n    pSrcResource->GetType(&srcResourceDim);\n\n    if (dstResourceDim != srcResourceDim)\n      return;\n\n    if (dstResourceDim == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      auto dstBuffer = static_cast<D3D11Buffer*>(pDstResource);\n      auto srcBuffer = static_cast<D3D11Buffer*>(pSrcResource);\n\n      if (dstBuffer->Desc()->ByteWidth != srcBuffer->Desc()->ByteWidth)\n        return;\n\n      CopyBuffer(dstBuffer, 0, srcBuffer, 0, -1);\n    } else {\n      auto dstTexture = GetCommonTexture(pDstResource);\n      auto srcTexture = GetCommonTexture(pSrcResource);\n\n      auto dstDesc = dstTexture->Desc();\n      auto srcDesc = srcTexture->Desc();\n\n      // The subresource count must match as well\n      if (dstDesc->ArraySize != srcDesc->ArraySize\n       || dstDesc->MipLevels != srcDesc->MipLevels)\n        return;\n\n      auto dstFormatInfo = lookupFormatInfo(dstTexture->GetPackedFormat());\n      auto srcFormatInfo = lookupFormatInfo(srcTexture->GetPackedFormat());\n\n      for (uint32_t i = 0; i < dstDesc->MipLevels; i++) {\n        VkImageSubresourceLayers dstLayers = { dstFormatInfo->aspectMask, i, 0, dstDesc->ArraySize };\n        VkImageSubresourceLayers srcLayers = { srcFormatInfo->aspectMask, i, 0, srcDesc->ArraySize };\n\n        CopyImage(\n          dstTexture, &dstLayers, VkOffset3D(),\n          srcTexture, &srcLayers, VkOffset3D(),\n          srcTexture->MipLevelExtent(i));\n      }\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CopyStructureCount(\n          ID3D11Buffer*                     pDstBuffer,\n          UINT                              DstAlignedByteOffset,\n          ID3D11UnorderedAccessView*        pSrcView) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto buf = static_cast<D3D11Buffer*>(pDstBuffer);\n    auto uav = static_cast<D3D11UnorderedAccessView*>(pSrcView);\n\n    if (!buf || !uav)\n      return;\n\n    auto counterView = uav->GetCounterView();\n\n    if (counterView == nullptr)\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    EmitCs([\n      cDstSlice = buf->GetBufferSlice(DstAlignedByteOffset),\n      cSrcSlice = DxvkBufferSlice(counterView)\n    ] (DxvkContext* ctx) {\n      ctx->copyBuffer(\n        cDstSlice.buffer(),\n        cDstSlice.offset(),\n        cSrcSlice.buffer(),\n        cSrcSlice.offset(),\n        sizeof(uint32_t));\n    });\n\n    if (buf->HasSequenceNumber())\n      GetTypedContext()->TrackBufferSequenceNumber(buf);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::ClearRenderTargetView(\n          ID3D11RenderTargetView*           pRenderTargetView,\n    const FLOAT                             ColorRGBA[4]) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto rtv = static_cast<D3D11RenderTargetView*>(pRenderTargetView);\n\n    if (!rtv)\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    auto view  = rtv->GetImageView();\n    auto color = ConvertColorValue(ColorRGBA, view->formatInfo());\n\n    EmitCs([\n      cClearValue = color,\n      cImageView  = std::move(view)\n    ] (DxvkContext* ctx) {\n      ctx->clearRenderTarget(cImageView,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        cClearValue, 0u);\n    });\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::ClearUnorderedAccessViewUint(\n          ID3D11UnorderedAccessView*        pUnorderedAccessView,\n    const UINT                              Values[4]) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (!pUnorderedAccessView)\n      return;\n\n    Com<ID3D11UnorderedAccessView> qiUav;\n\n    if (FAILED(pUnorderedAccessView->QueryInterface(IID_PPV_ARGS(&qiUav))))\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    auto uav = static_cast<D3D11UnorderedAccessView*>(qiUav.ptr());\n\n    // Gather UAV format info. We'll use this to determine\n    // whether we need to create a temporary view or not.\n    D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;\n    uav->GetDesc(&uavDesc);\n\n    VkFormat uavFormat = m_parent->LookupFormat(uavDesc.Format, DXGI_VK_FORMAT_MODE_ANY).Format;\n    VkFormat rawFormat = m_parent->LookupFormat(uavDesc.Format, DXGI_VK_FORMAT_MODE_RAW).Format;\n\n    if (uavDesc.Format == DXGI_FORMAT_A8_UNORM)\n      rawFormat = uavFormat;\n\n    if (uavFormat && !rawFormat) {\n      Logger::err(str::format(\"D3D11: ClearUnorderedAccessViewUint: No raw format found for \", uavFormat));\n      return;\n    }\n\n    VkClearValue clearValue = { };\n\n    if (uavDesc.Format == DXGI_FORMAT_R11G11B10_FLOAT) {\n      // R11G11B10 is a special case since there's no corresponding\n      // integer format with the same bit layout. Use R32 instead.\n      clearValue.color.uint32[0] = ((Values[0] & 0x7FF) <<  0)\n                                 | ((Values[1] & 0x7FF) << 11)\n                                 | ((Values[2] & 0x3FF) << 22);\n      clearValue.color.uint32[1] = 0;\n      clearValue.color.uint32[2] = 0;\n      clearValue.color.uint32[3] = 0;\n    } else if (uavDesc.Format == DXGI_FORMAT_A8_UNORM) {\n      // Use the unorm format itself to execute the clear, regardless\n      // of whether we use A8 or emulate the format with R8. This is\n      // necessary because we cannot create R8_UINT views for A8.\n      float a = float(Values[3] & 0xff) / 255.0f;\n\n      clearValue.color.float32[0] = a;\n      clearValue.color.float32[1] = a;\n      clearValue.color.float32[2] = a;\n      clearValue.color.float32[3] = a;\n    } else {\n      clearValue.color.uint32[0] = Values[0];\n      clearValue.color.uint32[1] = Values[1];\n      clearValue.color.uint32[2] = Values[2];\n      clearValue.color.uint32[3] = Values[3];\n    }\n\n    if (uav->GetResourceType() == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      // In case of raw and structured buffers as well as typed\n      // buffers that can be used for atomic operations, we can\n      // use the fast Vulkan buffer clear function.\n      Rc<DxvkBufferView> bufferView = uav->GetBufferView();\n\n      if (bufferView->info().format == VK_FORMAT_R32_UINT\n       || bufferView->info().format == VK_FORMAT_R32_SINT\n       || bufferView->info().format == VK_FORMAT_R32_SFLOAT\n       || bufferView->info().format == VK_FORMAT_B10G11R11_UFLOAT_PACK32) {\n        EmitCs([\n          cClearValue = clearValue.color.uint32[0],\n          cDstSlice   = DxvkBufferSlice(bufferView)\n        ] (DxvkContext* ctx) {\n          ctx->clearBuffer(\n            cDstSlice.buffer(),\n            cDstSlice.offset(),\n            cDstSlice.length(),\n            cClearValue);\n        });\n      } else {\n        // Create a view with an integer format if necessary\n        if (uavFormat != rawFormat)  {\n          DxvkBufferViewKey info = bufferView->info();\n          info.format = rawFormat;\n\n          bufferView = bufferView->buffer()->createView(info);\n        }\n\n        EmitCs([\n          cClearValue = clearValue,\n          cDstView    = bufferView\n        ] (DxvkContext* ctx) {\n          ctx->clearBufferView(\n            cDstView, 0,\n            cDstView->elementCount(),\n            cClearValue.color);\n        });\n      }\n    } else {\n      Rc<DxvkImageView> imageView = uav->GetImageView();\n\n      // If the clear value is zero, we can use the original view regardless of\n      // the format since the bit pattern will not change in any supported format.\n      bool isZeroClearValue = !(clearValue.color.uint32[0] | clearValue.color.uint32[1]\n                              | clearValue.color.uint32[2] | clearValue.color.uint32[3]);\n\n      EmitCs([\n        cClearValue = clearValue,\n        cDstView    = imageView,\n        cDstFormat  = isZeroClearValue ? uavFormat : rawFormat\n      ] (DxvkContext* ctx) {\n        // Ensure that we can write to the image with the given format\n        DxvkImageUsageInfo imageUsage = { };\n        imageUsage.usage = VK_IMAGE_USAGE_STORAGE_BIT;\n        imageUsage.viewFormatCount = 1;\n        imageUsage.viewFormats = &cDstFormat;\n\n        ctx->ensureImageCompatibility(cDstView->image(), imageUsage);\n\n        // If necessary, recreate the view\n        Rc<DxvkImageView> view = cDstView;\n\n        if (view->info().format != cDstFormat) {\n          DxvkImageViewKey key = cDstView->info();\n          key.format = cDstFormat;\n\n          view = cDstView->image()->createView(key);\n        }\n\n        ctx->clearImageView(view,\n          VkOffset3D { 0, 0, 0 },\n          cDstView->mipLevelExtent(0),\n          VK_IMAGE_ASPECT_COLOR_BIT,\n          cClearValue);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::ClearUnorderedAccessViewFloat(\n          ID3D11UnorderedAccessView*        pUnorderedAccessView,\n    const FLOAT                             Values[4]) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto uav = static_cast<D3D11UnorderedAccessView*>(pUnorderedAccessView);\n\n    if (!uav)\n      return;\n\n    auto imgView = uav->GetImageView();\n    auto bufView = uav->GetBufferView();\n\n    const DxvkFormatInfo* info = nullptr;\n    if (imgView != nullptr) info = imgView->formatInfo();\n    if (bufView != nullptr) info = bufView->formatInfo();\n\n    if (!info || info->flags.any(DxvkFormatFlag::SampledSInt, DxvkFormatFlag::SampledUInt))\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    VkClearValue clearValue;\n    clearValue.color.float32[0] = Values[0];\n    clearValue.color.float32[1] = Values[1];\n    clearValue.color.float32[2] = Values[2];\n    clearValue.color.float32[3] = Values[3];\n\n    if (uav->GetResourceType() == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      EmitCs([\n        cClearValue = clearValue,\n        cDstView    = std::move(bufView)\n      ] (DxvkContext* ctx) {\n        ctx->clearBufferView(\n          cDstView, 0,\n          cDstView->elementCount(),\n          cClearValue.color);\n      });\n    } else {\n      EmitCs([\n        cClearValue = clearValue,\n        cDstView    = std::move(imgView)\n      ] (DxvkContext* ctx) {\n        ctx->clearImageView(cDstView,\n          VkOffset3D { 0, 0, 0 },\n          cDstView->mipLevelExtent(0),\n          VK_IMAGE_ASPECT_COLOR_BIT,\n          cClearValue);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::ClearDepthStencilView(\n          ID3D11DepthStencilView*           pDepthStencilView,\n          UINT                              ClearFlags,\n          FLOAT                             Depth,\n          UINT8                             Stencil) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto dsv = static_cast<D3D11DepthStencilView*>(pDepthStencilView);\n\n    if (!dsv)\n      return;\n\n    // Figure out which aspects to clear based on\n    // the image view properties and clear flags.\n    VkImageAspectFlags aspectMask = 0;\n\n    if (ClearFlags & D3D11_CLEAR_DEPTH)\n      aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;\n\n    if (ClearFlags & D3D11_CLEAR_STENCIL)\n      aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;\n\n    aspectMask &= dsv->GetWritableAspectMask();\n\n    if (!aspectMask)\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    VkClearValue clearValue;\n    clearValue.depthStencil.depth   = Depth;\n    clearValue.depthStencil.stencil = Stencil;\n\n    EmitCs([\n      cClearValue = clearValue,\n      cAspectMask = aspectMask,\n      cImageView  = dsv->GetImageView()\n    ] (DxvkContext* ctx) {\n      ctx->clearRenderTarget(cImageView,\n        cAspectMask, cClearValue, 0u);\n    });\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::ClearView(\n          ID3D11View*                       pView,\n    const FLOAT                             Color[4],\n    const D3D11_RECT*                       pRect,\n          UINT                              NumRects) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (NumRects && !pRect)\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    // ID3D11View has no methods to query the exact type of\n    // the view, so we'll have to check each possible class\n    auto dsv = dynamic_cast<D3D11DepthStencilView*>(pView);\n    auto rtv = dynamic_cast<D3D11RenderTargetView*>(pView);\n    auto uav = dynamic_cast<D3D11UnorderedAccessView*>(pView);\n    auto vov = dynamic_cast<D3D11VideoProcessorOutputView*>(pView);\n\n    // Retrieve underlying resource view\n    if (dsv) {\n      Rc<DxvkImageView> imgView = dsv->GetImageView();\n\n      if (imgView)\n        ClearImageView(std::move(imgView), Color, pRect, NumRects);\n    } else if (rtv) {\n      Rc<DxvkImageView> imgView = rtv->GetImageView();\n\n      if (imgView)\n        ClearImageView(std::move(imgView), Color, pRect, NumRects);\n    } else if (uav) {\n      Rc<DxvkImageView> imgView = uav->GetImageView();\n      Rc<DxvkBufferView> bufView = uav->GetBufferView();\n\n      if (imgView)\n        ClearImageView(std::move(imgView), Color, pRect, NumRects);\n\n      if (bufView)\n        ClearBufferView(std::move(bufView), Color, pRect, NumRects);\n    } else if (vov) {\n      auto views = vov->GetCommon().GetViews();\n      auto shadow = vov->GetCommon().GetShadow();\n\n      // If we have to assume that the image is only partially cleared,\n      // make sure to properly sync it with the shadow image.\n      VkImageSubresourceLayers imageLayers = vov->GetCommon().GetImageSubresource();\n\n      VkImageSubresourceLayers shadowLayers = { };\n      shadowLayers.aspectMask = imageLayers.aspectMask;\n      shadowLayers.layerCount = imageLayers.layerCount;\n\n      if (shadow && NumRects)\n        SyncImage(shadow, shadowLayers, vov->GetCommon().GetImage(), imageLayers);\n\n      // Assume that planar video formats use Y - Cb - Cr\n      // order for the purpose of mapping color components.\n      uint32_t component = 0u;\n      FLOAT planeColor[4] = { };\n\n      for (uint32_t i = 0u; i < views.size(); i++) {\n        if (!views[i])\n          break;\n\n        // Extract relevant color components from the clear color\n        // and shift the input array accordingly.\n        uint32_t n = bit::popcnt(views[i]->formatInfo()->componentMask);\n\n        for (uint32_t c = 0u; c < 4u; c++)\n          planeColor[c] = c < n && component + c < 4u ? Color[c] : 0.0f;\n\n        component += n;\n\n        // Perform the actual clear. Rects will be adjusted by called method.\n        ClearImageView(std::move(views[i]), planeColor, pRect, NumRects);\n      }\n\n      // If the video view has a shadow image, copy it back to the base image\n      if (shadow)\n        SyncImage(vov->GetCommon().GetImage(), imageLayers, shadow, shadowLayers);\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GenerateMips(ID3D11ShaderResourceView* pShaderResourceView) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto view = static_cast<D3D11ShaderResourceView*>(pShaderResourceView);\n\n    if (!view || view->GetResourceType() == D3D11_RESOURCE_DIMENSION_BUFFER)\n      return;\n\n    D3D11_COMMON_RESOURCE_DESC resourceDesc = view->GetResourceDesc();\n\n    if (!(resourceDesc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS))\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    EmitCs([cDstImageView = view->GetImageView()]\n    (DxvkContext* ctx) {\n      ctx->generateMipmaps(cDstImageView, VK_FILTER_LINEAR);\n    });\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::ResolveSubresource(\n          ID3D11Resource*                   pDstResource,\n          UINT                              DstSubresource,\n          ID3D11Resource*                   pSrcResource,\n          UINT                              SrcSubresource,\n          DXGI_FORMAT                       Format) {\n    D3D10DeviceLock lock = LockContext();\n\n    bool isSameSubresource = pDstResource   == pSrcResource\n                          && DstSubresource == SrcSubresource;\n\n    if (!pDstResource || !pSrcResource || isSameSubresource)\n      return;\n\n    D3D11_RESOURCE_DIMENSION dstResourceType;\n    D3D11_RESOURCE_DIMENSION srcResourceType;\n\n    pDstResource->GetType(&dstResourceType);\n    pSrcResource->GetType(&srcResourceType);\n\n    if (dstResourceType != D3D11_RESOURCE_DIMENSION_TEXTURE2D\n     || srcResourceType != D3D11_RESOURCE_DIMENSION_TEXTURE2D)\n      return;\n\n    auto dstTexture = static_cast<D3D11Texture2D*>(pDstResource);\n    auto srcTexture = static_cast<D3D11Texture2D*>(pSrcResource);\n\n    D3D11_TEXTURE2D_DESC dstDesc;\n    D3D11_TEXTURE2D_DESC srcDesc;\n\n    dstTexture->GetDesc(&dstDesc);\n    srcTexture->GetDesc(&srcDesc);\n\n    if (dstDesc.SampleDesc.Count != 1)\n      return;\n\n    D3D11CommonTexture* dstTextureInfo = GetCommonTexture(pDstResource);\n    D3D11CommonTexture* srcTextureInfo = GetCommonTexture(pSrcResource);\n\n    const DXGI_VK_FORMAT_INFO dstFormatInfo = m_parent->LookupFormat(dstDesc.Format, DXGI_VK_FORMAT_MODE_ANY);\n    const DXGI_VK_FORMAT_INFO srcFormatInfo = m_parent->LookupFormat(srcDesc.Format, DXGI_VK_FORMAT_MODE_ANY);\n\n    auto dstVulkanFormatInfo = lookupFormatInfo(dstFormatInfo.Format);\n    auto srcVulkanFormatInfo = lookupFormatInfo(srcFormatInfo.Format);\n\n    if (DstSubresource >= dstTextureInfo->CountSubresources()\n     || SrcSubresource >= srcTextureInfo->CountSubresources())\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    const VkImageSubresource dstSubresource =\n      dstTextureInfo->GetSubresourceFromIndex(\n        dstVulkanFormatInfo->aspectMask, DstSubresource);\n\n    const VkImageSubresource srcSubresource =\n      srcTextureInfo->GetSubresourceFromIndex(\n        srcVulkanFormatInfo->aspectMask, SrcSubresource);\n\n    const VkImageSubresourceLayers dstSubresourceLayers = {\n      dstSubresource.aspectMask,\n      dstSubresource.mipLevel,\n      dstSubresource.arrayLayer, 1 };\n\n    const VkImageSubresourceLayers srcSubresourceLayers = {\n      srcSubresource.aspectMask,\n      srcSubresource.mipLevel,\n      srcSubresource.arrayLayer, 1 };\n\n    if (srcDesc.SampleDesc.Count == 1 || m_parent->GetOptions()->disableMsaa) {\n      EmitCs([\n        cDstImage  = dstTextureInfo->GetImage(),\n        cSrcImage  = srcTextureInfo->GetImage(),\n        cDstLayers = dstSubresourceLayers,\n        cSrcLayers = srcSubresourceLayers\n      ] (DxvkContext* ctx) {\n        ctx->copyImage(\n          cDstImage, cDstLayers, VkOffset3D { 0, 0, 0 },\n          cSrcImage, cSrcLayers, VkOffset3D { 0, 0, 0 },\n          cDstImage->mipLevelExtent(cDstLayers.mipLevel));\n      });\n    } else {\n      VkFormat format = m_parent->LookupFormat(\n        Format, DXGI_VK_FORMAT_MODE_ANY).Format;\n\n      EmitCs([\n        cDstImage  = dstTextureInfo->GetImage(),\n        cSrcImage  = srcTextureInfo->GetImage(),\n        cDstSubres = dstSubresourceLayers,\n        cSrcSubres = srcSubresourceLayers,\n        cFormat    = format\n      ] (DxvkContext* ctx) {\n        VkFormat format = cFormat ? cFormat : cSrcImage->info().format;\n\n        VkImageResolve region;\n        region.srcSubresource = cSrcSubres;\n        region.srcOffset      = VkOffset3D { 0, 0, 0 };\n        region.dstSubresource = cDstSubres;\n        region.dstOffset      = VkOffset3D { 0, 0, 0 };\n        region.extent         = cDstImage->mipLevelExtent(cDstSubres.mipLevel);\n\n        ctx->resolveImage(cDstImage, cSrcImage, region, format,\n          getDefaultResolveMode(format), VK_RESOLVE_MODE_NONE);\n      });\n    }\n\n    if (dstTextureInfo->HasSequenceNumber())\n      GetTypedContext()->TrackTextureSequenceNumber(dstTextureInfo, DstSubresource);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::UpdateSubresource(\n          ID3D11Resource*                   pDstResource,\n          UINT                              DstSubresource,\n    const D3D11_BOX*                        pDstBox,\n    const void*                             pSrcData,\n          UINT                              SrcRowPitch,\n          UINT                              SrcDepthPitch) {\n    if (IsDeferred && unlikely(pDstBox != nullptr) && unlikely(!m_parent->GetOptions()->exposeDriverCommandLists)) {\n      // If called from a deferred context and native command list support is not\n      // exposed, we need to apply the destination box to the source pointer. This\n      // only applies to UpdateSubresource, not to UpdateSubresource1. See MSDN:\n      // https://msdn.microsoft.com/en-us/library/windows/desktop/ff476486(v=vs.85).aspx)\n      size_t srcOffset = pDstBox->left;\n\n      // For textures, the offset logic needs to take the format into account.\n      // Ignore that multi-planar images exist, this is hairy enough already.\n      D3D11CommonTexture* dstTexture = GetCommonTexture(pDstResource);\n\n      if (dstTexture) {\n        auto dstFormat = dstTexture->GetPackedFormat();\n        auto dstFormatInfo = lookupFormatInfo(dstFormat);\n\n        size_t blockSize = dstFormatInfo->elementSize;\n\n        VkOffset3D offset;\n        offset.x = pDstBox->left / dstFormatInfo->blockSize.width;\n        offset.y = pDstBox->top / dstFormatInfo->blockSize.height;\n        offset.z = pDstBox->front / dstFormatInfo->blockSize.depth;\n\n        srcOffset = offset.x * blockSize + offset.y * SrcRowPitch + offset.z * SrcDepthPitch;\n      }\n\n      pSrcData = reinterpret_cast<const char*>(pSrcData) + srcOffset;\n    }\n\n    UpdateResource(pDstResource, DstSubresource, pDstBox,\n      pSrcData, SrcRowPitch, SrcDepthPitch, 0);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::UpdateSubresource1(\n          ID3D11Resource*                   pDstResource,\n          UINT                              DstSubresource,\n    const D3D11_BOX*                        pDstBox,\n    const void*                             pSrcData,\n          UINT                              SrcRowPitch,\n          UINT                              SrcDepthPitch,\n          UINT                              CopyFlags) {\n    UpdateResource(pDstResource, DstSubresource, pDstBox,\n      pSrcData, SrcRowPitch, SrcDepthPitch, CopyFlags);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DrawAuto() {\n    D3D10DeviceLock lock = LockContext();\n\n    D3D11Buffer* buffer = m_state.ia.vertexBuffers[0].buffer.ptr();\n\n    if (!buffer)\n      return;\n\n    DxvkBufferSlice vtxBuf = buffer->GetBufferSlice();\n    DxvkBufferSlice ctrBuf = buffer->GetSOCounter();\n\n    if (!ctrBuf.defined())\n      return;\n\n    if (unlikely(HasDirtyGraphicsBindings()))\n      ApplyDirtyGraphicsBindings();\n\n    // We bind the SO counter as an indirect count buffer,\n    // so reset any tracking we may have been doing here.\n    m_state.id.reset();\n\n    EmitCs([=] (DxvkContext* ctx) mutable {\n      ctx->bindDrawBuffers(DxvkBufferSlice(),\n        Forwarder::move(ctrBuf));\n\n      ctx->drawIndirectXfb(0u,\n        vtxBuf.buffer()->getXfbVertexStride(),\n        vtxBuf.offset());\n\n      // Reset draw buffer right away so we don't\n      // keep the SO counter alive indefinitely\n      ctx->bindDrawBuffers(DxvkBufferSlice(),\n        DxvkBufferSlice());\n    });\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::Draw(\n          UINT            VertexCount,\n          UINT            StartVertexLocation) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!VertexCount))\n      return;\n\n    VkDrawIndirectCommand draw = { };\n    draw.vertexCount   = VertexCount;\n    draw.instanceCount = 1u;\n    draw.firstVertex   = StartVertexLocation;\n    draw.firstInstance = 0u;\n\n    BatchDraw(draw);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DrawIndexed(\n          UINT            IndexCount,\n          UINT            StartIndexLocation,\n          INT             BaseVertexLocation) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!IndexCount))\n      return;\n\n    VkDrawIndexedIndirectCommand draw = { };\n    draw.indexCount    = IndexCount;\n    draw.instanceCount = 1u;\n    draw.firstIndex    = StartIndexLocation;\n    draw.vertexOffset  = BaseVertexLocation;\n    draw.firstInstance = 0u;\n\n    BatchDrawIndexed(draw);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DrawInstanced(\n          UINT            VertexCountPerInstance,\n          UINT            InstanceCount,\n          UINT            StartVertexLocation,\n          UINT            StartInstanceLocation) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!VertexCountPerInstance || !InstanceCount))\n      return;\n\n    VkDrawIndirectCommand draw = { };\n    draw.vertexCount   = VertexCountPerInstance;\n    draw.instanceCount = InstanceCount;\n    draw.firstVertex   = StartVertexLocation;\n    draw.firstInstance = StartInstanceLocation;\n\n    BatchDraw(draw);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DrawIndexedInstanced(\n          UINT            IndexCountPerInstance,\n          UINT            InstanceCount,\n          UINT            StartIndexLocation,\n          INT             BaseVertexLocation,\n          UINT            StartInstanceLocation) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!IndexCountPerInstance || !InstanceCount))\n      return;\n\n    VkDrawIndexedIndirectCommand draw = { };\n    draw.indexCount    = IndexCountPerInstance;\n    draw.instanceCount = InstanceCount;\n    draw.firstIndex    = StartIndexLocation;\n    draw.vertexOffset  = BaseVertexLocation;\n    draw.firstInstance = StartInstanceLocation;\n\n    BatchDrawIndexed(draw);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DrawIndexedInstancedIndirect(\n          ID3D11Buffer*   pBufferForArgs,\n          UINT            AlignedByteOffsetForArgs) {\n    D3D10DeviceLock lock = LockContext();\n    SetDrawBuffers(pBufferForArgs, nullptr);\n\n    if (!ValidateDrawBufferSize(pBufferForArgs, AlignedByteOffsetForArgs, sizeof(VkDrawIndexedIndirectCommand)))\n      return;\n\n    if (unlikely(HasDirtyGraphicsBindings()))\n      ApplyDirtyGraphicsBindings();\n\n    // If possible, batch multiple indirect draw calls into one single multidraw call\n    if (m_csDataType == D3D11CmdType::DrawIndirectIndexed) {\n      auto cmdData = static_cast<D3D11CmdDrawIndirectData*>(m_csData->first());\n      auto stride = GetIndirectCommandStride(cmdData, AlignedByteOffsetForArgs, sizeof(VkDrawIndexedIndirectCommand));\n\n      if (stride) {\n        cmdData->count += 1;\n        cmdData->stride = stride;\n        return;\n      }\n    }\n\n    // Need to start a new draw sequence\n    EmitCsCmd<D3D11CmdDrawIndirectData>(D3D11CmdType::DrawIndirectIndexed, 1u,\n      [] (DxvkContext* ctx, const D3D11CmdDrawIndirectData* data, size_t) {\n        ctx->drawIndexedIndirect(data->offset, data->count, data->stride, true);\n      });\n\n    auto cmdData = new (m_csData->first()) D3D11CmdDrawIndirectData();\n    cmdData->offset = AlignedByteOffsetForArgs;\n    cmdData->count  = 1;\n    cmdData->stride = 0;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DrawInstancedIndirect(\n          ID3D11Buffer*   pBufferForArgs,\n          UINT            AlignedByteOffsetForArgs) {\n    D3D10DeviceLock lock = LockContext();\n    SetDrawBuffers(pBufferForArgs, nullptr);\n\n    if (!ValidateDrawBufferSize(pBufferForArgs, AlignedByteOffsetForArgs, sizeof(VkDrawIndirectCommand)))\n      return;\n\n    if (unlikely(HasDirtyGraphicsBindings()))\n      ApplyDirtyGraphicsBindings();\n\n    // If possible, batch multiple indirect draw calls into one single multidraw call\n    if (m_csDataType == D3D11CmdType::DrawIndirect) {\n      auto cmdData = static_cast<D3D11CmdDrawIndirectData*>(m_csData->first());\n      auto stride = GetIndirectCommandStride(cmdData, AlignedByteOffsetForArgs, sizeof(VkDrawIndirectCommand));\n\n      if (stride) {\n        cmdData->count += 1;\n        cmdData->stride = stride;\n        return;\n      }\n    }\n\n    // Need to start a new draw sequence\n    EmitCsCmd<D3D11CmdDrawIndirectData>(D3D11CmdType::DrawIndirect, 1u,\n      [] (DxvkContext* ctx, const D3D11CmdDrawIndirectData* data, size_t) {\n        ctx->drawIndirect(data->offset, data->count, data->stride, true);\n      });\n\n    auto cmdData = new (m_csData->first()) D3D11CmdDrawIndirectData();\n    cmdData->offset = AlignedByteOffsetForArgs;\n    cmdData->count  = 1;\n    cmdData->stride = 0;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::Dispatch(\n          UINT            ThreadGroupCountX,\n          UINT            ThreadGroupCountY,\n          UINT            ThreadGroupCountZ) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!ThreadGroupCountX || !ThreadGroupCountY || !ThreadGroupCountZ))\n      return;\n\n    AddCost(GpuCostEstimate::Dispatch);\n\n    if (unlikely(HasDirtyComputeBindings()))\n      ApplyDirtyComputeBindings();\n\n    EmitCs([=] (DxvkContext* ctx) {\n      ctx->dispatch(\n        ThreadGroupCountX,\n        ThreadGroupCountY,\n        ThreadGroupCountZ);\n    });\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DispatchIndirect(\n          ID3D11Buffer*   pBufferForArgs,\n          UINT            AlignedByteOffsetForArgs) {\n    D3D10DeviceLock lock = LockContext();\n    SetDrawBuffers(pBufferForArgs, nullptr);\n\n    if (!ValidateDrawBufferSize(pBufferForArgs, AlignedByteOffsetForArgs, sizeof(VkDispatchIndirectCommand)))\n      return;\n\n    AddCost(GpuCostEstimate::DispatchIndirect);\n\n    if (unlikely(HasDirtyComputeBindings()))\n      ApplyDirtyComputeBindings();\n\n    EmitCs([cOffset = AlignedByteOffsetForArgs]\n    (DxvkContext* ctx) {\n      ctx->dispatchIndirect(cOffset);\n    });\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::IASetInputLayout(ID3D11InputLayout* pInputLayout) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto inputLayout = static_cast<D3D11InputLayout*>(pInputLayout);\n\n    if (m_state.ia.inputLayout != inputLayout) {\n      bool equal = false;\n\n      // Some games (e.g. Grim Dawn) create lots and lots of\n      // identical input layouts, so we'll only apply the state\n      // if the input layouts has actually changed between calls.\n      if (m_state.ia.inputLayout != nullptr && inputLayout != nullptr)\n        equal = m_state.ia.inputLayout->Compare(inputLayout);\n\n      m_state.ia.inputLayout = inputLayout;\n\n      if (!equal)\n        ApplyInputLayout();\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY Topology) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (m_state.ia.primitiveTopology != Topology) {\n      m_state.ia.primitiveTopology = Topology;\n      ApplyPrimitiveTopology();\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::IASetVertexBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppVertexBuffers,\n    const UINT*                             pStrides,\n    const UINT*                             pOffsets) {\n    D3D10DeviceLock lock = LockContext();\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      auto newBuffer = static_cast<D3D11Buffer*>(ppVertexBuffers[i]);\n\n      if (m_state.ia.vertexBuffers[StartSlot + i].buffer != newBuffer) {\n        m_state.ia.vertexBuffers[StartSlot + i].buffer = newBuffer;\n        m_state.ia.vertexBuffers[StartSlot + i].offset = pOffsets[i];\n        m_state.ia.vertexBuffers[StartSlot + i].stride = pStrides[i];\n\n        BindVertexBuffer(StartSlot + i, newBuffer, pOffsets[i], pStrides[i]);\n      } else if (m_state.ia.vertexBuffers[StartSlot + i].offset != pOffsets[i]\n              || m_state.ia.vertexBuffers[StartSlot + i].stride != pStrides[i]) {\n        m_state.ia.vertexBuffers[StartSlot + i].offset = pOffsets[i];\n        m_state.ia.vertexBuffers[StartSlot + i].stride = pStrides[i];\n\n        BindVertexBufferRange(StartSlot + i, newBuffer, pOffsets[i], pStrides[i]);\n      }\n    }\n\n    m_state.ia.maxVbCount = std::clamp(StartSlot + NumBuffers,\n      m_state.ia.maxVbCount, uint32_t(m_state.ia.vertexBuffers.size()));\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::IASetIndexBuffer(\n          ID3D11Buffer*                     pIndexBuffer,\n          DXGI_FORMAT                       Format,\n          UINT                              Offset) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto newBuffer = static_cast<D3D11Buffer*>(pIndexBuffer);\n\n    if (m_state.ia.indexBuffer.buffer != newBuffer) {\n      m_state.ia.indexBuffer.buffer = newBuffer;\n      m_state.ia.indexBuffer.offset = Offset;\n      m_state.ia.indexBuffer.format = Format;\n\n      BindIndexBuffer(newBuffer, Offset, Format);\n    } else if (m_state.ia.indexBuffer.offset != Offset\n            || m_state.ia.indexBuffer.format != Format) {\n      m_state.ia.indexBuffer.offset = Offset;\n      m_state.ia.indexBuffer.format = Format;\n\n      BindIndexBufferRange(newBuffer, Offset, Format);\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::IAGetInputLayout(ID3D11InputLayout** ppInputLayout) {\n    D3D10DeviceLock lock = LockContext();\n\n    *ppInputLayout = m_state.ia.inputLayout.ref();\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::IAGetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY* pTopology) {\n    D3D10DeviceLock lock = LockContext();\n\n    *pTopology = m_state.ia.primitiveTopology;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::IAGetVertexBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppVertexBuffers,\n          UINT*                             pStrides,\n          UINT*                             pOffsets) {\n    D3D10DeviceLock lock = LockContext();\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      const bool inRange = StartSlot + i < m_state.ia.vertexBuffers.size();\n\n      if (ppVertexBuffers) {\n        ppVertexBuffers[i] = inRange\n          ? m_state.ia.vertexBuffers[StartSlot + i].buffer.ref()\n          : nullptr;\n      }\n\n      if (pStrides) {\n        pStrides[i] = inRange\n          ? m_state.ia.vertexBuffers[StartSlot + i].stride\n          : 0u;\n      }\n\n      if (pOffsets) {\n        pOffsets[i] = inRange\n          ? m_state.ia.vertexBuffers[StartSlot + i].offset\n          : 0u;\n      }\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::IAGetIndexBuffer(\n          ID3D11Buffer**                    ppIndexBuffer,\n          DXGI_FORMAT*                      pFormat,\n          UINT*                             pOffset) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppIndexBuffer)\n      *ppIndexBuffer = m_state.ia.indexBuffer.buffer.ref();\n\n    if (pFormat)\n      *pFormat = m_state.ia.indexBuffer.format;\n\n    if (pOffset)\n      *pOffset = m_state.ia.indexBuffer.offset;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSSetShader(\n          ID3D11VertexShader*               pVertexShader,\n          ID3D11ClassInstance* const*       ppClassInstances,\n          UINT                              NumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto shader = static_cast<D3D11VertexShader*>(pVertexShader);\n    SetClassInstances<D3D11ShaderType::eVertex>(\n      shader ? shader->GetCommonShader() : nullptr, ppClassInstances, NumClassInstances);\n\n    if (m_state.vs != shader) {\n      m_state.vs = shader;\n\n      BindShader<D3D11ShaderType::eVertex>(GetCommonShader(shader));\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSSetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers<D3D11ShaderType::eVertex>(\n      StartSlot, NumBuffers, ppConstantBuffers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSSetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers,\n    const UINT*                             pFirstConstant,\n    const UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers1<D3D11ShaderType::eVertex>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSSetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView* const*  ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetShaderResources<D3D11ShaderType::eVertex>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSSetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState* const*        ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetSamplers<D3D11ShaderType::eVertex>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSGetShader(\n          ID3D11VertexShader**              ppVertexShader,\n          ID3D11ClassInstance**             ppClassInstances,\n          UINT*                             pNumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppVertexShader)\n      *ppVertexShader = m_state.vs.ref();\n\n    GetClassInstances<D3D11ShaderType::eVertex>(ppClassInstances, pNumClassInstances);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSGetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eVertex>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      nullptr, nullptr);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSGetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers,\n          UINT*                             pFirstConstant,\n          UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eVertex>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSGetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView**        ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetShaderResources<D3D11ShaderType::eVertex>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::VSGetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState**              ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetSamplers<D3D11ShaderType::eVertex>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSSetShader(\n          ID3D11HullShader*                 pHullShader,\n          ID3D11ClassInstance* const*       ppClassInstances,\n          UINT                              NumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto shader = static_cast<D3D11HullShader*>(pHullShader);\n    SetClassInstances<D3D11ShaderType::eHull>(\n      shader ? shader->GetCommonShader() : nullptr, ppClassInstances, NumClassInstances);\n\n    if (m_state.hs != shader) {\n      m_state.hs = shader;\n\n      BindShader<D3D11ShaderType::eHull>(GetCommonShader(shader));\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSSetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers<D3D11ShaderType::eHull>(\n      StartSlot, NumBuffers, ppConstantBuffers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSSetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers,\n    const UINT*                             pFirstConstant,\n    const UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers1<D3D11ShaderType::eHull>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSSetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView* const*  ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetShaderResources<D3D11ShaderType::eHull>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSSetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState* const*        ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetSamplers<D3D11ShaderType::eHull>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSGetShader(\n          ID3D11HullShader**                ppHullShader,\n          ID3D11ClassInstance**             ppClassInstances,\n          UINT*                             pNumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppHullShader)\n      *ppHullShader = m_state.hs.ref();\n\n    GetClassInstances<D3D11ShaderType::eHull>(ppClassInstances, pNumClassInstances);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSGetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eHull>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      nullptr, nullptr);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSGetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers,\n          UINT*                             pFirstConstant,\n          UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eHull>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSGetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView**        ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetShaderResources<D3D11ShaderType::eHull>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::HSGetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState**              ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetSamplers<D3D11ShaderType::eHull>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSSetShader(\n          ID3D11DomainShader*               pDomainShader,\n          ID3D11ClassInstance* const*       ppClassInstances,\n          UINT                              NumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto shader = static_cast<D3D11DomainShader*>(pDomainShader);\n    SetClassInstances<D3D11ShaderType::eDomain>(\n      shader ? shader->GetCommonShader() : nullptr, ppClassInstances, NumClassInstances);\n\n    if (m_state.ds != shader) {\n      m_state.ds = shader;\n\n      BindShader<D3D11ShaderType::eDomain>(GetCommonShader(shader));\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSSetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers<D3D11ShaderType::eDomain>(\n      StartSlot, NumBuffers, ppConstantBuffers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSSetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers,\n    const UINT*                             pFirstConstant,\n    const UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers1<D3D11ShaderType::eDomain>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSSetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView* const*  ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetShaderResources<D3D11ShaderType::eDomain>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSSetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState* const*        ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetSamplers<D3D11ShaderType::eDomain>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSGetShader(\n          ID3D11DomainShader**              ppDomainShader,\n          ID3D11ClassInstance**             ppClassInstances,\n          UINT*                             pNumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppDomainShader)\n      *ppDomainShader = m_state.ds.ref();\n\n    GetClassInstances<D3D11ShaderType::eDomain>(ppClassInstances, pNumClassInstances);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSGetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eDomain>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      nullptr, nullptr);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSGetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers,\n          UINT*                             pFirstConstant,\n          UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eDomain>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSGetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView**        ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetShaderResources<D3D11ShaderType::eDomain>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::DSGetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState**              ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetSamplers<D3D11ShaderType::eDomain>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSSetShader(\n          ID3D11GeometryShader*             pShader,\n          ID3D11ClassInstance* const*       ppClassInstances,\n          UINT                              NumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto shader = static_cast<D3D11GeometryShader*>(pShader);\n    SetClassInstances<D3D11ShaderType::eGeometry>(\n      shader ? shader->GetCommonShader() : nullptr, ppClassInstances, NumClassInstances);\n\n    if (m_state.gs != shader) {\n      m_state.gs = shader;\n\n      BindShader<D3D11ShaderType::eGeometry>(GetCommonShader(shader));\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSSetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers<D3D11ShaderType::eGeometry>(\n      StartSlot, NumBuffers, ppConstantBuffers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSSetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers,\n    const UINT*                             pFirstConstant,\n    const UINT*                             pNumConstants) {  \n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers1<D3D11ShaderType::eGeometry>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSSetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView* const*  ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetShaderResources<D3D11ShaderType::eGeometry>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSSetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState* const*        ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetSamplers<D3D11ShaderType::eGeometry>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSGetShader(\n          ID3D11GeometryShader**            ppGeometryShader,\n          ID3D11ClassInstance**             ppClassInstances,\n          UINT*                             pNumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppGeometryShader)\n      *ppGeometryShader = m_state.gs.ref();\n\n    GetClassInstances<D3D11ShaderType::eGeometry>(ppClassInstances, pNumClassInstances);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSGetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eGeometry>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      nullptr, nullptr);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSGetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers,\n          UINT*                             pFirstConstant,\n          UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eGeometry>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSGetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView**        ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetShaderResources<D3D11ShaderType::eGeometry>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GSGetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState**              ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetSamplers<D3D11ShaderType::eGeometry>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSSetShader(\n          ID3D11PixelShader*                pPixelShader,\n          ID3D11ClassInstance* const*       ppClassInstances,\n          UINT                              NumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto shader = static_cast<D3D11PixelShader*>(pPixelShader);\n    SetClassInstances<D3D11ShaderType::ePixel>(\n      shader ? shader->GetCommonShader() : nullptr, ppClassInstances, NumClassInstances);\n\n    if (m_state.ps != shader) {\n      m_state.ps = shader;\n\n      BindShader<D3D11ShaderType::ePixel>(GetCommonShader(shader));\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSSetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers<D3D11ShaderType::ePixel>(\n      StartSlot, NumBuffers, ppConstantBuffers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSSetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers,\n    const UINT*                             pFirstConstant,\n    const UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers1<D3D11ShaderType::ePixel>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSSetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView* const*  ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetShaderResources<D3D11ShaderType::ePixel>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSSetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState* const*        ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetSamplers<D3D11ShaderType::ePixel>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSGetShader(\n          ID3D11PixelShader**               ppPixelShader,\n          ID3D11ClassInstance**             ppClassInstances,\n          UINT*                             pNumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppPixelShader)\n      *ppPixelShader = m_state.ps.ref();\n\n    GetClassInstances<D3D11ShaderType::ePixel>(ppClassInstances, pNumClassInstances);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSGetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::ePixel>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      nullptr, nullptr);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSGetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers,\n          UINT*                             pFirstConstant,\n          UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::ePixel>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSGetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView**        ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetShaderResources<D3D11ShaderType::ePixel>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::PSGetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState**              ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetSamplers<D3D11ShaderType::ePixel>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSSetShader(\n          ID3D11ComputeShader*              pComputeShader,\n          ID3D11ClassInstance* const*       ppClassInstances,\n          UINT                              NumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto shader = static_cast<D3D11ComputeShader*>(pComputeShader);\n    SetClassInstances<D3D11ShaderType::eCompute>(\n      shader ? shader->GetCommonShader() : nullptr, ppClassInstances, NumClassInstances);\n\n    if (m_state.cs != shader) {\n      m_state.cs = shader;\n\n      BindShader<D3D11ShaderType::eCompute>(GetCommonShader(shader));\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSSetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers<D3D11ShaderType::eCompute>(\n      StartSlot, NumBuffers, ppConstantBuffers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSSetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers,\n    const UINT*                             pFirstConstant,\n    const UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetConstantBuffers1<D3D11ShaderType::eCompute>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSSetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView* const*  ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetShaderResources<D3D11ShaderType::eCompute>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSSetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState* const*        ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetSamplers<D3D11ShaderType::eCompute>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSSetUnorderedAccessViews(\n          UINT                              StartSlot,\n          UINT                              NumUAVs,\n          ID3D11UnorderedAccessView* const* ppUnorderedAccessViews,\n    const UINT*                             pUAVInitialCounts) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (TestRtvUavHazards(0, nullptr, NumUAVs, ppUnorderedAccessViews))\n      return;\n\n    // Unbind previously bound conflicting UAVs\n    int32_t uavId = m_state.uav.mask.findNext(0);\n\n    while (uavId >= 0) {\n      if (uint32_t(uavId) < StartSlot || uint32_t(uavId) >= StartSlot + NumUAVs) {\n        for (uint32_t i = 0; i < NumUAVs; i++) {\n          auto uav = static_cast<D3D11UnorderedAccessView*>(ppUnorderedAccessViews[i]);\n\n          if (CheckViewOverlap(uav, m_state.uav.views[uavId].ptr())) {\n            m_state.uav.views[uavId] = nullptr;\n            m_state.uav.mask.clr(uavId);\n\n            if (!DirtyComputeUnorderedAccessView(uavId, true))\n              BindUnorderedAccessView(D3D11ShaderType::eCompute, uavId, nullptr);\n          }\n        }\n\n        uavId = m_state.uav.mask.findNext(uavId + 1);\n      } else {\n        uavId = m_state.uav.mask.findNext(StartSlot + NumUAVs);\n      }\n    }\n\n    // Actually bind the given UAVs\n    for (uint32_t i = 0; i < NumUAVs; i++) {\n      auto uav = static_cast<D3D11UnorderedAccessView*>(ppUnorderedAccessViews[i]);\n      auto ctr = pUAVInitialCounts ? pUAVInitialCounts[i] : ~0u;\n\n      if (ctr != ~0u && uav && uav->HasCounter())\n        UpdateUnorderedAccessViewCounter(uav, ctr);\n\n      if (m_state.uav.views[StartSlot + i] != uav) {\n        m_state.uav.views[StartSlot + i] = uav;\n        m_state.uav.mask.set(StartSlot + i, uav != nullptr);\n\n        if (!DirtyComputeUnorderedAccessView(StartSlot + i, !uav))\n          BindUnorderedAccessView(D3D11ShaderType::eCompute, StartSlot + i, uav);\n\n        ResolveCsSrvHazards(uav);\n      }\n    }\n\n    m_state.uav.maxCount = std::clamp(StartSlot + NumUAVs,\n      m_state.uav.maxCount, uint32_t(m_state.uav.views.size()));\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSGetShader(\n          ID3D11ComputeShader**             ppComputeShader,\n          ID3D11ClassInstance**             ppClassInstances,\n          UINT*                             pNumClassInstances) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppComputeShader)\n      *ppComputeShader = m_state.cs.ref();\n\n    GetClassInstances<D3D11ShaderType::eCompute>(ppClassInstances, pNumClassInstances);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSGetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eCompute>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      nullptr, nullptr);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSGetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers,\n          UINT*                             pFirstConstant,\n          UINT*                             pNumConstants) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetConstantBuffers<D3D11ShaderType::eCompute>(\n      StartSlot, NumBuffers, ppConstantBuffers,\n      pFirstConstant, pNumConstants);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSGetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView**        ppShaderResourceViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetShaderResources<D3D11ShaderType::eCompute>(\n      StartSlot, NumViews, ppShaderResourceViews);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSGetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState**              ppSamplers) {\n    D3D10DeviceLock lock = LockContext();\n\n    GetSamplers<D3D11ShaderType::eCompute>(\n      StartSlot, NumSamplers, ppSamplers);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CSGetUnorderedAccessViews(\n          UINT                              StartSlot,\n          UINT                              NumUAVs,\n          ID3D11UnorderedAccessView**       ppUnorderedAccessViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    for (uint32_t i = 0; i < NumUAVs; i++) {\n      ppUnorderedAccessViews[i] = StartSlot + i < m_state.uav.views.size()\n        ? m_state.uav.views[StartSlot + i].ref()\n        : nullptr;\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::OMSetRenderTargets(\n          UINT                              NumViews,\n          ID3D11RenderTargetView* const*    ppRenderTargetViews,\n          ID3D11DepthStencilView*           pDepthStencilView) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetRenderTargetsAndUnorderedAccessViews(\n      NumViews, ppRenderTargetViews, pDepthStencilView,\n      NumViews, 0, nullptr, nullptr);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::OMSetRenderTargetsAndUnorderedAccessViews(\n          UINT                              NumRTVs,\n          ID3D11RenderTargetView* const*    ppRenderTargetViews,\n          ID3D11DepthStencilView*           pDepthStencilView,\n          UINT                              UAVStartSlot,\n          UINT                              NumUAVs,\n          ID3D11UnorderedAccessView* const* ppUnorderedAccessViews,\n    const UINT*                             pUAVInitialCounts) {\n    D3D10DeviceLock lock = LockContext();\n\n    SetRenderTargetsAndUnorderedAccessViews(\n      NumRTVs, ppRenderTargetViews, pDepthStencilView,\n      UAVStartSlot, NumUAVs, ppUnorderedAccessViews, pUAVInitialCounts);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::OMSetBlendState(\n          ID3D11BlendState*                 pBlendState,\n    const FLOAT                             BlendFactor[4],\n          UINT                              SampleMask) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto blendState = static_cast<D3D11BlendState*>(pBlendState);\n\n    if (m_state.om.cbState    != blendState\n     || m_state.om.sampleMask != SampleMask) {\n      m_state.om.cbState    = blendState;\n      m_state.om.sampleMask = SampleMask;\n\n      ApplyBlendState();\n    }\n\n    if (BlendFactor != nullptr) {\n      for (uint32_t i = 0; i < 4; i++)\n        m_state.om.blendFactor[i] = BlendFactor[i];\n\n      ApplyBlendFactor();\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::OMSetDepthStencilState(\n          ID3D11DepthStencilState*          pDepthStencilState,\n          UINT                              StencilRef) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto depthStencilState = static_cast<D3D11DepthStencilState*>(pDepthStencilState);\n\n    if (m_state.om.dsState != depthStencilState) {\n      m_state.om.dsState = depthStencilState;\n      ApplyDepthStencilState();\n    }\n\n    // The D3D11 runtime only appears to store the low 8 bits,\n    // and some games rely on this behaviour. Do the same here.\n    StencilRef &= 0xFF;\n\n    if (m_state.om.stencilRef != StencilRef) {\n      m_state.om.stencilRef = StencilRef;\n      ApplyStencilRef();\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::OMGetRenderTargets(\n          UINT                              NumViews,\n          ID3D11RenderTargetView**          ppRenderTargetViews,\n          ID3D11DepthStencilView**          ppDepthStencilView) {\n    OMGetRenderTargetsAndUnorderedAccessViews(\n      NumViews, ppRenderTargetViews, ppDepthStencilView,\n      NumViews, 0, nullptr);\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::OMGetRenderTargetsAndUnorderedAccessViews(\n          UINT                              NumRTVs,\n          ID3D11RenderTargetView**          ppRenderTargetViews,\n          ID3D11DepthStencilView**          ppDepthStencilView,\n          UINT                              UAVStartSlot,\n          UINT                              NumUAVs,\n          ID3D11UnorderedAccessView**       ppUnorderedAccessViews) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppRenderTargetViews) {\n      for (UINT i = 0; i < NumRTVs; i++) {\n        ppRenderTargetViews[i] = i < m_state.om.rtvs.size()\n          ? m_state.om.rtvs[i].ref()\n          : nullptr;\n      }\n    }\n\n    if (ppDepthStencilView)\n      *ppDepthStencilView = m_state.om.dsv.ref();\n\n    if (ppUnorderedAccessViews) {\n      for (UINT i = 0; i < NumUAVs; i++) {\n        ppUnorderedAccessViews[i] = UAVStartSlot + i < m_state.om.uavs.size()\n          ? m_state.om.uavs[UAVStartSlot + i].ref()\n          : nullptr;\n      }\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::OMGetBlendState(\n          ID3D11BlendState**                ppBlendState,\n          FLOAT                             BlendFactor[4],\n          UINT*                             pSampleMask) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppBlendState)\n      *ppBlendState = m_state.om.cbState.ref();\n\n    if (BlendFactor)\n      std::memcpy(BlendFactor, m_state.om.blendFactor, sizeof(FLOAT) * 4);\n\n    if (pSampleMask)\n      *pSampleMask = m_state.om.sampleMask;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::OMGetDepthStencilState(\n          ID3D11DepthStencilState**         ppDepthStencilState,\n          UINT*                             pStencilRef) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppDepthStencilState)\n      *ppDepthStencilState = m_state.om.dsState.ref();\n\n    if (pStencilRef)\n      *pStencilRef = m_state.om.stencilRef;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::RSSetState(ID3D11RasterizerState* pRasterizerState) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto newRasterizerState = static_cast<D3D11RasterizerState*>(pRasterizerState);\n\n    if (m_state.rs.state != newRasterizerState) {\n      // Need to keep the previous rasterizer state object alive for the time being\n      auto oldRasterizerState = std::move(m_state.rs.state);\n\n      m_state.rs.state = newRasterizerState;\n      ApplyRasterizerState();\n\n      // If necessary, update the rasterizer sample count push constant\n      uint32_t oldSampleCount = oldRasterizerState ? oldRasterizerState->Desc().ForcedSampleCount : 0;\n      uint32_t newSampleCount = newRasterizerState ? newRasterizerState->Desc().ForcedSampleCount : 0;\n\n      if (oldSampleCount != newSampleCount)\n        ApplyRasterizerSampleCount();\n\n      // In D3D11, the rasterizer state defines whether the scissor test is\n      // enabled, so if that changes, we need to update scissor rects as well.\n      bool oldScissorEnable = oldRasterizerState && oldRasterizerState->Desc().ScissorEnable;\n      bool newScissorEnable = newRasterizerState && newRasterizerState->Desc().ScissorEnable;\n\n      if (oldScissorEnable != newScissorEnable)\n        ApplyViewportState();\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::RSSetViewports(\n          UINT                              NumViewports,\n    const D3D11_VIEWPORT*                   pViewports) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(NumViewports > m_state.rs.viewports.size()))\n      return;\n\n    for (uint32_t i = 0; i < NumViewports; i++) {\n      const D3D11_VIEWPORT& vp = pViewports[i];\n\n      bool valid = vp.Width >= 0.0f && vp.Height >= 0.0f\n                && vp.MinDepth >= 0.0f && vp.MaxDepth <= 1.0f\n                && vp.MinDepth <= vp.MaxDepth;\n\n      if (!valid)\n        return;\n    }\n\n    bool dirty = m_state.rs.numViewports != NumViewports;\n    m_state.rs.numViewports = NumViewports;\n\n    for (uint32_t i = 0; i < NumViewports; i++) {\n      const D3D11_VIEWPORT& vp = m_state.rs.viewports[i];\n\n      dirty |= vp.TopLeftX != pViewports[i].TopLeftX\n            || vp.TopLeftY != pViewports[i].TopLeftY\n            || vp.Width    != pViewports[i].Width\n            || vp.Height   != pViewports[i].Height\n            || vp.MinDepth != pViewports[i].MinDepth\n            || vp.MaxDepth != pViewports[i].MaxDepth;\n      \n      m_state.rs.viewports[i] = pViewports[i];\n    }\n\n    if (dirty)\n      ApplyViewportState();\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::RSSetScissorRects(\n          UINT                              NumRects,\n    const D3D11_RECT*                       pRects) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(NumRects > m_state.rs.scissors.size()))\n      return;\n\n    bool dirty = m_state.rs.numScissors != NumRects;\n    m_state.rs.numScissors = NumRects;\n\n    for (uint32_t i = 0; i < NumRects; i++) {\n      if (pRects[i].bottom >= pRects[i].top\n       && pRects[i].right  >= pRects[i].left) {\n        const D3D11_RECT& sr = m_state.rs.scissors[i];\n\n        dirty |= sr.top    != pRects[i].top\n              || sr.left   != pRects[i].left\n              || sr.bottom != pRects[i].bottom\n              || sr.right  != pRects[i].right;\n\n        m_state.rs.scissors[i] = pRects[i];\n      }\n    }\n\n    if (dirty && m_state.rs.state && m_state.rs.state->Desc().ScissorEnable)\n      ApplyViewportState();\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::RSGetState(ID3D11RasterizerState** ppRasterizerState) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppRasterizerState)\n      *ppRasterizerState = m_state.rs.state.ref();\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::RSGetViewports(\n          UINT*                             pNumViewports,\n          D3D11_VIEWPORT*                   pViewports) {\n    D3D10DeviceLock lock = LockContext();\n    uint32_t numWritten = m_state.rs.numViewports;\n\n    if (pViewports) {\n      numWritten = std::min(numWritten, *pNumViewports);\n\n      for (uint32_t i = 0; i < *pNumViewports; i++) {\n        if (i < m_state.rs.numViewports) {\n          pViewports[i] = m_state.rs.viewports[i];\n        } else {\n          pViewports[i].TopLeftX = 0.0f;\n          pViewports[i].TopLeftY = 0.0f;\n          pViewports[i].Width    = 0.0f;\n          pViewports[i].Height   = 0.0f;\n          pViewports[i].MinDepth = 0.0f;\n          pViewports[i].MaxDepth = 0.0f;\n        }\n      }\n    }\n\n    *pNumViewports = numWritten;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::RSGetScissorRects(\n          UINT*                             pNumRects,\n          D3D11_RECT*                       pRects) {\n    D3D10DeviceLock lock = LockContext();\n    uint32_t numWritten = m_state.rs.numScissors;\n\n    if (pRects) {\n      numWritten = std::min(numWritten, *pNumRects);\n\n      for (uint32_t i = 0; i < *pNumRects; i++) {\n        if (i < m_state.rs.numScissors) {\n          pRects[i] = m_state.rs.scissors[i];\n        } else {\n          pRects[i].left   = 0;\n          pRects[i].top    = 0;\n          pRects[i].right  = 0;\n          pRects[i].bottom = 0;\n        }\n      }\n    }\n\n    *pNumRects = m_state.rs.numScissors;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::SOSetTargets(\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppSOTargets,\n    const UINT*                             pOffsets) {\n    D3D10DeviceLock lock = LockContext();\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      D3D11Buffer* buffer = static_cast<D3D11Buffer*>(ppSOTargets[i]);\n      UINT         offset = pOffsets != nullptr ? pOffsets[i] : 0;\n\n      m_state.so.targets[i].buffer = buffer;\n      m_state.so.targets[i].offset = offset;\n    }\n\n    for (uint32_t i = NumBuffers; i < D3D11_SO_BUFFER_SLOT_COUNT; i++) {\n      m_state.so.targets[i].buffer = nullptr;\n      m_state.so.targets[i].offset = 0;\n    }\n\n    for (uint32_t i = 0; i < D3D11_SO_BUFFER_SLOT_COUNT; i++) {\n      BindXfbBuffer(i,\n        m_state.so.targets[i].buffer.ptr(),\n        m_state.so.targets[i].offset);\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::SOGetTargets(\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppSOTargets) {\n    D3D10DeviceLock lock = LockContext();\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      ppSOTargets[i] = i < m_state.so.targets.size()\n        ? m_state.so.targets[i].buffer.ref()\n        : nullptr;\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::SOGetTargetsWithOffsets(\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppSOTargets,\n          UINT*                             pOffsets) {\n    D3D10DeviceLock lock = LockContext();\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      const bool inRange = i < m_state.so.targets.size();\n\n      if (ppSOTargets) {\n        ppSOTargets[i] = inRange\n          ? m_state.so.targets[i].buffer.ref()\n          : nullptr;\n      }\n\n      if (pOffsets) {\n        pOffsets[i] = inRange\n          ? m_state.so.targets[i].offset\n          : 0u;\n      }\n    }\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::SetPredication(\n          ID3D11Predicate*                  pPredicate,\n          BOOL                              PredicateValue) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto predicate = D3D11Query::FromPredicate(pPredicate);\n    m_state.pr.predicateObject = predicate;\n    m_state.pr.predicateValue  = PredicateValue;\n\n    static bool s_errorShown = false;\n\n    if (pPredicate && !std::exchange(s_errorShown, true))\n      Logger::err(\"D3D11DeviceContext::SetPredication: Stub\");\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GetPredication(\n          ID3D11Predicate**                 ppPredicate,\n          BOOL*                             pPredicateValue) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (ppPredicate)\n      *ppPredicate = D3D11Query::AsPredicate(m_state.pr.predicateObject.ref());\n\n    if (pPredicateValue)\n      *pPredicateValue = m_state.pr.predicateValue;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::SetResourceMinLOD(\n          ID3D11Resource*                   pResource,\n          FLOAT                             MinLOD) {\n    bool s_errorShown = false;\n\n    if (std::exchange(s_errorShown, true))\n      Logger::err(\"D3D11DeviceContext::SetResourceMinLOD: Not implemented\");\n  }\n  \n  \n  template<typename ContextType>\n  FLOAT STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GetResourceMinLOD(ID3D11Resource* pResource) {\n    bool s_errorShown = false;\n\n    if (std::exchange(s_errorShown, true))\n      Logger::err(\"D3D11DeviceContext::GetResourceMinLOD: Not implemented\");\n\n    return 0.0f;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CopyTiles(\n          ID3D11Resource*                   pTiledResource,\n    const D3D11_TILED_RESOURCE_COORDINATE*  pTileRegionStartCoordinate,\n    const D3D11_TILE_REGION_SIZE*           pTileRegionSize,\n          ID3D11Buffer*                     pBuffer,\n          UINT64                            BufferStartOffsetInBytes,\n          UINT                              Flags) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (!pTiledResource || !pBuffer)\n      return;\n\n    auto buffer = static_cast<D3D11Buffer*>(pBuffer);\n\n    // Get buffer slice and just forward the call\n    VkDeviceSize bufferSize = pTileRegionSize->NumTiles * SparseMemoryPageSize;\n\n    if (buffer->Desc()->ByteWidth < BufferStartOffsetInBytes + bufferSize)\n      return;\n\n    DxvkBufferSlice slice = buffer->GetBufferSlice(BufferStartOffsetInBytes, bufferSize);\n\n    CopyTiledResourceData(pTiledResource,\n      pTileRegionStartCoordinate,\n      pTileRegionSize, slice, Flags);\n\n    if (buffer->HasSequenceNumber())\n      GetTypedContext()->TrackBufferSequenceNumber(buffer);\n  }\n\n\n  template<typename ContextType>\n  HRESULT STDMETHODCALLTYPE D3D11CommonContext<ContextType>::CopyTileMappings(\n          ID3D11Resource*                   pDestTiledResource,\n    const D3D11_TILED_RESOURCE_COORDINATE*  pDestRegionCoordinate,\n          ID3D11Resource*                   pSourceTiledResource,\n    const D3D11_TILED_RESOURCE_COORDINATE*  pSourceRegionCoordinate,\n    const D3D11_TILE_REGION_SIZE*           pTileRegionSize,\n          UINT                              Flags) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (!pDestTiledResource || !pSourceTiledResource)\n      return E_INVALIDARG;\n\n    if constexpr (!IsDeferred)\n      GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n\n    DxvkSparseBindInfo bindInfo;\n    bindInfo.dstResource = GetPagedResource(pDestTiledResource);\n    bindInfo.srcResource = GetPagedResource(pSourceTiledResource);\n\n    auto dstPageTable = bindInfo.dstResource->getSparsePageTable();\n    auto srcPageTable = bindInfo.srcResource->getSparsePageTable();\n\n    if (!dstPageTable || !srcPageTable)\n      return E_INVALIDARG;\n\n    if (pDestRegionCoordinate->Subresource >= dstPageTable->getSubresourceCount()\n     || pSourceRegionCoordinate->Subresource >= srcPageTable->getSubresourceCount())\n      return E_INVALIDARG;\n\n    VkOffset3D dstRegionOffset = {\n      int32_t(pDestRegionCoordinate->X),\n      int32_t(pDestRegionCoordinate->Y),\n      int32_t(pDestRegionCoordinate->Z) };\n\n    VkOffset3D srcRegionOffset = {\n      int32_t(pSourceRegionCoordinate->X),\n      int32_t(pSourceRegionCoordinate->Y),\n      int32_t(pSourceRegionCoordinate->Z) };\n\n    VkExtent3D regionExtent = {\n      uint32_t(pTileRegionSize->Width),\n      uint32_t(pTileRegionSize->Height),\n      uint32_t(pTileRegionSize->Depth) };\n\n    for (uint32_t i = 0; i < pTileRegionSize->NumTiles; i++) {\n      // We don't know the current tile mappings of either resource since\n      // this may be called on a deferred context and tile mappings are\n      // updated on the CS thread, so just resolve the copy in the backend\n      uint32_t dstTile = dstPageTable->computePageIndex(\n        pDestRegionCoordinate->Subresource, dstRegionOffset,\n        regionExtent, !pTileRegionSize->bUseBox, i);\n\n      uint32_t srcTile = srcPageTable->computePageIndex(\n        pSourceRegionCoordinate->Subresource, srcRegionOffset,\n        regionExtent, !pTileRegionSize->bUseBox, i);\n\n      if (dstTile >= dstPageTable->getPageCount()\n       || srcTile >= srcPageTable->getPageCount())\n        return E_INVALIDARG;\n\n      DxvkSparseBind bind;\n      bind.mode = DxvkSparseBindMode::Copy;\n      bind.dstPage = dstTile;\n      bind.srcPage = srcTile;\n\n      bindInfo.binds.push_back(bind);\n    }\n\n    DxvkSparseBindFlags flags = (Flags & D3D11_TILE_MAPPING_NO_OVERWRITE)\n      ? DxvkSparseBindFlags(DxvkSparseBindFlag::SkipSynchronization)\n      : DxvkSparseBindFlags();\n\n    EmitCs([\n      cBindInfo = std::move(bindInfo),\n      cFlags    = flags\n    ] (DxvkContext* ctx) {\n      ctx->updatePageTable(cBindInfo, cFlags);\n    });\n\n    return S_OK;\n  }\n\n\n  template<typename ContextType>\n  HRESULT STDMETHODCALLTYPE D3D11CommonContext<ContextType>::ResizeTilePool(\n          ID3D11Buffer*                     pTilePool,\n          UINT64                            NewSizeInBytes) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (NewSizeInBytes % SparseMemoryPageSize)\n      return E_INVALIDARG;\n\n    auto buffer = static_cast<D3D11Buffer*>(pTilePool);\n\n    if (!buffer->IsTilePool())\n      return E_INVALIDARG;\n\n    // Perform the resize operation. This is somewhat trivialized\n    // since all lifetime tracking is done by the backend.\n    EmitCs([\n      cAllocator  = buffer->GetSparseAllocator(),\n      cPageCount  = NewSizeInBytes / SparseMemoryPageSize\n    ] (DxvkContext* ctx) {\n      cAllocator->setCapacity(cPageCount);\n    });\n\n    return S_OK;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::TiledResourceBarrier(\n          ID3D11DeviceChild*                pTiledResourceOrViewAccessBeforeBarrier,\n          ID3D11DeviceChild*                pTiledResourceOrViewAccessAfterBarrier) {\n    D3D10DeviceLock lock = LockContext();\n\n    DxvkGlobalPipelineBarrier srcBarrier = GetTiledResourceDependency(pTiledResourceOrViewAccessBeforeBarrier);\n    DxvkGlobalPipelineBarrier dstBarrier = GetTiledResourceDependency(pTiledResourceOrViewAccessAfterBarrier);\n\n    if (srcBarrier.stages && dstBarrier.stages) {\n      EmitCs([\n        cSrcBarrier = srcBarrier,\n        cDstBarrier = dstBarrier\n      ] (DxvkContext* ctx) {\n        ctx->emitGraphicsBarrier(\n          cSrcBarrier.stages, cSrcBarrier.access,\n          cDstBarrier.stages, cDstBarrier.access);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  HRESULT STDMETHODCALLTYPE D3D11CommonContext<ContextType>::UpdateTileMappings(\n          ID3D11Resource*                   pTiledResource,\n          UINT                              NumRegions,\n    const D3D11_TILED_RESOURCE_COORDINATE*  pRegionCoordinates,\n    const D3D11_TILE_REGION_SIZE*           pRegionSizes,\n          ID3D11Buffer*                     pTilePool,\n          UINT                              NumRanges,\n    const UINT*                             pRangeFlags,\n    const UINT*                             pRangeTileOffsets,\n    const UINT*                             pRangeTileCounts,\n          UINT                              Flags) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (!pTiledResource || !NumRegions || !NumRanges)\n      return E_INVALIDARG;\n\n    if constexpr (!IsDeferred)\n      GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n\n    // Find sparse allocator if the tile pool is defined\n    DxvkSparseBindInfo bindInfo;\n\n    if (pTilePool) {\n      auto tilePool = static_cast<D3D11Buffer*>(pTilePool);\n      bindInfo.srcAllocator = tilePool->GetSparseAllocator();\n\n      if (bindInfo.srcAllocator == nullptr)\n        return E_INVALIDARG;\n    }\n\n    // Find resource and sparse page table for the given resource\n    bindInfo.dstResource = GetPagedResource(pTiledResource);\n    auto pageTable = bindInfo.dstResource->getSparsePageTable();\n\n    if (!pageTable)\n      return E_INVALIDARG;\n\n    // Lookup table in case the app tries to bind the same\n    // page multiple times. We should resolve that here and\n    // only consider the last bind to any given page.\n    std::vector<uint32_t> bindIndices(pageTable->getPageCount(), ~0u);\n\n    // This function allows pretty much every parameter to be nullptr\n    // in some way, so initialize some defaults as necessary\n    D3D11_TILED_RESOURCE_COORDINATE regionCoord = { };\n    D3D11_TILE_REGION_SIZE regionSize = { };\n\n    if (!pRegionSizes) {\n      regionSize.NumTiles = pRegionCoordinates\n        ? 1 : pageTable->getPageCount();\n    }\n\n    uint32_t rangeFlag = 0u;\n    uint32_t rangeTileOffset = 0u;\n    uint32_t rangeTileCount = ~0u;\n\n    // For now, just generate a simple list of tile index to\n    // page index mappings, and let the backend optimize later\n    uint32_t regionIdx = 0u;\n    uint32_t regionTile = 0u;\n    uint32_t rangeIdx = 0u;\n    uint32_t rangeTile = 0u;\n\n    while (regionIdx < NumRegions && rangeIdx < NumRanges) {\n      if (!regionTile) {\n        if (pRegionCoordinates)\n          regionCoord = pRegionCoordinates[regionIdx];\n\n        if (pRegionSizes)\n          regionSize = pRegionSizes[regionIdx];\n      }\n\n      if (!rangeTile) {\n        if (pRangeFlags)\n          rangeFlag = pRangeFlags[rangeIdx];\n\n        if (pRangeTileOffsets)\n          rangeTileOffset = pRangeTileOffsets[rangeIdx];\n\n        if (pRangeTileCounts)\n          rangeTileCount = pRangeTileCounts[rangeIdx];\n      }\n\n      if (!(rangeFlag & D3D11_TILE_RANGE_SKIP)) {\n        if (regionCoord.Subresource >= pageTable->getSubresourceCount())\n          return E_INVALIDARG;\n\n        if (regionSize.bUseBox && regionSize.NumTiles !=\n            regionSize.Width * regionSize.Height * regionSize.Depth)\n          return E_INVALIDARG;\n\n        VkOffset3D regionOffset = {\n          int32_t(regionCoord.X),\n          int32_t(regionCoord.Y),\n          int32_t(regionCoord.Z) };\n\n        VkExtent3D regionExtent = {\n          uint32_t(regionSize.Width),\n          uint32_t(regionSize.Height),\n          uint32_t(regionSize.Depth) };\n\n        uint32_t resourceTile = pageTable->computePageIndex(regionCoord.Subresource,\n          regionOffset, regionExtent, !regionSize.bUseBox, regionTile);\n\n        // Fill in bind info for the current tile\n        DxvkSparseBind bind = { };\n        bind.dstPage = resourceTile;\n\n        if (rangeFlag & D3D11_TILE_RANGE_NULL) {\n          bind.mode = DxvkSparseBindMode::Null;\n        } else if (pTilePool) {\n          bind.mode = DxvkSparseBindMode::Bind;\n          bind.srcPage = rangeFlag & D3D11_TILE_RANGE_REUSE_SINGLE_TILE\n            ? rangeTileOffset\n            : rangeTileOffset + rangeTile;\n        } else {\n          return E_INVALIDARG;\n        }\n\n        // Add bind info to the bind list, overriding\n        // any existing bind for the same resource page\n        if (resourceTile < pageTable->getPageCount()) {\n          if (bindIndices[resourceTile] < bindInfo.binds.size())\n            bindInfo.binds[bindIndices[resourceTile]] = bind;\n          else\n            bindInfo.binds.push_back(bind);\n        }\n      }\n\n      if (++regionTile == regionSize.NumTiles) {\n        regionTile = 0;\n        regionIdx += 1;\n      }\n\n      if (++rangeTile == rangeTileCount) {\n        rangeTile = 0;\n        rangeIdx += 1;\n      }\n    }\n\n    // Translate flags. The backend benefits from NO_OVERWRITE since\n    // otherwise we have to serialize execution of the current command\n    // buffer, the sparse binding operation, and subsequent commands.\n    // With NO_OVERWRITE, we can execute it more or less asynchronously.\n    DxvkSparseBindFlags flags = (Flags & D3D11_TILE_MAPPING_NO_OVERWRITE)\n      ? DxvkSparseBindFlags(DxvkSparseBindFlag::SkipSynchronization)\n      : DxvkSparseBindFlags();\n\n    EmitCs([\n      cBindInfo = std::move(bindInfo),\n      cFlags    = flags\n    ] (DxvkContext* ctx) {\n      ctx->updatePageTable(cBindInfo, cFlags);\n    });\n\n    return S_OK;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::UpdateTiles(\n          ID3D11Resource*                   pDestTiledResource,\n    const D3D11_TILED_RESOURCE_COORDINATE*  pDestTileRegionStartCoordinate,\n    const D3D11_TILE_REGION_SIZE*           pDestTileRegionSize,\n    const void*                             pSourceTileData,\n          UINT                              Flags) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (!pDestTiledResource || !pSourceTileData)\n      return;\n\n    // Allocate staging memory and copy source data into it, at a\n    // 64k page granularity. It is not clear whether this behaviour\n    // is correct in case we're writing to incmplete pages.\n    VkDeviceSize bufferSize = pDestTileRegionSize->NumTiles * SparseMemoryPageSize;\n\n    DxvkBufferSlice slice = AllocStagingBuffer(bufferSize);\n    std::memcpy(slice.mapPtr(0), pSourceTileData, bufferSize);\n\n    // Fix up flags. The runtime probably validates this in some\n    // way but our internal function relies on correct flags anyway.\n    Flags &= D3D11_TILE_MAPPING_NO_OVERWRITE;\n    Flags |= D3D11_TILE_COPY_LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE;\n\n    CopyTiledResourceData(pDestTiledResource,\n      pDestTileRegionStartCoordinate,\n      pDestTileRegionSize, slice, Flags);\n\n    if constexpr (!IsDeferred)\n      static_cast<ContextType*>(this)->ThrottleAllocation();\n  }\n\n\n  template<typename ContextType>\n  BOOL STDMETHODCALLTYPE D3D11CommonContext<ContextType>::IsAnnotationEnabled() {\n    return m_annotation.GetStatus();\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::SetMarkerInt(\n          LPCWSTR                           pLabel,\n          INT                               Data) {\n    // Not implemented in the backend, ignore\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::BeginEventInt(\n          LPCWSTR                           pLabel,\n          INT                               Data) {\n    // Not implemented in the backend, ignore\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::EndEvent() {\n    // Not implemented in the backend, ignore\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::GetHardwareProtectionState(\n          BOOL*                             pHwProtectionEnable) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::err(\"D3D11DeviceContext::GetHardwareProtectionState: Not implemented\");\n\n    if (pHwProtectionEnable)\n      *pHwProtectionEnable = FALSE;\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::SetHardwareProtectionState(\n          BOOL                              HwProtectionEnable) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::err(\"D3D11DeviceContext::SetHardwareProtectionState: Not implemented\");\n  }\n\n\n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11CommonContext<ContextType>::TransitionSurfaceLayout(\n          IDXGIVkInteropSurface*    pSurface,\n    const VkImageSubresourceRange*  pSubresources,\n          VkImageLayout             OldLayout,\n          VkImageLayout             NewLayout) {\n    D3D10DeviceLock lock = LockContext();\n\n    // Get the underlying D3D11 resource\n    Com<ID3D11Resource> resource;\n\n    pSurface->QueryInterface(__uuidof(ID3D11Resource),\n      reinterpret_cast<void**>(&resource));\n\n    // Get the texture from that resource\n    D3D11CommonTexture* texture = GetCommonTexture(resource.ptr());\n\n    EmitCs([\n      cImage        = texture->GetImage(),\n      cSubresources = *pSubresources,\n      cOldLayout    = OldLayout,\n      cNewLayout    = NewLayout\n    ] (DxvkContext* ctx) {\n      ctx->transformImage(\n        cImage, cSubresources,\n        cOldLayout, cNewLayout);\n    });\n  }\n\n\n  template<typename ContextType>\n  DxvkCsChunkRef D3D11CommonContext<ContextType>::AllocCsChunk() {\n    return m_parent->AllocCsChunk(m_csFlags);\n  }\n\n\n  template<typename ContextType>\n  DxvkBufferSlice D3D11CommonContext<ContextType>::AllocStagingBuffer(\n          VkDeviceSize                      Size) {\n    return m_staging.alloc(Size);\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyDirtyConstantBuffers(\n          D3D11ShaderType                   Stage,\n    const D3D11BindingMask&                 BoundMask,\n          D3D11BindingMask&                 DirtyMask) {\n    uint32_t bindMask = BoundMask.cbvMask & DirtyMask.cbvMask;\n\n    if (!bindMask)\n      return;\n\n    // Need to clear dirty bits before binding\n    const auto& state = m_state.cbv[Stage];\n    DirtyMask.cbvMask -= bindMask;\n\n    for (uint32_t slot : bit::BitMask(bindMask)) {\n      const auto& cbv = state.buffers[slot];\n\n      BindConstantBuffer(Stage, slot, cbv.buffer.ptr(),\n        cbv.constantOffset, cbv.constantBound);\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyDirtySamplers(\n          D3D11ShaderType                   Stage,\n    const D3D11BindingMask&                 BoundMask,\n          D3D11BindingMask&                 DirtyMask) {\n    uint32_t bindMask = BoundMask.samplerMask & DirtyMask.samplerMask;\n\n    if (!bindMask)\n      return;\n\n    // Need to clear dirty bits before binding\n    const auto& state = m_state.samplers[Stage];\n    DirtyMask.samplerMask -= bindMask;\n\n    for (uint32_t slot : bit::BitMask(bindMask))\n      BindSampler(Stage, slot, state.samplers[slot].ptr());\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyDirtyShaderResources(\n          D3D11ShaderType                   Stage,\n    const D3D11BindingMask&                 BoundMask,\n          D3D11BindingMask&                 DirtyMask) {\n    const auto& state = m_state.srv[Stage];\n\n    for (uint32_t i = 0; i < state.maxCount; i += 64u) {\n      uint32_t maskIndex = i / 64u;\n      uint64_t bindMask = BoundMask.srvMask[maskIndex] & DirtyMask.srvMask[maskIndex];\n\n      if (!bindMask)\n        continue;\n\n    // Need to clear dirty bits before binding\n      DirtyMask.srvMask[maskIndex] -= bindMask;\n\n      for (uint32_t slot : bit::BitMask(bindMask))\n        BindShaderResource(Stage, slot + i, state.views[slot + i].ptr());\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyDirtyUnorderedAccessViews(\n          D3D11ShaderType                   Stage,\n    const D3D11BindingMask&                 BoundMask,\n          D3D11BindingMask&                 DirtyMask) {\n    uint64_t bindMask = BoundMask.uavMask & DirtyMask.uavMask;\n\n    if (!bindMask)\n      return;\n\n    const auto& views = Stage == D3D11ShaderType::eCompute\n      ? m_state.uav.views\n      : m_state.om.uavs;\n\n    // Need to clear dirty bits before binding\n    DirtyMask.uavMask -= bindMask;\n\n    for (uint32_t slot : bit::BitMask(bindMask))\n      BindUnorderedAccessView(Stage, slot, views[slot].ptr());\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyDirtyGraphicsBindings() {\n    auto dirtyMask = m_state.lazy.shadersDirty & m_state.lazy.shadersUsed;\n    dirtyMask.clr(D3D11ShaderType::eCompute);\n\n    if (unlikely(!(dirtyMask & m_state.lazy.graphicsUavShaders).isClear())) {\n      D3D11ShaderType stage = D3D11ShaderType::ePixel;\n\n      auto& boundMask = m_state.lazy.bindingsUsed[stage];\n      auto& dirtyMask = m_state.lazy.bindingsDirty[stage];\n\n      ApplyDirtyUnorderedAccessViews(stage, boundMask, dirtyMask);\n    }\n\n    for (uint32_t stageIndex : bit::BitMask(uint32_t(dirtyMask.raw()))) {\n      D3D11ShaderType stage = D3D11ShaderType(stageIndex);\n\n      auto& boundMask = m_state.lazy.bindingsUsed[stage];\n      auto& dirtyMask = m_state.lazy.bindingsDirty[stage];\n\n      ApplyDirtySamplers(stage, boundMask, dirtyMask);\n      ApplyDirtyConstantBuffers(stage, boundMask, dirtyMask);\n      ApplyDirtyShaderResources(stage, boundMask, dirtyMask);\n\n      m_state.lazy.shadersDirty.clr(stage);\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyDirtyComputeBindings() {\n    D3D11ShaderType stage = D3D11ShaderType::eCompute;\n\n    auto& boundMask = m_state.lazy.bindingsUsed[stage];\n    auto& dirtyMask = m_state.lazy.bindingsDirty[stage];\n\n    ApplyDirtySamplers(stage, boundMask, dirtyMask);\n    ApplyDirtyConstantBuffers(stage, boundMask, dirtyMask);\n    ApplyDirtyShaderResources(stage, boundMask, dirtyMask);\n    ApplyDirtyUnorderedAccessViews(stage, boundMask, dirtyMask);\n\n    m_state.lazy.shadersDirty.clr(stage);\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyInputLayout() {\n    if (likely(m_state.ia.inputLayout != nullptr)) {\n      uint32_t attributeCount = m_state.ia.inputLayout->GetAttributeCount();\n      uint32_t bindingCount = m_state.ia.inputLayout->GetBindingCount();\n\n      EmitCsCmd<DxvkVertexInput>(D3D11CmdType::None, attributeCount + bindingCount, [\n        cAttributeCount   = attributeCount,\n        cBindingCount     = bindingCount\n      ] (DxvkContext* ctx, const DxvkVertexInput* layout, size_t) {\n        ctx->setInputLayout(cAttributeCount, &layout[0],\n          cBindingCount, &layout[cAttributeCount]);\n      });\n\n      for (uint32_t i = 0; i < attributeCount + bindingCount; i++)\n        new (m_csData->at(i)) DxvkVertexInput(m_state.ia.inputLayout->GetInput(i));\n    } else {\n      EmitCs([] (DxvkContext* ctx) {\n        ctx->setInputLayout(0, nullptr, 0, nullptr);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyPrimitiveTopology() {\n    D3D11_PRIMITIVE_TOPOLOGY topology = m_state.ia.primitiveTopology;\n    DxvkInputAssemblyState iaState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false);\n\n    if (topology <= D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ) {\n      static const std::array<DxvkInputAssemblyState, 14> s_iaStates = {{\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false),\n        // Regular topologies\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_POINT_LIST,     false),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_LIST,      false),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,     true),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,  false),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, true),\n        // Gap. This includes triangle fan which isn't supported in D3D11\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false),\n        // Adjacency topologies\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,       false),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,      true),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,   false),\n        DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,  true),\n      }};\n\n      iaState = s_iaStates[uint32_t(topology)];\n    } else if (topology >= D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST\n            && topology <= D3D11_PRIMITIVE_TOPOLOGY_32_CONTROL_POINT_PATCHLIST) {\n      // The number of control points per patch can be inferred from the enum value in D3D11\n      iaState = DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, false);\n      iaState.setPatchVertexCount(topology - D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + 1);\n    }\n\n    EmitCs([iaState] (DxvkContext* ctx) {\n      ctx->setInputAssemblyState(iaState);\n    });\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyBlendState() {\n    if (m_state.om.cbState != nullptr) {\n      EmitCs([\n        cBlendState = m_state.om.cbState->GetBlendState(),\n        cMsState    = m_state.om.cbState->GetMsState(m_state.om.sampleMask),\n        cLoState    = m_state.om.cbState->GetLoState()\n      ] (DxvkContext* ctx) {\n        for (uint32_t i = 0; i < cBlendState.size(); i++)\n          ctx->setBlendMode(i, cBlendState[i]);\n\n        ctx->setMultisampleState(cMsState);\n        ctx->setLogicOpState(cLoState);\n      });\n    } else {\n      EmitCs([\n        cSampleMask = m_state.om.sampleMask\n      ] (DxvkContext* ctx) {\n        DxvkBlendMode cbState = InitDefaultBlendState();\n\n        for (uint32_t i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)\n          ctx->setBlendMode(i, cbState);\n\n        ctx->setMultisampleState(InitDefaultMultisampleState(cSampleMask));\n        ctx->setLogicOpState(InitDefaultLogicOpState());\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyBlendFactor() {\n    EmitCs([\n      cBlendConstants = DxvkBlendConstants {\n        m_state.om.blendFactor[0], m_state.om.blendFactor[1],\n        m_state.om.blendFactor[2], m_state.om.blendFactor[3] }\n    ] (DxvkContext* ctx) {\n      ctx->setBlendConstants(cBlendConstants);\n    });\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyDepthStencilState() {\n    if (m_state.om.dsState != nullptr) {\n      EmitCs([\n        cState = m_state.om.dsState->GetState()\n      ] (DxvkContext* ctx) {\n        ctx->setDepthStencilState(cState);\n      });\n    } else {\n      EmitCs([] (DxvkContext* ctx) {\n        ctx->setDepthStencilState(InitDefaultDepthStencilState());\n      });\n    }\n  }\n  \n  \n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyStencilRef() {\n    EmitCs([\n      cStencilRef = m_state.om.stencilRef\n    ] (DxvkContext* ctx) {\n      ctx->setStencilReference(cStencilRef);\n    });\n  }\n  \n  \n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyRasterizerState() {\n    if (m_state.rs.state != nullptr) {\n      EmitCs([\n        cState      = m_state.rs.state->GetState(),\n        cDepthBias  = m_state.rs.state->GetDepthBias()\n      ] (DxvkContext* ctx) {\n        ctx->setRasterizerState(cState);\n        ctx->setDepthBias(cDepthBias);\n      });\n    } else {\n      EmitCs([] (DxvkContext* ctx) {\n        ctx->setRasterizerState(InitDefaultRasterizerState());\n        ctx->setDepthBias(DxvkDepthBias());\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyRasterizerSampleCount() {\n    uint32_t sampleCount = m_state.om.sampleCount;\n\n    if (unlikely(!sampleCount)) {\n      sampleCount = m_state.rs.state\n        ? m_state.rs.state->Desc().ForcedSampleCount\n        : 0u;\n\n      if (!sampleCount)\n        sampleCount = 1u;\n    }\n\n    EmitCs([\n      cPushConstants = DxvkBuiltInPushData(sampleCount, m_maxTessFactor)\n    ] (DxvkContext* ctx) {\n      ctx->pushData(VK_SHADER_STAGE_ALL_GRAPHICS,\n        0u, sizeof(cPushConstants), &cPushConstants);\n    });\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ApplyViewportState() {\n    uint32_t viewportCount = m_state.rs.numViewports;\n\n    if (likely(viewportCount)) {\n      EmitCsCmd<DxvkViewport>(D3D11CmdType::None, viewportCount,\n        [] (DxvkContext* ctx, const DxvkViewport* viewports, size_t count) {\n          ctx->setViewports(count, viewports);\n        });\n\n      // Vulkan does not provide an easy way to disable the scissor test,\n      // Set scissor rects that are at least as large as the framebuffer.\n      bool enableScissorTest = m_state.rs.state && m_state.rs.state->Desc().ScissorEnable;\n\n      // D3D11's coordinate system has its origin in the bottom left,\n      // but the viewport coordinates are aligned to the top-left\n      // corner so we can get away with flipping the viewport.\n      for (uint32_t i = 0; i < viewportCount; i++) {\n        const auto& vp = m_state.rs.viewports[i];\n\n        auto* dst = new (m_csData->at(i)) DxvkViewport();\n        dst->viewport.x = vp.TopLeftX;\n        dst->viewport.y = vp.Height + vp.TopLeftY;\n        dst->viewport.width = vp.Width;\n        dst->viewport.height = -vp.Height;\n        dst->viewport.minDepth = vp.MinDepth;\n        dst->viewport.maxDepth = vp.MaxDepth;\n\n        if (!enableScissorTest) {\n          dst->scissor.offset = VkOffset2D { 0, 0 };\n          dst->scissor.extent = VkExtent2D {\n            D3D11_VIEWPORT_BOUNDS_MAX,\n            D3D11_VIEWPORT_BOUNDS_MAX };\n        } else if (i < m_state.rs.numScissors) {\n          const auto& sr = m_state.rs.scissors[i];\n\n          dst->scissor.offset = VkOffset2D { sr.left, sr.top };\n          dst->scissor.extent = VkExtent2D {\n            uint32_t(std::max(sr.left, sr.right) - sr.left),\n            uint32_t(std::max(sr.top, sr.bottom) - sr.top) };\n        }\n      }\n    } else {\n      // The backend can't handle a viewport count of zero,\n      // so we should at least specify one empty viewport\n      EmitCs([] (DxvkContext* ctx) {\n        DxvkViewport viewport = { };\n        ctx->setViewports(1, &viewport);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BatchDraw(\n    const VkDrawIndirectCommand&            draw) {\n    if (unlikely(HasDirtyGraphicsBindings()))\n      ApplyDirtyGraphicsBindings();\n\n    // Batch consecutive draws if there are no state changes\n    if (m_csDataType == D3D11CmdType::Draw) {\n      auto* drawInfo = m_csChunk->pushData(m_csData, 1u);\n\n      if (likely(drawInfo)) {\n        new (drawInfo) VkDrawIndirectCommand(draw);\n        return;\n      }\n    }\n\n    EmitCsCmd<VkDrawIndirectCommand>(D3D11CmdType::Draw, 1u,\n      [] (DxvkContext* ctx, const VkDrawIndirectCommand* draws, size_t count) {\n        ctx->draw(count, draws);\n      });\n\n    new (m_csData->first()) VkDrawIndirectCommand(draw);\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BatchDrawIndexed(\n    const VkDrawIndexedIndirectCommand&     draw) {\n    if (unlikely(HasDirtyGraphicsBindings()))\n      ApplyDirtyGraphicsBindings();\n\n    // Batch consecutive draws if there are no state changes\n    if (m_csDataType == D3D11CmdType::DrawIndexed) {\n      auto* drawInfo = m_csChunk->pushData(m_csData, 1u);\n\n      if (likely(drawInfo)) {\n        new (drawInfo) VkDrawIndexedIndirectCommand(draw);\n        return;\n      }\n    }\n\n    EmitCsCmd<VkDrawIndexedIndirectCommand>(D3D11CmdType::DrawIndexed, 1u,\n      [] (DxvkContext* ctx, const VkDrawIndexedIndirectCommand* draws, size_t count) {\n        ctx->drawIndexed(count, draws);\n      });\n\n    new (m_csData->first()) VkDrawIndexedIndirectCommand(draw);\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::BindShader(\n    const D3D11CommonShader*    pShaderModule) {\n    uint64_t oldUavMask = m_state.lazy.bindingsUsed[ShaderStage].uavMask;\n\n    if (pShaderModule) {\n      auto buffer = pShaderModule->GetIcb();\n      auto shader = pShaderModule->GetShader();\n\n      if (unlikely(shader->needsCompile()))\n        m_device->requestCompileShader(shader);\n\n      // If this shader activates any bindings that have not yet been applied,\n      // mark the shader stage as dirty so it gets applied on the next draw.\n      // Don't apply it right away since any dirty bindings are likely redundant.\n      m_state.lazy.shadersUsed.set(ShaderStage);\n      m_state.lazy.bindingsUsed[ShaderStage] = pShaderModule->GetBindingMask();\n\n      if (!m_state.lazy.shadersDirty.test(ShaderStage) && (DebugLazyBinding != Tristate::False)) {\n        if (!(m_state.lazy.bindingsDirty[ShaderStage] & m_state.lazy.bindingsUsed[ShaderStage]).empty())\n          m_state.lazy.shadersDirty.set(ShaderStage);\n      }\n\n      EmitCs([\n        cBuffer = std::move(buffer),\n        cShader = std::move(shader)\n      ] (DxvkContext* ctx) mutable {\n        constexpr VkShaderStageFlagBits stage = GetShaderStage(ShaderStage);\n\n        uint32_t slotId = D3D11ShaderResourceMapping::computeCbvBinding(ShaderStage,\n          D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT);\n\n        ctx->bindShader<stage>(\n          Forwarder::move(cShader));\n        ctx->bindUniformBuffer(stage, slotId,\n          Forwarder::move(cBuffer));\n      });\n    } else {\n      // Mark shader stage as inactive and clean since we'll have no active\n      // bindings. This works because if the app changes any binding at all\n      // for this stage, it will get flagged as dirty, and if another shader\n      // gets bound, it will check for any dirty bindings again.\n      m_state.lazy.shadersUsed.clr(ShaderStage);\n      m_state.lazy.shadersDirty.clr(ShaderStage);\n\n      m_state.lazy.bindingsUsed[ShaderStage].reset();\n\n      EmitCs([] (DxvkContext* ctx) {\n        constexpr VkShaderStageFlagBits stage = GetShaderStage(ShaderStage);\n\n        uint32_t slotId = D3D11ShaderResourceMapping::computeCbvBinding(ShaderStage,\n          D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT);\n\n        ctx->bindShader<stage>(nullptr);\n        ctx->bindUniformBuffer(stage, slotId, DxvkBufferSlice());\n      });\n    }\n\n    // On graphics, UAVs are available to all stages, but we treat them as part\n    // of the pixel shader binding set. Re-compute the active UAV mask. We don't\n    // need to set the PS as active or dirty here though since the UAV update\n    // code will mark all other stages that access UAVs as dirty, too.\n    uint64_t newUavMask = m_state.lazy.bindingsUsed[ShaderStage].uavMask;\n\n    if (ShaderStage != D3D11ShaderType::eCompute && oldUavMask != newUavMask) {\n      constexpr D3D11ShaderType ps = D3D11ShaderType::ePixel;\n\n      // Since dirty UAVs are only tracked on the PS mask, we need to mark the\n      // stage as dirty if any of the used UAVs overlap with the dirty PS mask.\n      if (m_state.lazy.bindingsDirty[ps].uavMask & newUavMask)\n        m_state.lazy.shadersDirty.set(ShaderStage);\n\n      // Accumulate graphics UAV mask and write it back to the pixel shader mask.\n      m_state.lazy.graphicsUavShaders.clr(ShaderStage);\n\n      for (uint32_t stageIndex : bit::BitMask(uint32_t(m_state.lazy.graphicsUavShaders.raw())))\n        newUavMask |= m_state.lazy.bindingsUsed[D3D11ShaderType(stageIndex)].uavMask;\n\n      m_state.lazy.bindingsUsed[ps].uavMask = newUavMask;\n\n      // Update bit mask of shaders actively accessing graphics UAVs\n      if (newUavMask)\n        m_state.lazy.graphicsUavShaders.set(ShaderStage);\n    }\n  }\n\n\n  static VkDepthBiasRepresentationEXT FormatToDepthBiasRepresentation(DXGI_FORMAT format) {\n    switch (format) {\n      default:\n      case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:\n      case DXGI_FORMAT_D32_FLOAT:\n        return VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT;\n\n      case DXGI_FORMAT_D24_UNORM_S8_UINT:\n      case DXGI_FORMAT_D16_UNORM:\n        return VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT;\n    }\n  }\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindFramebuffer() {\n    DxvkDepthBiasRepresentation depthBiasRepresentation =\n      { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT,\n        m_device->features().extDepthBiasControl.depthBiasExact };\n    DxvkRenderTargets attachments;\n    uint32_t sampleCount = 0;\n\n    // D3D11 doesn't have the concept of a framebuffer object,\n    // so we'll just create a new one every time the render\n    // target bindings are updated. Set up the attachments.\n    for (UINT i = 0; i < m_state.om.rtvs.size(); i++) {\n      if (m_state.om.rtvs[i] != nullptr) {\n        attachments.color[i].view = m_state.om.rtvs[i]->GetImageView();\n        sampleCount = m_state.om.rtvs[i]->GetSampleCount();\n      }\n    }\n\n    if (m_state.om.dsv != nullptr) {\n      attachments.depth.view = m_state.om.dsv->GetImageView();\n      sampleCount = m_state.om.dsv->GetSampleCount();\n\n      if (m_device->features().extDepthBiasControl.leastRepresentableValueForceUnormRepresentation)\n        depthBiasRepresentation.depthBiasRepresentation = FormatToDepthBiasRepresentation(m_state.om.dsv->GetViewFormat());\n    }\n\n    // Create and bind the framebuffer object to the context\n    EmitCs([\n      cAttachments    = std::move(attachments),\n      cRepresentation = depthBiasRepresentation\n    ] (DxvkContext* ctx) mutable {\n      ctx->setDepthBiasRepresentation(cRepresentation);\n      ctx->bindRenderTargets(Forwarder::move(cAttachments), 0u);\n    });\n\n    // If necessary, update push constant for the sample count\n    if (m_state.om.sampleCount != sampleCount) {\n      m_state.om.sampleCount = sampleCount;\n      ApplyRasterizerSampleCount();\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindDrawBuffers(\n          D3D11Buffer*                     pBufferForArgs,\n          D3D11Buffer*                     pBufferForCount) {\n    EmitCs([\n      cArgBuffer = pBufferForArgs  ? pBufferForArgs->GetBufferSlice()  : DxvkBufferSlice(),\n      cCntBuffer = pBufferForCount ? pBufferForCount->GetBufferSlice() : DxvkBufferSlice()\n    ] (DxvkContext* ctx) mutable {\n      ctx->bindDrawBuffers(\n        Forwarder::move(cArgBuffer),\n        Forwarder::move(cCntBuffer));\n    });\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindVertexBuffer(\n          UINT                              Slot,\n          D3D11Buffer*                      pBuffer,\n          UINT                              Offset,\n          UINT                              Stride) {\n    if (pBuffer) {\n      EmitCs([\n        cSlotId       = Slot,\n        cBufferSlice  = pBuffer->GetBufferSlice(Offset),\n        cStride       = Stride\n      ] (DxvkContext* ctx) mutable {\n        ctx->bindVertexBuffer(cSlotId,\n          Forwarder::move(cBufferSlice),\n          cStride);\n      });\n    } else {\n      EmitCs([\n        cSlotId       = Slot\n      ] (DxvkContext* ctx) {\n        ctx->bindVertexBuffer(cSlotId, DxvkBufferSlice(), 0);\n      });\n    }\n  }\n  \n  \n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindVertexBufferRange(\n          UINT                              Slot,\n          D3D11Buffer*                      pBuffer,\n          UINT                              Offset,\n          UINT                              Stride) {\n    if (pBuffer) {\n      VkDeviceSize offset = Offset;\n      VkDeviceSize length = pBuffer->GetRemainingSize(Offset);\n\n      EmitCs([\n        cSlotId       = Slot,\n        cBufferOffset = offset,\n        cBufferLength = length,\n        cStride       = Stride\n      ] (DxvkContext* ctx) mutable {\n        ctx->bindVertexBufferRange(cSlotId,\n          cBufferOffset, cBufferLength, cStride);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindIndexBuffer(\n          D3D11Buffer*                      pBuffer,\n          UINT                              Offset,\n          DXGI_FORMAT                       Format) {\n    VkIndexType indexType = Format == DXGI_FORMAT_R16_UINT\n      ? VK_INDEX_TYPE_UINT16\n      : VK_INDEX_TYPE_UINT32;\n\n    if (pBuffer) {\n      EmitCs([\n        cBufferSlice  = pBuffer->GetBufferSlice(Offset),\n        cIndexType    = indexType\n      ] (DxvkContext* ctx) mutable {\n        ctx->bindIndexBuffer(\n          Forwarder::move(cBufferSlice),\n          cIndexType);\n      });\n    } else {\n      EmitCs([\n        cIndexType    = indexType\n      ] (DxvkContext* ctx) {\n        ctx->bindIndexBuffer(DxvkBufferSlice(), cIndexType);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindIndexBufferRange(\n          D3D11Buffer*                      pBuffer,\n          UINT                              Offset,\n          DXGI_FORMAT                       Format) {\n    if (pBuffer) {\n      VkIndexType indexType = Format == DXGI_FORMAT_R16_UINT\n        ? VK_INDEX_TYPE_UINT16\n        : VK_INDEX_TYPE_UINT32;\n\n      VkDeviceSize offset = Offset;\n      VkDeviceSize length = pBuffer->GetRemainingSize(Offset);\n\n      EmitCs([\n        cBufferOffset = offset,\n        cBufferLength = length,\n        cIndexType    = indexType\n      ] (DxvkContext* ctx) mutable {\n        ctx->bindIndexBufferRange(\n          cBufferOffset, cBufferLength,\n          cIndexType);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindXfbBuffer(\n          UINT                              Slot,\n          D3D11Buffer*                      pBuffer,\n          UINT                              Offset) {\n    if (pBuffer) {\n      EmitCs([\n        cSlotId       = Slot,\n        cOffset       = Offset,\n        cBufferSlice  = pBuffer->GetBufferSlice(),\n        cCounterSlice = pBuffer->GetSOCounter()\n      ] (DxvkContext* ctx) mutable {\n        if (cCounterSlice.defined() && cOffset != ~0u) {\n          ctx->updateBuffer(\n            cCounterSlice.buffer(),\n            cCounterSlice.offset(),\n            sizeof(cOffset),\n            &cOffset);\n        }\n\n        ctx->bindXfbBuffer(cSlotId,\n          Forwarder::move(cBufferSlice),\n          Forwarder::move(cCounterSlice));\n      });\n    } else {\n      EmitCs([\n        cSlotId       = Slot\n      ] (DxvkContext* ctx) {\n        ctx->bindXfbBuffer(cSlotId,\n          DxvkBufferSlice(),\n          DxvkBufferSlice());\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindConstantBuffer(\n          D3D11ShaderType                   ShaderStage,\n          UINT                              Slot,\n          D3D11Buffer*                      pBuffer,\n          UINT                              Offset,\n          UINT                              Length) {\n    uint32_t slotId = D3D11ShaderResourceMapping::computeCbvBinding(ShaderStage, Slot);\n\n    if (pBuffer) {\n      EmitCs([\n        cSlotId      = slotId,\n        cStage       = GetShaderStage(ShaderStage),\n        cBufferSlice = pBuffer->GetBufferSlice(16 * Offset, 16 * Length)\n      ] (DxvkContext* ctx) mutable {\n        ctx->bindUniformBuffer(cStage, cSlotId,\n          Forwarder::move(cBufferSlice));\n      });\n    } else {\n      EmitCs([\n        cSlotId      = slotId,\n        cStage       = GetShaderStage(ShaderStage)\n      ] (DxvkContext* ctx) {\n        ctx->bindUniformBuffer(cStage, cSlotId, DxvkBufferSlice());\n      });\n    }\n  }\n  \n  \n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindConstantBufferRange(\n          D3D11ShaderType                   ShaderStage,\n          UINT                              Slot,\n          UINT                              Offset,\n          UINT                              Length) {\n    uint32_t slotId = D3D11ShaderResourceMapping::computeCbvBinding(ShaderStage, Slot);\n\n    EmitCs([\n      cSlotId = slotId,\n      cStage  = GetShaderStage(ShaderStage),\n      cOffset = 16u * Offset,\n      cLength = 16u * Length\n    ] (DxvkContext* ctx) {\n      ctx->bindUniformBufferRange(cStage, cSlotId, cOffset, cLength);\n    });\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindSampler(\n          D3D11ShaderType                   ShaderStage,\n          UINT                              Slot,\n          D3D11SamplerState*                pSampler) {\n    uint32_t slotId = D3D11ShaderResourceMapping::computeSamplerBinding(ShaderStage, Slot);\n\n    if (pSampler) {\n      EmitCs([\n        cSlotId   = slotId,\n        cStage    = GetShaderStage(ShaderStage),\n        cSampler  = pSampler->GetDXVKSampler()\n      ] (DxvkContext* ctx) mutable {\n        ctx->bindResourceSampler(cStage, cSlotId,\n          Forwarder::move(cSampler));\n      });\n    } else {\n      EmitCs([\n        cSlotId   = slotId,\n        cStage    = GetShaderStage(ShaderStage)\n      ] (DxvkContext* ctx) {\n        ctx->bindResourceSampler(cStage, cSlotId, nullptr);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindShaderResource(\n          D3D11ShaderType                   ShaderStage,\n          UINT                              Slot,\n          D3D11ShaderResourceView*          pResource) {\n    uint32_t slotId = D3D11ShaderResourceMapping::computeSrvBinding(ShaderStage, Slot);\n\n    if (pResource) {\n      if (pResource->GetViewInfo().Dimension != D3D11_RESOURCE_DIMENSION_BUFFER) {\n        EmitCs([\n          cSlotId = slotId,\n          cStage  = GetShaderStage(ShaderStage),\n          cView   = pResource->GetImageView()\n        ] (DxvkContext* ctx) mutable {\n          ctx->bindResourceImageView(cStage, cSlotId,\n            Forwarder::move(cView));\n        });\n      } else {\n        EmitCs([\n          cSlotId = slotId,\n          cStage  = GetShaderStage(ShaderStage),\n          cView   = pResource->GetBufferView()\n        ] (DxvkContext* ctx) mutable {\n          ctx->bindResourceBufferView(cStage, cSlotId,\n            Forwarder::move(cView));\n        });\n      }\n    } else {\n      EmitCs([\n        cSlotId = slotId,\n        cStage  = GetShaderStage(ShaderStage)\n      ] (DxvkContext* ctx) {\n        ctx->bindResourceImageView(cStage, cSlotId, nullptr);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::BindUnorderedAccessView(\n          D3D11ShaderType                   ShaderStage,\n          UINT                              Slot,\n          D3D11UnorderedAccessView*         pUav) {\n    uint32_t uavSlotId = D3D11ShaderResourceMapping::computeUavBinding(ShaderStage, Slot);\n    uint32_t ctrSlotId = D3D11ShaderResourceMapping::computeUavCounterBinding(ShaderStage, Slot);\n\n    VkShaderStageFlags stages = ShaderStage == D3D11ShaderType::eCompute\n      ? VK_SHADER_STAGE_COMPUTE_BIT\n      : VK_SHADER_STAGE_ALL_GRAPHICS;\n\n    if (pUav) {\n      if (pUav->GetViewInfo().Dimension == D3D11_RESOURCE_DIMENSION_BUFFER) {\n        EmitCs([\n          cUavSlotId    = uavSlotId,\n          cCtrSlotId    = ctrSlotId,\n          cStages       = stages,\n          cBufferView   = pUav->GetBufferView(),\n          cCounterView  = pUav->GetCounterView()\n        ] (DxvkContext* ctx) mutable {\n          ctx->bindResourceBufferView(cStages, cUavSlotId,\n            Forwarder::move(cBufferView));\n          ctx->bindResourceBufferView(cStages, cCtrSlotId,\n            Forwarder::move(cCounterView));\n        });\n      } else {\n        EmitCs([\n          cUavSlotId    = uavSlotId,\n          cCtrSlotId    = ctrSlotId,\n          cStages       = stages,\n          cImageView    = pUav->GetImageView()\n        ] (DxvkContext* ctx) mutable {\n          ctx->bindResourceImageView(cStages, cUavSlotId,\n            Forwarder::move(cImageView));\n          ctx->bindResourceBufferView(cStages, cCtrSlotId, nullptr);\n        });\n      }\n    } else {\n      EmitCs([\n        cUavSlotId    = uavSlotId,\n        cCtrSlotId    = ctrSlotId,\n        cStages       = stages\n      ] (DxvkContext* ctx) {\n        ctx->bindResourceImageView(cStages, cUavSlotId, nullptr);\n        ctx->bindResourceBufferView(cStages, cCtrSlotId, nullptr);\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ClearImageView(\n          Rc<DxvkImageView>                 View,\n    const FLOAT                             Color[4],\n    const D3D11_RECT*                       pRects,\n          UINT                              NumRects) {\n    // 3D views are unsupported\n    if (View->info().viewType == VK_IMAGE_VIEW_TYPE_3D)\n      return;\n\n    // Convert clear value\n    auto clearValue = ConvertColorValue(Color, View->formatInfo());\n\n    VkExtent3D extent3D = View->mipLevelExtent(0);\n    VkExtent2D extent2D = { extent3D.width, extent3D.height };\n\n    // Figure out which plane we're clearing for subsampling\n    const DxvkPlaneFormatInfo* plane = nullptr;\n    auto imageFormatInfo = View->image()->formatInfo();\n\n    if (imageFormatInfo->flags.test(DxvkFormatFlag::MultiPlane))\n      plane = &imageFormatInfo->planes[vk::getPlaneIndex(View->info().aspects)];\n\n    // Clear all non-empty rectangles\n    EmitCsCmd<VkRect2D>(D3D11CmdType::None, std::max(NumRects, 1u), [\n      cView       = std::move(View),\n      cClearValue = clearValue\n    ] (DxvkContext* ctx, const VkRect2D* rects, size_t count) {\n      constexpr VkImageUsageFlags rtUsage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n      VkImageAspectFlags clearAspect = cView->formatInfo()->aspectMask & (VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT);\n\n      for (size_t i = 0; i < count; i++) {\n        VkOffset3D offset = { rects[i].offset.x, rects[i].offset.y, 0 };\n        VkExtent3D extent = { rects[i].extent.width, rects[i].extent.height, 1u };\n\n        if (extent.width && extent.height) {\n          bool isFullSize = cView->mipLevelExtent(0) == extent;\n\n          if ((cView->info().usage & rtUsage) && isFullSize)\n            ctx->clearRenderTarget(cView, clearAspect, cClearValue, 0u);\n          else\n            ctx->clearImageView(cView, offset, extent, clearAspect, cClearValue);\n        }\n      }\n    });\n\n    if (NumRects) {\n      for (uint32_t i = 0; i < NumRects; i++) {\n        D3D11_RECT subsampledRect = pRects[i];\n\n        if (plane) {\n          subsampledRect.left   /= plane->blockSize.width;\n          subsampledRect.top    /= plane->blockSize.height;\n          subsampledRect.right  /= plane->blockSize.width;\n          subsampledRect.bottom /= plane->blockSize.height;\n        }\n\n        new (m_csData->at(i)) VkRect2D(ConvertRect(subsampledRect, extent2D));\n      }\n    } else {\n      auto vkRect = new (m_csData->first()) VkRect2D();\n      vkRect->offset = VkOffset2D { 0, 0 };\n      vkRect->extent = extent2D;\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ClearBufferView(\n          Rc<DxvkBufferView>                View,\n    const FLOAT                             Color[4],\n    const D3D11_RECT*                       pRects,\n          UINT                              NumRects) {\n    // Convert clear value\n    auto formatInfo = View->formatInfo();\n    auto clearValue = ConvertColorValue(Color, formatInfo);\n\n    // Just pass the rectangles through, even though we only need one dimension\n    VkExtent2D extent2D = { uint32_t(View->info().size / formatInfo->elementSize), 1u };\n\n    EmitCsCmd<VkRect2D>(D3D11CmdType::None, std::max(NumRects, 1u), [\n      cView       = std::move(View),\n      cClearValue = clearValue\n    ] (DxvkContext* ctx, const VkRect2D* rects, size_t count) {\n      for (size_t i = 0; i < count; i++) {\n        if (rects[i].extent.width) {\n          ctx->clearBufferView(cView,\n            rects[i].offset.x, rects[i].extent.width,\n            cClearValue.color);\n        }\n      }\n    });\n\n    if (NumRects) {\n      for (uint32_t i = 0; i < NumRects; i++)\n        new (m_csData->at(i)) VkRect2D(ConvertRect(pRects[i], extent2D));\n    } else {\n      auto vkRect = new (m_csData->first()) VkRect2D();\n      vkRect->offset = VkOffset2D { 0, 0 };\n      vkRect->extent = extent2D;\n    }\n  }\n\n\n  template<typename ContextType>\n  VkClearValue D3D11CommonContext<ContextType>::ConvertColorValue(\n    const FLOAT                             Color[4],\n    const DxvkFormatInfo*                   pFormatInfo) {\n    VkClearValue result;\n\n    if (pFormatInfo->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {\n      for (uint32_t i = 0; i < 4; i++) {\n        if (pFormatInfo->flags.test(DxvkFormatFlag::SampledUInt))\n          result.color.uint32[i] = uint32_t(std::max(0.0f, Color[i]));\n        else if (pFormatInfo->flags.test(DxvkFormatFlag::SampledSInt))\n          result.color.int32[i] = int32_t(Color[i]);\n        else\n          result.color.float32[i] = Color[i];\n      }\n    } else {\n      result.depthStencil.depth = Color[0];\n      result.depthStencil.stencil = 0;\n    }\n\n    return result;\n  }\n\n\n  template<typename ContextType>\n  VkRect2D D3D11CommonContext<ContextType>::ConvertRect(\n          D3D11_RECT                        Rect,\n          VkExtent2D                        Extent) {\n    Rect.left   = std::max<int32_t>(Rect.left,   0);\n    Rect.top    = std::max<int32_t>(Rect.top,    0);\n    Rect.right  = std::min<int32_t>(Rect.right,  Extent.width);\n    Rect.bottom = std::min<int32_t>(Rect.bottom, Extent.height);\n\n    if (Rect.left >= Rect.right || Rect.top >= Rect.bottom)\n      return VkRect2D();\n\n    VkRect2D result = { };\n    result.offset.x = Rect.left;\n    result.offset.y = Rect.top;\n    result.extent.width = Rect.right - Rect.left;\n    result.extent.height = Rect.bottom - Rect.top;\n    return result;\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::CopyBuffer(\n          D3D11Buffer*                      pDstBuffer,\n          VkDeviceSize                      DstOffset,\n          D3D11Buffer*                      pSrcBuffer,\n          VkDeviceSize                      SrcOffset,\n          VkDeviceSize                      ByteCount) {\n    // Clamp copy region to prevent out-of-bounds access\n    VkDeviceSize dstLength = pDstBuffer->Desc()->ByteWidth;\n    VkDeviceSize srcLength = pSrcBuffer->Desc()->ByteWidth;\n\n    if (SrcOffset >= srcLength || DstOffset >= dstLength || !ByteCount)\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    ByteCount = std::min(dstLength - DstOffset, ByteCount);\n    ByteCount = std::min(srcLength - SrcOffset, ByteCount);\n\n    EmitCs([\n      cDstBuffer = pDstBuffer->GetBufferSlice(DstOffset, ByteCount),\n      cSrcBuffer = pSrcBuffer->GetBufferSlice(SrcOffset, ByteCount)\n    ] (DxvkContext* ctx) {\n      if (cDstBuffer.buffer() != cSrcBuffer.buffer()) {\n        ctx->copyBuffer(\n          cDstBuffer.buffer(),\n          cDstBuffer.offset(),\n          cSrcBuffer.buffer(),\n          cSrcBuffer.offset(),\n          cSrcBuffer.length());\n      } else {\n        ctx->copyBufferRegion(\n          cDstBuffer.buffer(),\n          cDstBuffer.offset(),\n          cSrcBuffer.offset(),\n          cSrcBuffer.length());\n      }\n    });\n\n    if (pDstBuffer->HasSequenceNumber())\n      GetTypedContext()->TrackBufferSequenceNumber(pDstBuffer);\n    if (pSrcBuffer->HasSequenceNumber())\n      GetTypedContext()->TrackBufferSequenceNumber(pSrcBuffer);\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::CopyImage(\n          D3D11CommonTexture*               pDstTexture,\n    const VkImageSubresourceLayers*         pDstLayers,\n          VkOffset3D                        DstOffset,\n          D3D11CommonTexture*               pSrcTexture,\n    const VkImageSubresourceLayers*         pSrcLayers,\n          VkOffset3D                        SrcOffset,\n          VkExtent3D                        SrcExtent) {\n    // Image formats must be size-compatible\n    auto dstFormatInfo = lookupFormatInfo(pDstTexture->GetPackedFormat());\n    auto srcFormatInfo = lookupFormatInfo(pSrcTexture->GetPackedFormat());\n\n    if (dstFormatInfo->elementSize != srcFormatInfo->elementSize)\n      return;\n\n    // Sample counts must match\n    if (pDstTexture->Desc()->SampleDesc.Count != pSrcTexture->Desc()->SampleDesc.Count)\n      return;\n\n    // Obviously, the copy region must not be empty\n    VkExtent3D dstMipExtent = pDstTexture->MipLevelExtent(pDstLayers->mipLevel);\n    VkExtent3D srcMipExtent = pSrcTexture->MipLevelExtent(pSrcLayers->mipLevel);\n\n    if (uint32_t(DstOffset.x) >= dstMipExtent.width\n     || uint32_t(DstOffset.y) >= dstMipExtent.height\n     || uint32_t(DstOffset.z) >= dstMipExtent.depth)\n      return;\n\n    if (uint32_t(SrcOffset.x) >= srcMipExtent.width\n     || uint32_t(SrcOffset.y) >= srcMipExtent.height\n     || uint32_t(SrcOffset.z) >= srcMipExtent.depth)\n      return;\n\n    // Don't perform the copy if the offsets aren't block-aligned\n    if (!util::isBlockAligned(SrcOffset, srcFormatInfo->blockSize)\n     || !util::isBlockAligned(DstOffset, dstFormatInfo->blockSize))\n      return;\n\n    // Clamp the image region in order to avoid out-of-bounds access\n    VkExtent3D blockCount    = util::computeBlockCount(SrcExtent, srcFormatInfo->blockSize);\n    VkExtent3D dstBlockCount = util::computeMaxBlockCount(DstOffset, dstMipExtent, dstFormatInfo->blockSize);\n    VkExtent3D srcBlockCount = util::computeMaxBlockCount(SrcOffset, srcMipExtent, srcFormatInfo->blockSize);\n\n    blockCount = util::minExtent3D(blockCount, dstBlockCount);\n    blockCount = util::minExtent3D(blockCount, srcBlockCount);\n\n    SrcExtent = util::computeBlockExtent(blockCount, srcFormatInfo->blockSize);\n    SrcExtent = util::snapExtent3D(SrcOffset, SrcExtent, srcMipExtent);\n\n    if (!SrcExtent.width || !SrcExtent.height || !SrcExtent.depth)\n      return;\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    // While copying between 2D and 3D images is allowed in CopySubresourceRegion,\n    // copying more than one slice at a time is not suppoted. Layer counts are 1.\n    if ((pDstTexture->GetVkImageType() == VK_IMAGE_TYPE_3D)\n     != (pSrcTexture->GetVkImageType() == VK_IMAGE_TYPE_3D))\n      SrcExtent.depth = 1;\n\n    // Certain types of copies require us to pass the destination extent to\n    // the backend. This may be different when copying between compressed\n    // and uncompressed image formats.\n    VkExtent3D dstExtent = util::computeBlockExtent(blockCount, dstFormatInfo->blockSize);\n    dstExtent = util::snapExtent3D(DstOffset, dstExtent, dstMipExtent);\n\n    // It is possible for any of the given images to be a staging image with\n    // no actual image, so we need to account for all possibilities here.\n    bool dstIsImage = pDstTexture->HasImage();\n    bool srcIsImage = pSrcTexture->HasImage();\n\n    if (dstIsImage && srcIsImage) {\n      EmitCs([\n        cDstImage  = pDstTexture->GetImage(),\n        cSrcImage  = pSrcTexture->GetImage(),\n        cDstLayers = *pDstLayers,\n        cSrcLayers = *pSrcLayers,\n        cDstOffset = DstOffset,\n        cSrcOffset = SrcOffset,\n        cExtent    = SrcExtent\n      ] (DxvkContext* ctx) {\n        // CopyResource can only copy between different images, and\n        // CopySubresourceRegion can only copy data from one single\n        // subresource at a time, so this check is safe.\n        if (cDstImage != cSrcImage || cDstLayers != cSrcLayers) {\n          ctx->copyImage(\n            cDstImage, cDstLayers, cDstOffset,\n            cSrcImage, cSrcLayers, cSrcOffset,\n            cExtent);\n        } else {\n          ctx->copyImageRegion(\n            cDstImage, cDstLayers, cDstOffset,\n            cSrcOffset, cExtent);\n        }\n      });\n    } else {\n      // Since each subresource uses a dedicated buffer, we are going\n      // to need one call per subresource for staging resource copies\n      for (uint32_t i = 0; i < pDstLayers->layerCount; i++) {\n        uint32_t dstSubresource = D3D11CalcSubresource(pDstLayers->mipLevel, pDstLayers->baseArrayLayer + i, pDstTexture->Desc()->MipLevels);\n        uint32_t srcSubresource = D3D11CalcSubresource(pSrcLayers->mipLevel, pSrcLayers->baseArrayLayer + i, pSrcTexture->Desc()->MipLevels);\n\n        // For multi-plane image data stored in a buffer, the backend\n        // assumes that the second plane immediately follows the first\n        // plane in memory, which is only true if we copy the full image.\n        uint32_t planeCount = 1;\n\n        if (dstFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n          bool needsSeparateCopies = !dstIsImage && !srcIsImage;\n\n          if (!dstIsImage)\n            needsSeparateCopies |= pDstTexture->MipLevelExtent(pDstLayers->mipLevel) != SrcExtent;\n          if (!srcIsImage)\n            needsSeparateCopies |= pSrcTexture->MipLevelExtent(pSrcLayers->mipLevel) != SrcExtent;\n\n          if (needsSeparateCopies)\n            planeCount = vk::getPlaneCount(srcFormatInfo->aspectMask);\n        }\n\n        for (uint32_t j = 0; j < planeCount; j++) {\n          VkImageAspectFlags dstAspectMask = dstFormatInfo->aspectMask;\n          VkImageAspectFlags srcAspectMask = srcFormatInfo->aspectMask;\n\n          if (planeCount > 1) {\n            dstAspectMask = vk::getPlaneAspect(j);\n            srcAspectMask = dstAspectMask;\n          }\n\n          if (dstIsImage) {\n            VkImageSubresourceLayers dstLayer = { dstAspectMask,\n              pDstLayers->mipLevel, pDstLayers->baseArrayLayer + i, 1 };\n\n            EmitCs([\n              cDstImage   = pDstTexture->GetImage(),\n              cDstLayers  = dstLayer,\n              cDstOffset  = DstOffset,\n              cDstExtent  = dstExtent,\n              cDstFormat  = pDstTexture->GetPackedFormat(),\n              cSrcBuffer  = pSrcTexture->GetMappedBuffer(srcSubresource),\n              cSrcLayout  = pSrcTexture->GetSubresourceLayout(srcAspectMask, srcSubresource),\n              cSrcOffset  = pSrcTexture->ComputeMappedOffset(srcSubresource, j, SrcOffset),\n              cSrcCoord   = SrcOffset,\n              cSrcExtent  = srcMipExtent\n            ] (DxvkContext* ctx) {\n              ctx->copyBufferToImage(cDstImage, cDstLayers, cDstOffset, cDstExtent,\n                cSrcBuffer, cSrcOffset, cSrcLayout.RowPitch, cSrcLayout.DepthPitch,\n                cDstFormat);\n            });\n          } else if (srcIsImage) {\n            VkImageSubresourceLayers srcLayer = { srcAspectMask,\n              pSrcLayers->mipLevel, pSrcLayers->baseArrayLayer + i, 1 };\n\n            EmitCs([\n              cSrcImage   = pSrcTexture->GetImage(),\n              cSrcFormat  = pSrcTexture->GetPackedFormat(),\n              cSrcLayers  = srcLayer,\n              cSrcOffset  = SrcOffset,\n              cSrcExtent  = SrcExtent,\n              cDstBuffer  = pDstTexture->GetMappedBuffer(dstSubresource),\n              cDstLayout  = pDstTexture->GetSubresourceLayout(dstAspectMask, dstSubresource),\n              cDstOffset  = pDstTexture->ComputeMappedOffset(dstSubresource, j, DstOffset),\n              cDstCoord   = DstOffset,\n              cDstExtent  = dstMipExtent\n            ] (DxvkContext* ctx) {\n              ctx->copyImageToBuffer(cDstBuffer, cDstOffset, cDstLayout.RowPitch,\n                cDstLayout.DepthPitch, cSrcFormat, cSrcImage, cSrcLayers, cSrcOffset, cSrcExtent);\n            });\n          } else {\n            // The backend is not aware of image metadata in this case,\n            // so we need to handle image planes and block sizes here\n            VkDeviceSize elementSize = dstFormatInfo->elementSize;\n            VkExtent3D dstBlockSize = dstFormatInfo->blockSize;\n            VkExtent3D srcBlockSize = srcFormatInfo->blockSize;\n            VkExtent3D planeBlockSize = { 1u, 1u, 1u };\n\n            if (planeCount > 1) {\n              auto plane = &dstFormatInfo->planes[j];\n              dstBlockSize.width  *= plane->blockSize.width;\n              dstBlockSize.height *= plane->blockSize.height;\n              srcBlockSize.width  *= plane->blockSize.width;\n              srcBlockSize.height *= plane->blockSize.height;\n\n              planeBlockSize.width  = plane->blockSize.width;\n              planeBlockSize.height = plane->blockSize.height;\n              elementSize = plane->elementSize;\n            }\n\n            EmitCs([\n              cPixelSize = elementSize,\n              cSrcBuffer = pSrcTexture->GetMappedBuffer(srcSubresource),\n              cSrcStart  = pSrcTexture->GetSubresourceLayout(srcAspectMask, srcSubresource).Offset,\n              cSrcOffset = util::computeBlockOffset(SrcOffset, srcBlockSize),\n              cSrcSize   = util::computeBlockCount(srcMipExtent, srcBlockSize),\n              cDstBuffer = pDstTexture->GetMappedBuffer(dstSubresource),\n              cDstStart  = pDstTexture->GetSubresourceLayout(dstAspectMask, dstSubresource).Offset,\n              cDstOffset = util::computeBlockOffset(DstOffset, dstBlockSize),\n              cDstSize   = util::computeBlockCount(dstMipExtent, dstBlockSize),\n              cExtent    = util::computeBlockCount(blockCount, planeBlockSize)\n            ] (DxvkContext* ctx) {\n              ctx->copyPackedBufferImage(\n                cDstBuffer, cDstStart, cDstOffset, cDstSize,\n                cSrcBuffer, cSrcStart, cSrcOffset, cSrcSize,\n                cExtent, cPixelSize);\n            });\n          }\n        }\n      }\n    }\n\n    if (pDstTexture->HasSequenceNumber()) {\n      for (uint32_t i = 0; i < pDstLayers->layerCount; i++) {\n        GetTypedContext()->TrackTextureSequenceNumber(pDstTexture, D3D11CalcSubresource(\n          pDstLayers->mipLevel, pDstLayers->baseArrayLayer + i, pDstTexture->Desc()->MipLevels));\n      }\n    }\n\n    if (pSrcTexture->HasSequenceNumber()) {\n      for (uint32_t i = 0; i < pSrcLayers->layerCount; i++) {\n        GetTypedContext()->TrackTextureSequenceNumber(pSrcTexture, D3D11CalcSubresource(\n          pSrcLayers->mipLevel, pSrcLayers->baseArrayLayer + i, pSrcTexture->Desc()->MipLevels));\n      }\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::CopyTiledResourceData(\n          ID3D11Resource*                   pResource,\n    const D3D11_TILED_RESOURCE_COORDINATE*  pRegionCoordinate,\n    const D3D11_TILE_REGION_SIZE*           pRegionSize,\n          DxvkBufferSlice                   BufferSlice,\n          UINT                              Flags) {\n    Rc<DxvkPagedResource> resource = GetPagedResource(pResource);\n\n    // Do some validation based on page table properties\n    auto pageTable = resource->getSparsePageTable();\n\n    if (!pageTable)\n      return;\n\n    if (pRegionSize->bUseBox && pRegionSize->NumTiles !=\n        pRegionSize->Width * pRegionSize->Height * pRegionSize->Depth)\n      return;\n\n    if (pRegionSize->NumTiles > pageTable->getPageCount())\n      return;\n\n    // Ignore call if buffer access would be out of bounds\n    VkDeviceSize bufferSize = pRegionSize->NumTiles * SparseMemoryPageSize;\n\n    if (BufferSlice.length() < bufferSize)\n      return;\n\n    // Compute list of tile indices to copy\n    std::vector<uint32_t> tiles(pRegionSize->NumTiles);\n\n    for (uint32_t i = 0; i < pRegionSize->NumTiles; i++) {\n      VkOffset3D regionOffset = {\n        int32_t(pRegionCoordinate->X),\n        int32_t(pRegionCoordinate->Y),\n        int32_t(pRegionCoordinate->Z) };\n\n      VkExtent3D regionExtent = {\n        uint32_t(pRegionSize->Width),\n        uint32_t(pRegionSize->Height),\n        uint32_t(pRegionSize->Depth) };\n\n      uint32_t tile = pageTable->computePageIndex(\n        pRegionCoordinate->Subresource, regionOffset,\n        regionExtent, !pRegionSize->bUseBox, i);\n\n      // Check that the tile is valid and not part of the mip tail\n      auto tileInfo = pageTable->getPageInfo(tile);\n\n      if (tileInfo.type != DxvkSparsePageType::Buffer\n       && tileInfo.type != DxvkSparsePageType::Image)\n        return;\n\n      tiles[i] = tile;\n    }\n\n    AddCost(GpuCostEstimate::Transfer);\n\n    // If D3D12 is anything to go by, not passing this flag will trigger\n    // the other code path, regardless of whether TO_LINEAR_BUFFER is set.\n    if (Flags & D3D11_TILE_COPY_LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE) {\n      EmitCs([\n        cResource = std::move(resource),\n        cTiles    = std::move(tiles),\n        cBuffer   = std::move(BufferSlice)\n      ] (DxvkContext* ctx) {\n        ctx->copySparsePagesFromBuffer(\n          cResource,\n          cTiles.size(),\n          cTiles.data(),\n          cBuffer.buffer(),\n          cBuffer.offset());\n      });\n    } else {\n      EmitCs([\n        cResource = std::move(resource),\n        cTiles    = std::move(tiles),\n        cBuffer   = std::move(BufferSlice)\n      ] (DxvkContext* ctx) {\n        ctx->copySparsePagesToBuffer(\n          cBuffer.buffer(),\n          cBuffer.offset(),\n          cResource,\n          cTiles.size(),\n          cTiles.data());\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  template<typename T>\n  bool D3D11CommonContext<ContextType>::DirtyBindingGeneric(\n          D3D11ShaderType                   ShaderStage,\n          T                                 BoundMask,\n          T&                                DirtyMask,\n          T                                 DirtyBit,\n          bool                              IsNull) {\n    // Forward immediately if lazy binding is forced off\n    if (DebugLazyBinding == Tristate::False)\n      return false;\n\n    if ((BoundMask & ~DirtyMask) & DirtyBit) {\n      // If we're binding a non-null resource to an active slot that has not been\n      // marked for lazy binding yet, forward the call immediately in order to\n      // avoid tracking overhead. This is by far the most common case.\n      if (likely(!IsNull && DebugLazyBinding != Tristate::True))\n        return false;\n\n      // If we are binding a null resource to an active slot, the app will likely\n      // either bind something else or bind a shader that does not use this slot.\n      // In that case, avoid likely redundant CS traffic and apply the binding on\n      // the next draw.\n      m_state.lazy.shadersDirty.set(ShaderStage);\n    }\n\n    // Binding is either inactive or already dirty. In the inactive case, there\n    // is no need to mark the shader stage as dirty since binding a shader that\n    // activates the binding will implicitly do so.\n    DirtyMask |= DirtyBit;\n    return true;\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::DirtyConstantBuffer(\n          D3D11ShaderType                   ShaderStage,\n          uint32_t                          Slot,\n          bool                              IsNull) {\n    return DirtyBindingGeneric(ShaderStage,\n      m_state.lazy.bindingsUsed[ShaderStage].cbvMask,\n      m_state.lazy.bindingsDirty[ShaderStage].cbvMask,\n      1u << Slot, IsNull);\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::DirtySampler(\n          D3D11ShaderType                   ShaderStage,\n          uint32_t                          Slot,\n          bool                              IsNull) {\n    return DirtyBindingGeneric(ShaderStage,\n      m_state.lazy.bindingsUsed[ShaderStage].samplerMask,\n      m_state.lazy.bindingsDirty[ShaderStage].samplerMask,\n      1u << Slot, IsNull);\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::DirtyShaderResource(\n          D3D11ShaderType                   ShaderStage,\n          uint32_t                          Slot,\n          bool                              IsNull) {\n    uint32_t idx = Slot / 64u;\n\n    return DirtyBindingGeneric(ShaderStage,\n      m_state.lazy.bindingsUsed[ShaderStage].srvMask[idx],\n      m_state.lazy.bindingsDirty[ShaderStage].srvMask[idx],\n      uint64_t(1u) << Slot, IsNull);\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::DirtyComputeUnorderedAccessView(\n          uint32_t                          Slot,\n          bool                              IsNull) {\n    constexpr D3D11ShaderType ShaderStage = D3D11ShaderType::eCompute;\n\n    return DirtyBindingGeneric(ShaderStage,\n      m_state.lazy.bindingsUsed[ShaderStage].uavMask,\n      m_state.lazy.bindingsDirty[ShaderStage].uavMask,\n      uint64_t(1u) << Slot, IsNull);\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::DirtyGraphicsUnorderedAccessView(\n          uint32_t                          Slot) {\n    constexpr D3D11ShaderType ShaderStage = D3D11ShaderType::ePixel;\n\n    if (DebugLazyBinding == Tristate::False)\n      return false;\n\n    // Use different logic here and always use lazy binding for graphics UAVs.\n    // Since graphics UAVs are generally bound together with render targets,\n    // looking at the active binding mask doesn't really help us here.\n    uint64_t dirtyBit = uint64_t(1u) << Slot;\n\n    if (m_state.lazy.bindingsUsed[ShaderStage].uavMask & dirtyBit) {\n      // Need to mark all graphics stages that use UAVs as dirty here to\n      // make sure that bindings actually get reapplied properly. There\n      // may be no pixel shader bound in this case, even though we do\n      // all the tracking on the pixel shader bit mask.\n      m_state.lazy.shadersDirty.set(m_state.lazy.graphicsUavShaders);\n    }\n\n    m_state.lazy.bindingsDirty[ShaderStage].uavMask |= dirtyBit;\n    return true;\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::DiscardBuffer(\n          ID3D11Resource*                   pResource) {\n    auto buffer = static_cast<D3D11Buffer*>(pResource);\n\n    if (buffer->GetMapMode() != D3D11_COMMON_BUFFER_MAP_MODE_NONE) {\n      D3D11_MAPPED_SUBRESOURCE sr;\n\n      Map(pResource, 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);\n      Unmap(pResource, 0);\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::DiscardTexture(\n          ID3D11Resource*                   pResource,\n          UINT                              Subresource) {\n    auto texture = GetCommonTexture(pResource);\n\n    if (texture->GetMapMode() != D3D11_COMMON_TEXTURE_MAP_MODE_NONE) {\n      D3D11_MAPPED_SUBRESOURCE sr;\n\n      Map(pResource, Subresource, D3D11_MAP_WRITE_DISCARD, 0, &sr);\n      Unmap(pResource, Subresource);\n    }\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::GetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer**                    ppConstantBuffers,\n          UINT*                             pFirstConstant,\n          UINT*                             pNumConstants) {\n    const auto& bindings = m_state.cbv[ShaderStage];\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      const bool inRange = StartSlot + i < bindings.buffers.size();\n\n      if (ppConstantBuffers) {\n        ppConstantBuffers[i] = inRange\n          ? bindings.buffers[StartSlot + i].buffer.ref()\n          : nullptr;\n      }\n\n      if (pFirstConstant) {\n        pFirstConstant[i] = inRange\n          ? bindings.buffers[StartSlot + i].constantOffset\n          : 0u;\n      }\n\n      if (pNumConstants) {\n        pNumConstants[i] = inRange\n          ? bindings.buffers[StartSlot + i].constantCount\n          : 0u;\n      }\n    }\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::GetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumViews,\n          ID3D11ShaderResourceView**        ppShaderResourceViews) {\n    const auto& bindings = m_state.srv[ShaderStage];\n\n    for (uint32_t i = 0; i < NumViews; i++) {\n      ppShaderResourceViews[i] = StartSlot + i < bindings.views.size()\n        ? bindings.views[StartSlot + i].ref()\n        : nullptr;\n    }\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::GetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState**              ppSamplers) {\n    const auto& bindings = m_state.samplers[ShaderStage];\n\n    for (uint32_t i = 0; i < NumSamplers; i++) {\n      ppSamplers[i] = StartSlot + i < bindings.samplers.size()\n        ? bindings.samplers[StartSlot + i].ref()\n        : nullptr;\n    }\n  }\n\n\n  template<typename ContextType>\n  DxvkGlobalPipelineBarrier D3D11CommonContext<ContextType>::GetTiledResourceDependency(\n          ID3D11DeviceChild*                pObject) {\n    if (!pObject) {\n      DxvkGlobalPipelineBarrier result;\n      result.stages = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;\n      result.access = VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT;\n      return result;\n    } else {\n      Com<ID3D11Resource> resource;\n\n      if (FAILED(pObject->QueryInterface(IID_PPV_ARGS(&resource)))) {\n        Com<ID3D11View> view;\n\n        if (FAILED(pObject->QueryInterface(IID_PPV_ARGS(&view))))\n          return DxvkGlobalPipelineBarrier();\n\n        view->GetResource(&resource);\n      }\n\n      D3D11CommonTexture* texture = GetCommonTexture(resource.ptr());\n\n      if (texture) {\n        Rc<DxvkImage> image = texture->GetImage();\n\n        DxvkGlobalPipelineBarrier result;\n        result.stages = image->info().stages;\n        result.access = image->info().access;\n        return result;\n      } else {\n        Rc<DxvkBuffer> buffer = static_cast<D3D11Buffer*>(resource.ptr())->GetBuffer();\n\n        if (buffer == nullptr)\n          return DxvkGlobalPipelineBarrier();\n\n        DxvkGlobalPipelineBarrier result;\n        result.stages = buffer->info().stages;\n        result.access = buffer->info().access;\n        return result;\n      }\n    }\n  }\n\n\n  template<typename ContextType>\n  D3D11MaxUsedBindings D3D11CommonContext<ContextType>::GetMaxUsedBindings() {\n    D3D11MaxUsedBindings result;\n\n    for (uint32_t i = 0; i < result.stages.size(); i++) {\n      auto stage = D3D11ShaderType(i);\n\n      result.stages[i].cbvCount = m_state.cbv[stage].maxCount;\n      result.stages[i].srvCount = m_state.srv[stage].maxCount;\n      result.stages[i].uavCount = 0;\n      result.stages[i].samplerCount = m_state.samplers[stage].maxCount;\n      result.stages[i].reserved = 0;\n    }\n\n    result.stages[uint32_t(D3D11ShaderType::ePixel)].uavCount = m_state.om.maxUav;\n    result.stages[uint32_t(D3D11ShaderType::eCompute)].uavCount = m_state.uav.maxCount;\n\n    result.vbCount = m_state.ia.maxVbCount;\n    result.soCount = D3D11_SO_BUFFER_SLOT_COUNT;\n    return result;\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::HasDirtyComputeBindings() {\n    return m_state.lazy.shadersDirty.test(D3D11ShaderType::eCompute);\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::HasDirtyGraphicsBindings() {\n    return (m_state.lazy.shadersDirty & m_state.lazy.shadersUsed).any(\n      D3D11ShaderType::eVertex, D3D11ShaderType::eGeometry,\n      D3D11ShaderType::eHull,   D3D11ShaderType::eDomain,\n      D3D11ShaderType::ePixel);\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ResetCommandListState() {\n    EmitCs([\n      cUsedBindings  = GetMaxUsedBindings(),\n      cMaxTessFactor = m_maxTessFactor\n    ] (DxvkContext* ctx) {\n      // Reset render targets\n      ctx->bindRenderTargets(DxvkRenderTargets(), 0u);\n\n      // Reset vertex input state\n      ctx->setInputLayout(0, nullptr, 0, nullptr);\n\n      // Reset render states\n      ctx->setInputAssemblyState(InitDefaultPrimitiveTopology());\n      ctx->setDepthStencilState(InitDefaultDepthStencilState());\n      ctx->setRasterizerState(InitDefaultRasterizerState());\n      ctx->setDepthBias(DxvkDepthBias());\n      ctx->setLogicOpState(InitDefaultLogicOpState());\n      ctx->setMultisampleState(InitDefaultMultisampleState(D3D11_DEFAULT_SAMPLE_MASK));\n\n      DxvkBlendMode cbState = InitDefaultBlendState();\n\n      for (uint32_t i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)\n        ctx->setBlendMode(i, cbState);\n\n      // Reset dynamic states\n      ctx->setBlendConstants(DxvkBlendConstants { 1.0f, 1.0f, 1.0f, 1.0f });\n      ctx->setStencilReference(D3D11_DEFAULT_STENCIL_REFERENCE);\n\n      // Reset viewports\n      DxvkViewport viewport = { };\n      ctx->setViewports(1, &viewport);\n\n      // Unbind indirect draw buffer\n      ctx->bindDrawBuffers(DxvkBufferSlice(), DxvkBufferSlice());\n\n      // Unbind index and vertex buffers\n      ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32);\n\n      for (uint32_t i = 0; i < cUsedBindings.vbCount; i++)\n        ctx->bindVertexBuffer(i, DxvkBufferSlice(), 0);\n\n      // Unbind transform feedback buffers\n      for (uint32_t i = 0; i < cUsedBindings.soCount; i++)\n        ctx->bindXfbBuffer(i, DxvkBufferSlice(), DxvkBufferSlice());\n\n      // Unbind all shaders\n      ctx->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(nullptr);\n      ctx->bindShader<VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT>(nullptr);\n      ctx->bindShader<VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT>(nullptr);\n      ctx->bindShader<VK_SHADER_STAGE_GEOMETRY_BIT>(nullptr);\n      ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(nullptr);\n      ctx->bindShader<VK_SHADER_STAGE_COMPUTE_BIT>(nullptr);\n\n      // Unbind per-shader stage resources\n      for (uint32_t i = 0; i < 6; i++) {\n        auto programType = D3D11ShaderType(i);\n        auto stage = GetShaderStage(programType);\n\n        // Unbind constant buffers, including the shader's ICB and instance data buffer\n        auto cbSlotId = D3D11ShaderResourceMapping::computeCbvBinding(programType, 0);\n        ctx->bindUniformBuffer(stage, cbSlotId + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT + 0u, DxvkBufferSlice());\n        ctx->bindUniformBuffer(stage, cbSlotId + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT + 1u, DxvkBufferSlice());\n\n        for (uint32_t j = 0; j < cUsedBindings.stages[i].cbvCount; j++)\n          ctx->bindUniformBuffer(stage, cbSlotId + j, DxvkBufferSlice());\n\n        // Unbind shader resource views\n        auto srvSlotId = D3D11ShaderResourceMapping::computeSrvBinding(programType, 0);\n\n        for (uint32_t j = 0; j < cUsedBindings.stages[i].srvCount; j++)\n          ctx->bindResourceImageView(stage, srvSlotId + j, nullptr);\n\n        // Unbind texture samplers\n        auto samplerSlotId = D3D11ShaderResourceMapping::computeSamplerBinding(programType, 0);\n\n        for (uint32_t j = 0; j < cUsedBindings.stages[i].samplerCount; j++)\n          ctx->bindResourceSampler(stage, samplerSlotId + j, nullptr);\n\n        // Unbind UAVs for supported stages\n        if (programType == D3D11ShaderType::ePixel\n         || programType == D3D11ShaderType::eCompute) {\n          VkShaderStageFlags stages = programType == D3D11ShaderType::ePixel\n            ? VK_SHADER_STAGE_ALL_GRAPHICS\n            : VK_SHADER_STAGE_COMPUTE_BIT;\n\n          auto uavSlotId = D3D11ShaderResourceMapping::computeUavBinding(programType, 0);\n          auto ctrSlotId = D3D11ShaderResourceMapping::computeUavCounterBinding(programType, 0);\n\n          for (uint32_t j = 0; j < cUsedBindings.stages[i].uavCount; j++) {\n            ctx->bindResourceImageView(stages, uavSlotId, nullptr);\n            ctx->bindResourceBufferView(stages, ctrSlotId, nullptr);\n          }\n        }\n      }\n\n      // Initialize push constants\n      DxvkBuiltInPushData pc(1u, cMaxTessFactor);\n      ctx->pushData(VK_SHADER_STAGE_ALL_GRAPHICS, 0, sizeof(pc), &pc);\n    });\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ResetContextState() {\n    // Reset shaders\n    m_state.vs = nullptr;\n    m_state.hs = nullptr;\n    m_state.ds = nullptr;\n    m_state.gs = nullptr;\n    m_state.ps = nullptr;\n    m_state.cs = nullptr;\n\n    // Reset render state\n    m_state.id.reset();\n    m_state.ia.reset();\n    m_state.om.reset();\n    m_state.rs.reset();\n    m_state.so.reset();\n    m_state.pr.reset();\n\n    // Reset resource bindings\n    m_state.cbv.reset();\n    m_state.srv.reset();\n    m_state.uav.reset();\n    m_state.samplers.reset();\n\n    // Reset dirty tracking\n    m_state.lazy.reset();\n\n    // Reset class instances\n    m_state.instances.reset();\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ResetDirtyTracking() {\n    // Must only be called when all bindings are guaranteed to get applied\n    // to the DXVK context before the next draw or dispatch command.\n    m_state.lazy.bindingsDirty.reset();\n    m_state.lazy.shadersDirty = 0u;\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ResetStagingBuffer() {\n    m_staging.reset();\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage, typename T>\n  void D3D11CommonContext<ContextType>::ResolveSrvHazards(\n          T*                                pView) {\n    auto& bindings = m_state.srv[ShaderStage];\n    int32_t srvId = bindings.hazardous.findNext(0);\n\n    while (srvId >= 0) {\n      auto srv = bindings.views[srvId].ptr();\n\n      if (likely(srv && srv->TestHazards())) {\n        bool hazard = CheckViewOverlap(pView, srv);\n\n        if (unlikely(hazard)) {\n          bindings.views[srvId] = nullptr;\n          bindings.hazardous.clr(srvId);\n\n          if (!DirtyShaderResource(ShaderStage, srvId, true))\n            BindShaderResource(ShaderStage, srvId, nullptr);\n        }\n      } else {\n        // Avoid further redundant iterations\n        bindings.hazardous.clr(srvId);\n      }\n\n      srvId = bindings.hazardous.findNext(srvId + 1);\n    }\n  }\n\n\n  template<typename ContextType>\n  template<typename T>\n  void D3D11CommonContext<ContextType>::ResolveCsSrvHazards(\n          T*                                pView) {\n    if (!pView) return;\n    ResolveSrvHazards<D3D11ShaderType::eCompute>(pView);\n  }\n\n\n  template<typename ContextType>\n  template<typename T>\n  void D3D11CommonContext<ContextType>::ResolveOmSrvHazards(\n          T*                                pView) {\n    if (!pView) return;\n    ResolveSrvHazards<D3D11ShaderType::eVertex>(pView);\n    ResolveSrvHazards<D3D11ShaderType::eHull>(pView);\n    ResolveSrvHazards<D3D11ShaderType::eDomain>(pView);\n    ResolveSrvHazards<D3D11ShaderType::eGeometry>(pView);\n    ResolveSrvHazards<D3D11ShaderType::ePixel>(pView);\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::ResolveOmRtvHazards(\n          D3D11UnorderedAccessView*         pView) {\n    if (!pView || !pView->HasBindFlag(D3D11_BIND_RENDER_TARGET))\n      return false;\n\n    bool hazard = false;\n\n    if (CheckViewOverlap(pView, m_state.om.dsv.ptr())) {\n      m_state.om.dsv = nullptr;\n      hazard = true;\n    }\n\n    for (uint32_t i = 0; i < m_state.om.maxRtv; i++) {\n      if (CheckViewOverlap(pView, m_state.om.rtvs[i].ptr())) {\n        m_state.om.rtvs[i] = nullptr;\n        hazard = true;\n      }\n    }\n\n    return hazard;\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::ResolveOmUavHazards(\n          D3D11RenderTargetView*            pView) {\n    if (!pView || !pView->HasBindFlag(D3D11_BIND_UNORDERED_ACCESS))\n      return;\n\n    for (uint32_t i = 0; i < m_state.om.maxUav; i++) {\n      if (CheckViewOverlap(pView, m_state.om.uavs[i].ptr())) {\n        m_state.om.uavs[i] = nullptr;\n\n        if (!DirtyGraphicsUnorderedAccessView(i))\n          BindUnorderedAccessView(D3D11ShaderType::ePixel, i, nullptr);\n      }\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::RestoreCommandListState() {\n    BindFramebuffer();\n\n    BindShader<D3D11ShaderType::eVertex>(GetCommonShader(m_state.vs.ptr()));\n    BindShader<D3D11ShaderType::eHull>(GetCommonShader(m_state.hs.ptr()));\n    BindShader<D3D11ShaderType::eDomain>(GetCommonShader(m_state.ds.ptr()));\n    BindShader<D3D11ShaderType::eGeometry>(GetCommonShader(m_state.gs.ptr()));\n    BindShader<D3D11ShaderType::ePixel>(GetCommonShader(m_state.ps.ptr()));\n    BindShader<D3D11ShaderType::eCompute>(GetCommonShader(m_state.cs.ptr()));\n\n    ApplyInputLayout();\n    ApplyPrimitiveTopology();\n    ApplyBlendState();\n    ApplyBlendFactor();\n    ApplyDepthStencilState();\n    ApplyStencilRef();\n    ApplyRasterizerState();\n    ApplyRasterizerSampleCount();\n    ApplyViewportState();\n\n    BindIndexBuffer(\n      m_state.ia.indexBuffer.buffer.ptr(),\n      m_state.ia.indexBuffer.offset,\n      m_state.ia.indexBuffer.format);\n\n    for (uint32_t i = 0; i < m_state.ia.maxVbCount; i++) {\n      BindVertexBuffer(i,\n        m_state.ia.vertexBuffers[i].buffer.ptr(),\n        m_state.ia.vertexBuffers[i].offset,\n        m_state.ia.vertexBuffers[i].stride);\n    }\n\n    for (uint32_t i = 0; i < m_state.so.targets.size(); i++)\n      BindXfbBuffer(i, m_state.so.targets[i].buffer.ptr(), ~0u);\n\n    // Reset dirty binding and shader masks before applying\n    // bindings to avoid implicit null binding overrids.\n    ResetDirtyTracking();\n\n    for (uint32_t i = 0; i < D3D11ShaderTypeCount; i++) {\n      auto stage = D3D11ShaderType(i);\n\n      RestoreConstantBuffers(stage);\n      RestoreShaderResources(stage);\n      RestoreSamplers(stage);\n    }\n\n    RestoreUnorderedAccessViews(D3D11ShaderType::ePixel);\n    RestoreUnorderedAccessViews(D3D11ShaderType::eCompute);\n\n    // Draw buffer bindings aren't persistent at the API level, and\n    // we can't meaningfully track them. Just reset this state here\n    // and reapply on the next indirect draw.\n    SetDrawBuffers(nullptr, nullptr);\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::RestoreConstantBuffers(\n          D3D11ShaderType                   Stage) {\n    const auto& bindings = m_state.cbv[Stage];\n\n    for (uint32_t i = 0; i < bindings.maxCount; i++) {\n      BindConstantBuffer(Stage, i, bindings.buffers[i].buffer.ptr(),\n        bindings.buffers[i].constantOffset, bindings.buffers[i].constantBound);\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::RestoreSamplers(\n          D3D11ShaderType                   Stage) {\n    const auto& bindings = m_state.samplers[Stage];\n\n    for (uint32_t i = 0; i < bindings.maxCount; i++)\n      BindSampler(Stage, i, bindings.samplers[i].ptr());\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::RestoreShaderResources(\n          D3D11ShaderType                   Stage) {\n    const auto& bindings = m_state.srv[Stage];\n\n    for (uint32_t i = 0; i < bindings.maxCount; i++)\n      BindShaderResource(Stage, i, bindings.views[i].ptr());\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::RestoreUnorderedAccessViews(\n          D3D11ShaderType                   Stage) {\n    const auto& views = Stage == D3D11ShaderType::eCompute\n      ? m_state.uav.views\n      : m_state.om.uavs;\n\n    uint32_t maxCount = Stage == D3D11ShaderType::eCompute\n      ? m_state.uav.maxCount\n      : m_state.om.maxUav;\n\n    for (uint32_t i = 0; i < maxCount; i++)\n      BindUnorderedAccessView(Stage, i, views[i].ptr());\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::SetConstantBuffers(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers) {\n    auto& bindings = m_state.cbv[ShaderStage];\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      auto newBuffer = static_cast<D3D11Buffer*>(ppConstantBuffers[i]);\n\n      uint32_t constantCount = newBuffer\n        ? std::min(newBuffer->Desc()->ByteWidth / 16, UINT(D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT))\n        : 0u;\n\n      if (bindings.buffers[StartSlot + i].buffer         != newBuffer\n       || bindings.buffers[StartSlot + i].constantOffset != 0\n       || bindings.buffers[StartSlot + i].constantCount  != constantCount) {\n        bindings.buffers[StartSlot + i].buffer         = newBuffer;\n        bindings.buffers[StartSlot + i].constantOffset = 0;\n        bindings.buffers[StartSlot + i].constantCount  = constantCount;\n        bindings.buffers[StartSlot + i].constantBound  = constantCount;\n\n        if (!DirtyConstantBuffer(ShaderStage, StartSlot + i, !newBuffer))\n          BindConstantBuffer(ShaderStage, StartSlot + i, newBuffer, 0, constantCount);\n      }\n    }\n\n    bindings.maxCount = std::clamp(StartSlot + NumBuffers,\n      bindings.maxCount, uint32_t(bindings.buffers.size()));\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::SetConstantBuffers1(\n          UINT                              StartSlot,\n          UINT                              NumBuffers,\n          ID3D11Buffer* const*              ppConstantBuffers,\n    const UINT*                             pFirstConstant,\n    const UINT*                             pNumConstants) {\n    auto& bindings = m_state.cbv[ShaderStage];\n\n    for (uint32_t i = 0; i < NumBuffers; i++) {\n      auto newBuffer = static_cast<D3D11Buffer*>(ppConstantBuffers[i]);\n\n      UINT constantOffset;\n      UINT constantCount;\n      UINT constantBound;\n\n      if (likely(newBuffer != nullptr)) {\n        UINT bufferConstantsCount = newBuffer->Desc()->ByteWidth / 16;\n        constantBound = std::min(bufferConstantsCount, UINT(D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT));\n\n        if (likely(pFirstConstant && pNumConstants)) {\n          constantOffset  = pFirstConstant[i];\n          constantCount   = pNumConstants [i];\n\n          if (unlikely(constantCount > D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT))\n            continue;\n\n          constantBound = (constantOffset + constantCount > bufferConstantsCount)\n            ? bufferConstantsCount - std::min(constantOffset, bufferConstantsCount)\n            : constantCount;\n        } else {\n          constantOffset  = 0;\n          constantCount   = constantBound;\n        }\n      } else {\n        constantOffset  = 0;\n        constantCount   = 0;\n        constantBound   = 0;\n      }\n\n      // Do a full rebind if either the buffer changes\n      if (bindings.buffers[StartSlot + i].buffer != newBuffer) {\n        bindings.buffers[StartSlot + i].buffer = newBuffer;\n        bindings.buffers[StartSlot + i].constantOffset = constantOffset;\n        bindings.buffers[StartSlot + i].constantCount  = constantCount;\n        bindings.buffers[StartSlot + i].constantBound  = constantBound;\n\n        if (!DirtyConstantBuffer(ShaderStage, StartSlot + i, !newBuffer))\n          BindConstantBuffer(ShaderStage, StartSlot + i, newBuffer, constantOffset, constantBound);\n      } else if (bindings.buffers[StartSlot + i].constantOffset != constantOffset\n              || bindings.buffers[StartSlot + i].constantCount  != constantCount) {\n        bindings.buffers[StartSlot + i].constantOffset = constantOffset;\n        bindings.buffers[StartSlot + i].constantCount  = constantCount;\n        bindings.buffers[StartSlot + i].constantBound  = constantBound;\n\n        if (!DirtyConstantBuffer(ShaderStage, StartSlot + i, !newBuffer))\n          BindConstantBufferRange(ShaderStage, StartSlot + i, constantOffset, constantBound);\n      }\n    }\n\n    bindings.maxCount = std::clamp(StartSlot + NumBuffers,\n      bindings.maxCount, uint32_t(bindings.buffers.size()));\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::SetShaderResources(\n          UINT                              StartSlot,\n          UINT                              NumResources,\n          ID3D11ShaderResourceView* const*  ppResources) {\n    auto& bindings = m_state.srv[ShaderStage];\n\n    for (uint32_t i = 0; i < NumResources; i++) {\n      auto resView = static_cast<D3D11ShaderResourceView*>(ppResources[i]);\n\n      if (bindings.views[StartSlot + i] != resView) {\n        if (likely(resView != nullptr)) {\n          if (unlikely(resView->TestHazards())) {\n            if (TestSrvHazards<ShaderStage>(resView))\n              resView = nullptr;\n\n            // Only set if necessary, but don't reset it on every\n            // bind as this would be more expensive than a few\n            // redundant checks in OMSetRenderTargets and friends.\n            bindings.hazardous.set(StartSlot + i, resView);\n          }\n        }\n\n        bindings.views[StartSlot + i] = resView;\n\n        if (!DirtyShaderResource(ShaderStage, StartSlot + i, !resView))\n          BindShaderResource(ShaderStage, StartSlot + i, resView);\n      }\n    }\n\n    bindings.maxCount = std::clamp(StartSlot + NumResources,\n      bindings.maxCount, uint32_t(bindings.views.size()));\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::SetSamplers(\n          UINT                              StartSlot,\n          UINT                              NumSamplers,\n          ID3D11SamplerState* const*        ppSamplers) {\n    auto& bindings = m_state.samplers[ShaderStage];\n\n    for (uint32_t i = 0; i < NumSamplers; i++) {\n      auto sampler = static_cast<D3D11SamplerState*>(ppSamplers[i]);\n\n      if (bindings.samplers[StartSlot + i] != sampler) {\n        bindings.samplers[StartSlot + i] = sampler;\n\n        if (!DirtySampler(ShaderStage, StartSlot + i, !sampler))\n          BindSampler(ShaderStage, StartSlot + i, sampler);\n      }\n    }\n\n    bindings.maxCount = std::clamp(StartSlot + NumSamplers,\n      bindings.maxCount, uint32_t(bindings.samplers.size()));\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::SetRenderTargetsAndUnorderedAccessViews(\n          UINT                              NumRTVs,\n          ID3D11RenderTargetView* const*    ppRenderTargetViews,\n          ID3D11DepthStencilView*           pDepthStencilView,\n          UINT                              UAVStartSlot,\n          UINT                              NumUAVs,\n          ID3D11UnorderedAccessView* const* ppUnorderedAccessViews,\n    const UINT*                             pUAVInitialCounts) {\n    if (TestRtvUavHazards(NumRTVs, ppRenderTargetViews, NumUAVs, ppUnorderedAccessViews))\n      return;\n\n    bool needsUpdate = false;\n\n    if (likely(NumRTVs != D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL)) {\n      // Native D3D11 does not change the render targets if\n      // the parameters passed to this method are invalid.\n      if (!ValidateRenderTargets(NumRTVs, ppRenderTargetViews, pDepthStencilView))\n        return;\n\n      for (uint32_t i = 0; i < m_state.om.rtvs.size(); i++) {\n        auto rtv = i < NumRTVs\n          ? static_cast<D3D11RenderTargetView*>(ppRenderTargetViews[i])\n          : nullptr;\n\n        if (m_state.om.rtvs[i] != rtv) {\n          m_state.om.rtvs[i] = rtv;\n          needsUpdate = true;\n          ResolveOmSrvHazards(rtv);\n\n          if (NumUAVs == D3D11_KEEP_UNORDERED_ACCESS_VIEWS)\n            ResolveOmUavHazards(rtv);\n        }\n      }\n\n      auto dsv = static_cast<D3D11DepthStencilView*>(pDepthStencilView);\n\n      if (m_state.om.dsv != dsv) {\n        m_state.om.dsv = dsv;\n        needsUpdate = true;\n        ResolveOmSrvHazards(dsv);\n      }\n\n      m_state.om.maxRtv = NumRTVs;\n    }\n\n    if (unlikely(NumUAVs || m_state.om.maxUav)) {\n      if (likely(NumUAVs != D3D11_KEEP_UNORDERED_ACCESS_VIEWS)) {\n        uint32_t newMinUav = NumUAVs ? UAVStartSlot : D3D11_1_UAV_SLOT_COUNT;\n        uint32_t newMaxUav = NumUAVs ? UAVStartSlot + NumUAVs : 0u;\n\n        uint32_t oldMinUav = std::exchange(m_state.om.minUav, newMinUav);\n        uint32_t oldMaxUav = std::exchange(m_state.om.maxUav, newMaxUav);\n\n        for (uint32_t i = std::min(oldMinUav, newMinUav);\n                      i < std::max(oldMaxUav, newMaxUav); i++) {\n          D3D11UnorderedAccessView* uav = nullptr;\n          uint32_t                  ctr = ~0u;\n\n          if (i >= UAVStartSlot && i < UAVStartSlot + NumUAVs) {\n            uav = static_cast<D3D11UnorderedAccessView*>(ppUnorderedAccessViews[i - UAVStartSlot]);\n            ctr = pUAVInitialCounts ? pUAVInitialCounts[i - UAVStartSlot] : ~0u;\n          }\n\n          if (ctr != ~0u && uav && uav->HasCounter())\n            UpdateUnorderedAccessViewCounter(uav, ctr);\n\n          if (m_state.om.uavs[i] != uav) {\n            m_state.om.uavs[i] = uav;\n\n            if (!DirtyGraphicsUnorderedAccessView(i))\n              BindUnorderedAccessView(D3D11ShaderType::ePixel, i, uav);\n\n            ResolveOmSrvHazards(uav);\n\n            if (NumRTVs == D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL)\n              needsUpdate |= ResolveOmRtvHazards(uav);\n          }\n        }\n      }\n    }\n\n    if (needsUpdate) {\n      AddCost(GpuCostEstimate::RenderPass);\n      BindFramebuffer();\n\n      if constexpr (!IsDeferred)\n        GetTypedContext()->NotifyRenderPassBoundary();\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::SetDrawBuffers(\n          ID3D11Buffer*                     pBufferForArgs,\n          ID3D11Buffer*                     pBufferForCount) {\n    auto argBuffer = static_cast<D3D11Buffer*>(pBufferForArgs);\n    auto cntBuffer = static_cast<D3D11Buffer*>(pBufferForCount);\n\n    auto argBufferCookie = argBuffer ? argBuffer->GetCookie() : 0u;\n    auto cntBufferCookie = cntBuffer ? cntBuffer->GetCookie() : 0u;\n\n    if (m_state.id.argBufferCookie != argBufferCookie\n     || m_state.id.cntBufferCookie != cntBufferCookie) {\n      m_state.id.argBufferCookie = argBufferCookie;\n      m_state.id.cntBufferCookie = cntBufferCookie;\n\n      BindDrawBuffers(argBuffer, cntBuffer);\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::SyncImage(\n    const Rc<DxvkImage>&                    DstImage,\n    const VkImageSubresourceLayers&         DstLayers,\n    const Rc<DxvkImage>&                    SrcImage,\n    const VkImageSubresourceLayers&         SrcLayers) {\n    EmitCs([\n      cDstImage = DstImage,\n      cDstLayers = DstLayers,\n      cSrcImage = SrcImage,\n      cSrcLayers = SrcLayers\n    ] (DxvkContext* ctx) {\n      ctx->copyImage(\n        cDstImage, cDstLayers, VkOffset3D(),\n        cSrcImage, cSrcLayers, VkOffset3D(),\n        cDstImage->mipLevelExtent(cDstLayers.mipLevel));\n    });\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::TestRtvUavHazards(\n          UINT                              NumRTVs,\n          ID3D11RenderTargetView* const*    ppRTVs,\n          UINT                              NumUAVs,\n          ID3D11UnorderedAccessView* const* ppUAVs) {\n    if (NumRTVs == D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL) NumRTVs = 0;\n    if (NumUAVs == D3D11_KEEP_UNORDERED_ACCESS_VIEWS)           NumUAVs = 0;\n\n    for (uint32_t i = 0; i < NumRTVs; i++) {\n      auto rtv = static_cast<D3D11RenderTargetView*>(ppRTVs[i]);\n\n      if (!rtv)\n        continue;\n\n      for (uint32_t j = 0; j < i; j++) {\n        if (CheckViewOverlap(rtv, static_cast<D3D11RenderTargetView*>(ppRTVs[j])))\n          return true;\n      }\n\n      if (rtv->HasBindFlag(D3D11_BIND_UNORDERED_ACCESS)) {\n        for (uint32_t j = 0; j < NumUAVs; j++) {\n          if (CheckViewOverlap(rtv, static_cast<D3D11UnorderedAccessView*>(ppUAVs[j])))\n            return true;\n        }\n      }\n    }\n\n    for (uint32_t i = 0; i < NumUAVs; i++) {\n      auto uav = static_cast<D3D11UnorderedAccessView*>(ppUAVs[i]);\n\n      if (!uav)\n        continue;\n\n      for (uint32_t j = 0; j < i; j++) {\n        if (CheckViewOverlap(uav, static_cast<D3D11UnorderedAccessView*>(ppUAVs[j])))\n          return true;\n      }\n    }\n\n    return false;\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  bool D3D11CommonContext<ContextType>::TestSrvHazards(\n          D3D11ShaderResourceView*          pView) {\n    bool hazard = false;\n\n    if (ShaderStage == D3D11ShaderType::eCompute) {\n      int32_t uav = m_state.uav.mask.findNext(0);\n\n      while (uav >= 0 && !hazard) {\n        hazard = CheckViewOverlap(pView, m_state.uav.views[uav].ptr());\n        uav = m_state.uav.mask.findNext(uav + 1);\n      }\n    } else {\n      hazard = CheckViewOverlap(pView, m_state.om.dsv.ptr());\n\n      for (uint32_t i = 0; !hazard && i < m_state.om.maxRtv; i++)\n        hazard = CheckViewOverlap(pView, m_state.om.rtvs[i].ptr());\n\n      for (uint32_t i = 0; !hazard && i < m_state.om.maxUav; i++)\n        hazard = CheckViewOverlap(pView, m_state.om.uavs[i].ptr());\n    }\n\n    return hazard;\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::TrackResourceSequenceNumber(\n          ID3D11Resource*             pResource) {\n    if (!pResource)\n      return;\n\n    D3D11CommonTexture* texture = GetCommonTexture(pResource);\n\n    if (texture) {\n      if (texture->HasSequenceNumber()) {\n        for (uint32_t i = 0; i < texture->CountSubresources(); i++)\n          GetTypedContext()->TrackTextureSequenceNumber(texture, i);\n      }\n    } else {\n      D3D11Buffer* buffer = static_cast<D3D11Buffer*>(pResource);\n\n      if (buffer->HasSequenceNumber())\n        GetTypedContext()->TrackBufferSequenceNumber(buffer);\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::UpdateBuffer(\n          D3D11Buffer*                      pDstBuffer,\n          UINT                              Offset,\n          UINT                              Length,\n    const void*                             pSrcData) {\n    constexpr uint32_t MaxDirectUpdateSize = 64u;\n\n    DxvkBufferSlice bufferSlice = pDstBuffer->GetBufferSlice(Offset, Length);\n\n    if (Length <= MaxDirectUpdateSize && !((Offset | Length) & 0x3)) {\n      // The backend has special code paths for small buffer updates,\n      // however both offset and size must be aligned to four bytes.\n      // Write the data directly to the CS chunk.\n      uint32_t dwordCount = Length / sizeof(uint32_t);\n\n      EmitCsCmd<uint32_t>(D3D11CmdType::None, dwordCount, [\n        cBufferSlice = std::move(bufferSlice)\n      ] (DxvkContext* ctx, const uint32_t* data, size_t) {\n        ctx->updateBuffer(\n          cBufferSlice.buffer(),\n          cBufferSlice.offset(),\n          cBufferSlice.length(), data);\n      });\n\n      // Compiler should be able to vectorize here, but GCC only does\n      // if we cast the destination pointer to the correct type first\n      auto src = reinterpret_cast<const uint32_t*>(pSrcData);\n      auto dst = reinterpret_cast<uint32_t*>(m_csData->first());\n\n      for (uint32_t i = 0; i < dwordCount; i++)\n        new (dst + i) uint32_t(src[i]);\n    } else {\n      // Write directly to a staging buffer and dispatch a copy\n      DxvkBufferSlice stagingSlice = AllocStagingBuffer(Length);\n      std::memcpy(stagingSlice.mapPtr(0), pSrcData, Length);\n\n      EmitCs([\n        cStagingSlice = std::move(stagingSlice),\n        cBufferSlice  = std::move(bufferSlice)\n      ] (DxvkContext* ctx) {\n        ctx->copyBuffer(\n          cBufferSlice.buffer(),\n          cBufferSlice.offset(),\n          cStagingSlice.buffer(),\n          cStagingSlice.offset(),\n          cBufferSlice.length());\n      });\n    }\n\n    if (pDstBuffer->HasSequenceNumber())\n      GetTypedContext()->TrackBufferSequenceNumber(pDstBuffer);\n\n    if constexpr (!IsDeferred)\n      static_cast<ContextType*>(this)->ThrottleAllocation();\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::UpdateTexture(\n          D3D11CommonTexture*               pDstTexture,\n          UINT                              DstSubresource,\n    const D3D11_BOX*                        pDstBox,\n    const void*                             pSrcData,\n          UINT                              SrcRowPitch,\n          UINT                              SrcDepthPitch) {\n    if (DstSubresource >= pDstTexture->CountSubresources())\n      return;\n\n    VkFormat packedFormat = pDstTexture->GetPackedFormat();\n\n    auto formatInfo = lookupFormatInfo(packedFormat);\n    auto subresource = pDstTexture->GetSubresourceFromIndex(\n        formatInfo->aspectMask, DstSubresource);\n\n    VkExtent3D mipExtent = pDstTexture->MipLevelExtent(subresource.mipLevel);\n\n    VkOffset3D offset = { 0, 0, 0 };\n    VkExtent3D extent = mipExtent;\n\n    if (pDstBox != nullptr) {\n      if (pDstBox->left >= pDstBox->right\n        || pDstBox->top >= pDstBox->bottom\n        || pDstBox->front >= pDstBox->back)\n        return;  // no-op, but legal\n\n      offset.x = pDstBox->left;\n      offset.y = pDstBox->top;\n      offset.z = pDstBox->front;\n\n      extent.width  = pDstBox->right - pDstBox->left;\n      extent.height = pDstBox->bottom - pDstBox->top;\n      extent.depth  = pDstBox->back - pDstBox->front;\n    }\n\n    if (!util::isBlockAligned(offset, extent, formatInfo->blockSize, mipExtent))\n      return;\n\n    auto stagingSlice = AllocStagingBuffer(util::computeImageDataSize(packedFormat, extent));\n\n    util::packImageData(stagingSlice.mapPtr(0),\n      pSrcData, SrcRowPitch, SrcDepthPitch, 0, 0,\n      pDstTexture->GetVkImageType(), extent, 1,\n      formatInfo, formatInfo->aspectMask);\n\n    UpdateImage(pDstTexture, &subresource,\n      offset, extent, std::move(stagingSlice));\n\n    if constexpr (!IsDeferred)\n      static_cast<ContextType*>(this)->ThrottleAllocation();\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::UpdateImage(\n          D3D11CommonTexture*               pDstTexture,\n    const VkImageSubresource*               pDstSubresource,\n          VkOffset3D                        DstOffset,\n          VkExtent3D                        DstExtent,\n          DxvkBufferSlice                   StagingBuffer) {\n    bool dstIsImage = pDstTexture->HasImage();\n\n    uint32_t dstSubresource = D3D11CalcSubresource(pDstSubresource->mipLevel,\n      pDstSubresource->arrayLayer, pDstTexture->Desc()->MipLevels);\n\n    if (dstIsImage) {\n      EmitCs([\n        cDstImage         = pDstTexture->GetImage(),\n        cDstLayers        = vk::makeSubresourceLayers(*pDstSubresource),\n        cDstOffset        = DstOffset,\n        cDstExtent        = DstExtent,\n        cStagingSlice     = std::move(StagingBuffer),\n        cPackedFormat     = pDstTexture->GetPackedFormat()\n      ] (DxvkContext* ctx) {\n        ctx->copyBufferToImage(cDstImage,\n          cDstLayers, cDstOffset, cDstExtent,\n          cStagingSlice.buffer(),\n          cStagingSlice.offset(), 0, 0,\n          cPackedFormat);\n      });\n    } else {\n      // If the destination image is backed only by a buffer, we need to use\n      // the packed buffer copy function which does not know about planes and\n      // format metadata, so deal with it manually here.\n      VkExtent3D dstMipExtent = pDstTexture->MipLevelExtent(pDstSubresource->mipLevel);\n\n      auto dstFormat = pDstTexture->GetPackedFormat();\n      auto dstFormatInfo = lookupFormatInfo(dstFormat);\n\n      uint32_t planeCount = 1;\n\n      if (dstFormatInfo->flags.test(DxvkFormatFlag::MultiPlane))\n        planeCount = vk::getPlaneCount(dstFormatInfo->aspectMask);\n\n      // The source data isn't stored in an image so we'll also need to\n      // track the offset for that while iterating over the planes.\n      VkDeviceSize srcPlaneOffset = 0;\n\n      for (uint32_t i = 0; i < planeCount; i++) {\n        VkImageAspectFlags dstAspectMask = dstFormatInfo->aspectMask;\n        VkDeviceSize elementSize = dstFormatInfo->elementSize;\n        VkExtent3D blockSize = dstFormatInfo->blockSize;\n\n        if (dstFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n          dstAspectMask = vk::getPlaneAspect(i);\n\n          auto plane = &dstFormatInfo->planes[i];\n          blockSize.width  *= plane->blockSize.width;\n          blockSize.height *= plane->blockSize.height;\n          elementSize = plane->elementSize;\n        }\n\n        VkExtent3D blockCount = util::computeBlockCount(DstExtent, blockSize);\n\n        EmitCs([\n          cDstBuffer      = pDstTexture->GetMappedBuffer(dstSubresource),\n          cDstStart       = pDstTexture->GetSubresourceLayout(dstAspectMask, dstSubresource).Offset,\n          cDstOffset      = util::computeBlockOffset(DstOffset, blockSize),\n          cDstSize        = util::computeBlockCount(dstMipExtent, blockSize),\n          cDstExtent      = blockCount,\n          cSrcBuffer      = StagingBuffer.buffer(),\n          cSrcStart       = StagingBuffer.offset() + srcPlaneOffset,\n          cPixelSize      = elementSize\n        ] (DxvkContext* ctx) {\n          ctx->copyPackedBufferImage(\n            cDstBuffer, cDstStart, cDstOffset, cDstSize,\n            cSrcBuffer, cSrcStart, VkOffset3D(), cDstExtent,\n            cDstExtent, cPixelSize);\n        });\n\n        srcPlaneOffset += util::flattenImageExtent(blockCount) * elementSize;\n      }\n    }\n\n    if (pDstTexture->HasSequenceNumber())\n      GetTypedContext()->TrackTextureSequenceNumber(pDstTexture, dstSubresource);\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::UpdateResource(\n          ID3D11Resource*                   pDstResource,\n          UINT                              DstSubresource,\n    const D3D11_BOX*                        pDstBox,\n    const void*                             pSrcData,\n          UINT                              SrcRowPitch,\n          UINT                              SrcDepthPitch,\n          UINT                              CopyFlags) {\n    auto context = static_cast<ContextType*>(this);\n    D3D10DeviceLock lock = context->LockContext();\n\n    if (!pDstResource)\n      return;\n\n    // We need a different code path for buffers\n    D3D11_RESOURCE_DIMENSION resourceType;\n    pDstResource->GetType(&resourceType);\n\n    if (likely(resourceType == D3D11_RESOURCE_DIMENSION_BUFFER)) {\n      const auto bufferResource = static_cast<D3D11Buffer*>(pDstResource);\n      uint64_t bufferSize = bufferResource->Desc()->ByteWidth;\n\n      // Provide a fast path for mapped buffer updates since some\n      // games use UpdateSubresource to update constant buffers.\n      if (likely(bufferResource->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_DIRECT) && likely(!pDstBox)) {\n        context->UpdateMappedBuffer(bufferResource, 0, bufferSize, pSrcData, 0);\n        return;\n      }\n\n      // Validate buffer range to update\n      uint64_t offset = 0;\n      uint64_t length = bufferSize;\n\n      if (pDstBox) {\n        offset = pDstBox->left;\n        length = pDstBox->right - offset;\n      }\n\n      if (unlikely(offset + length > bufferSize))\n        return;\n\n      // Still try to be fast if a box is provided but we update the full buffer\n      if (likely(bufferResource->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_DIRECT)) {\n        CopyFlags &= D3D11_COPY_DISCARD | D3D11_COPY_NO_OVERWRITE;\n\n        if (likely(length == bufferSize) || unlikely(CopyFlags != 0)) {\n          context->UpdateMappedBuffer(bufferResource, offset, length, pSrcData, CopyFlags);\n          return;\n        }\n      }\n\n      // Otherwise we can't really do anything fancy, so just do a GPU copy\n      if (likely(length))\n        context->UpdateBuffer(bufferResource, offset, length, pSrcData);\n    } else {\n      D3D11CommonTexture* textureResource = GetCommonTexture(pDstResource);\n\n      context->UpdateTexture(textureResource,\n        DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch);\n    }\n  }\n\n\n  template<typename ContextType>\n  void D3D11CommonContext<ContextType>::UpdateUnorderedAccessViewCounter(\n          D3D11UnorderedAccessView*         pUav,\n          uint32_t                          CounterValue) {\n    EmitCs([\n      cView    = pUav->GetCounterView(),\n      cCounter = CounterValue\n    ] (DxvkContext* ctx) {\n      ctx->updateBuffer(cView->buffer(),\n        cView->info().offset, sizeof(cCounter), &cCounter);\n    });\n  }\n\n\n  template<typename ContextType>\n  bool D3D11CommonContext<ContextType>::ValidateRenderTargets(\n          UINT                              NumViews,\n          ID3D11RenderTargetView* const*    ppRenderTargetViews,\n          ID3D11DepthStencilView*           pDepthStencilView) {\n    Rc<DxvkImageView> refView;\n\n    VkExtent3D dsvExtent = { 0u, 0u, 0u };\n    VkExtent3D rtvExtent = { 0u, 0u, 0u };\n\n    if (pDepthStencilView != nullptr) {\n      refView = static_cast<D3D11DepthStencilView*>(\n        pDepthStencilView)->GetImageView();\n      dsvExtent = refView->mipLevelExtent(0);\n    }\n\n    for (uint32_t i = 0; i < NumViews; i++) {\n      if (ppRenderTargetViews[i] != nullptr) {\n        auto curView = static_cast<D3D11RenderTargetView*>(\n          ppRenderTargetViews[i])->GetImageView();\n\n        if (!rtvExtent.width)\n          rtvExtent = curView->mipLevelExtent(0);\n\n        if (refView != nullptr) {\n          // Render target views must all have the same sample count,\n          // layer count, and type. The size can mismatch under certain\n          // conditions, the D3D11 documentation is wrong here.\n          if (curView->info().viewType != refView->info().viewType\n           || curView->info().layerCount != refView->info().layerCount)\n            return false;\n\n          if (curView->image()->info().sampleCount\n           != refView->image()->info().sampleCount)\n            return false;\n\n          // Color targets must all be the same size\n          VkExtent3D curExtent = curView->mipLevelExtent(0);\n\n          if (curExtent.width  != rtvExtent.width\n           || curExtent.height != rtvExtent.height)\n            return false;\n        } else {\n          // Set reference view. All remaining views\n          // must be compatible to the reference view.\n          refView = curView;\n        }\n      }\n    }\n\n    // Based on testing, the depth-stencil target is allowed\n    // to be larger than all color targets, but not smaller\n    if (rtvExtent.width && dsvExtent.width) {\n      if (rtvExtent.width  > dsvExtent.width\n       || rtvExtent.height > dsvExtent.height)\n        return false;\n    }\n\n    return true;\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::SetClassInstances(\n    const D3D11CommonShader*                pShader,\n          ID3D11ClassInstance* const*       ppClassInstances,\n          UINT                              NumClassInstances) {\n    auto& state = m_state.instances[ShaderStage];\n\n    if (likely(!NumClassInstances) && likely(!state.instanceCount))\n      return;\n\n    // Not sure if this is the right thing to do, but if we don't\n    // have a shader we also don't really have anything to bind\n    if (!pShader)\n      NumClassInstances = 0u;\n\n    // Assign class instances for state tracking and populate\n    // constant buffer containing class instance data */\n    for (uint32_t i = 0u; i < NumClassInstances && i < D3D11ClassInstanceState::MaxInstances; i++)\n      state.instances[i] = static_cast<D3D11ClassInstance*>(ppClassInstances[i]);\n\n    // Unset previously bound class instances, if any\n    for (uint32_t i = NumClassInstances; i < state.instanceCount; i++)\n      state.instances[i] = nullptr;\n\n    uint32_t slotId = D3D11ShaderResourceMapping::computeCbvBinding(ShaderStage,\n      D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT + 1u);\n\n    if (NumClassInstances) {\n      auto buffer = AllocInstanceDataBuffer(ShaderStage);\n      auto slice = buffer->allocateStorage();\n\n      auto data = reinterpret_cast<D3D11InstanceData*>(slice->mapPtr());\n\n      for (uint32_t i = 0u; i < D3D11ClassInstanceState::MaxInstances; i++)\n        data[i] = pShader->GetClassInstanceData(i, state.instances[i].ptr());\n\n      EmitCs([\n        cSlotId = slotId,\n        cStage  = GetShaderStage(ShaderStage),\n        cBuffer = std::move(buffer),\n        cSlice  = std::move(slice)\n      ] (DxvkContext* ctx) mutable {\n        ctx->invalidateBuffer(cBuffer, Forwarder::move(cSlice));\n        ctx->bindUniformBuffer(cStage, cSlotId, DxvkBufferSlice(Forwarder::move(cBuffer)));\n      });\n    } else {\n      EmitCs([\n        cSlotId = slotId,\n        cStage  = GetShaderStage(ShaderStage)\n      ] (DxvkContext* ctx) mutable {\n        ctx->bindUniformBuffer(cStage, cSlotId, DxvkBufferSlice());\n      });\n    }\n  }\n\n\n  template<typename ContextType>\n  template<D3D11ShaderType ShaderStage>\n  void D3D11CommonContext<ContextType>::GetClassInstances(\n          ID3D11ClassInstance**             ppClassInstances,\n          UINT*                             pNumClassInstances) {\n    if (unlikely(pNumClassInstances)) {\n      const auto& state = m_state.instances[ShaderStage];\n\n      if (ppClassInstances) {\n        for (uint32_t i = 0u; i < *pNumClassInstances; i++) {\n          ppClassInstances[i] = i < state.instanceCount\n            ? state.instances[i].ref()\n            : nullptr;\n        }\n      }\n\n      *pNumClassInstances = state.instanceCount;\n    }\n  }\n\n\n  template<typename ContextType>\n  Rc<DxvkBuffer> D3D11CommonContext<ContextType>::AllocInstanceDataBuffer(\n          D3D11ShaderType                   ShaderStage) {\n    if (!m_instanceData[ShaderStage]) {\n      DxvkBufferCreateInfo bufferInfo = { };\n      bufferInfo.size = sizeof(D3D11InstanceData) * D3D11ClassInstanceState::MaxInstances;\n      bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n      bufferInfo.stages = GetShaderStage(ShaderStage);\n      bufferInfo.access = VK_ACCESS_UNIFORM_READ_BIT | VK_ACCESS_SHADER_READ_BIT;\n      bufferInfo.debugName = \"Instance data\";\n\n      m_instanceData[ShaderStage] = m_device->createBuffer(bufferInfo,\n        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n        VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n    }\n\n    return m_instanceData[ShaderStage];\n  }\n\n\n  template<typename ContextType>\n  DxvkInputAssemblyState D3D11CommonContext<ContextType>::InitDefaultPrimitiveTopology() {\n    return DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, false);\n  }\n\n\n  template<typename ContextType>\n  DxvkRasterizerState D3D11CommonContext<ContextType>::InitDefaultRasterizerState() {\n    DxvkRasterizerState rsState = { };\n    rsState.setPolygonMode(VK_POLYGON_MODE_FILL);\n    rsState.setCullMode(VK_CULL_MODE_BACK_BIT);\n    rsState.setFrontFace(VK_FRONT_FACE_CLOCKWISE);\n    rsState.setDepthClip(true);\n    rsState.setConservativeMode(VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT);\n    rsState.setSampleCount(0);\n    rsState.setFlatShading(false);\n    rsState.setLineMode(VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT);\n    return rsState;\n  }\n\n\n  template<typename ContextType>\n  DxvkDepthStencilState D3D11CommonContext<ContextType>::InitDefaultDepthStencilState() {\n    DxvkDepthStencilState dsState = { };\n    dsState.setDepthTest(true);\n    dsState.setDepthWrite(true);\n    dsState.setDepthCompareOp(VK_COMPARE_OP_LESS);\n    return dsState;\n  }\n\n\n  template<typename ContextType>\n  DxvkMultisampleState D3D11CommonContext<ContextType>::InitDefaultMultisampleState(\n          UINT                              SampleMask) {\n    DxvkMultisampleState msState = { };\n    msState.setSampleMask(SampleMask);\n    return msState;\n  }\n\n\n  template<typename ContextType>\n  DxvkLogicOpState D3D11CommonContext<ContextType>::InitDefaultLogicOpState() {\n    DxvkLogicOpState loState = { };\n    loState.setLogicOp(false, VK_LOGIC_OP_NO_OP);\n    return loState;\n  }\n\n\n  template<typename ContextType>\n  DxvkBlendMode D3D11CommonContext<ContextType>::InitDefaultBlendState() {\n    DxvkBlendMode cbState = { };\n    cbState.setBlendEnable(false);\n    cbState.setColorOp(VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD);\n    cbState.setAlphaOp(VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD);\n    cbState.setWriteMask(VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT\n                       | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT);\n    return cbState;\n  }\n\n  // Explicitly instantiate here\n  template class D3D11CommonContext<D3D11DeferredContext>;\n  template class D3D11CommonContext<D3D11ImmediateContext>;\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_context.h",
    "content": "#pragma once\n\n#include <algorithm>\n#include <type_traits>\n#include <vector>\n\n#include \"../dxvk/dxvk_adapter.h\"\n#include \"../dxvk/dxvk_cs.h\"\n#include \"../dxvk/dxvk_device.h\"\n#include \"../dxvk/dxvk_staging.h\"\n\n#include \"../d3d10/d3d10_multithread.h\"\n\n#include \"../util/util_flush.h\"\n\n#include \"d3d11_annotation.h\"\n#include \"d3d11_buffer.h\"\n#include \"d3d11_cmd.h\"\n#include \"d3d11_context_ext.h\"\n#include \"d3d11_context_state.h\"\n#include \"d3d11_device_child.h\"\n#include \"d3d11_texture.h\"\n\nnamespace dxvk {\n\n  class D3D11DeferredContext;\n  class D3D11ImmediateContext;\n\n  template<bool IsDeferred>\n  struct D3D11ContextObjectForwarder;\n\n  /**\n   * \\brief Object forwarder for immediate contexts\n   *\n   * Binding methods can use this to efficiently bind objects\n   * to the DXVK context without redundant reference counting.\n   */\n  template<>\n  struct D3D11ContextObjectForwarder<false> {\n    template<typename T>\n    static T&& move(T& object) {\n      return std::move(object);\n    }\n  };\n\n  /**\n   * \\brief Object forwarder for deferred contexts\n   *\n   * This forwarder will create a copy of the object passed\n   * into it, so that CS chunks can be reused if necessary.\n   */\n  template<>\n  struct D3D11ContextObjectForwarder<true> {\n    template<typename T>\n    static T move(const T& object) {\n      return object;\n    }\n  };\n\n  /**\n   * \\brief Common D3D11 device context implementation\n   *\n   * Implements all common device context methods, but since this is\n   * templates with the actual context type (deferred or immediate),\n   * all methods can call back into context-specific methods without\n   * having to use virtual methods.\n   */\n  template<typename ContextType>\n  class D3D11CommonContext : public D3D11DeviceChild<ID3D11DeviceContext4> {\n    constexpr static bool IsDeferred = std::is_same_v<ContextType, D3D11DeferredContext>;\n    using Forwarder = D3D11ContextObjectForwarder<IsDeferred>;\n\n    template<typename T> friend class D3D11DeviceContextExt;\n    template<typename T> friend class D3D11UserDefinedAnnotation;\n\n    // Use a local staging buffer to handle tiny uploads, most\n    // of the time we're fine with hitting the global allocator\n    constexpr static VkDeviceSize StagingBufferSize = 256ull << 10;\n  protected:\n    // Compile-time debug flag to force lazy binding on (True) or off (False)\n    constexpr static Tristate DebugLazyBinding = Tristate::Auto;\n  public:\n    \n    D3D11CommonContext(\n            D3D11Device*            pParent,\n      const Rc<DxvkDevice>&         Device,\n            UINT                    ContextFlags,\n            DxvkCsChunkFlags        CsFlags);\n\n    ~D3D11CommonContext();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject);\n\n    D3D11_DEVICE_CONTEXT_TYPE STDMETHODCALLTYPE GetType();\n\n    UINT STDMETHODCALLTYPE GetContextFlags();\n    \n    void STDMETHODCALLTYPE ClearState();\n\n    void STDMETHODCALLTYPE DiscardResource(ID3D11Resource *pResource);\n\n    void STDMETHODCALLTYPE DiscardView(ID3D11View* pResourceView);\n\n    void STDMETHODCALLTYPE DiscardView1(\n            ID3D11View*                      pResourceView,\n      const D3D11_RECT*                      pRects,\n            UINT                             NumRects);\n\n    void STDMETHODCALLTYPE DiscardViewBase(\n            ID3D11View*                      pResourceView,\n      const D3D11_RECT*                      pRects,\n            UINT                             NumRects);\n\n    void STDMETHODCALLTYPE CopySubresourceRegion(\n            ID3D11Resource*                   pDstResource,\n            UINT                              DstSubresource,\n            UINT                              DstX,\n            UINT                              DstY,\n            UINT                              DstZ,\n            ID3D11Resource*                   pSrcResource,\n            UINT                              SrcSubresource,\n      const D3D11_BOX*                        pSrcBox);\n\n    void STDMETHODCALLTYPE CopySubresourceRegion1(\n            ID3D11Resource*                   pDstResource,\n            UINT                              DstSubresource,\n            UINT                              DstX,\n            UINT                              DstY,\n            UINT                              DstZ,\n            ID3D11Resource*                   pSrcResource,\n            UINT                              SrcSubresource,\n      const D3D11_BOX*                        pSrcBox,\n            UINT                              CopyFlags);\n\n    void STDMETHODCALLTYPE CopySubresourceRegionBase(\n            ID3D11Resource*                   pDstResource,\n            UINT                              DstSubresource,\n            UINT                              DstX,\n            UINT                              DstY,\n            UINT                              DstZ,\n            ID3D11Resource*                   pSrcResource,\n            UINT                              SrcSubresource,\n      const D3D11_BOX*                        pSrcBox,\n            UINT                              CopyFlags);\n\n    void STDMETHODCALLTYPE CopyResource(\n            ID3D11Resource*                   pDstResource,\n            ID3D11Resource*                   pSrcResource);\n\n    void STDMETHODCALLTYPE CopyStructureCount(\n            ID3D11Buffer*                     pDstBuffer,\n            UINT                              DstAlignedByteOffset,\n            ID3D11UnorderedAccessView*        pSrcView);\n\n    void STDMETHODCALLTYPE ClearRenderTargetView(\n            ID3D11RenderTargetView*           pRenderTargetView,\n      const FLOAT                             ColorRGBA[4]);\n\n    void STDMETHODCALLTYPE ClearUnorderedAccessViewUint(\n            ID3D11UnorderedAccessView*        pUnorderedAccessView,\n      const UINT                              Values[4]);\n\n    void STDMETHODCALLTYPE ClearUnorderedAccessViewFloat(\n            ID3D11UnorderedAccessView*        pUnorderedAccessView,\n      const FLOAT                             Values[4]);\n\n    void STDMETHODCALLTYPE ClearDepthStencilView(\n            ID3D11DepthStencilView*           pDepthStencilView,\n            UINT                              ClearFlags,\n            FLOAT                             Depth,\n            UINT8                             Stencil);\n\n    void STDMETHODCALLTYPE ClearView(\n            ID3D11View                        *pView,\n      const FLOAT                             Color[4],\n      const D3D11_RECT                        *pRect,\n            UINT                              NumRects);\n\n    void STDMETHODCALLTYPE GenerateMips(\n            ID3D11ShaderResourceView*         pShaderResourceView);\n\n    void STDMETHODCALLTYPE ResolveSubresource(\n            ID3D11Resource*                   pDstResource,\n            UINT                              DstSubresource,\n            ID3D11Resource*                   pSrcResource,\n            UINT                              SrcSubresource,\n            DXGI_FORMAT                       Format);\n\n    void STDMETHODCALLTYPE UpdateSubresource(\n            ID3D11Resource*                   pDstResource,\n            UINT                              DstSubresource,\n      const D3D11_BOX*                        pDstBox,\n      const void*                             pSrcData,\n            UINT                              SrcRowPitch,\n            UINT                              SrcDepthPitch);\n\n    void STDMETHODCALLTYPE UpdateSubresource1(\n            ID3D11Resource*                   pDstResource,\n            UINT                              DstSubresource,\n      const D3D11_BOX*                        pDstBox,\n      const void*                             pSrcData,\n            UINT                              SrcRowPitch,\n            UINT                              SrcDepthPitch,\n            UINT                              CopyFlags);\n\n    void STDMETHODCALLTYPE DrawAuto();\n\n    void STDMETHODCALLTYPE Draw(\n            UINT            VertexCount,\n            UINT            StartVertexLocation);\n\n    void STDMETHODCALLTYPE DrawIndexed(\n            UINT            IndexCount,\n            UINT            StartIndexLocation,\n            INT             BaseVertexLocation);\n\n    void STDMETHODCALLTYPE DrawInstanced(\n            UINT            VertexCountPerInstance,\n            UINT            InstanceCount,\n            UINT            StartVertexLocation,\n            UINT            StartInstanceLocation);\n\n    void STDMETHODCALLTYPE DrawIndexedInstanced(\n            UINT            IndexCountPerInstance,\n            UINT            InstanceCount,\n            UINT            StartIndexLocation,\n            INT             BaseVertexLocation,\n            UINT            StartInstanceLocation);\n\n    void STDMETHODCALLTYPE DrawIndexedInstancedIndirect(\n            ID3D11Buffer*   pBufferForArgs,\n            UINT            AlignedByteOffsetForArgs);\n\n    void STDMETHODCALLTYPE DrawInstancedIndirect(\n            ID3D11Buffer*   pBufferForArgs,\n            UINT            AlignedByteOffsetForArgs);\n\n    void STDMETHODCALLTYPE Dispatch(\n            UINT            ThreadGroupCountX,\n            UINT            ThreadGroupCountY,\n            UINT            ThreadGroupCountZ);\n\n    void STDMETHODCALLTYPE DispatchIndirect(\n            ID3D11Buffer*   pBufferForArgs,\n            UINT            AlignedByteOffsetForArgs);\n\n    void STDMETHODCALLTYPE IASetInputLayout(\n            ID3D11InputLayout*                pInputLayout);\n\n    void STDMETHODCALLTYPE IASetPrimitiveTopology(\n            D3D11_PRIMITIVE_TOPOLOGY          Topology);\n\n    void STDMETHODCALLTYPE IASetVertexBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppVertexBuffers,\n      const UINT*                             pStrides,\n      const UINT*                             pOffsets);\n\n    void STDMETHODCALLTYPE IASetIndexBuffer(\n            ID3D11Buffer*                     pIndexBuffer,\n            DXGI_FORMAT                       Format,\n            UINT                              Offset);\n\n    void STDMETHODCALLTYPE IAGetInputLayout(\n            ID3D11InputLayout**               ppInputLayout);\n\n    void STDMETHODCALLTYPE IAGetPrimitiveTopology(\n            D3D11_PRIMITIVE_TOPOLOGY*         pTopology);\n\n    void STDMETHODCALLTYPE IAGetVertexBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppVertexBuffers,\n            UINT*                             pStrides,\n            UINT*                             pOffsets);\n\n    void STDMETHODCALLTYPE IAGetIndexBuffer(\n            ID3D11Buffer**                    ppIndexBuffer,\n            DXGI_FORMAT*                      pFormat,\n            UINT*                             pOffset);\n\n    void STDMETHODCALLTYPE VSSetShader(\n            ID3D11VertexShader*               pVertexShader,\n            ID3D11ClassInstance* const*       ppClassInstances,\n            UINT                              NumClassInstances);\n\n    void STDMETHODCALLTYPE VSSetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers);\n\n     void STDMETHODCALLTYPE VSSetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers,\n      const UINT*                             pFirstConstant,\n      const UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE VSSetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView* const*  ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE VSSetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState* const*        ppSamplers);\n\n    void STDMETHODCALLTYPE VSGetShader(\n            ID3D11VertexShader**              ppVertexShader,\n            ID3D11ClassInstance**             ppClassInstances,\n            UINT*                             pNumClassInstances);\n\n    void STDMETHODCALLTYPE VSGetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers);\n\n    void STDMETHODCALLTYPE VSGetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers,\n            UINT*                             pFirstConstant,\n            UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE VSGetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView**        ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE VSGetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState**              ppSamplers);\n\n    void STDMETHODCALLTYPE HSSetShader(\n            ID3D11HullShader*                 pHullShader,\n            ID3D11ClassInstance* const*       ppClassInstances,\n            UINT                              NumClassInstances);\n\n    void STDMETHODCALLTYPE HSSetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers);\n\n    void STDMETHODCALLTYPE HSSetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers,\n      const UINT*                             pFirstConstant,\n      const UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE HSSetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView* const*  ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE HSSetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState* const*        ppSamplers);\n\n    void STDMETHODCALLTYPE HSGetShader(\n            ID3D11HullShader**                ppHullShader,\n            ID3D11ClassInstance**             ppClassInstances,\n            UINT*                             pNumClassInstances);\n\n    void STDMETHODCALLTYPE HSGetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers);\n\n     void STDMETHODCALLTYPE HSGetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers,\n            UINT*                             pFirstConstant,\n            UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE HSGetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView**        ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE HSGetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState**              ppSamplers);\n\n    void STDMETHODCALLTYPE DSSetShader(\n            ID3D11DomainShader*               pDomainShader,\n            ID3D11ClassInstance* const*       ppClassInstances,\n            UINT                              NumClassInstances);\n\n    void STDMETHODCALLTYPE DSSetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers);\n\n    void STDMETHODCALLTYPE DSSetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers,\n      const UINT*                             pFirstConstant,\n      const UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE DSSetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView* const*  ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE DSSetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState* const*        ppSamplers);\n\n    void STDMETHODCALLTYPE DSGetShader(\n            ID3D11DomainShader**              ppDomainShader,\n            ID3D11ClassInstance**             ppClassInstances,\n            UINT*                             pNumClassInstances);\n\n    void STDMETHODCALLTYPE DSGetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers);\n\n     void STDMETHODCALLTYPE DSGetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers,\n            UINT*                             pFirstConstant,\n            UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE DSGetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView**        ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE DSGetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState**              ppSamplers);\n\n    void STDMETHODCALLTYPE GSSetShader(\n            ID3D11GeometryShader*             pShader,\n            ID3D11ClassInstance* const*       ppClassInstances,\n            UINT                              NumClassInstances);\n\n    void STDMETHODCALLTYPE GSSetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers);\n\n    void STDMETHODCALLTYPE GSSetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers,\n      const UINT*                             pFirstConstant,\n      const UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE GSSetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView* const*  ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE GSSetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState* const*        ppSamplers);\n\n    void STDMETHODCALLTYPE GSGetShader(\n            ID3D11GeometryShader**            ppGeometryShader,\n            ID3D11ClassInstance**             ppClassInstances,\n            UINT*                             pNumClassInstances);\n\n    void STDMETHODCALLTYPE GSGetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers);\n\n     void STDMETHODCALLTYPE GSGetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers,\n            UINT*                             pFirstConstant,\n            UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE GSGetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView**        ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE GSGetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState**              ppSamplers);\n\n    void STDMETHODCALLTYPE PSSetShader(\n            ID3D11PixelShader*                pPixelShader,\n            ID3D11ClassInstance* const*       ppClassInstances,\n            UINT                              NumClassInstances);\n\n    void STDMETHODCALLTYPE PSSetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers);\n\n    void STDMETHODCALLTYPE PSSetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers,\n      const UINT*                             pFirstConstant,\n      const UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE PSSetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView* const*  ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE PSSetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState* const*        ppSamplers);\n\n    void STDMETHODCALLTYPE PSGetShader(\n            ID3D11PixelShader**               ppPixelShader,\n            ID3D11ClassInstance**             ppClassInstances,\n            UINT*                             pNumClassInstances);\n\n    void STDMETHODCALLTYPE PSGetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers);\n\n    void STDMETHODCALLTYPE PSGetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers,\n            UINT*                             pFirstConstant,\n            UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE PSGetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView**        ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE PSGetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState**              ppSamplers);\n\n    void STDMETHODCALLTYPE CSSetShader(\n            ID3D11ComputeShader*              pComputeShader,\n            ID3D11ClassInstance* const*       ppClassInstances,\n            UINT                              NumClassInstances);\n\n    void STDMETHODCALLTYPE CSSetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers);\n\n    void STDMETHODCALLTYPE CSSetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers,\n      const UINT*                             pFirstConstant,\n      const UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE CSSetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView* const*  ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE CSSetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState* const*        ppSamplers);\n\n    void STDMETHODCALLTYPE CSSetUnorderedAccessViews(\n            UINT                              StartSlot,\n            UINT                              NumUAVs,\n            ID3D11UnorderedAccessView* const* ppUnorderedAccessViews,\n      const UINT*                             pUAVInitialCounts);\n\n    void STDMETHODCALLTYPE CSGetShader(\n            ID3D11ComputeShader**             ppComputeShader,\n            ID3D11ClassInstance**             ppClassInstances,\n            UINT*                             pNumClassInstances);\n\n    void STDMETHODCALLTYPE CSGetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers);\n\n    void STDMETHODCALLTYPE CSGetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers,\n            UINT*                             pFirstConstant,\n            UINT*                             pNumConstants);\n\n    void STDMETHODCALLTYPE CSGetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView**        ppShaderResourceViews);\n\n    void STDMETHODCALLTYPE CSGetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState**              ppSamplers);\n\n    void STDMETHODCALLTYPE CSGetUnorderedAccessViews(\n            UINT                              StartSlot,\n            UINT                              NumUAVs,\n            ID3D11UnorderedAccessView**       ppUnorderedAccessViews);\n\n    void STDMETHODCALLTYPE OMSetRenderTargets(\n            UINT                              NumViews,\n            ID3D11RenderTargetView* const*    ppRenderTargetViews,\n            ID3D11DepthStencilView*           pDepthStencilView);\n\n    void STDMETHODCALLTYPE OMSetRenderTargetsAndUnorderedAccessViews(\n            UINT                              NumRTVs,\n            ID3D11RenderTargetView* const*    ppRenderTargetViews,\n            ID3D11DepthStencilView*           pDepthStencilView,\n            UINT                              UAVStartSlot,\n            UINT                              NumUAVs,\n            ID3D11UnorderedAccessView* const* ppUnorderedAccessViews,\n      const UINT*                             pUAVInitialCounts);\n\n    void STDMETHODCALLTYPE OMSetBlendState(\n            ID3D11BlendState*                 pBlendState,\n      const FLOAT                             BlendFactor[4],\n            UINT                              SampleMask);\n\n    void STDMETHODCALLTYPE OMSetDepthStencilState(\n            ID3D11DepthStencilState*          pDepthStencilState,\n            UINT                              StencilRef);\n\n    void STDMETHODCALLTYPE OMGetRenderTargets(\n            UINT                              NumViews,\n            ID3D11RenderTargetView**          ppRenderTargetViews,\n            ID3D11DepthStencilView**          ppDepthStencilView);\n\n    void STDMETHODCALLTYPE OMGetRenderTargetsAndUnorderedAccessViews(\n            UINT                              NumRTVs,\n            ID3D11RenderTargetView**          ppRenderTargetViews,\n            ID3D11DepthStencilView**          ppDepthStencilView,\n            UINT                              UAVStartSlot,\n            UINT                              NumUAVs,\n            ID3D11UnorderedAccessView**       ppUnorderedAccessViews);\n\n    void STDMETHODCALLTYPE OMGetBlendState(\n            ID3D11BlendState**                ppBlendState,\n            FLOAT                             BlendFactor[4],\n            UINT*                             pSampleMask);\n\n    void STDMETHODCALLTYPE OMGetDepthStencilState(\n            ID3D11DepthStencilState**         ppDepthStencilState,\n            UINT*                             pStencilRef);\n\n    void STDMETHODCALLTYPE RSSetState(\n            ID3D11RasterizerState*            pRasterizerState);\n\n    void STDMETHODCALLTYPE RSSetViewports(\n            UINT                              NumViewports,\n      const D3D11_VIEWPORT*                   pViewports);\n\n    void STDMETHODCALLTYPE RSSetScissorRects(\n            UINT                              NumRects,\n      const D3D11_RECT*                       pRects);\n\n    void STDMETHODCALLTYPE RSGetState(\n            ID3D11RasterizerState**           ppRasterizerState);\n\n    void STDMETHODCALLTYPE RSGetViewports(\n            UINT*                             pNumViewports,\n            D3D11_VIEWPORT*                   pViewports);\n\n    void STDMETHODCALLTYPE RSGetScissorRects(\n            UINT*                             pNumRects,\n            D3D11_RECT*                       pRects);\n\n    void STDMETHODCALLTYPE SOSetTargets(\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppSOTargets,\n      const UINT*                             pOffsets);\n\n    void STDMETHODCALLTYPE SOGetTargets(\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppSOTargets);\n\n    void STDMETHODCALLTYPE SOGetTargetsWithOffsets(\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppSOTargets,\n            UINT*                             pOffsets);\n\n    void STDMETHODCALLTYPE SetPredication(\n            ID3D11Predicate*                  pPredicate,\n            BOOL                              PredicateValue);\n\n    void STDMETHODCALLTYPE GetPredication(\n            ID3D11Predicate**                 ppPredicate,\n            BOOL*                             pPredicateValue);\n\n    void STDMETHODCALLTYPE SetResourceMinLOD(\n            ID3D11Resource*                   pResource,\n            FLOAT                             MinLOD);\n\n    FLOAT STDMETHODCALLTYPE GetResourceMinLOD(\n            ID3D11Resource*                   pResource);\n\n    void STDMETHODCALLTYPE CopyTiles(\n            ID3D11Resource*                   pTiledResource,\n      const D3D11_TILED_RESOURCE_COORDINATE*  pTileRegionStartCoordinate,\n      const D3D11_TILE_REGION_SIZE*           pTileRegionSize,\n            ID3D11Buffer*                     pBuffer,\n            UINT64                            BufferStartOffsetInBytes,\n            UINT                              Flags);\n\n    HRESULT STDMETHODCALLTYPE CopyTileMappings(\n            ID3D11Resource*                   pDestTiledResource,\n      const D3D11_TILED_RESOURCE_COORDINATE*  pDestRegionCoordinate,\n            ID3D11Resource*                   pSourceTiledResource,\n      const D3D11_TILED_RESOURCE_COORDINATE*  pSourceRegionCoordinate,\n      const D3D11_TILE_REGION_SIZE*           pTileRegionSize,\n            UINT                              Flags);\n\n    HRESULT STDMETHODCALLTYPE ResizeTilePool(\n            ID3D11Buffer*                     pTilePool,\n            UINT64                            NewSizeInBytes);\n\n    void STDMETHODCALLTYPE TiledResourceBarrier(\n            ID3D11DeviceChild*                pTiledResourceOrViewAccessBeforeBarrier,\n            ID3D11DeviceChild*                pTiledResourceOrViewAccessAfterBarrier);\n\n    HRESULT STDMETHODCALLTYPE UpdateTileMappings(\n            ID3D11Resource*                   pTiledResource,\n            UINT                              NumRegions,\n      const D3D11_TILED_RESOURCE_COORDINATE*  pRegionCoordinates,\n      const D3D11_TILE_REGION_SIZE*           pRegionSizes,\n            ID3D11Buffer*                     pTilePool,\n            UINT                              NumRanges,\n      const UINT*                             pRangeFlags,\n      const UINT*                             pRangeTileOffsets,\n      const UINT*                             pRangeTileCounts,\n            UINT                              Flags);\n\n    void STDMETHODCALLTYPE UpdateTiles(\n            ID3D11Resource*                   pDestTiledResource,\n      const D3D11_TILED_RESOURCE_COORDINATE*  pDestTileRegionStartCoordinate,\n      const D3D11_TILE_REGION_SIZE*           pDestTileRegionSize,\n      const void*                             pSourceTileData,\n            UINT                              Flags);\n\n    BOOL STDMETHODCALLTYPE IsAnnotationEnabled();\n\n    void STDMETHODCALLTYPE SetMarkerInt(\n            LPCWSTR                           pLabel,\n            INT                               Data);\n\n    void STDMETHODCALLTYPE BeginEventInt(\n            LPCWSTR                           pLabel,\n            INT                               Data);\n\n    void STDMETHODCALLTYPE EndEvent();\n\n    void STDMETHODCALLTYPE GetHardwareProtectionState(\n            BOOL*                             pHwProtectionEnable);\n\n    void STDMETHODCALLTYPE SetHardwareProtectionState(\n            BOOL                              HwProtectionEnable);\n\n    void STDMETHODCALLTYPE TransitionSurfaceLayout(\n            IDXGIVkInteropSurface*    pSurface,\n      const VkImageSubresourceRange*  pSubresources,\n            VkImageLayout             OldLayout,\n            VkImageLayout             NewLayout);\n\n  protected:\n\n    D3D11DeviceContextExt<ContextType>        m_contextExt;\n    D3D11UserDefinedAnnotation<ContextType>   m_annotation;\n\n    Rc<DxvkDevice>              m_device;\n\n    D3D11ContextState           m_state;\n    UINT                        m_flags;\n\n    uint32_t                    m_maxTessFactor = 0u;\n\n    DxvkStagingBuffer           m_staging;\n\n    D3D11CmdType                m_csDataType = D3D11CmdType::None;\n\n    DxvkCsChunkFlags            m_csFlags;\n    DxvkCsChunkRef              m_csChunk;\n    DxvkCsDataBlock*            m_csData = nullptr;\n\n    uint64_t                    m_estimatedCost = 0u;\n\n    DxvkLocalAllocationCache    m_allocationCache;\n\n    D3D11ShaderStageState<Rc<DxvkBuffer>> m_instanceData;\n\n    DxvkCsChunkRef AllocCsChunk();\n    \n    DxvkBufferSlice AllocStagingBuffer(\n            VkDeviceSize                      Size);\n\n    void ApplyDirtyConstantBuffers(\n            D3D11ShaderType                   Stage,\n      const D3D11BindingMask&                 BoundMask,\n            D3D11BindingMask&                 DirtyMask);\n\n    void ApplyDirtySamplers(\n            D3D11ShaderType                   Stage,\n      const D3D11BindingMask&                 BoundMask,\n            D3D11BindingMask&                 DirtyMask);\n\n    void ApplyDirtyShaderResources(\n            D3D11ShaderType                   Stage,\n      const D3D11BindingMask&                 BoundMask,\n            D3D11BindingMask&                 DirtyMask);\n\n    void ApplyDirtyUnorderedAccessViews(\n            D3D11ShaderType                   Stage,\n      const D3D11BindingMask&                 BoundMask,\n            D3D11BindingMask&                 DirtyMask);\n\n    void ApplyDirtyGraphicsBindings();\n\n    void ApplyDirtyComputeBindings();\n\n    void ApplyInputLayout();\n    \n    void ApplyPrimitiveTopology();\n    \n    void ApplyBlendState();\n    \n    void ApplyBlendFactor();\n    \n    void ApplyDepthStencilState();\n    \n    void ApplyStencilRef();\n    \n    void ApplyRasterizerState();\n    \n    void ApplyRasterizerSampleCount();\n\n    void ApplyViewportState();\n\n    void BatchDraw(\n      const VkDrawIndirectCommand&            draw);\n\n    void BatchDrawIndexed(\n      const VkDrawIndexedIndirectCommand&     draw);\n\n    template<D3D11ShaderType ShaderStage>\n    void BindShader(\n      const D3D11CommonShader*                pShaderModule);\n\n    void BindFramebuffer();\n\n    void BindDrawBuffers(\n            D3D11Buffer*                      pBufferForArgs,\n            D3D11Buffer*                      pBufferForCount);\n\n    void BindVertexBuffer(\n            UINT                              Slot,\n            D3D11Buffer*                      pBuffer,\n            UINT                              Offset,\n            UINT                              Stride);\n\n    void BindVertexBufferRange(\n            UINT                              Slot,\n            D3D11Buffer*                      pBuffer,\n            UINT                              Offset,\n            UINT                              Stride);\n\n    void BindIndexBuffer(\n            D3D11Buffer*                      pBuffer,\n            UINT                              Offset,\n            DXGI_FORMAT                       Format);\n\n    void BindIndexBufferRange(\n            D3D11Buffer*                      pBuffer,\n            UINT                              Offset,\n            DXGI_FORMAT                       Format);\n\n    void BindXfbBuffer(\n            UINT                              Slot,\n            D3D11Buffer*                      pBuffer,\n            UINT                              Offset);\n\n    void BindConstantBuffer(\n            D3D11ShaderType                   ShaderStage,\n            UINT                              Slot,\n            D3D11Buffer*                      pBuffer,\n            UINT                              Offset,\n            UINT                              Length);\n\n    void BindConstantBufferRange(\n            D3D11ShaderType                   ShaderStage,\n            UINT                              Slot,\n            UINT                              Offset,\n            UINT                              Length);\n\n    void BindSampler(\n            D3D11ShaderType                   ShaderStage,\n            UINT                              Slot,\n            D3D11SamplerState*                pSampler);\n\n    void BindShaderResource(\n            D3D11ShaderType                   ShaderStage,\n            UINT                              Slot,\n            D3D11ShaderResourceView*          pResource);\n\n    void BindUnorderedAccessView(\n            D3D11ShaderType                   ShaderStage,\n            UINT                              Slot,\n            D3D11UnorderedAccessView*         pUav);\n\n    void ClearImageView(\n            Rc<DxvkImageView>                 View,\n      const FLOAT                             Color[4],\n      const D3D11_RECT*                       pRects,\n            UINT                              NumRects);\n\n    void ClearBufferView(\n            Rc<DxvkBufferView>                View,\n      const FLOAT                             Color[4],\n      const D3D11_RECT*                       pRects,\n            UINT                              NumRects);\n\n    VkClearValue ConvertColorValue(\n      const FLOAT                             Color[4],\n      const DxvkFormatInfo*                   pFormatInfo);\n\n    VkRect2D ConvertRect(\n            D3D11_RECT                        Rect,\n            VkExtent2D                        Extent);\n\n    void CopyBuffer(\n            D3D11Buffer*                      pDstBuffer,\n            VkDeviceSize                      DstOffset,\n            D3D11Buffer*                      pSrcBuffer,\n            VkDeviceSize                      SrcOffset,\n            VkDeviceSize                      ByteCount);\n\n    void CopyImage(\n            D3D11CommonTexture*               pDstTexture,\n      const VkImageSubresourceLayers*         pDstLayers,\n            VkOffset3D                        DstOffset,\n            D3D11CommonTexture*               pSrcTexture,\n      const VkImageSubresourceLayers*         pSrcLayers,\n            VkOffset3D                        SrcOffset,\n            VkExtent3D                        SrcExtent);\n\n    void CopyTiledResourceData(\n            ID3D11Resource*                   pResource,\n      const D3D11_TILED_RESOURCE_COORDINATE*  pRegionCoordinate,\n      const D3D11_TILE_REGION_SIZE*           pRegionSize,\n            DxvkBufferSlice                   BufferSlice,\n            UINT                              Flags);\n\n    template<typename T>\n    bool DirtyBindingGeneric(\n            D3D11ShaderType                   ShaderStage,\n            T                                 BoundMask,\n            T&                                DirtyMask,\n            T                                 DirtyBit,\n            bool                              IsNull);\n\n    bool DirtyConstantBuffer(\n            D3D11ShaderType                   ShaderStage,\n            uint32_t                          Slot,\n            bool                              IsNull);\n\n    bool DirtySampler(\n            D3D11ShaderType                   ShaderStage,\n            uint32_t                          Slot,\n            bool                              IsNull);\n\n    bool DirtyShaderResource(\n            D3D11ShaderType                   ShaderStage,\n            uint32_t                          Slot,\n            bool                              IsNull);\n\n    bool DirtyComputeUnorderedAccessView(\n            uint32_t                          Slot,\n            bool                              IsNull);\n\n    bool DirtyGraphicsUnorderedAccessView(\n            uint32_t                          Slot);\n\n    void DiscardBuffer(\n            ID3D11Resource*                   pResource);\n\n    void DiscardTexture(\n            ID3D11Resource*                   pResource,\n            UINT                              Subresource);\n\n    template<D3D11ShaderType ShaderStage>\n    void GetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer**                    ppConstantBuffers,\n            UINT*                             pFirstConstant,\n            UINT*                             pNumConstants);\n\n    template<D3D11ShaderType ShaderStage>\n    void GetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumViews,\n            ID3D11ShaderResourceView**        ppShaderResourceViews);\n\n    template<D3D11ShaderType ShaderStage>\n    void GetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState**              ppSamplers);\n\n    DxvkGlobalPipelineBarrier GetTiledResourceDependency(\n            ID3D11DeviceChild*                pObject);\n\n    D3D11MaxUsedBindings GetMaxUsedBindings();\n\n    bool HasDirtyComputeBindings();\n\n    bool HasDirtyGraphicsBindings();\n\n    void ResetCommandListState();\n\n    void ResetContextState();\n\n    void ResetDirtyTracking();\n\n    void ResetStagingBuffer();\n\n    template<D3D11ShaderType ShaderStage, typename T>\n    void ResolveSrvHazards(\n            T*                                pView);\n\n    template<typename T>\n    void ResolveCsSrvHazards(\n            T*                                pView);\n\n    template<typename T>\n    void ResolveOmSrvHazards(\n            T*                                pView);\n\n    bool ResolveOmRtvHazards(\n            D3D11UnorderedAccessView*         pView);\n\n    void ResolveOmUavHazards(\n            D3D11RenderTargetView*            pView);\n\n    void RestoreCommandListState();\n    \n    void RestoreConstantBuffers(\n            D3D11ShaderType                   Stage);\n    \n    void RestoreSamplers(\n            D3D11ShaderType                   Stage);\n\n    void RestoreShaderResources(\n            D3D11ShaderType                   Stage);\n\n    void RestoreUnorderedAccessViews(\n            D3D11ShaderType                   Stage);\n\n    template<D3D11ShaderType ShaderStage>\n    void SetConstantBuffers(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers);\n\n    template<D3D11ShaderType ShaderStage>\n    void SetConstantBuffers1(\n            UINT                              StartSlot,\n            UINT                              NumBuffers,\n            ID3D11Buffer* const*              ppConstantBuffers,\n      const UINT*                             pFirstConstant,\n      const UINT*                             pNumConstants);\n\n    template<D3D11ShaderType ShaderStage>\n    void SetShaderResources(\n            UINT                              StartSlot,\n            UINT                              NumResources,\n            ID3D11ShaderResourceView* const*  ppResources);\n\n    template<D3D11ShaderType ShaderStage>\n    void SetSamplers(\n            UINT                              StartSlot,\n            UINT                              NumSamplers,\n            ID3D11SamplerState* const*        ppSamplers);\n\n    void SetRenderTargetsAndUnorderedAccessViews(\n            UINT                              NumRTVs,\n            ID3D11RenderTargetView* const*    ppRenderTargetViews,\n            ID3D11DepthStencilView*           pDepthStencilView,\n            UINT                              UAVStartSlot,\n            UINT                              NumUAVs,\n            ID3D11UnorderedAccessView* const* ppUnorderedAccessViews,\n      const UINT*                             pUAVInitialCounts);\n\n    void SetDrawBuffers(\n            ID3D11Buffer*                     pBufferForArgs,\n            ID3D11Buffer*                     pBufferForCount);\n\n    void SyncImage(\n      const Rc<DxvkImage>&                    DstImage,\n      const VkImageSubresourceLayers&         DstLayers,\n      const Rc<DxvkImage>&                    SrcImage,\n      const VkImageSubresourceLayers&         SrcLayers);\n\n    bool TestRtvUavHazards(\n            UINT                              NumRTVs,\n            ID3D11RenderTargetView* const*    ppRTVs,\n            UINT                              NumUAVs,\n            ID3D11UnorderedAccessView* const* ppUAVs);\n\n    template<D3D11ShaderType ShaderStage>\n    bool TestSrvHazards(\n            D3D11ShaderResourceView*          pView);\n\n    void TrackResourceSequenceNumber(\n            ID3D11Resource*                   pResource);\n\n    void UpdateBuffer(\n            D3D11Buffer*                      pDstBuffer,\n            UINT                              Offset,\n            UINT                              Length,\n      const void*                             pSrcData);\n\n    void UpdateTexture(\n            D3D11CommonTexture*               pDstTexture,\n            UINT                              DstSubresource,\n      const D3D11_BOX*                        pDstBox,\n      const void*                             pSrcData,\n            UINT                              SrcRowPitch,\n            UINT                              SrcDepthPitch);\n\n    void UpdateImage(\n            D3D11CommonTexture*               pDstTexture,\n      const VkImageSubresource*               pDstSubresource,\n            VkOffset3D                        DstOffset,\n            VkExtent3D                        DstExtent,\n            DxvkBufferSlice                   StagingBuffer);\n\n    void UpdateResource(\n            ID3D11Resource*                   pDstResource,\n            UINT                              DstSubresource,\n      const D3D11_BOX*                        pDstBox,\n      const void*                             pSrcData,\n            UINT                              SrcRowPitch,\n            UINT                              SrcDepthPitch,\n            UINT                              CopyFlags);\n\n    void UpdateUnorderedAccessViewCounter(\n            D3D11UnorderedAccessView*         pUav,\n            uint32_t                          CounterValue);\n\n    bool ValidateRenderTargets(\n            UINT                              NumViews,\n            ID3D11RenderTargetView* const*    ppRenderTargetViews,\n            ID3D11DepthStencilView*           pDepthStencilView);\n\n    template<D3D11ShaderType ShaderStage>\n    void SetClassInstances(\n      const D3D11CommonShader*                pShader,\n            ID3D11ClassInstance* const*       ppClassInstances,\n            UINT                              NumClassInstances);\n\n    template<D3D11ShaderType ShaderStage>\n    void GetClassInstances(\n            ID3D11ClassInstance**             ppClassInstances,\n            UINT*                             pNumClassInstances);\n\n    Rc<DxvkBuffer> AllocInstanceDataBuffer(\n            D3D11ShaderType                   ShaderStage);\n\n    force_inline void AddCost(uint64_t Value) {\n      m_estimatedCost += Value;\n    }\n\n    static DxvkInputAssemblyState InitDefaultPrimitiveTopology();\n\n    static DxvkRasterizerState InitDefaultRasterizerState();\n\n    static DxvkDepthStencilState InitDefaultDepthStencilState();\n\n    static DxvkMultisampleState InitDefaultMultisampleState(\n            UINT                              SampleMask);\n\n    static DxvkLogicOpState InitDefaultLogicOpState();\n\n    static DxvkBlendMode InitDefaultBlendState();\n\n    template<bool AllowFlush = true, typename Cmd>\n    void EmitCs(Cmd&& command) {\n      if (unlikely(m_csDataType != D3D11CmdType::None)) {\n        m_csData = nullptr;\n        m_csDataType = D3D11CmdType::None;\n      }\n\n      if (unlikely(!m_csChunk->push(command))) {\n        GetTypedContext()->EmitCsChunk(std::move(m_csChunk));\n        m_csChunk = AllocCsChunk();\n\n        if constexpr (!IsDeferred && AllowFlush)\n          GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n\n        m_csChunk->push(command);\n      }\n    }\n\n    template<typename M, bool AllowFlush = true, typename Cmd>\n    void EmitCsCmd(D3D11CmdType type, size_t count, Cmd&& command) {\n      m_csDataType = type;\n      m_csData = m_csChunk->pushCmd<M, Cmd>(command, count);\n\n      if (unlikely(!m_csData)) {\n        GetTypedContext()->EmitCsChunk(std::move(m_csChunk));\n        m_csChunk = AllocCsChunk();\n\n        if constexpr (!IsDeferred && AllowFlush)\n          GetTypedContext()->ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n\n        // We must record this command after the potential\n        // flush since the caller may still access the data\n        m_csData = m_csChunk->pushCmd<M, Cmd>(command, count);\n      }\n    }\n\n    void FlushCsChunk() {\n      if (likely(!m_csChunk->empty())) {\n        m_csData = nullptr;\n        m_csDataType = D3D11CmdType::None;\n\n        GetTypedContext()->EmitCsChunk(std::move(m_csChunk));\n        m_csChunk = AllocCsChunk();\n      }\n    }\n\n    template<typename T>\n    const D3D11CommonShader* GetCommonShader(T* pShader) const {\n      return pShader != nullptr ? pShader->GetCommonShader() : nullptr;\n    }\n\n    static uint32_t GetIndirectCommandStride(const D3D11CmdDrawIndirectData* cmdData, uint32_t offset, uint32_t minStride) {\n      if (likely(cmdData->stride))\n        return cmdData->offset + cmdData->count * cmdData->stride == offset ? cmdData->stride : 0;\n\n      uint32_t stride = offset - cmdData->offset;\n      return stride >= minStride && stride <= 32 ? stride : 0;\n    }\n\n    static bool ValidateDrawBufferSize(ID3D11Buffer* pBuffer, UINT Offset, UINT Size) {\n      UINT bufferSize = 0;\n\n      if (likely(pBuffer != nullptr))\n        bufferSize = static_cast<D3D11Buffer*>(pBuffer)->Desc()->ByteWidth;\n\n      return uint64_t(bufferSize) >= uint64_t(Offset) + uint64_t(Size);\n    }\n\n  private:\n\n    ContextType* GetTypedContext() {\n      return static_cast<ContextType*>(this);\n    }\n\n    D3D10DeviceLock LockContext() {\n      return GetTypedContext()->LockContext();\n    }\n\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_context_def.cpp",
    "content": "#include \"d3d11_context_def.h\"\n#include \"d3d11_device.h\"\n\nnamespace dxvk {\n  \n  D3D11DeferredContext::D3D11DeferredContext(\n          D3D11Device*    pParent,\n    const Rc<DxvkDevice>& Device,\n          UINT            ContextFlags)\n  : D3D11CommonContext<D3D11DeferredContext>(pParent, Device, ContextFlags, 0u),\n    m_commandList(CreateCommandList()),\n    m_destructionNotifier(this) {\n    ResetContextState();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DeferredContext::QueryInterface(REFIID riid, void** ppvObject) {\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    return D3D11CommonContext<D3D11DeferredContext>::QueryInterface(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DeferredContext::GetData(\n          ID3D11Asynchronous*               pAsync,\n          void*                             pData,\n          UINT                              DataSize,\n          UINT                              GetDataFlags) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11: GetData called on a deferred context\");\n\n    return DXGI_ERROR_INVALID_CALL;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11DeferredContext::Begin(\n          ID3D11Asynchronous*         pAsync) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!pAsync))\n      return;\n\n    Com<D3D11Query, false> query(static_cast<D3D11Query*>(pAsync));\n\n    if (unlikely(!query->IsScoped()))\n      return;\n\n    auto entry = std::find(\n      m_queriesBegun.begin(),\n      m_queriesBegun.end(), query);\n\n    if (unlikely(entry != m_queriesBegun.end()))\n      return;\n\n    EmitCs([cQuery = query]\n    (DxvkContext* ctx) {\n      cQuery->Begin(ctx);\n    });\n\n    m_queriesBegun.push_back(std::move(query));\n  }\n\n\n  void STDMETHODCALLTYPE D3D11DeferredContext::End(\n          ID3D11Asynchronous*         pAsync) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!pAsync))\n      return;\n\n    Com<D3D11Query, false> query(static_cast<D3D11Query*>(pAsync));\n\n    if (query->IsScoped()) {\n      auto entry = std::find(\n        m_queriesBegun.begin(),\n        m_queriesBegun.end(), query);\n\n      if (likely(entry != m_queriesBegun.end())) {\n        m_queriesBegun.erase(entry);\n      } else {\n        EmitCs([cQuery = query]\n        (DxvkContext* ctx) {\n          cQuery->Begin(ctx);\n        });\n      }\n    }\n\n    m_commandList->AddQuery(query.ptr());\n\n    EmitCs([cQuery = std::move(query)]\n    (DxvkContext* ctx) {\n      cQuery->End(ctx);\n    });\n  }\n\n\n  void STDMETHODCALLTYPE D3D11DeferredContext::Flush() {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11: Flush called on a deferred context\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11DeferredContext::Flush1(\n          D3D11_CONTEXT_TYPE          ContextType,\n          HANDLE                      hEvent) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11: Flush1 called on a deferred context\");\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DeferredContext::Signal(\n          ID3D11Fence*                pFence,\n          UINT64                      Value) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11: Signal called on a deferred context\");\n\n    return DXGI_ERROR_INVALID_CALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DeferredContext::Wait(\n          ID3D11Fence*                pFence,\n          UINT64                      Value) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11: Wait called on a deferred context\");\n\n    return DXGI_ERROR_INVALID_CALL;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11DeferredContext::ExecuteCommandList(\n          ID3D11CommandList*  pCommandList,\n          BOOL                RestoreContextState) {\n    D3D10DeviceLock lock = LockContext();\n\n    // Clear state so that the command list can't observe any\n    // current context state. The command list itself will clean\n    // up after execution to ensure that no state changes done\n    // by the command list are visible to the immediate context.\n    ResetCommandListState();\n\n    // Flush any outstanding commands so that\n    // we don't mess up the execution order\n    FlushCsChunk();\n    \n    // Record any chunks from the given command list into the\n    // current command list and deal with context state\n    auto commandList = static_cast<D3D11CommandList*>(pCommandList);\n    m_chunkId = m_commandList->AddCommandList(commandList);\n    \n    // Restore deferred context state\n    if (RestoreContextState)\n      RestoreCommandListState();\n    else\n      ResetContextState();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DeferredContext::FinishCommandList(\n          BOOL                RestoreDeferredContextState,\n          ID3D11CommandList   **ppCommandList) {\n    D3D10DeviceLock lock = LockContext();\n\n    // End all queries that were left active by the app\n    FinalizeQueries();\n\n    // Clean up command list state so that the any state changed\n    // by this command list does not affect the calling context.\n    // This also ensures that the command list is never empty.\n    ResetCommandListState();\n\n    // Make sure all commands are visible to the command list\n    FlushCsChunk();\n    \n    if (ppCommandList)\n      *ppCommandList = m_commandList.ref();\n\n    // Create a clean command list, and if requested, restore all\n    // previously set context state. Otherwise, reset the context.\n    // Any use of ExecuteCommandList will reset command list state\n    // before the command list is actually executed.\n    m_commandList = CreateCommandList();\n    m_chunkId = 0;\n    \n    if (RestoreDeferredContextState)\n      RestoreCommandListState();\n    else\n      ResetContextState();\n    \n    m_mappedResources.clear();\n    ResetStagingBuffer();\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DeferredContext::Map(\n          ID3D11Resource*             pResource,\n          UINT                        Subresource,\n          D3D11_MAP                   MapType,\n          UINT                        MapFlags,\n          D3D11_MAPPED_SUBRESOURCE*   pMappedResource) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!pResource || !pMappedResource))\n      return E_INVALIDARG;\n    \n    if (likely(MapType == D3D11_MAP_WRITE_DISCARD)) {\n      D3D11_RESOURCE_DIMENSION resourceDim;\n      pResource->GetType(&resourceDim);\n\n      return likely(resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER)\n        ? MapBuffer(pResource, pMappedResource)\n        : MapImage(pResource, Subresource, pMappedResource);\n    } else if (likely(MapType == D3D11_MAP_WRITE_NO_OVERWRITE)) {\n      // The resource must be mapped with D3D11_MAP_WRITE_DISCARD\n      // before it can be mapped with D3D11_MAP_WRITE_NO_OVERWRITE.\n      D3D11_RESOURCE_DIMENSION resourceDim;\n      pResource->GetType(&resourceDim);\n\n      if (likely(resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER)) {\n        D3D11_MAPPED_SUBRESOURCE sr = FindMapEntry(static_cast<D3D11Buffer*>(pResource)->GetCookie());\n        pMappedResource->pData = sr.pData;\n\n        if (unlikely(!sr.pData))\n          return D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD;\n\n        pMappedResource->RowPitch = sr.RowPitch;\n        pMappedResource->DepthPitch = sr.DepthPitch;\n        return S_OK;\n      } else {\n        // Images cannot be mapped with NO_OVERWRITE\n        pMappedResource->pData = nullptr;\n        return E_INVALIDARG;\n      }\n    } else {\n      // Not allowed on deferred contexts\n      pMappedResource->pData = nullptr;\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11DeferredContext::Unmap(\n          ID3D11Resource*             pResource,\n          UINT                        Subresource) {\n    // No-op, updates are committed in Map\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11DeferredContext::SwapDeviceContextState(\n          ID3DDeviceContextState*           pState,\n          ID3DDeviceContextState**          ppPreviousState) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11: SwapDeviceContextState called on a deferred context\");\n  }\n\n\n  HRESULT D3D11DeferredContext::MapBuffer(\n          ID3D11Resource*               pResource,\n          D3D11_MAPPED_SUBRESOURCE*     pMappedResource) {\n    D3D11Buffer* pBuffer = static_cast<D3D11Buffer*>(pResource);\n\n    if (unlikely(pBuffer->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_NONE)) {\n      Logger::err(\"D3D11: Cannot map a device-local buffer\");\n      pMappedResource->pData = nullptr;\n      return E_INVALIDARG;\n    }\n\n    auto bufferSlice = pBuffer->AllocSlice(&m_allocationCache);\n    pMappedResource->pData        = bufferSlice->mapPtr();\n    pMappedResource->RowPitch     = pBuffer->Desc()->ByteWidth;\n    pMappedResource->DepthPitch   = pBuffer->Desc()->ByteWidth;\n\n    EmitCs([\n      cDstBuffer = pBuffer->GetBuffer(),\n      cDstSlice  = std::move(bufferSlice)\n    ] (DxvkContext* ctx) {\n      ctx->invalidateBuffer(cDstBuffer, Rc<DxvkResourceAllocation>(cDstSlice));\n    });\n\n    AddMapEntry(pBuffer->GetCookie(), *pMappedResource);\n    return S_OK;\n  }\n  \n  \n  HRESULT D3D11DeferredContext::MapImage(\n          ID3D11Resource*               pResource,\n          UINT                          Subresource,\n          D3D11_MAPPED_SUBRESOURCE*     pMappedResource) {\n    D3D11CommonTexture* pTexture = GetCommonTexture(pResource);\n    \n    if (unlikely(Subresource >= pTexture->CountSubresources())) {\n      pMappedResource->pData = nullptr;\n      return E_INVALIDARG;\n    }\n\n    if (unlikely(pTexture->Desc()->Usage != D3D11_USAGE_DYNAMIC)) {\n      pMappedResource->pData = nullptr;\n      return E_INVALIDARG;\n    }\n\n    VkFormat packedFormat = pTexture->GetPackedFormat();\n    auto formatInfo = lookupFormatInfo(packedFormat);\n    auto layout = pTexture->GetSubresourceLayout(formatInfo->aspectMask, Subresource);\n\n    if (pTexture->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) {\n      auto storage = pTexture->AllocStorage();\n      auto mapPtr = storage->mapPtr();\n\n      EmitCs([\n        cImage = pTexture->GetImage(),\n        cStorage = std::move(storage)\n      ] (DxvkContext* ctx) {\n        ctx->invalidateImage(cImage, Rc<DxvkResourceAllocation>(cStorage), VK_IMAGE_LAYOUT_PREINITIALIZED);\n      });\n\n      pMappedResource->RowPitch   = layout.RowPitch;\n      pMappedResource->DepthPitch = layout.DepthPitch;\n      pMappedResource->pData      = mapPtr;\n      return S_OK;\n    } else {\n      auto dataSlice = AllocStagingBuffer(layout.Size);\n\n      pMappedResource->RowPitch   = layout.RowPitch;\n      pMappedResource->DepthPitch = layout.DepthPitch;\n      pMappedResource->pData      = dataSlice.mapPtr(0);\n\n      auto subresource = pTexture->GetSubresourceFromIndex(formatInfo->aspectMask, Subresource);\n      auto mipExtent = pTexture->MipLevelExtent(subresource.mipLevel);\n\n      UpdateImage(pTexture, &subresource,\n        VkOffset3D { 0, 0, 0 }, mipExtent,\n        std::move(dataSlice));\n\n      return S_OK;\n    }\n  }\n  \n  \n  void D3D11DeferredContext::UpdateMappedBuffer(\n          D3D11Buffer*                  pDstBuffer,\n          UINT                          Offset,\n          UINT                          Length,\n    const void*                         pSrcData,\n          UINT                          CopyFlags) {\n    void* mapPtr = nullptr;\n\n    if (unlikely(CopyFlags == D3D11_COPY_NO_OVERWRITE))\n      mapPtr = FindMapEntry(pDstBuffer->GetCookie()).pData;\n\n    if (likely(!mapPtr)) {\n      // The caller validates the map mode, so we can\n      // safely ignore the MapBuffer return value here\n      D3D11_MAPPED_SUBRESOURCE mapInfo;\n      MapBuffer(pDstBuffer, &mapInfo);\n      AddMapEntry(pDstBuffer->GetCookie(), mapInfo);\n      mapPtr = mapInfo.pData;\n    }\n\n    std::memcpy(reinterpret_cast<char*>(mapPtr) + Offset, pSrcData, Length);\n  }\n\n\n  void D3D11DeferredContext::FinalizeQueries() {\n    for (auto& query : m_queriesBegun) {\n      m_commandList->AddQuery(query.ptr());\n\n      EmitCs([cQuery = std::move(query)]\n      (DxvkContext* ctx) {\n        cQuery->End(ctx);\n      });\n    }\n\n    m_queriesBegun.clear();\n  }\n\n\n  Com<D3D11CommandList> D3D11DeferredContext::CreateCommandList() {\n    return new D3D11CommandList(m_parent, m_flags);\n  }\n  \n  \n  void D3D11DeferredContext::EmitCsChunk(DxvkCsChunkRef&& chunk) {\n    m_chunkId = m_commandList->AddChunk(std::move(chunk), m_estimatedCost);\n    m_estimatedCost = 0u;\n  }\n\n\n  uint64_t D3D11DeferredContext::GetCurrentChunkId() const {\n    return m_csChunk->empty() ? m_chunkId : m_chunkId + 1;\n  }\n\n\n  void D3D11DeferredContext::TrackTextureSequenceNumber(\n          D3D11CommonTexture*         pResource,\n          UINT                        Subresource) {\n    m_commandList->TrackResourceUsage(\n      pResource->GetInterface(),\n      pResource->GetDimension(),\n      Subresource, GetCurrentChunkId());\n  }\n\n\n  void D3D11DeferredContext::TrackBufferSequenceNumber(\n          D3D11Buffer*                pResource) {\n    m_commandList->TrackResourceUsage(pResource,\n      D3D11_RESOURCE_DIMENSION_BUFFER, 0,\n      GetCurrentChunkId());\n  }\n\n\n  D3D11_MAPPED_SUBRESOURCE D3D11DeferredContext::FindMapEntry(\n          uint64_t                      Cookie) {\n    // Recently mapped resources as well as entries with\n    // up-to-date map infos will be located at the end\n    // of the resource array, so scan in reverse order.\n    size_t size = m_mappedResources.size();\n\n    for (size_t i = 1; i <= size; i++) {\n      const auto& entry = m_mappedResources[size - i];\n\n      if (entry.ResourceCookie == Cookie)\n        return entry.MapInfo;\n    }\n\n    return D3D11_MAPPED_SUBRESOURCE();\n  }\n\n  void D3D11DeferredContext::AddMapEntry(\n          uint64_t                      Cookie,\n    const D3D11_MAPPED_SUBRESOURCE&     MapInfo) {\n    m_mappedResources.push_back({ Cookie, MapInfo });\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_context_def.h",
    "content": "#pragma once\n\n#include \"d3d11_cmdlist.h\"\n#include \"d3d11_context.h\"\n\n#include <vector>\n\nnamespace dxvk {\n  \n  struct D3D11DeferredContextMapEntry {\n    uint64_t                  ResourceCookie = 0u;\n    D3D11_MAPPED_SUBRESOURCE  MapInfo = { };\n  };\n  \n  class D3D11DeferredContext : public D3D11CommonContext<D3D11DeferredContext> {\n    friend class D3D11CommonContext<D3D11DeferredContext>;\n  public:\n    \n    D3D11DeferredContext(\n            D3D11Device*    pParent,\n      const Rc<DxvkDevice>& Device,\n            UINT            ContextFlags);\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                      riid,\n            void**                      ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetData(\n            ID3D11Asynchronous*         pAsync,\n            void*                       pData,\n            UINT                        DataSize,\n            UINT                        GetDataFlags);\n    \n    void STDMETHODCALLTYPE Begin(\n            ID3D11Asynchronous*         pAsync);\n\n    void STDMETHODCALLTYPE End(\n            ID3D11Asynchronous*         pAsync);\n\n    void STDMETHODCALLTYPE Flush();\n\n    void STDMETHODCALLTYPE Flush1(\n            D3D11_CONTEXT_TYPE          ContextType,\n            HANDLE                      hEvent);\n\n    HRESULT STDMETHODCALLTYPE Signal(\n            ID3D11Fence*                pFence,\n            UINT64                      Value);\n    \n    HRESULT STDMETHODCALLTYPE Wait(\n            ID3D11Fence*                pFence,\n            UINT64                      Value);\n\n    void STDMETHODCALLTYPE ExecuteCommandList(\n            ID3D11CommandList*          pCommandList,\n            BOOL                        RestoreContextState);\n    \n    HRESULT STDMETHODCALLTYPE FinishCommandList(\n            BOOL                        RestoreDeferredContextState,\n            ID3D11CommandList**         ppCommandList);\n    \n    HRESULT STDMETHODCALLTYPE Map(\n            ID3D11Resource*             pResource,\n            UINT                        Subresource,\n            D3D11_MAP                   MapType,\n            UINT                        MapFlags,\n            D3D11_MAPPED_SUBRESOURCE*   pMappedResource);\n    \n    void STDMETHODCALLTYPE Unmap(\n            ID3D11Resource*             pResource,\n            UINT                        Subresource);\n    \n    void STDMETHODCALLTYPE SwapDeviceContextState(\n           ID3DDeviceContextState*           pState,\n           ID3DDeviceContextState**          ppPreviousState);\n\n    D3D10DeviceLock LockContext() {\n      return D3D10DeviceLock();\n    }\n\n  private:\n    \n    // Command list that we're recording\n    Com<D3D11CommandList> m_commandList;\n    \n    // Info about currently mapped (sub)resources. Using a vector\n    // here is reasonable since there will usually only be a small\n    // number of mapped resources per command list.\n    std::vector<D3D11DeferredContextMapEntry> m_mappedResources;\n    \n    // Begun and ended queries, will also be stored in command list\n    std::vector<Com<D3D11Query, false>> m_queriesBegun;\n\n    // Chunk ID within the current command list\n    uint64_t m_chunkId = 0ull;\n\n    D3DDestructionNotifier m_destructionNotifier;\n\n    HRESULT MapBuffer(\n            ID3D11Resource*               pResource,\n            D3D11_MAPPED_SUBRESOURCE*     pMappedResource);\n    \n    HRESULT MapImage(\n            ID3D11Resource*               pResource,\n            UINT                          Subresource,\n            D3D11_MAPPED_SUBRESOURCE*     pMappedResource);\n\n    void UpdateMappedBuffer(\n            D3D11Buffer*                  pDstBuffer,\n            UINT                          Offset,\n            UINT                          Length,\n      const void*                         pSrcData,\n            UINT                          CopyFlags);\n\n    void FinalizeQueries();\n\n    Com<D3D11CommandList> CreateCommandList();\n    \n    void EmitCsChunk(DxvkCsChunkRef&& chunk);\n\n    uint64_t GetCurrentChunkId() const;\n\n    void TrackTextureSequenceNumber(\n            D3D11CommonTexture*           pResource,\n            UINT                          Subresource);\n\n    void TrackBufferSequenceNumber(\n            D3D11Buffer*                  pResource);\n\n    D3D11_MAPPED_SUBRESOURCE FindMapEntry(\n            uint64_t                      Coookie);\n\n    void AddMapEntry(\n            uint64_t                      Cookie,\n      const D3D11_MAPPED_SUBRESOURCE&     MapInfo);\n\n    static DxvkCsChunkFlags GetCsChunkFlags(\n            D3D11Device*                  pDevice);\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_context_ext.cpp",
    "content": "#include <vector>\n#include <utility>\n#include <cstring>\n\n#include \"d3d11_device.h\"\n#include \"d3d11_context_imm.h\"\n#include \"d3d11_context_def.h\"\n#include \"d3d11_cuda.h\"\n\n#include \"../util/log/log.h\"\n\nnamespace dxvk {\n  \n  template<typename ContextType>\n  D3D11DeviceContextExt<ContextType>::D3D11DeviceContextExt(\n          ContextType*          pContext)\n  : m_ctx(pContext) {\n    \n  }\n  \n  \n  template<typename ContextType>\n  ULONG STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::AddRef() {\n    return m_ctx->AddRef();\n  }\n  \n  \n  template<typename ContextType>\n  ULONG STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::Release() {\n    return m_ctx->Release();\n  }\n  \n  \n  template<typename ContextType>\n  HRESULT STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_ctx->QueryInterface(riid, ppvObject);\n  }\n  \n  \n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::MultiDrawIndirect(\n          UINT                    DrawCount,\n          ID3D11Buffer*           pBufferForArgs,\n          UINT                    ByteOffsetForArgs,\n          UINT                    ByteStrideForArgs) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n    m_ctx->SetDrawBuffers(pBufferForArgs, nullptr);\n    \n    if (unlikely(m_ctx->HasDirtyGraphicsBindings()))\n      m_ctx->ApplyDirtyGraphicsBindings();\n\n    m_ctx->EmitCs([\n      cCount  = DrawCount,\n      cOffset = ByteOffsetForArgs,\n      cStride = ByteStrideForArgs\n    ] (DxvkContext* ctx) {\n      ctx->drawIndirect(cOffset, cCount, cStride, false);\n    });\n  }\n  \n  \n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::MultiDrawIndexedIndirect(\n          UINT                    DrawCount,\n          ID3D11Buffer*           pBufferForArgs,\n          UINT                    ByteOffsetForArgs,\n          UINT                    ByteStrideForArgs) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n    m_ctx->SetDrawBuffers(pBufferForArgs, nullptr);\n\n    if (unlikely(m_ctx->HasDirtyGraphicsBindings()))\n      m_ctx->ApplyDirtyGraphicsBindings();\n\n    m_ctx->EmitCs([\n      cCount  = DrawCount,\n      cOffset = ByteOffsetForArgs,\n      cStride = ByteStrideForArgs\n    ] (DxvkContext* ctx) {\n      ctx->drawIndexedIndirect(cOffset, cCount, cStride, false);\n    });\n  }\n  \n  \n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::MultiDrawIndirectCount(\n          UINT                    MaxDrawCount,\n          ID3D11Buffer*           pBufferForCount,\n          UINT                    ByteOffsetForCount,\n          ID3D11Buffer*           pBufferForArgs,\n          UINT                    ByteOffsetForArgs,\n          UINT                    ByteStrideForArgs) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n    m_ctx->SetDrawBuffers(pBufferForArgs, pBufferForCount);\n\n    if (unlikely(m_ctx->HasDirtyGraphicsBindings()))\n      m_ctx->ApplyDirtyGraphicsBindings();\n\n    m_ctx->EmitCs([\n      cMaxCount  = MaxDrawCount,\n      cArgOffset = ByteOffsetForArgs,\n      cCntOffset = ByteOffsetForCount,\n      cStride    = ByteStrideForArgs\n    ] (DxvkContext* ctx) {\n      ctx->drawIndirectCount(cArgOffset, cCntOffset, cMaxCount, cStride);\n    });\n  }\n  \n  \n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::MultiDrawIndexedIndirectCount(\n          UINT                    MaxDrawCount,\n          ID3D11Buffer*           pBufferForCount,\n          UINT                    ByteOffsetForCount,\n          ID3D11Buffer*           pBufferForArgs,\n          UINT                    ByteOffsetForArgs,\n          UINT                    ByteStrideForArgs) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n    m_ctx->SetDrawBuffers(pBufferForArgs, pBufferForCount);\n\n    if (unlikely(m_ctx->HasDirtyGraphicsBindings()))\n      m_ctx->ApplyDirtyGraphicsBindings();\n\n    m_ctx->EmitCs([\n      cMaxCount  = MaxDrawCount,\n      cArgOffset = ByteOffsetForArgs,\n      cCntOffset = ByteOffsetForCount,\n      cStride    = ByteStrideForArgs\n    ] (DxvkContext* ctx) {\n      ctx->drawIndexedIndirectCount(cArgOffset, cCntOffset, cMaxCount, cStride);\n    });\n  }\n  \n  \n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::SetDepthBoundsTest(\n          BOOL                    Enable,\n          FLOAT                   MinDepthBounds,\n          FLOAT                   MaxDepthBounds) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    DxvkDepthBounds db = { };\n    db.minDepthBounds = Enable ? MinDepthBounds : 0.0f;\n    db.maxDepthBounds = Enable ? MaxDepthBounds : 1.0f;\n    \n    m_ctx->EmitCs([cDepthBounds = db] (DxvkContext* ctx) {\n      ctx->setDepthBounds(cDepthBounds);\n    });\n  }\n  \n  \n  template<typename ContextType>\n  void STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::SetBarrierControl(\n          UINT                    ControlFlags) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n    D3D11Device* parent = static_cast<D3D11Device*>(m_ctx->GetParentInterface());\n    DxvkBarrierControlFlags flags = parent->GetOptionsBarrierControlFlags();\n\n    if (ControlFlags & D3D11_VK_BARRIER_CONTROL_IGNORE_WRITE_AFTER_WRITE) {\n      flags.set(DxvkBarrierControl::ComputeAllowReadWriteOverlap,\n                DxvkBarrierControl::GraphicsAllowReadWriteOverlap);\n    }\n\n    m_ctx->EmitCs([cFlags = flags] (DxvkContext* ctx) {\n      ctx->setBarrierControl(cFlags);\n    });\n  }\n\n\n  template<typename ContextType>\n  bool STDMETHODCALLTYPE D3D11DeviceContextExt<ContextType>::LaunchCubinShaderNVX(IUnknown* hShader, uint32_t GridX, uint32_t GridY, uint32_t GridZ,\n      const void* pParams, uint32_t ParamSize, void* const* pReadResources, uint32_t NumReadResources, void* const* pWriteResources, uint32_t NumWriteResources) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    CubinShaderWrapper* cubinShader = static_cast<CubinShaderWrapper*>(hShader);\n    CubinShaderLaunchInfo launchInfo;\n\n    const uint32_t maxResources = NumReadResources + NumWriteResources;\n    launchInfo.buffers.reserve(maxResources);\n    launchInfo.images.reserve(maxResources);\n\n    for (uint32_t i = 0; i < NumReadResources; i++)\n      launchInfo.insertResource(static_cast<ID3D11Resource*>(pReadResources[i]), DxvkAccess::Read);\n\n    for (uint32_t i = 0; i < NumWriteResources; i++)\n      launchInfo.insertResource(static_cast<ID3D11Resource*>(pWriteResources[i]), DxvkAccess::Write);\n\n    launchInfo.paramSize = ParamSize;\n    launchInfo.params.resize(launchInfo.paramSize);\n    std::memcpy(launchInfo.params.data(), pParams, ParamSize);\n\n    launchInfo.cuLaunchConfig[0] = reinterpret_cast<void*>(0x01); // CU_LAUNCH_PARAM_BUFFER_POINTER\n    launchInfo.cuLaunchConfig[1] = launchInfo.params.data();\n    launchInfo.cuLaunchConfig[2] = reinterpret_cast<void*>(0x02); // CU_LAUNCH_PARAM_BUFFER_SIZE\n    launchInfo.cuLaunchConfig[3] = &launchInfo.paramSize; // yes, this actually requires a pointer to a size_t containing the parameter size\n    launchInfo.cuLaunchConfig[4] = reinterpret_cast<void*>(0x00); // CU_LAUNCH_PARAM_END\n\n    launchInfo.nvxLaunchInfo.function       = cubinShader->cuFunction();\n    launchInfo.nvxLaunchInfo.gridDimX       = GridX;\n    launchInfo.nvxLaunchInfo.gridDimY       = GridY;\n    launchInfo.nvxLaunchInfo.gridDimZ       = GridZ;\n    launchInfo.nvxLaunchInfo.blockDimX      = cubinShader->blockDim().width;\n    launchInfo.nvxLaunchInfo.blockDimY      = cubinShader->blockDim().height;\n    launchInfo.nvxLaunchInfo.blockDimZ      = cubinShader->blockDim().depth;\n    launchInfo.nvxLaunchInfo.sharedMemBytes = 0;\n    launchInfo.nvxLaunchInfo.paramCount     = 0;\n    launchInfo.nvxLaunchInfo.pParams        = nullptr;\n    launchInfo.nvxLaunchInfo.extraCount     = 1;\n    launchInfo.nvxLaunchInfo.pExtras        = launchInfo.cuLaunchConfig.data();\n\n    launchInfo.shader = cubinShader;\n\n    /* Need to capture by value in case this gets called from a deferred context */\n    m_ctx->AddCost(GpuCostEstimate::Dispatch);\n\n    m_ctx->EmitCs([cLaunchInfo = std::move(launchInfo)] (DxvkContext* ctx) {\n      ctx->launchCuKernelNVX(cLaunchInfo.nvxLaunchInfo, cLaunchInfo.buffers, cLaunchInfo.images);\n    });\n\n    // Track resource usage as necessary\n    for (uint32_t i = 0; i < NumReadResources; i++)\n      m_ctx->TrackResourceSequenceNumber(static_cast<ID3D11Resource*>(pReadResources[i]));\n\n    for (uint32_t i = 0; i < NumWriteResources; i++)\n      m_ctx->TrackResourceSequenceNumber(static_cast<ID3D11Resource*>(pWriteResources[i]));\n\n    return true;\n  }\n\n\n  template class D3D11DeviceContextExt<D3D11DeferredContext>;\n  template class D3D11DeviceContextExt<D3D11ImmediateContext>;\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_context_ext.h",
    "content": "#pragma once\n\n#include \"d3d11_interfaces.h\"\n\nnamespace dxvk {\n  \n  class D3D11DeferredContext;\n  class D3D11ImmediateContext;\n\n  template<typename ContextType>\n  class D3D11DeviceContextExt : public ID3D11VkExtContext1 {\n    \n  public:\n    \n    D3D11DeviceContextExt(\n            ContextType*            pContext);\n    \n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n    \n    void STDMETHODCALLTYPE MultiDrawIndirect(\n            UINT                    DrawCount,\n            ID3D11Buffer*           pBufferForArgs,\n            UINT                    ByteOffsetForArgs,\n            UINT                    ByteStrideForArgs);\n    \n    void STDMETHODCALLTYPE MultiDrawIndexedIndirect(\n            UINT                    DrawCount,\n            ID3D11Buffer*           pBufferForArgs,\n            UINT                    ByteOffsetForArgs,\n            UINT                    ByteStrideForArgs);\n    \n    void STDMETHODCALLTYPE MultiDrawIndirectCount(\n            UINT                    MaxDrawCount,\n            ID3D11Buffer*           pBufferForCount,\n            UINT                    ByteOffsetForCount,\n            ID3D11Buffer*           pBufferForArgs,\n            UINT                    ByteOffsetForArgs,\n            UINT                    ByteStrideForArgs);\n    \n    void STDMETHODCALLTYPE MultiDrawIndexedIndirectCount(\n            UINT                    MaxDrawCount,\n            ID3D11Buffer*           pBufferForCount,\n            UINT                    ByteOffsetForCount,\n            ID3D11Buffer*           pBufferForArgs,\n            UINT                    ByteOffsetForArgs,\n            UINT                    ByteStrideForArgs);\n    \n    void STDMETHODCALLTYPE SetDepthBoundsTest(\n            BOOL                    Enable,\n            FLOAT                   MinDepthBounds,\n            FLOAT                   MaxDepthBounds);\n    \n    void STDMETHODCALLTYPE SetBarrierControl(\n            UINT                    ControlFlags);\n\n    bool STDMETHODCALLTYPE LaunchCubinShaderNVX(\n            IUnknown*               hShader,\n            uint32_t                GridX,\n            uint32_t                GridY,\n            uint32_t                GridZ,\n            const void*             pParams,\n            uint32_t                paramSize,\n            void* const*            pReadResources,\n            uint32_t                NumReadResources,\n            void* const*            pWriteResources,\n            uint32_t                NumWriteResources);\n\n  private:\n    \n    ContextType* m_ctx;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_context_imm.cpp",
    "content": "#include \"d3d11_cmdlist.h\"\n#include \"d3d11_context_imm.h\"\n#include \"d3d11_device.h\"\n#include \"d3d11_fence.h\"\n#include \"d3d11_texture.h\"\n\n#include \"../util/util_win32_compat.h\"\n\nconstexpr static uint32_t MinFlushIntervalUs = 750;\nconstexpr static uint32_t IncFlushIntervalUs = 250;\nconstexpr static uint32_t MaxPendingSubmits  = 6;\n\nnamespace dxvk {\n  \n  D3D11ImmediateContext::D3D11ImmediateContext(\n          D3D11Device*    pParent,\n    const Rc<DxvkDevice>& Device)\n  : D3D11CommonContext<D3D11ImmediateContext>(pParent, Device, 0, DxvkCsChunkFlag::SingleUse),\n    m_csThread(Device, Device->createContext()),\n    m_submissionFence(new sync::CallbackFence()),\n    m_flushTracker(GetMaxFlushType(pParent, Device)),\n    m_stagingBufferFence(new sync::Fence(0)),\n    m_multithread(this, false, pParent->GetOptions()->enableContextLock),\n    m_videoContext(this, Device),\n    m_destructionNotifier(this) {\n    EmitCs([\n      cDevice                 = m_device,\n      cBarrierControlFlags    = pParent->GetOptionsBarrierControlFlags()\n    ] (DxvkContext* ctx) {\n      ctx->beginRecording(cDevice->createCommandList());\n\n      ctx->setBarrierControl(cBarrierControlFlags);\n    });\n\n    // Stall here so that external submissions to the\n    // CS thread can actually access the command list\n    SynchronizeCsThread(DxvkCsThread::SynchronizeAll);\n    \n    ClearState();\n  }\n  \n  \n  D3D11ImmediateContext::~D3D11ImmediateContext() {\n    // Avoids hanging when in this state, see comment\n    // in DxvkDevice::~DxvkDevice.\n    if (this_thread::isInModuleDetachment())\n      return;\n\n    ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true);\n    SynchronizeCsThread(DxvkCsThread::SynchronizeAll);\n    SynchronizeDevice();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::QueryInterface(REFIID riid, void** ppvObject) {\n    if (riid == __uuidof(ID3D10Multithread)) {\n      *ppvObject = ref(&m_multithread);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D11VideoContext)) {\n      *ppvObject = ref(&m_videoContext);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    return D3D11CommonContext<D3D11ImmediateContext>::QueryInterface(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::GetData(\n          ID3D11Asynchronous*               pAsync,\n          void*                             pData,\n          UINT                              DataSize,\n          UINT                              GetDataFlags) {\n    if (!pAsync || (DataSize && !pData))\n      return E_INVALIDARG;\n    \n    // Check whether the data size is actually correct\n    if (DataSize && DataSize != pAsync->GetDataSize())\n      return E_INVALIDARG;\n    \n    // Passing a non-null pData is actually allowed if\n    // DataSize is 0, but we should ignore that pointer\n    pData = DataSize ? pData : nullptr;\n\n    // Get query status directly from the query object\n    auto query = static_cast<D3D11Query*>(pAsync);\n    HRESULT hr = query->GetData(pData, GetDataFlags);\n    \n    // If we're likely going to spin on the asynchronous object,\n    // flush the context so that we're keeping the GPU busy.\n    if (hr == S_FALSE) {\n      // Don't mark the event query as stalling if the app does\n      // not intend to spin on it. This reduces flushes on End.\n      if (!(GetDataFlags & D3D11_ASYNC_GETDATA_DONOTFLUSH))\n        query->NotifyStall();\n\n      // Ignore the DONOTFLUSH flag here as some games will spin\n      // on queries without ever flushing the context otherwise.\n      D3D10DeviceLock lock = LockContext();\n\n      if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n        m_flushReason = \"Query read-back\";\n\n      ConsiderFlush(GpuFlushType::ImplicitSynchronization);\n    }\n    \n    return hr;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11ImmediateContext::Begin(ID3D11Asynchronous* pAsync) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!pAsync))\n      return;\n    \n    auto query = static_cast<D3D11Query*>(pAsync);\n\n    if (unlikely(!query->DoBegin()))\n      return;\n\n    EmitCs([cQuery = Com<D3D11Query, false>(query)]\n    (DxvkContext* ctx) {\n      cQuery->Begin(ctx);\n    });\n  }\n\n\n  void STDMETHODCALLTYPE D3D11ImmediateContext::End(ID3D11Asynchronous* pAsync) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!pAsync))\n      return;\n    \n    auto query = static_cast<D3D11Query*>(pAsync);\n\n    if (unlikely(!query->DoEnd())) {\n      EmitCs([cQuery = Com<D3D11Query, false>(query)]\n      (DxvkContext* ctx) {\n        cQuery->Begin(ctx);\n      });\n    }\n\n    EmitCs([cQuery = Com<D3D11Query, false>(query)]\n    (DxvkContext* ctx) {\n      cQuery->End(ctx);\n    });\n\n    if (unlikely(query->TrackStalls())) {\n      query->NotifyEnd();\n\n      if (query->IsStalling())\n        ExecuteFlush(GpuFlushType::ImplicitSynchronization, nullptr, false);\n      else if (query->IsEvent())\n        ConsiderFlush(GpuFlushType::ImplicitStrongHint);\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D11ImmediateContext::Flush() {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      m_flushReason = \"Explicit Flush\";\n\n    ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true);\n  }\n\n\n  void STDMETHODCALLTYPE D3D11ImmediateContext::Flush1(\n          D3D11_CONTEXT_TYPE          ContextType,\n          HANDLE                      hEvent) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      m_flushReason = \"Explicit Flush\";\n\n    ExecuteFlush(GpuFlushType::ExplicitFlush, hEvent, true);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Signal(\n          ID3D11Fence*                pFence,\n          UINT64                      Value) {\n    D3D10DeviceLock lock = LockContext();\n    auto fence = static_cast<D3D11Fence*>(pFence);\n\n    if (!fence)\n      return E_INVALIDARG;\n\n    EmitCs([\n      cFence = fence->GetFence(),\n      cValue = Value\n    ] (DxvkContext* ctx) {\n      ctx->signalFence(cFence, cValue);\n    });\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      m_flushReason = \"Fence signal\";\n\n    ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true);\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Wait(\n          ID3D11Fence*                pFence,\n          UINT64                      Value) {\n    D3D10DeviceLock lock = LockContext();\n    auto fence = static_cast<D3D11Fence*>(pFence);\n\n    if (!fence)\n      return E_INVALIDARG;\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      m_flushReason = \"Fence wait\";\n\n    ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true);\n\n    EmitCs([\n      cFence = fence->GetFence(),\n      cValue = Value\n    ] (DxvkContext* ctx) {\n      ctx->waitFence(cFence, cValue);\n    });\n\n    return S_OK;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11ImmediateContext::ExecuteCommandList(\n          ID3D11CommandList*  pCommandList,\n          BOOL                RestoreContextState) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto commandList = static_cast<D3D11CommandList*>(pCommandList);\n\n    // Reset dirty binding tracking before submitting any CS chunks.\n    // This is needed so that any submission that might occur during\n    // this call does not disrupt bindings set by the deferred context.\n    ResetDirtyTracking();\n\n    // Clear state so that the command list can't observe any\n    // current context state. The command list itself will clean\n    // up after execution to ensure that no state changes done\n    // by the command list are visible to the immediate context.\n    ResetCommandListState();\n\n    // Flush any outstanding commands so that\n    // we don't mess up the execution order\n    FlushCsChunk();\n    \n    // As an optimization, flush everything if the\n    // number of pending draw calls is high enough.\n    ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n\n    // Dispatch command list to the CS thread\n    commandList->EmitToCsThread([this] (DxvkCsChunkRef&& chunk, uint64_t cost, GpuFlushType flushType) {\n      EmitCsChunk(std::move(chunk));\n\n      // Return the sequence number from before the flush since\n      // that is actually going to be needed for resource tracking\n      uint64_t csSeqNum = m_csSeqNum;\n\n      // Consider a flush after every chunk in case the app\n      // submits a very large command list or the GPU is idle\n      AddCost(cost);\n      ConsiderFlush(flushType);\n      return csSeqNum;\n    });\n\n    // Restore the immediate context's state\n    if (RestoreContextState)\n      RestoreCommandListState();\n    else\n      ResetContextState();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::FinishCommandList(\n          BOOL                RestoreDeferredContextState,\n          ID3D11CommandList   **ppCommandList) {\n    InitReturnPtr(ppCommandList);\n    \n    Logger::err(\"D3D11: FinishCommandList called on immediate context\");\n    return DXGI_ERROR_INVALID_CALL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Map(\n          ID3D11Resource*             pResource,\n          UINT                        Subresource,\n          D3D11_MAP                   MapType,\n          UINT                        MapFlags,\n          D3D11_MAPPED_SUBRESOURCE*   pMappedResource) {\n    D3D10DeviceLock lock = LockContext();\n\n    if (unlikely(!pResource))\n      return E_INVALIDARG;\n    \n    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resourceDim);\n\n    if (likely(resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER)) {\n      return MapBuffer(\n        static_cast<D3D11Buffer*>(pResource),\n        MapType, MapFlags, pMappedResource);\n    } else {\n      return MapImage(GetCommonTexture(pResource),\n        Subresource, MapType, MapFlags, pMappedResource);\n    }\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11ImmediateContext::Unmap(\n          ID3D11Resource*             pResource,\n          UINT                        Subresource) {\n    // Since it is very uncommon for images to be mapped compared\n    // to buffers, we count the currently mapped images in order\n    // to avoid a virtual method call in the common case.\n    if (unlikely(m_mappedImageCount > 0)) {\n      D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n      pResource->GetType(&resourceDim);\n\n      if (resourceDim != D3D11_RESOURCE_DIMENSION_BUFFER) {\n        D3D10DeviceLock lock = LockContext();\n        UnmapImage(GetCommonTexture(pResource), Subresource);\n      }\n    }\n  }\n\n\n  HRESULT D3D11ImmediateContext::MapBuffer(\n          D3D11Buffer*                pResource,\n          D3D11_MAP                   MapType,\n          UINT                        MapFlags,\n          D3D11_MAPPED_SUBRESOURCE*   pMappedResource) {\n    if (unlikely(!pMappedResource))\n      return E_INVALIDARG;\n\n    if (unlikely(pResource->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_NONE)) {\n      Logger::err(\"D3D11: Cannot map a device-local buffer\");\n      pMappedResource->pData = nullptr;\n      return E_INVALIDARG;\n    }\n\n    VkDeviceSize bufferSize = pResource->Desc()->ByteWidth;\n\n    if (likely(MapType == D3D11_MAP_WRITE_DISCARD)) {\n      // Allocate a new backing slice for the buffer and set\n      // it as the 'new' mapped slice. This assumes that the\n      // only way to invalidate a buffer is by mapping it.\n      auto bufferSlice = pResource->DiscardSlice(&m_allocationCache);\n      pMappedResource->pData      = bufferSlice->mapPtr();\n      pMappedResource->RowPitch   = bufferSize;\n      pMappedResource->DepthPitch = bufferSize;\n      \n      EmitCs([\n        cBuffer      = pResource->GetBuffer(),\n        cBufferSlice = std::move(bufferSlice)\n      ] (DxvkContext* ctx) mutable {\n        ctx->invalidateBuffer(cBuffer, std::move(cBufferSlice));\n      });\n\n      // Ignore small buffers here. These are often updated per\n      // draw and won't contribute much to memory waste anyway.\n      if (unlikely(bufferSize > DxvkPageAllocator::PageSize))\n        ThrottleDiscard(bufferSize);\n\n      return S_OK;\n    } else if (likely(MapType == D3D11_MAP_WRITE_NO_OVERWRITE)) {\n      // Put this on a fast path without any extra checks since it's\n      // a somewhat desired method to partially update large buffers\n      pMappedResource->pData      = pResource->GetMapPtr();\n      pMappedResource->RowPitch   = bufferSize;\n      pMappedResource->DepthPitch = bufferSize;\n      return S_OK;\n    } else {\n      // Quantum Break likes using MAP_WRITE on resources which would force\n      // us to synchronize with the GPU multiple times per frame. In those\n      // situations, if there are no pending GPU writes to the resource, we\n      // can promote it to MAP_WRITE_DISCARD, but preserve the data by doing\n      // a CPU copy from the previous buffer slice, to avoid the sync point.\n      bool doInvalidatePreserve = false;\n\n      auto buffer = pResource->GetBuffer();\n      auto sequenceNumber = pResource->GetSequenceNumber();\n\n      if (MapType != D3D11_MAP_READ && !MapFlags && bufferSize <= D3D11Initializer::MaxMemoryPerSubmission) {\n        SynchronizeCsThread(sequenceNumber);\n\n        bool hasWoAccess = buffer->isInUse(DxvkAccess::Write);\n        bool hasRwAccess = buffer->isInUse(DxvkAccess::Read);\n\n        if (hasRwAccess && !hasWoAccess) {\n          // Uncached reads can be so slow that a GPU sync may actually be faster\n          doInvalidatePreserve = buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_CACHED_BIT;\n        }\n      }\n\n      if (doInvalidatePreserve) {\n        auto srcPtr = pResource->GetMapPtr();\n\n        auto dstSlice = pResource->DiscardSlice(nullptr);\n        auto dstPtr = dstSlice->mapPtr();\n\n        EmitCs([\n          cBuffer      = std::move(buffer),\n          cBufferSlice = std::move(dstSlice)\n        ] (DxvkContext* ctx) mutable {\n          ctx->invalidateBuffer(cBuffer, std::move(cBufferSlice));\n        });\n\n        std::memcpy(dstPtr, srcPtr, bufferSize);\n        pMappedResource->pData      = dstPtr;\n        pMappedResource->RowPitch   = bufferSize;\n        pMappedResource->DepthPitch = bufferSize;\n\n        ThrottleDiscard(bufferSize);\n        return S_OK;\n      } else {\n        if (!WaitForResource(*buffer, sequenceNumber, MapType, MapFlags)) {\n          pMappedResource->pData = nullptr;\n          return DXGI_ERROR_WAS_STILL_DRAWING;\n        }\n\n        pMappedResource->pData      = pResource->GetMapPtr();\n        pMappedResource->RowPitch   = bufferSize;\n        pMappedResource->DepthPitch = bufferSize;\n        return S_OK;\n      }\n    }\n  }\n  \n  \n  HRESULT D3D11ImmediateContext::MapImage(\n          D3D11CommonTexture*         pResource,\n          UINT                        Subresource,\n          D3D11_MAP                   MapType,\n          UINT                        MapFlags,\n          D3D11_MAPPED_SUBRESOURCE*   pMappedResource) {\n    auto mapMode = pResource->GetMapMode();\n\n    if (pMappedResource)\n      pMappedResource->pData = nullptr;\n\n    if (unlikely(Subresource >= pResource->CountSubresources()))\n      return E_INVALIDARG;\n\n    switch (MapType) {\n      case D3D11_MAP_READ: {\n        if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_READ))\n          return E_INVALIDARG;\n      } break;\n\n      case D3D11_MAP_READ_WRITE: {\n        if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_READ)\n         || !(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE))\n          return E_INVALIDARG;\n      } break;\n\n      case D3D11_MAP_WRITE: {\n        if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)\n         || (pResource->Desc()->Usage == D3D11_USAGE_DYNAMIC))\n          return E_INVALIDARG;\n      } break;\n\n      case D3D11_MAP_WRITE_DISCARD: {\n        if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)\n         || pResource->Desc()->Usage != D3D11_USAGE_DYNAMIC)\n          return E_INVALIDARG;\n      } break;\n\n      case D3D11_MAP_WRITE_NO_OVERWRITE: {\n        // NO_OVERWRITE is explcitly banned for dynamic images\n        if (!(pResource->Desc()->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)\n         || (pResource->Desc()->Usage != D3D11_USAGE_DEFAULT))\n          return E_INVALIDARG;\n      } break;\n    }\n\n    if (likely(pMappedResource != nullptr)) {\n      // Resources with an unknown memory layout cannot return a pointer\n      if (pResource->Desc()->Usage         == D3D11_USAGE_DEFAULT\n       && pResource->Desc()->TextureLayout == D3D11_TEXTURE_LAYOUT_UNDEFINED)\n        return E_INVALIDARG;\n    } else {\n      if (pResource->Desc()->Usage != D3D11_USAGE_DEFAULT)\n        return E_INVALIDARG;\n    }\n\n    VkFormat packedFormat = m_parent->LookupPackedFormat(\n      pResource->Desc()->Format, pResource->GetFormatMode()).Format;\n    \n    uint64_t sequenceNumber = pResource->GetSequenceNumber(Subresource);\n\n    auto formatInfo = lookupFormatInfo(packedFormat);\n    auto layout = pResource->GetSubresourceLayout(formatInfo->aspectMask, Subresource);\n\n    if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) {\n      Rc<DxvkImage> mappedImage = pResource->GetImage();\n\n      if (MapType == D3D11_MAP_WRITE_DISCARD) {\n        EmitCs([\n          cImage = std::move(mappedImage),\n          cStorage = pResource->DiscardStorage()\n        ] (DxvkContext* ctx) {\n          ctx->invalidateImage(cImage, Rc<DxvkResourceAllocation>(cStorage), VK_IMAGE_LAYOUT_PREINITIALIZED);\n        });\n\n        ThrottleDiscard(layout.Size);\n      } else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) {\n        if (!WaitForResource(*mappedImage, sequenceNumber, MapType, MapFlags))\n          return DXGI_ERROR_WAS_STILL_DRAWING;\n      }\n    } else if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC) {\n      // Nothing else to really do here, NotifyMap will ensure that we\n      // actually get a staging buffer that isn't currently in use.\n      ThrottleDiscard(layout.Size);\n    } else {\n      Rc<DxvkBuffer> mappedBuffer = pResource->GetMappedBuffer(Subresource);\n\n      constexpr uint32_t DoInvalidate = (1u << 0);\n      constexpr uint32_t DoPreserve   = (1u << 1);\n      constexpr uint32_t DoWait       = (1u << 2);\n      uint32_t doFlags;\n\n      if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) {\n        // If the image can be written by the GPU, we need to update the\n        // mapped staging buffer to reflect the current image contents.\n        if (pResource->Desc()->Usage == D3D11_USAGE_DEFAULT) {\n          bool needsReadback = !pResource->NeedsDirtyRegionTracking();\n\n          needsReadback |= MapType == D3D11_MAP_READ\n                        || MapType == D3D11_MAP_READ_WRITE;\n\n          if (needsReadback)\n            ReadbackImageBuffer(pResource, Subresource);\n        }\n      }\n\n      if (MapType == D3D11_MAP_READ) {\n        // Reads will not change the image content, so we only need\n        // to wait for the GPU to finish writing to the mapped buffer.\n        doFlags = DoWait;\n      } else if (MapType == D3D11_MAP_WRITE_DISCARD) {\n        doFlags = DoInvalidate;\n\n        // If we know for sure that the mapped buffer is currently not\n        // in use by the GPU, we don't have to allocate a new slice.\n        if (m_csThread.lastSequenceNumber() >= sequenceNumber && !mappedBuffer->isInUse(DxvkAccess::Read))\n          doFlags = 0;\n      } else if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING && (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT)) {\n        // Always respect DO_NOT_WAIT for mapped staging images\n        doFlags = DoWait;\n      } else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE || mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) {\n        // Need to synchronize thread to determine pending GPU accesses\n        SynchronizeCsThread(sequenceNumber);\n\n        // Don't implicitly discard large very large resources\n        // since that might lead to memory issues.\n        VkDeviceSize bufferSize = mappedBuffer->info().size;\n\n        if (bufferSize > D3D11Initializer::MaxMemoryPerSubmission) {\n          // Don't check access flags, WaitForResource will return\n          // early anyway if the resource is currently in use\n          doFlags = DoWait;\n        } else if (mappedBuffer->isInUse(DxvkAccess::Write)) {\n          // There are pending GPU writes, need to wait for those\n          doFlags = DoWait;\n        } else if (mappedBuffer->isInUse(DxvkAccess::Read)) {\n          // All pending GPU accesses are reads, so the buffer data\n          // is still current, and we can prevent GPU synchronization\n          // by creating a new slice with an exact copy of the data.\n          doFlags = DoInvalidate | DoPreserve;\n        } else {\n          // There are no pending accesses, so we don't need to wait\n          doFlags = 0;\n        }\n      } else {\n        // No need to synchronize staging resources with NO_OVERWRITE\n        // since the buffer will be used directly.\n        doFlags = 0;\n      }\n\n      if (doFlags & DoInvalidate) {\n        VkDeviceSize bufferSize = mappedBuffer->info().size;\n\n        auto srcSlice = pResource->GetMappedSlice(Subresource);\n        auto dstSlice = pResource->DiscardSlice(Subresource);\n\n        auto srcPtr = srcSlice->mapPtr();\n        auto dstPtr = dstSlice->mapPtr();\n\n        EmitCs([\n          cImageBuffer      = std::move(mappedBuffer),\n          cImageBufferSlice = std::move(dstSlice)\n        ] (DxvkContext* ctx) mutable {\n          ctx->invalidateBuffer(cImageBuffer, std::move(cImageBufferSlice));\n        });\n\n        if (doFlags & DoPreserve)\n          std::memcpy(dstPtr, srcPtr, bufferSize);\n\n        ThrottleDiscard(bufferSize);\n      } else {\n        if (doFlags & DoWait) {\n          // We cannot respect DO_NOT_WAIT for buffer-mapped resources since\n          // our internal copies need to be transparent to the application.\n          if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER)\n            MapFlags &= ~D3D11_MAP_FLAG_DO_NOT_WAIT;\n\n          // Wait for mapped buffer to become available\n          if (!WaitForResource(*mappedBuffer, sequenceNumber, MapType, MapFlags))\n            return DXGI_ERROR_WAS_STILL_DRAWING;\n        }\n      }\n    }\n\n    // Mark the subresource as successfully mapped\n    pResource->NotifyMap(Subresource, MapType);\n\n    if (pMappedResource) {\n      pMappedResource->pData      = pResource->GetMapPtr(Subresource, layout.Offset);\n      pMappedResource->RowPitch   = layout.RowPitch;\n      pMappedResource->DepthPitch = layout.DepthPitch;\n    }\n\n    m_mappedImageCount += 1;\n    return S_OK;\n  }\n  \n  \n  void D3D11ImmediateContext::UnmapImage(\n          D3D11CommonTexture*         pResource,\n          UINT                        Subresource) {\n    auto mapType = pResource->GetMapType(Subresource);\n    auto mapMode = pResource->GetMapMode();\n\n    if (mapType == D3D11CommonTexture::UnmappedSubresource)\n      return;\n\n    // Decrement mapped image counter only after making sure\n    // the given subresource is actually mapped right now\n    m_mappedImageCount -= 1;\n\n    // If the texture has an image as well as a staging buffer,\n    // upload the written buffer data to the image\n    bool needsUpload = mapType != uint32_t(D3D11_MAP_READ)\n      && (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER || mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC);\n\n    if (needsUpload) {\n      if (pResource->NeedsDirtyRegionTracking()) {\n        for (uint32_t i = 0; i < pResource->GetDirtyRegionCount(Subresource); i++) {\n          D3D11_COMMON_TEXTURE_REGION region = pResource->GetDirtyRegion(Subresource, i);\n          UpdateDirtyImageRegion(pResource, Subresource, &region);\n        }\n      } else {\n        UpdateDirtyImageRegion(pResource, Subresource, nullptr);\n      }\n    }\n\n    // Unmap the subresource. This will implicitly destroy the\n    // staging buffer for dynamically mapped images.\n    pResource->NotifyUnmap(Subresource);\n  }\n  \n  \n  void D3D11ImmediateContext::ReadbackImageBuffer(\n          D3D11CommonTexture*         pResource,\n          UINT                        Subresource) {\n    VkImageAspectFlags aspectMask = lookupFormatInfo(pResource->GetPackedFormat())->aspectMask;\n    VkImageSubresource subresource = pResource->GetSubresourceFromIndex(aspectMask, Subresource);\n\n    EmitCs([\n      cSrcImage           = pResource->GetImage(),\n      cSrcSubresource     = vk::makeSubresourceLayers(subresource),\n      cDstBuffer          = pResource->GetMappedBuffer(Subresource),\n      cPackedFormat       = pResource->GetPackedFormat()\n    ] (DxvkContext* ctx) {\n      VkOffset3D offset = { 0, 0, 0 };\n      VkExtent3D extent = cSrcImage->mipLevelExtent(cSrcSubresource.mipLevel);\n\n      ctx->copyImageToBuffer(cDstBuffer, 0, 0, 0, cPackedFormat,\n        cSrcImage, cSrcSubresource, offset, extent);\n    });\n\n    if (pResource->HasSequenceNumber())\n      TrackTextureSequenceNumber(pResource, Subresource);\n  }\n\n\n  void D3D11ImmediateContext::UpdateDirtyImageRegion(\n          D3D11CommonTexture*         pResource,\n          UINT                        Subresource,\n    const D3D11_COMMON_TEXTURE_REGION* pRegion) {\n    auto formatInfo = lookupFormatInfo(pResource->GetPackedFormat());\n    auto subresource = vk::makeSubresourceLayers(\n      pResource->GetSubresourceFromIndex(formatInfo->aspectMask, Subresource));\n\n    // Update the entire image if no dirty region was specified\n    D3D11_COMMON_TEXTURE_REGION region;\n\n    if (pRegion) {\n      region = *pRegion;\n    } else {\n      region.Offset = VkOffset3D { 0, 0, 0 };\n      region.Extent = pResource->MipLevelExtent(subresource.mipLevel);\n    }\n\n    auto subresourceLayout = pResource->GetSubresourceLayout(formatInfo->aspectMask, Subresource);\n\n    // Update dirty region one aspect at a time, due to\n    // how the data is laid out in the staging buffer.\n    for (uint32_t i = 0; i < pResource->GetPlaneCount(); i++) {\n      subresource.aspectMask = formatInfo->aspectMask;\n\n      if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane))\n        subresource.aspectMask = vk::getPlaneAspect(i);\n\n      EmitCs([\n        cDstImage       = pResource->GetImage(),\n        cDstSubresource = subresource,\n        cDstOffset      = region.Offset,\n        cDstExtent      = region.Extent,\n        cSrcBuffer      = pResource->GetMappedBuffer(Subresource),\n        cSrcOffset      = pResource->ComputeMappedOffset(Subresource, i, region.Offset),\n        cSrcRowPitch    = subresourceLayout.RowPitch,\n        cSrcDepthPitch  = subresourceLayout.DepthPitch,\n        cPackedFormat   = pResource->GetPackedFormat()\n      ] (DxvkContext* ctx) {\n        ctx->copyBufferToImage(\n          cDstImage, cDstSubresource, cDstOffset, cDstExtent,\n          cSrcBuffer, cSrcOffset, cSrcRowPitch, cSrcDepthPitch,\n          cPackedFormat);\n      });\n    }\n\n    if (pResource->HasSequenceNumber())\n      TrackTextureSequenceNumber(pResource, Subresource);\n  }\n\n\n  void D3D11ImmediateContext::UpdateMappedBuffer(\n          D3D11Buffer*                  pDstBuffer,\n          UINT                          Offset,\n          UINT                          Length,\n    const void*                         pSrcData,\n          UINT                          CopyFlags) {\n    void* mapPtr = nullptr;\n\n    if (likely(CopyFlags != D3D11_COPY_NO_OVERWRITE)) {\n      auto bufferSlice = pDstBuffer->DiscardSlice(&m_allocationCache);\n      mapPtr = bufferSlice->mapPtr();\n\n      EmitCs([\n        cBuffer      = pDstBuffer->GetBuffer(),\n        cBufferSlice = std::move(bufferSlice)\n      ] (DxvkContext* ctx) mutable {\n        ctx->invalidateBuffer(cBuffer, std::move(cBufferSlice));\n      });\n    } else {\n      mapPtr = pDstBuffer->GetMapPtr();\n    }\n\n    std::memcpy(reinterpret_cast<char*>(mapPtr) + Offset, pSrcData, Length);\n  }\n\n\n  void STDMETHODCALLTYPE D3D11ImmediateContext::SwapDeviceContextState(\n          ID3DDeviceContextState*           pState,\n          ID3DDeviceContextState**          ppPreviousState) {\n    InitReturnPtr(ppPreviousState);\n\n    if (!pState)\n      return;\n\n    // Clear dirty tracking here since all context state will be\n    // re-applied anyway when the context state is swapped in again.\n    ResetDirtyTracking();\n\n    // Reset all state affected by the current context state.\n    ResetCommandListState();\n\n    Com<D3D11DeviceContextState, false> oldState = std::move(m_stateObject);\n    Com<D3D11DeviceContextState, false> newState = static_cast<D3D11DeviceContextState*>(pState);\n\n    if (oldState == nullptr)\n      oldState = new D3D11DeviceContextState(m_parent);\n    \n    if (ppPreviousState)\n      *ppPreviousState = oldState.ref();\n    \n    m_stateObject = newState;\n\n    oldState->SetState(m_state);\n    newState->GetState(m_state);\n\n    // Restore all state affected by the new context state\n    RestoreCommandListState();\n  }\n\n\n  void D3D11ImmediateContext::Acquire11on12Resource(\n          ID3D11Resource*             pResource,\n          VkImageLayout               SrcLayout) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto texture = GetCommonTexture(pResource);\n    auto buffer = GetCommonBuffer(pResource);\n\n    Rc<DxvkPagedResource> resource;\n\n    if (texture)\n      resource = texture->GetImage();\n    else if (buffer)\n      resource = buffer->GetBuffer();\n\n    if (resource) {\n      EmitCs([\n        cResource = std::move(resource),\n        cLayout   = SrcLayout\n      ] (DxvkContext* ctx) {\n        ctx->acquireExternalResource(cResource, cLayout);\n      });\n    }\n  }\n\n\n  void D3D11ImmediateContext::Release11on12Resource(\n          ID3D11Resource*             pResource,\n          VkImageLayout               DstLayout) {\n    D3D10DeviceLock lock = LockContext();\n\n    auto texture = GetCommonTexture(pResource);\n    auto buffer = GetCommonBuffer(pResource);\n\n    Rc<DxvkPagedResource> resource;\n\n    if (texture)\n      resource = texture->GetImage();\n    else if (buffer)\n      resource = buffer->GetBuffer();\n\n    if (resource) {\n      EmitCs([\n        cResource = std::move(resource),\n        cLayout   = DstLayout\n      ] (DxvkContext* ctx) {\n        ctx->releaseExternalResource(cResource, cLayout);\n      });\n    }\n  }\n\n\n  void D3D11ImmediateContext::SynchronizeCsThread(uint64_t SequenceNumber) {\n    D3D10DeviceLock lock = LockContext();\n\n    // Dispatch current chunk so that all commands\n    // recorded prior to this function will be run\n    if (SequenceNumber > m_csSeqNum)\n      FlushCsChunk();\n    \n    m_csThread.synchronize(SequenceNumber);\n  }\n  \n  \n  void D3D11ImmediateContext::SynchronizeDevice() {\n    m_device->waitForIdle();\n  }\n  \n  \n  void D3D11ImmediateContext::EndFrame(\n          Rc<DxvkLatencyTracker>      LatencyTracker) {\n    D3D10DeviceLock lock = LockContext();\n\n    // Don't keep draw buffers alive indefinitely. This cannot be\n    // done in ExecuteFlush because command recording itself might\n    // flush, so no state changes are allowed to happen there.\n    SetDrawBuffers(nullptr, nullptr);\n\n    EmitCs<false>([\n      cTracker = std::move(LatencyTracker)\n    ] (DxvkContext* ctx) {\n      ctx->endFrame();\n\n      if (cTracker && cTracker->needsAutoMarkers())\n        ctx->endLatencyTracking(cTracker);\n    });\n  }\n\n\n  bool D3D11ImmediateContext::WaitForResource(\n    const DxvkPagedResource&                Resource,\n          uint64_t                          SequenceNumber,\n          D3D11_MAP                         MapType,\n          UINT                              MapFlags) {\n    // Determine access type to wait for based on map mode\n    DxvkAccess access = MapType == D3D11_MAP_READ\n      ? DxvkAccess::Write\n      : DxvkAccess::Read;\n    \n    // Wait for any CS chunk using the resource to execute, since\n    // otherwise we cannot accurately determine if the resource is\n    // actually being used by the GPU right now.\n    if (!Resource.isInUse(access)) {\n      SynchronizeCsThread(SequenceNumber);\n\n      if (!Resource.isInUse(access))\n        return true;\n    }\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) {\n      m_flushReason = str::format(\"Map \", Resource.getDebugName(), \" (MAP\",\n        MapType != D3D11_MAP_WRITE ? \"_READ\" : \"\",\n        MapType != D3D11_MAP_READ ? \"_WRITE\" : \"\", \")\");\n    }\n\n    if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT) {\n      // We don't have to wait, but misbehaving games may\n      // still try to spin on `Map` until the resource is\n      // idle, so we should flush pending commands\n      ConsiderFlush(GpuFlushType::ImplicitSynchronization);\n      return false;\n    } else {\n      // Make sure pending commands using the resource get\n      // executed on the the GPU if we have to wait for it\n      ExecuteFlush(GpuFlushType::ImplicitSynchronization, nullptr, false);\n      SynchronizeCsThread(SequenceNumber);\n\n      m_device->waitForResource(Resource, access);\n      return true;\n    }\n  }\n\n\n  void D3D11ImmediateContext::InjectCsChunk(\n          DxvkCsQueue                 Queue,\n          DxvkCsChunkRef&&            Chunk,\n          bool                        Synchronize) {\n    // Do not update the sequence number when emitting a chunk\n    // from an external source since that would break tracking\n    m_csThread.injectChunk(Queue, std::move(Chunk), Synchronize);\n  }\n\n\n  void D3D11ImmediateContext::EmitCsChunk(DxvkCsChunkRef&& chunk) {\n    // Flush init commands so that the CS thread\n    // can processe them before the first use.\n    m_parent->FlushInitCommands();\n\n    m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk));\n  }\n\n\n  void D3D11ImmediateContext::TrackTextureSequenceNumber(\n          D3D11CommonTexture*         pResource,\n          UINT                        Subresource) {\n    uint64_t sequenceNumber = GetCurrentSequenceNumber();\n    pResource->TrackSequenceNumber(Subresource, sequenceNumber);\n\n    ConsiderFlush(GpuFlushType::ImplicitStrongHint);\n  }\n\n\n  void D3D11ImmediateContext::TrackBufferSequenceNumber(\n          D3D11Buffer*                pResource) {\n    uint64_t sequenceNumber = GetCurrentSequenceNumber();\n    pResource->TrackSequenceNumber(sequenceNumber);\n\n    ConsiderFlush(GpuFlushType::ImplicitStrongHint);\n  }\n\n\n  uint64_t D3D11ImmediateContext::GetCurrentSequenceNumber() {\n    // We do not flush empty chunks, so if we are tracking a resource\n    // immediately after a flush, we need to use the sequence number\n    // of the previously submitted chunk to prevent deadlocks.\n    return m_csChunk->empty() ? m_csSeqNum : m_csSeqNum + 1;\n  }\n\n\n  uint64_t D3D11ImmediateContext::GetPendingCsChunks() {\n    return GetCurrentSequenceNumber() - m_flushSeqNum;\n  }\n\n\n  void D3D11ImmediateContext::ApplyDirtyNullBindings() {\n    // At the end of a submission, set all bindings that have not been applied yet\n    // to null on the DXVK context. This way, we avoid keeping resources alive that\n    // are bound to the DXVK context but not to the immediate context.\n    //\n    // Note: This requires that all methods that may modify dirty bindings on the\n    // DXVK context also reset the corresponding dirty bits *before* performing the\n    // bind operation, or otherwise an implicit flush can potentially override them.\n    auto& dirtyState = m_state.lazy.bindingsDirty;\n\n    EmitCs<false>([\n      cDirtyState = dirtyState\n    ] (DxvkContext* ctx) {\n      for (uint32_t i = 0; i < D3D11ShaderTypeCount; i++) {\n        auto dxStage = D3D11ShaderType(i);\n        auto vkStage = GetShaderStage(dxStage);\n\n        // Unbind all dirty constant buffers\n        auto cbvSlot = D3D11ShaderResourceMapping::computeCbvBinding(dxStage, 0);\n\n        for (uint32_t index : bit::BitMask(cDirtyState[dxStage].cbvMask))\n          ctx->bindUniformBuffer(vkStage, cbvSlot + index, DxvkBufferSlice());\n\n        // Unbind all dirty samplers\n        auto samplerSlot = D3D11ShaderResourceMapping::computeSamplerBinding(dxStage, 0);\n\n        for (uint32_t index : bit::BitMask(cDirtyState[dxStage].samplerMask))\n          ctx->bindResourceSampler(vkStage, samplerSlot + index, nullptr);\n\n        // Unbind all dirty shader resource views\n        auto srvSlot = D3D11ShaderResourceMapping::computeSrvBinding(dxStage, 0);\n\n        for (uint32_t m = 0; m < cDirtyState[dxStage].srvMask.size(); m++) {\n          for (uint32_t index : bit::BitMask(cDirtyState[dxStage].srvMask[m]))\n            ctx->bindResourceImageView(vkStage, srvSlot + index + m * 64u, nullptr);\n        }\n\n        // Unbind all dirty unordered access views\n        VkShaderStageFlags uavStages = 0u;\n\n        if (dxStage == D3D11ShaderType::eCompute)\n          uavStages = VK_SHADER_STAGE_COMPUTE_BIT;\n        else if (dxStage == D3D11ShaderType::ePixel)\n          uavStages = VK_SHADER_STAGE_ALL_GRAPHICS;\n\n        if (uavStages) {\n          auto uavSlot = D3D11ShaderResourceMapping::computeUavBinding(dxStage, 0);\n          auto ctrSlot = D3D11ShaderResourceMapping::computeUavCounterBinding(dxStage, 0);\n\n          for (uint32_t index : bit::BitMask(cDirtyState[dxStage].uavMask)) {\n            ctx->bindResourceImageView(vkStage, uavSlot + index, nullptr);\n            ctx->bindResourceBufferView(vkStage, ctrSlot + index, nullptr);\n          }\n        }\n      }\n    });\n\n    // Since we set the DXVK context bindings to null, any bindings that are null\n    // on the D3D context are no longer dirty, so we can clear the respective bits.\n    for (uint32_t i = 0; i < D3D11ShaderTypeCount; i++) {\n      auto stage = D3D11ShaderType(i);\n\n      for (uint32_t index : bit::BitMask(dirtyState[stage].cbvMask)) {\n        if (!m_state.cbv[stage].buffers[index].buffer.ptr())\n          dirtyState[stage].cbvMask &= ~(1u << index);\n      }\n\n      for (uint32_t index : bit::BitMask(dirtyState[stage].samplerMask)) {\n        if (!m_state.samplers[stage].samplers[index])\n          dirtyState[stage].samplerMask &= ~(1u << index);\n      }\n\n      for (uint32_t m = 0; m < dirtyState[stage].srvMask.size(); m++) {\n        for (uint32_t index : bit::BitMask(dirtyState[stage].srvMask[m])) {\n          if (!m_state.srv[stage].views[index + m * 64u].ptr())\n            dirtyState[stage].srvMask[m] &= ~(uint64_t(1u) << index);\n        }\n      }\n\n      if (stage == D3D11ShaderType::eCompute || stage == D3D11ShaderType::ePixel) {\n        auto& uavs = stage == D3D11ShaderType::eCompute ? m_state.uav.views : m_state.om.uavs;\n\n        for (uint32_t index : bit::BitMask(dirtyState[stage].uavMask)) {\n          if (!uavs[index].ptr())\n            dirtyState[stage].uavMask &= ~(uint64_t(1u) << index);\n        }\n      }\n\n      if (dirtyState[stage].empty())\n        m_state.lazy.shadersDirty.clr(stage);\n    }\n  }\n\n\n  void D3D11ImmediateContext::ConsiderFlush(\n          GpuFlushType                FlushType) {\n    // In stress test mode, behave as if this would always flush\n    if (DebugLazyBinding == Tristate::True)\n      ApplyDirtyNullBindings();\n\n    uint64_t chunkId = GetCurrentSequenceNumber();\n    uint64_t submissionId = m_submissionFence->value();\n\n    if (m_flushTracker.considerFlush(FlushType, chunkId, submissionId, m_estimatedCost))\n      ExecuteFlush(FlushType, nullptr, false);\n  }\n\n\n  void D3D11ImmediateContext::ExecuteFlush(\n          GpuFlushType                FlushType,\n          HANDLE                      hEvent,\n          BOOL                        Synchronize) {\n    bool synchronizeSubmission = Synchronize && m_parent->Is11on12Device();\n\n    if (synchronizeSubmission)\n      m_submitStatus.result = VK_NOT_READY;\n\n    // Exit early if there's nothing to do\n    if (!GetPendingCsChunks() && !hEvent)\n      return;\n\n    // Unbind unused resources\n    ApplyDirtyNullBindings();\n\n    // Signal the submission fence and flush the command list\n    uint64_t submissionId = ++m_submissionId;\n\n    if (hEvent) {\n      m_submissionFence->setCallback(submissionId, [hEvent] {\n        SetEvent(hEvent);\n      });\n    }\n\n    EmitCs<false>([\n      cSubmissionFence  = m_submissionFence,\n      cSubmissionId     = submissionId,\n      cSubmissionStatus = synchronizeSubmission ? &m_submitStatus : nullptr,\n      cStagingFence     = m_stagingBufferFence,\n      cStagingMemory    = GetStagingMemoryStatistics().allocatedTotal,\n      cFlushReason      = std::exchange(m_flushReason, std::string())\n    ] (DxvkContext* ctx) {\n      auto debugLabel = vk::makeLabel(0xff5959, cFlushReason.c_str());\n\n      ctx->signal(cSubmissionFence, cSubmissionId);\n      ctx->signal(cStagingFence, cStagingMemory);\n      ctx->flushCommandList(&debugLabel, cSubmissionStatus);\n    });\n\n    FlushCsChunk();\n\n    // Notify flush tracker about the flush\n    m_flushSeqNum = m_csSeqNum;\n    m_flushTracker.notifyFlush(m_flushSeqNum, submissionId);\n\n    // If necessary, block calling thread until the\n    // Vulkan queue submission is performed.\n    if (synchronizeSubmission)\n      m_device->waitForSubmission(&m_submitStatus);\n\n    // Free local staging buffer so that we don't\n    // end up with a persistent allocation\n    ResetStagingBuffer();\n\n    // Reset counter for discarded memory in flight\n    m_discardMemoryOnFlush = m_discardMemoryCounter;\n\n    // Reset GPU execution cost estimate\n    m_estimatedCost = 0u;\n\n    // Notify the device that the context has been flushed,\n    // this resets some resource initialization heuristics.\n    m_parent->NotifyContextFlush();\n  }\n\n\n  void D3D11ImmediateContext::ThrottleAllocation() {\n    DxvkStagingBufferStats stats = GetStagingMemoryStatistics();\n\n    VkDeviceSize stagingMemoryInFlight = stats.allocatedTotal - m_stagingBufferFence->value();\n\n    if (stagingMemoryInFlight > stats.allocatedSinceLastReset + D3D11Initializer::MaxMemoryInFlight) {\n      // Stall calling thread to avoid situation where we keep growing the staging\n      // buffer indefinitely, but ignore the newly allocated amount so that we don't\n      // wait for the GPU to go fully idle in case of a large allocation.\n      ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, false);\n\n      m_device->waitForFence(*m_stagingBufferFence, stats.allocatedTotal -\n        stats.allocatedSinceLastReset - D3D11Initializer::MaxMemoryInFlight);\n    } else if (stats.allocatedSinceLastReset >= D3D11Initializer::MaxMemoryPerSubmission) {\n      // Flush somewhat aggressively if there's a lot of memory in flight\n      ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, false);\n    }\n  }\n\n\n  void D3D11ImmediateContext::ThrottleDiscard(\n          VkDeviceSize                Size) {\n    m_discardMemoryCounter += Size;\n\n    if (m_discardMemoryCounter - m_discardMemoryOnFlush >= D3D11Initializer::MaxMemoryPerSubmission)\n      ThrottleAllocation();\n  }\n\n\n  void D3D11ImmediateContext::NotifyRenderPassBoundary() {\n    // Doing this makes it less likely to flush during render passes\n    ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n\n    if (m_device->perfHints().preferRenderPassOps) {\n      // On tilers, we want to avoid submitting during a render pass or a sequence\n      // of render passes as much as possible, but if a submission request has been\n      // rejected before, we should do it now in order to avoid read-back delays.\n      GpuFlushType pending = m_flushTracker.getPendingType();\n\n      if (pending != GpuFlushType::None)\n        ExecuteFlush(pending, nullptr, false);\n    }\n  }\n\n\n  DxvkStagingBufferStats D3D11ImmediateContext::GetStagingMemoryStatistics() {\n    DxvkStagingBufferStats stats = m_staging.getStatistics();\n    stats.allocatedTotal += m_discardMemoryCounter;\n    stats.allocatedSinceLastReset += m_discardMemoryCounter - m_discardMemoryOnFlush;\n    return stats;\n  }\n\n\n  GpuFlushType D3D11ImmediateContext::GetMaxFlushType(\n          D3D11Device*    pParent,\n    const Rc<DxvkDevice>& Device) {\n    if (pParent->GetOptions()->reproducibleCommandStream)\n      return GpuFlushType::ExplicitFlush;\n    else if (Device->perfHints().preferRenderPassOps)\n      return GpuFlushType::ImplicitStrongHint;\n    else\n      return GpuFlushType::ImplicitWeakHint;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_context_imm.h",
    "content": "#pragma once\n\n#include \"../util/util_time.h\"\n\n#include \"../util/sync/sync_signal.h\"\n\n#include \"d3d11_context.h\"\n#include \"d3d11_state_object.h\"\n#include \"d3d11_video.h\"\n\nnamespace dxvk {\n  \n  class D3D11Buffer;\n  class D3D11CommonTexture;\n\n  class D3D11ImmediateContext : public D3D11CommonContext<D3D11ImmediateContext> {\n    friend class D3D11CommonContext<D3D11ImmediateContext>;\n    friend class D3D11SwapChain;\n    friend class D3D11VideoContext;\n    friend class D3D11DXGIKeyedMutex;\n  public:\n    \n    D3D11ImmediateContext(\n            D3D11Device*    pParent,\n      const Rc<DxvkDevice>& Device);\n    ~D3D11ImmediateContext();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetData(\n            ID3D11Asynchronous*         pAsync,\n            void*                       pData,\n            UINT                        DataSize,\n            UINT                        GetDataFlags);\n    \n    void STDMETHODCALLTYPE Begin(\n            ID3D11Asynchronous*         pAsync);\n    \n    void STDMETHODCALLTYPE End(\n            ID3D11Asynchronous*         pAsync);\n    \n    void STDMETHODCALLTYPE Flush();\n    \n    void STDMETHODCALLTYPE Flush1(\n            D3D11_CONTEXT_TYPE          ContextType,\n            HANDLE                      hEvent);\n\n    HRESULT STDMETHODCALLTYPE Signal(\n            ID3D11Fence*                pFence,\n            UINT64                      Value);\n    \n    HRESULT STDMETHODCALLTYPE Wait(\n            ID3D11Fence*                pFence,\n            UINT64                      Value);\n\n    void STDMETHODCALLTYPE ExecuteCommandList(\n            ID3D11CommandList*  pCommandList,\n            BOOL                RestoreContextState);\n    \n    HRESULT STDMETHODCALLTYPE FinishCommandList(\n            BOOL                RestoreDeferredContextState,\n            ID3D11CommandList   **ppCommandList);\n    \n    HRESULT STDMETHODCALLTYPE Map(\n            ID3D11Resource*             pResource,\n            UINT                        Subresource,\n            D3D11_MAP                   MapType,\n            UINT                        MapFlags,\n            D3D11_MAPPED_SUBRESOURCE*   pMappedResource);\n    \n    void STDMETHODCALLTYPE Unmap(\n            ID3D11Resource*             pResource,\n            UINT                        Subresource);\n            \n    void STDMETHODCALLTYPE SwapDeviceContextState(\n            ID3DDeviceContextState*           pState,\n            ID3DDeviceContextState**          ppPreviousState);\n\n    void Acquire11on12Resource(\n            ID3D11Resource*             pResource,\n            VkImageLayout               SrcLayout);\n\n    void Release11on12Resource(\n            ID3D11Resource*             pResource,\n            VkImageLayout               DstLayout);\n\n    void SynchronizeCsThread(\n            uint64_t                          SequenceNumber);\n\n    D3D10Multithread& GetMultithread() {\n        return m_multithread;\n    }\n\n    D3D10DeviceLock LockContext() {\n      return m_multithread.AcquireLock();\n    }\n\n    void InjectCsChunk(\n            DxvkCsQueue                 Queue,\n            DxvkCsChunkRef&&            Chunk,\n            bool                        Synchronize);\n\n    template<typename Fn>\n    void InjectCs(\n            DxvkCsQueue                 Queue,\n            Fn&&                        Command) {\n      auto chunk = AllocCsChunk();\n      chunk->push(std::move(Command));\n\n      InjectCsChunk(Queue, std::move(chunk), false);\n    }\n\n  private:\n    \n    DxvkCsThread            m_csThread;\n    uint64_t                m_csSeqNum = 0ull;\n\n    uint32_t                m_mappedImageCount = 0u;\n\n    Rc<sync::CallbackFence> m_submissionFence;\n    uint64_t                m_submissionId = 0ull;\n    DxvkSubmitStatus        m_submitStatus;\n\n    uint64_t                m_flushSeqNum = 0ull;\n    GpuFlushTracker         m_flushTracker;\n\n    Rc<sync::Fence>         m_stagingBufferFence;\n\n    VkDeviceSize            m_discardMemoryCounter = 0u;\n    VkDeviceSize            m_discardMemoryOnFlush = 0u;\n\n    D3D10Multithread        m_multithread;\n    D3D11VideoContext       m_videoContext;\n\n    Com<D3D11DeviceContextState, false> m_stateObject;\n\n    D3DDestructionNotifier  m_destructionNotifier;\n\n    std::string             m_flushReason;\n\n    HRESULT MapBuffer(\n            D3D11Buffer*                pResource,\n            D3D11_MAP                   MapType,\n            UINT                        MapFlags,\n            D3D11_MAPPED_SUBRESOURCE*   pMappedResource);\n    \n    HRESULT MapImage(\n            D3D11CommonTexture*         pResource,\n            UINT                        Subresource,\n            D3D11_MAP                   MapType,\n            UINT                        MapFlags,\n            D3D11_MAPPED_SUBRESOURCE*   pMappedResource);\n    \n    void UnmapImage(\n            D3D11CommonTexture*         pResource,\n            UINT                        Subresource);\n    \n    void ReadbackImageBuffer(\n            D3D11CommonTexture*         pResource,\n            UINT                        Subresource);\n\n    void UpdateDirtyImageRegion(\n            D3D11CommonTexture*         pResource,\n            UINT                        Subresource,\n      const D3D11_COMMON_TEXTURE_REGION* pRegion);\n\n    void UpdateMappedBuffer(\n            D3D11Buffer*                pDstBuffer,\n            UINT                        Offset,\n            UINT                        Length,\n      const void*                       pSrcData,\n            UINT                        CopyFlags);\n\n    void SynchronizeDevice();\n\n    void EndFrame(\n            Rc<DxvkLatencyTracker>      LatencyTracker);\n    \n    bool WaitForResource(\n      const DxvkPagedResource&          Resource,\n            uint64_t                    SequenceNumber,\n            D3D11_MAP                   MapType,\n            UINT                        MapFlags);\n    \n    void EmitCsChunk(DxvkCsChunkRef&& chunk);\n\n    void TrackTextureSequenceNumber(\n            D3D11CommonTexture*         pResource,\n            UINT                        Subresource);\n\n    void TrackBufferSequenceNumber(\n            D3D11Buffer*                pResource);\n\n    uint64_t GetCurrentSequenceNumber();\n\n    uint64_t GetPendingCsChunks();\n\n    void ApplyDirtyNullBindings();\n\n    void ConsiderFlush(\n            GpuFlushType                FlushType);\n\n    void ExecuteFlush(\n            GpuFlushType                FlushType,\n            HANDLE                      hEvent,\n            BOOL                        Synchronize);\n\n    void ThrottleAllocation();\n\n    void ThrottleDiscard(\n            VkDeviceSize                Size);\n\n    void NotifyRenderPassBoundary();\n\n    DxvkStagingBufferStats GetStagingMemoryStatistics();\n\n    static GpuFlushType GetMaxFlushType(\n            D3D11Device*    pParent,\n      const Rc<DxvkDevice>& Device);\n\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_context_state.h",
    "content": "#pragma once\n\n#include <array>\n\n#include \"d3d11_blend.h\"\n#include \"d3d11_buffer.h\"\n#include \"d3d11_depth_stencil.h\"\n#include \"d3d11_input_layout.h\"\n#include \"d3d11_query.h\"\n#include \"d3d11_rasterizer.h\"\n#include \"d3d11_sampler.h\"\n#include \"d3d11_shader.h\"\n#include \"d3d11_state.h\"\n#include \"d3d11_view_dsv.h\"\n#include \"d3d11_view_rtv.h\"\n#include \"d3d11_view_srv.h\"\n#include \"d3d11_view_uav.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Per-stage state\n   *\n   * Stores an object of the given type for each shader stage.\n   * \\tparam Object type\n   */\n  template<typename T>\n  class D3D11ShaderStageState {\n\n  public:\n\n          T& operator [] (D3D11ShaderType type)       { return m_state[uint32_t(type)]; }\n    const T& operator [] (D3D11ShaderType type) const { return m_state[uint32_t(type)]; }\n\n    /**\n     * \\brief Calls reset method on all objects\n     */\n    void reset() {\n      for (auto& state : m_state)\n        state.reset();\n    }\n\n  private:\n\n    std::array<T, 6> m_state = { };\n\n  };\n\n\n  /**\n   * \\brief Constant buffer bindings\n   *\n   * Stores the bound buffer range from a runtime point of view,\n   * as well as the range that is actually bound to the context.\n   */\n  struct D3D11ConstantBufferBinding {\n    Com<D3D11Buffer, false> buffer  = nullptr;\n    UINT             constantOffset = 0;\n    UINT             constantCount  = 0;\n    UINT             constantBound  = 0;\n  };\n  \n  struct D3D11ShaderStageCbvBinding {\n    std::array<D3D11ConstantBufferBinding, D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT> buffers = { };\n\n    uint32_t maxCount = 0;\n\n    void reset() {\n      for (uint32_t i = 0; i < maxCount; i++)\n        buffers[i] = D3D11ConstantBufferBinding();\n\n      maxCount = 0;\n    }\n  };\n\n  using D3D11CbvBindings = D3D11ShaderStageState<D3D11ShaderStageCbvBinding>;\n  \n  /**\n   * \\brief Shader resource bindings\n   *\n   * Stores bound shader resource views, as well as a bit\n   * set of views that are potentially hazardous.\n   */\n  struct D3D11ShaderStageSrvBinding {\n    std::array<Com<D3D11ShaderResourceView, false>, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> views     = { };\n    DxvkBindingSet<D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT>                           hazardous = { };\n\n    uint32_t maxCount = 0;\n\n    void reset() {\n      for (uint32_t i = 0; i < maxCount; i++)\n        views[i] = nullptr;\n\n      hazardous.clear();\n      maxCount = 0;\n    }\n  };\n    \n  using D3D11SrvBindings = D3D11ShaderStageState<D3D11ShaderStageSrvBinding>;\n\n  /**\n   * \\brief Sampler bindings\n   *\n   * Stores bound samplers.\n   */\n  struct D3D11ShaderStageSamplerBinding {\n    std::array<Com<D3D11SamplerState, false>, D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT> samplers = { };\n\n    uint32_t maxCount = 0;\n\n    void reset() {\n      for (uint32_t i = 0; i < maxCount; i++)\n        samplers[i] = nullptr;\n\n      maxCount = 0;\n    }\n  };\n    \n  using D3D11SamplerBindings = D3D11ShaderStageState<D3D11ShaderStageSamplerBinding>;\n\n  /**\n   * \\brief UAV bindings\n   *\n   * Stores bound UAVs. For compute shader UAVs,\n   * we also store a bit mask of bound UAVs.\n   */\n  using D3D11ShaderStageUavBinding = std::array<Com<D3D11UnorderedAccessView, false>, D3D11_1_UAV_SLOT_COUNT>;\n  \n  struct D3D11UavBindings {\n    D3D11ShaderStageUavBinding              views = { };\n    DxvkBindingSet<D3D11_1_UAV_SLOT_COUNT>  mask  = { };\n\n    uint32_t maxCount = 0;\n\n    void reset() {\n      for (uint32_t i = 0; i < maxCount; i++)\n        views[i] = nullptr;\n\n      mask.clear();\n      maxCount = 0;\n    }\n  };\n\n  /**\n   * \\brief Input assembly state\n   *\n   * Stores vertex buffers, the index buffer, the\n   * input layout, and the dynamic primitive topology.\n   */\n  struct D3D11VertexBufferBinding {\n    Com<D3D11Buffer, false> buffer = nullptr;\n    UINT                    offset = 0;\n    UINT                    stride = 0;\n  };\n  \n  struct D3D11IndexBufferBinding {\n    Com<D3D11Buffer, false> buffer = nullptr;\n    UINT                    offset = 0;\n    DXGI_FORMAT             format = DXGI_FORMAT_UNKNOWN;\n  };\n\n  struct D3D11ContextStateIA {\n    Com<D3D11InputLayout, false> inputLayout       = nullptr;\n    D3D11_PRIMITIVE_TOPOLOGY     primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;\n    \n    std::array<D3D11VertexBufferBinding, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> vertexBuffers = { };\n    D3D11IndexBufferBinding                                                         indexBuffer   = { };\n\n    uint32_t maxVbCount = 0;\n\n    void reset() {\n      inputLayout = nullptr;\n\n      primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;\n\n      for (uint32_t i = 0; i < maxVbCount; i++)\n        vertexBuffers[i] = D3D11VertexBufferBinding();\n\n      indexBuffer = D3D11IndexBufferBinding();\n    }\n  };\n  \n  /**\n   * \\brief Output merger state\n   *\n   * Stores RTV, DSV, and graphics UAV bindings, as well as related state.\n   */\n  using D3D11RenderTargetViewBinding = std::array<Com<D3D11RenderTargetView, false>, D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT>;\n  \n  struct D3D11ContextStateOM {\n    D3D11ShaderStageUavBinding        uavs  = { };\n    D3D11RenderTargetViewBinding      rtvs  = { };\n    Com<D3D11DepthStencilView, false> dsv   = { };\n    \n    Com<D3D11BlendState, false>        cbState = nullptr;\n    Com<D3D11DepthStencilState, false> dsState = nullptr;\n    \n    FLOAT blendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };\n\n    UINT  sampleCount    = 0u;\n    UINT  sampleMask     = D3D11_DEFAULT_SAMPLE_MASK;\n    UINT  stencilRef     = D3D11_DEFAULT_STENCIL_REFERENCE;\n\n    UINT  maxRtv         = 0u;\n    UINT  minUav         = D3D11_1_UAV_SLOT_COUNT;\n    UINT  maxUav         = 0u;\n\n    void reset() {\n      for (uint32_t i = minUav; i < maxUav; i++)\n        uavs[i] = nullptr;\n\n      for (uint32_t i = 0; i < maxRtv; i++)\n        rtvs[i] = nullptr;\n\n      dsv = nullptr;\n\n      cbState = nullptr;\n      dsState = nullptr;\n\n      for (uint32_t i = 0; i < 4; i++)\n        blendFactor[i] = 1.0f;\n\n      sampleCount = 0u;\n      sampleMask = D3D11_DEFAULT_SAMPLE_MASK;\n      stencilRef = D3D11_DEFAULT_STENCIL_REFERENCE;\n\n      maxRtv = 0u;\n      minUav = D3D11_1_UAV_SLOT_COUNT;\n      maxUav = 0u;\n    }\n  };\n  \n  /**\n   * \\brief Indirect draw state\n   *\n   * Stores the current indirct draw\n   * argument and draw count buffer.\n   */\n  struct D3D11ContextStateID {\n    uint64_t argBufferCookie = 0u;\n    uint64_t cntBufferCookie = 0u;\n\n    void reset() {\n      argBufferCookie = 0u;\n      cntBufferCookie = 0u;\n    }\n  };\n\n  /**\n   * \\brief Rasterizer state\n   *\n   * Stores viewport info and the rasterizer state object.\n   */\n  struct D3D11ContextStateRS {\n    uint32_t numViewports = 0;\n    uint32_t numScissors  = 0;\n    \n    std::array<D3D11_VIEWPORT, D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE> viewports = { };\n    std::array<D3D11_RECT,     D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE> scissors  = { };\n    \n    Com<D3D11RasterizerState, false> state;\n\n    void reset() {\n      for (uint32_t i = 0; i < numViewports; i++)\n        viewports[i] = D3D11_VIEWPORT();\n\n      for (uint32_t i = 0; i < numScissors; i++)\n        scissors[i] = D3D11_RECT();\n\n      numViewports = 0;\n      numScissors = 0;\n\n      state = nullptr;\n    }\n  };\n\n  /**\n   * \\brief Stream output binding\n   *\n   * Stores stream output buffers with offset.\n   */\n  struct D3D11ContextSoTarget {\n    Com<D3D11Buffer, false> buffer = nullptr;\n    UINT                    offset = 0;\n  };\n\n  struct D3D11ContextStateSO {\n    std::array<D3D11ContextSoTarget, D3D11_SO_BUFFER_SLOT_COUNT> targets = { };\n\n    void reset() {\n      for (uint32_t i = 0; i < targets.size(); i++)\n        targets[i] = D3D11ContextSoTarget();\n    }\n  };\n  \n  /**\n   * \\brief Predication state\n   *\n   * Stores predication info.\n   */\n  struct D3D11ContextStatePR {\n    Com<D3D11Query, false> predicateObject = nullptr;\n    BOOL                   predicateValue  = false;\n\n    void reset() {\n      predicateObject = nullptr;\n      predicateValue = false;\n    }\n  };\n\n\n  /**\n   * \\brief Lazy binding state\n   *\n   * Keeps track of what state needs to be\n   * re-applied to the context.\n   */\n  struct D3D11LazyBindings {\n    D3D11ShaderTypeFlags shadersUsed = 0u;\n    D3D11ShaderTypeFlags shadersDirty = 0u;\n    D3D11ShaderTypeFlags graphicsUavShaders = 0u;\n\n    D3D11ShaderStageState<D3D11BindingMask> bindingsUsed;\n    D3D11ShaderStageState<D3D11BindingMask> bindingsDirty;\n\n    void reset() {\n      shadersUsed = 0u;\n      shadersDirty = 0u;\n      graphicsUavShaders = 0u;\n\n      bindingsUsed.reset();\n      bindingsDirty.reset();\n    }\n  };\n\n\n  struct D3D11ClassInstanceState {\n    static constexpr uint32_t MaxInstances = 256u;\n\n    uint32_t instanceCount = 0u;\n    std::array<Com<D3D11ClassInstance, false>, MaxInstances> instances;\n\n    void reset() {\n      for (uint32_t i = 0u; i < instanceCount; i++)\n        instances[i] = nullptr;\n\n      instanceCount = 0u;\n    }\n  };\n\n  using D3D11ClassInstances = D3D11ShaderStageState<D3D11ClassInstanceState>;\n\n  \n  /**\n   * \\brief Context state\n   */\n  struct D3D11ContextState {\n    Com<D3D11VertexShader, false>    vs;\n    Com<D3D11HullShader, false>      hs;\n    Com<D3D11DomainShader, false>    ds;\n    Com<D3D11GeometryShader, false>  gs;\n    Com<D3D11PixelShader, false>     ps;\n    Com<D3D11ComputeShader, false>   cs;\n\n    D3D11ContextStateID id;\n    D3D11ContextStateIA ia;\n    D3D11ContextStateOM om;\n    D3D11ContextStateRS rs;\n    D3D11ContextStateSO so;\n    D3D11ContextStatePR pr;\n\n    D3D11CbvBindings    cbv;\n    D3D11SrvBindings    srv;\n    D3D11UavBindings    uav;\n    D3D11SamplerBindings samplers;\n\n    D3D11LazyBindings   lazy;\n    D3D11ClassInstances instances;\n  };\n\n  /**\n   * \\brief Maximum used binding numbers in a shader stage\n   */\n  struct D3D11MaxUsedStageBindings {\n    uint32_t cbvCount     : 5;\n    uint32_t srvCount     : 9;\n    uint32_t uavCount     : 7;\n    uint32_t samplerCount : 5;\n    uint32_t reserved     : 6;\n  };\n\n  /**\n   * \\brief Maximum used binding numbers for all context state\n   */\n  struct D3D11MaxUsedBindings {\n    std::array<D3D11MaxUsedStageBindings, D3D11ShaderTypeCount> stages;\n    uint32_t  vbCount;\n    uint32_t  soCount;\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_cuda.cpp",
    "content": "#include \"d3d11_cuda.h\"\n\nnamespace dxvk {\n\n  CubinShaderWrapper::CubinShaderWrapper(const Rc<dxvk::DxvkDevice>& dxvkDevice, VkCuModuleNVX cuModule, VkCuFunctionNVX cuFunction, VkExtent3D blockDim)\n  : m_dxvkDevice(dxvkDevice), m_module(cuModule), m_function(cuFunction), m_blockDim(blockDim) { };\n\n\n  CubinShaderWrapper::~CubinShaderWrapper() {\n    VkDevice vkDevice = m_dxvkDevice->handle();\n    m_dxvkDevice->vkd()->vkDestroyCuFunctionNVX(vkDevice, m_function, nullptr);\n    m_dxvkDevice->vkd()->vkDestroyCuModuleNVX(vkDevice, m_module, nullptr);\n  };\n\n\n  HRESULT STDMETHODCALLTYPE CubinShaderWrapper::QueryInterface(REFIID riid, void **ppvObject) {\n    if (riid == __uuidof(IUnknown)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    Logger::warn(\"CubinShaderWrapper::QueryInterface: Unknown interface query\");\n    Logger::warn(str::format(riid));\n    return E_NOINTERFACE;\n  }\n\n\n  void CubinShaderLaunchInfo::insertResource(ID3D11Resource* pResource, DxvkAccessFlags access) {\n    auto img = GetCommonTexture(pResource);\n    auto buf = GetCommonBuffer(pResource);\n\n    if (img)\n      insertUniqueResource(images, img->GetImage(), access);\n    if (buf)\n      insertUniqueResource(buffers, buf->GetBuffer(), access);\n  }\n\n\n  template<typename T>\n  void CubinShaderLaunchInfo::insertUniqueResource(std::vector<std::pair<T, DxvkAccessFlags>>& list, const T& resource, DxvkAccessFlags access) {\n    for (auto& entry : list) {\n      if (entry.first == resource) {\n        entry.second.set(access);\n        return;\n      }\n    }\n\n    list.push_back({ resource, access });\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_cuda.h",
    "content": "#pragma once\n\n#include <utility>\n#include <vector>\n\n#include \"../dxvk/dxvk_memory.h\"\n#include \"../dxvk/dxvk_sparse.h\"\n\n#include \"../util/com/com_guid.h\"\n#include \"../util/com/com_object.h\"\n\n#include \"d3d11_buffer.h\"\n#include \"d3d11_texture.h\"\n\nnamespace dxvk {\n\n  class CubinShaderWrapper : public ComObject<IUnknown> {\n\n  public:\n\n    CubinShaderWrapper(const Rc<dxvk::DxvkDevice>& dxvkDevice, VkCuModuleNVX cuModule, VkCuFunctionNVX cuFunction, VkExtent3D blockDim);\n    ~CubinShaderWrapper();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);\n\n    VkCuModuleNVX cuModule() const {\n      return m_module;\n    }\n\n    VkCuFunctionNVX cuFunction() const {\n      return m_function;\n    }\n\n    VkExtent3D blockDim() const {\n      return m_blockDim;\n    }\n\n  private:\n\n    Rc<DxvkDevice>  m_dxvkDevice;\n    VkCuModuleNVX   m_module;\n    VkCuFunctionNVX m_function;\n    VkExtent3D      m_blockDim;\n\n  };\n\n\n  struct CubinShaderLaunchInfo {\n\n    CubinShaderLaunchInfo() = default;\n\n    CubinShaderLaunchInfo(CubinShaderLaunchInfo&& other) {\n      shader         = std::move(other.shader);\n      params         = std::move(other.params);\n      paramSize      = std::move(other.paramSize);\n      nvxLaunchInfo  = std::move(other.nvxLaunchInfo);\n      cuLaunchConfig = other.cuLaunchConfig;\n      buffers        = std::move(other.buffers);\n      images         = std::move(other.images);\n      other.cuLaunchConfig[1] = nullptr;\n      other.cuLaunchConfig[3] = nullptr;\n      other.nvxLaunchInfo.pExtras = nullptr;\n      // fix-up internally-pointing pointers\n      cuLaunchConfig[1] = params.data();\n      cuLaunchConfig[3] = &paramSize;\n      nvxLaunchInfo.pExtras = cuLaunchConfig.data();\n    }\n\n    Com<CubinShaderWrapper> shader;\n    std::vector<uint8_t>    params;\n    size_t                  paramSize;\n    VkCuLaunchInfoNVX       nvxLaunchInfo = { VK_STRUCTURE_TYPE_CU_LAUNCH_INFO_NVX };\n    std::array<void*, 5>    cuLaunchConfig;\n\n    std::vector<std::pair<Rc<DxvkBuffer>, DxvkAccessFlags>> buffers;\n    std::vector<std::pair<Rc<DxvkImage>, DxvkAccessFlags>> images;\n\n    void insertResource(ID3D11Resource* pResource, DxvkAccessFlags access);\n\n    template<typename T>\n    static void insertUniqueResource(std::vector<std::pair<T, DxvkAccessFlags>>& list, const T& resource, DxvkAccessFlags access);\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_depth_stencil.cpp",
    "content": "#include \"d3d11_depth_stencil.h\"\n#include \"d3d11_device.h\"\n\nnamespace dxvk {\n  \n  D3D11DepthStencilState::D3D11DepthStencilState(\n          D3D11Device*              device,\n    const D3D11_DEPTH_STENCIL_DESC& desc,\n          Container*                container)\n  : D3D11StateObject<ID3D11DepthStencilState, D3D11DepthStencilState>(device, container),\n    m_desc(desc), m_d3d10(this), m_destructionNotifier(this) {\n    m_state.setDepthTest(desc.DepthEnable);\n    m_state.setDepthWrite(desc.DepthWriteMask == D3D11_DEPTH_WRITE_MASK_ALL);\n    m_state.setStencilTest(desc.StencilEnable);\n    m_state.setDepthCompareOp(DecodeCompareOp(desc.DepthFunc));\n    m_state.setStencilOpFront(DecodeStencilOpState(desc.FrontFace, desc));\n    m_state.setStencilOpBack(DecodeStencilOpState(desc.BackFace, desc));\n\n    m_state.normalize();\n  }\n  \n  \n  D3D11DepthStencilState::~D3D11DepthStencilState() {\n    \n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DepthStencilState::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11DepthStencilState)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10DepthStencilState)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11DepthStencilState), riid)) {\n      Logger::warn(\"D3D11DepthStencilState::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11DepthStencilState::GetDesc(D3D11_DEPTH_STENCIL_DESC* pDesc) {\n    *pDesc = m_desc;\n  }\n  \n  \n  HRESULT D3D11DepthStencilState::NormalizeDesc(D3D11_DEPTH_STENCIL_DESC* pDesc) {\n    if (pDesc->DepthEnable) {\n      pDesc->DepthEnable = TRUE;\n      \n      if (!ValidateDepthFunc(pDesc->DepthFunc))\n        return E_INVALIDARG;\n    } else {\n      pDesc->DepthFunc      = D3D11_COMPARISON_LESS;\n      pDesc->DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;\n    }\n    \n    if (!ValidateDepthWriteMask(pDesc->DepthWriteMask))\n      return E_INVALIDARG;\n    \n    if (pDesc->StencilEnable) {\n      pDesc->StencilEnable = TRUE;\n      \n      if (!ValidateStencilFunc(pDesc->FrontFace.StencilFunc)\n       || !ValidateStencilOp(pDesc->FrontFace.StencilFailOp)\n       || !ValidateStencilOp(pDesc->FrontFace.StencilDepthFailOp)\n       || !ValidateStencilOp(pDesc->FrontFace.StencilPassOp))\n        return E_INVALIDARG;\n      \n      if (!ValidateStencilFunc(pDesc->BackFace.StencilFunc)\n       || !ValidateStencilOp(pDesc->BackFace.StencilFailOp)\n       || !ValidateStencilOp(pDesc->BackFace.StencilDepthFailOp)\n       || !ValidateStencilOp(pDesc->BackFace.StencilPassOp))\n        return E_INVALIDARG;\n    } else {\n      D3D11_DEPTH_STENCILOP_DESC stencilOp;\n      stencilOp.StencilFailOp = D3D11_STENCIL_OP_KEEP;\n      stencilOp.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;\n      stencilOp.StencilPassOp = D3D11_STENCIL_OP_KEEP;\n      stencilOp.StencilFunc   = D3D11_COMPARISON_ALWAYS;\n\n      pDesc->FrontFace        = stencilOp;\n      pDesc->BackFace         = stencilOp;\n      pDesc->StencilReadMask  = D3D11_DEFAULT_STENCIL_READ_MASK;\n      pDesc->StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;\n    }\n    \n    return S_OK;\n  }\n  \n  \n  DxvkStencilOp D3D11DepthStencilState::DecodeStencilOpState(\n    const D3D11_DEPTH_STENCILOP_DESC& StencilDesc,\n    const D3D11_DEPTH_STENCIL_DESC&   Desc) const {\n    DxvkStencilOp result = { };\n\n    if (Desc.StencilEnable) {\n      result.setFailOp(DecodeStencilOp(StencilDesc.StencilFailOp));\n      result.setPassOp(DecodeStencilOp(StencilDesc.StencilPassOp));\n      result.setDepthFailOp(DecodeStencilOp(StencilDesc.StencilDepthFailOp));\n      result.setCompareOp(DecodeCompareOp(StencilDesc.StencilFunc));\n      result.setCompareMask(Desc.StencilReadMask);\n      result.setWriteMask(Desc.StencilWriteMask);\n    }\n\n    return result;\n  }\n  \n  \n  VkStencilOp D3D11DepthStencilState::DecodeStencilOp(D3D11_STENCIL_OP Op) const {\n    switch (Op) {\n      case D3D11_STENCIL_OP_KEEP:       return VK_STENCIL_OP_KEEP;\n      case D3D11_STENCIL_OP_ZERO:       return VK_STENCIL_OP_ZERO;\n      case D3D11_STENCIL_OP_REPLACE:    return VK_STENCIL_OP_REPLACE;\n      case D3D11_STENCIL_OP_INCR_SAT:   return VK_STENCIL_OP_INCREMENT_AND_CLAMP;\n      case D3D11_STENCIL_OP_DECR_SAT:   return VK_STENCIL_OP_DECREMENT_AND_CLAMP;\n      case D3D11_STENCIL_OP_INVERT:     return VK_STENCIL_OP_INVERT;\n      case D3D11_STENCIL_OP_INCR:       return VK_STENCIL_OP_INCREMENT_AND_WRAP;\n      case D3D11_STENCIL_OP_DECR:       return VK_STENCIL_OP_DECREMENT_AND_WRAP;\n      default:                          return VK_STENCIL_OP_KEEP;\n    }\n  }\n  \n  \n  bool D3D11DepthStencilState::ValidateDepthFunc(D3D11_COMPARISON_FUNC Comparison) {\n    return Comparison >= D3D11_COMPARISON_NEVER\n        && Comparison <= D3D11_COMPARISON_ALWAYS;\n  }\n  \n  \n  bool D3D11DepthStencilState::ValidateStencilFunc(D3D11_COMPARISON_FUNC Comparison) {\n    return Comparison >= D3D11_COMPARISON_NEVER\n        && Comparison <= D3D11_COMPARISON_ALWAYS;\n  }\n  \n  \n  bool D3D11DepthStencilState::ValidateStencilOp(D3D11_STENCIL_OP StencilOp) {\n    return StencilOp >= D3D11_STENCIL_OP_KEEP\n        && StencilOp <= D3D11_STENCIL_OP_DECR;\n  }\n  \n  \n  bool D3D11DepthStencilState::ValidateDepthWriteMask(D3D11_DEPTH_WRITE_MASK Mask) {\n    return Mask == D3D11_DEPTH_WRITE_MASK_ZERO \n        || Mask == D3D11_DEPTH_WRITE_MASK_ALL;\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_depth_stencil.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../d3d10/d3d10_depth_stencil.h\"\n\n#include \"d3d11_device_child.h\"\n#include \"d3d11_util.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  \n  class D3D11DepthStencilState : public D3D11StateObject<ID3D11DepthStencilState, D3D11DepthStencilState> {\n    using Container = D3D11StateObjectSet<D3D11DepthStencilState>;\n  public:\n    \n    using DescType = D3D11_DEPTH_STENCIL_DESC;\n    \n    D3D11DepthStencilState(\n            D3D11Device*              device,\n      const D3D11_DEPTH_STENCIL_DESC& desc,\n            Container*                container);\n    ~D3D11DepthStencilState();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_DEPTH_STENCIL_DESC* pDesc) final;\n\n    const D3D11_DEPTH_STENCIL_DESC& Desc() const {\n      return m_desc;\n    }\n\n    DxvkDepthStencilState GetState() const {\n      return m_state;\n    }\n\n    D3D10DepthStencilState* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n    \n    static HRESULT NormalizeDesc(\n            D3D11_DEPTH_STENCIL_DESC* pDesc);\n    \n  private:\n    \n    D3D11_DEPTH_STENCIL_DESC  m_desc;\n    DxvkDepthStencilState     m_state = { };\n    D3D10DepthStencilState    m_d3d10;\n\n    D3DDestructionNotifier    m_destructionNotifier;\n    \n    DxvkStencilOp DecodeStencilOpState(\n      const D3D11_DEPTH_STENCILOP_DESC& StencilDesc,\n      const D3D11_DEPTH_STENCIL_DESC&   Desc) const;\n    \n    VkStencilOp DecodeStencilOp(\n            D3D11_STENCIL_OP            Op) const;\n    \n    static bool ValidateDepthFunc(\n            D3D11_COMPARISON_FUNC  Comparison);\n\n    static bool ValidateStencilFunc(\n            D3D11_COMPARISON_FUNC  Comparison);\n\n    static bool ValidateStencilOp(\n            D3D11_STENCIL_OP       StencilOp);\n\n    static bool ValidateDepthWriteMask(\n            D3D11_DEPTH_WRITE_MASK Mask);\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_device.cpp",
    "content": "#include <algorithm>\n#include <cstring>\n\n#include <dxbc/dxbc_container.h>\n#include <dxbc/dxbc_parser.h>\n#include <dxbc/dxbc_signature.h>\n\n#include \"../dxgi/dxgi_monitor.h\"\n#include \"../dxgi/dxgi_surface.h\"\n#include \"../dxgi/dxgi_swapchain.h\"\n\n#include \"../dxvk/dxvk_adapter.h\"\n#include \"../dxvk/dxvk_instance.h\"\n\n#include \"d3d11_buffer.h\"\n#include \"d3d11_class_linkage.h\"\n#include \"d3d11_context_def.h\"\n#include \"d3d11_context_imm.h\"\n#include \"d3d11_device.h\"\n#include \"d3d11_fence.h\"\n#include \"d3d11_input_layout.h\"\n#include \"d3d11_interop.h\"\n#include \"d3d11_query.h\"\n#include \"d3d11_resource.h\"\n#include \"d3d11_sampler.h\"\n#include \"d3d11_shader.h\"\n#include \"d3d11_state_object.h\"\n#include \"d3d11_swapchain.h\"\n#include \"d3d11_texture.h\"\n#include \"d3d11_video.h\"\n\n#include \"../wsi/wsi_window.h\"\n\n#include \"../util/util_shared_res.h\"\n\nnamespace dxvk {\n  \n  constexpr uint32_t D3D11DXGIDevice::DefaultFrameLatency;\n\n\n\n  D3D11Device::D3D11Device(\n          D3D11DXGIDevice*    pContainer,\n          D3D_FEATURE_LEVEL   FeatureLevel,\n          UINT                FeatureFlags)\n  : m_container         (pContainer),\n    m_featureLevel      (FeatureLevel),\n    m_featureFlags      (FeatureFlags),\n    m_dxvkDevice        (pContainer->GetDXVKDevice()),\n    m_dxvkAdapter       (m_dxvkDevice->adapter()),\n    m_d3d11Formats      (m_dxvkDevice),\n    m_d3d11Options      (m_dxvkDevice->instance()->config()),\n    m_shaderOptions     (GetShaderOptions(m_dxvkDevice, m_d3d11Options)),\n    m_maxFeatureLevel   (GetMaxFeatureLevel(m_dxvkDevice->instance(), m_dxvkDevice->adapter())),\n    m_deviceFeatures    (m_dxvkDevice->instance(), m_dxvkDevice->adapter(), m_d3d11Options, m_featureLevel) {\n    m_initializer = new D3D11Initializer(this);\n    m_context     = new D3D11ImmediateContext(this, m_dxvkDevice);\n    m_d3d10Device = new D3D10Device(this, m_context.ptr());\n  }\n  \n  \n  D3D11Device::~D3D11Device() {\n    delete m_d3d10Device;\n    m_context = nullptr;\n    delete m_initializer;\n  }\n  \n  \n  ULONG STDMETHODCALLTYPE D3D11Device::AddRef() {\n    return m_container->AddRef();\n  }\n  \n  \n  ULONG STDMETHODCALLTYPE D3D11Device::Release() {\n    return m_container->Release();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::QueryInterface(REFIID riid, void** ppvObject) {\n    return m_container->QueryInterface(riid, ppvObject);\n  }\n    \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateBuffer(\n    const D3D11_BUFFER_DESC*      pDesc,\n    const D3D11_SUBRESOURCE_DATA* pInitialData,\n          ID3D11Buffer**          ppBuffer) {\n    InitReturnPtr(ppBuffer);\n    \n    if (!pDesc)\n      return E_INVALIDARG;\n    \n    D3D11_BUFFER_DESC desc = *pDesc;\n    HRESULT hr = D3D11Buffer::NormalizeBufferProperties(&desc);\n\n    if (FAILED(hr))\n      return hr;\n\n    if ((desc.MiscFlags & (D3D11_RESOURCE_MISC_TILED | D3D11_RESOURCE_MISC_TILE_POOL))\n     && !m_deviceFeatures.GetTiledResourcesTier())\n      return E_INVALIDARG;\n\n    if (!ppBuffer)\n      return S_FALSE;\n    \n    try {\n      const Com<D3D11Buffer> buffer = new D3D11Buffer(this, &desc, nullptr);\n\n      if (!(desc.MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL))\n        m_initializer->InitBuffer(buffer.ptr(), pInitialData);\n\n      *ppBuffer = buffer.ref();\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture1D(\n    const D3D11_TEXTURE1D_DESC*   pDesc,\n    const D3D11_SUBRESOURCE_DATA* pInitialData,\n          ID3D11Texture1D**       ppTexture1D) {\n    InitReturnPtr(ppTexture1D);\n\n    if (!pDesc)\n      return E_INVALIDARG;\n    \n    D3D11_COMMON_TEXTURE_DESC desc;\n    desc.Width          = pDesc->Width;\n    desc.Height         = 1;\n    desc.Depth          = 1;\n    desc.MipLevels      = pDesc->MipLevels;\n    desc.ArraySize      = pDesc->ArraySize;\n    desc.Format         = pDesc->Format;\n    desc.SampleDesc     = DXGI_SAMPLE_DESC { 1, 0 };\n    desc.Usage          = pDesc->Usage;\n    desc.BindFlags      = pDesc->BindFlags;\n    desc.CPUAccessFlags = pDesc->CPUAccessFlags;\n    desc.MiscFlags      = pDesc->MiscFlags;\n    desc.TextureLayout  = D3D11_TEXTURE_LAYOUT_UNDEFINED;\n    \n    HRESULT hr = D3D11CommonTexture::NormalizeTextureProperties(&desc);\n\n    if (FAILED(hr))\n      return hr;\n\n    if (desc.MiscFlags & D3D11_RESOURCE_MISC_TILED)\n      return E_INVALIDARG;\n\n    if (!ppTexture1D)\n      return S_FALSE;\n    \n    try {\n      const Com<D3D11Texture1D> texture = new D3D11Texture1D(this, &desc, nullptr);\n      m_initializer->InitTexture(texture->GetCommonTexture(), pInitialData);\n      *ppTexture1D = texture.ref();\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture2D(\n    const D3D11_TEXTURE2D_DESC*   pDesc,\n    const D3D11_SUBRESOURCE_DATA* pInitialData,\n          ID3D11Texture2D**       ppTexture2D) {\n    InitReturnPtr(ppTexture2D);\n\n    if (!pDesc)\n      return E_INVALIDARG;\n\n    D3D11_TEXTURE2D_DESC1 desc;\n    desc.Width          = pDesc->Width;\n    desc.Height         = pDesc->Height;\n    desc.MipLevels      = pDesc->MipLevels;\n    desc.ArraySize      = pDesc->ArraySize;\n    desc.Format         = pDesc->Format;\n    desc.SampleDesc     = pDesc->SampleDesc;\n    desc.Usage          = pDesc->Usage;\n    desc.BindFlags      = pDesc->BindFlags;\n    desc.CPUAccessFlags = pDesc->CPUAccessFlags;\n    desc.MiscFlags      = pDesc->MiscFlags;\n    desc.TextureLayout  = D3D11_TEXTURE_LAYOUT_UNDEFINED;\n    \n    ID3D11Texture2D1* texture2D = nullptr;\n    HRESULT hr = CreateTexture2DBase(&desc, pInitialData, ppTexture2D ? &texture2D : nullptr);\n\n    if (hr != S_OK)\n      return hr;\n    \n    *ppTexture2D = texture2D;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture2D1(\n    const D3D11_TEXTURE2D_DESC1*  pDesc,\n    const D3D11_SUBRESOURCE_DATA* pInitialData,\n          ID3D11Texture2D1**      ppTexture2D) {\n    InitReturnPtr(ppTexture2D);\n\n    if (!pDesc)\n      return E_INVALIDARG;\n    \n    return CreateTexture2DBase(pDesc, pInitialData, ppTexture2D);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture2DBase(\n    const D3D11_TEXTURE2D_DESC1*  pDesc,\n    const D3D11_SUBRESOURCE_DATA* pInitialData,\n          ID3D11Texture2D1**      ppTexture2D) {\n    D3D11_COMMON_TEXTURE_DESC desc;\n    desc.Width          = pDesc->Width;\n    desc.Height         = pDesc->Height;\n    desc.Depth          = 1;\n    desc.MipLevels      = pDesc->MipLevels;\n    desc.ArraySize      = pDesc->ArraySize;\n    desc.Format         = pDesc->Format;\n    desc.SampleDesc     = pDesc->SampleDesc;\n    desc.Usage          = pDesc->Usage;\n    desc.BindFlags      = pDesc->BindFlags;\n    desc.CPUAccessFlags = pDesc->CPUAccessFlags;\n    desc.MiscFlags      = pDesc->MiscFlags;\n    desc.TextureLayout  = pDesc->TextureLayout;\n    \n    HRESULT hr = D3D11CommonTexture::NormalizeTextureProperties(&desc);\n\n    if ((desc.MiscFlags & D3D11_RESOURCE_MISC_TILED)\n     && !m_deviceFeatures.GetTiledResourcesTier())\n      return E_INVALIDARG;\n\n    if (FAILED(hr))\n      return hr;\n    \n    if (!ppTexture2D)\n      return S_FALSE;\n    \n    try {\n      Com<D3D11Texture2D> texture = new D3D11Texture2D(this, &desc, nullptr, nullptr);\n      m_initializer->InitTexture(texture->GetCommonTexture(), pInitialData);\n      *ppTexture2D = texture.ref();\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture3D(\n    const D3D11_TEXTURE3D_DESC*   pDesc,\n    const D3D11_SUBRESOURCE_DATA* pInitialData,\n          ID3D11Texture3D**       ppTexture3D) {\n    InitReturnPtr(ppTexture3D);\n\n    if (!pDesc)\n      return E_INVALIDARG;\n    \n    D3D11_TEXTURE3D_DESC1 desc;\n    desc.Width          = pDesc->Width;\n    desc.Height         = pDesc->Height;\n    desc.Depth          = pDesc->Depth;\n    desc.MipLevels      = pDesc->MipLevels;\n    desc.Format         = pDesc->Format;\n    desc.Usage          = pDesc->Usage;\n    desc.BindFlags      = pDesc->BindFlags;\n    desc.CPUAccessFlags = pDesc->CPUAccessFlags;\n    desc.MiscFlags      = pDesc->MiscFlags;\n    desc.TextureLayout  = D3D11_TEXTURE_LAYOUT_UNDEFINED;\n    \n    ID3D11Texture3D1* texture3D = nullptr;\n    HRESULT hr = CreateTexture3DBase(&desc, pInitialData, ppTexture3D ? &texture3D : nullptr);\n\n    if (hr != S_OK)\n      return hr;\n    \n    *ppTexture3D = texture3D;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture3D1(\n    const D3D11_TEXTURE3D_DESC1*  pDesc,\n    const D3D11_SUBRESOURCE_DATA* pInitialData,\n          ID3D11Texture3D1**      ppTexture3D) {\n    InitReturnPtr(ppTexture3D);\n\n    if (!pDesc)\n      return E_INVALIDARG;\n\n    return CreateTexture3DBase(pDesc, pInitialData, ppTexture3D);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateTexture3DBase(\n    const D3D11_TEXTURE3D_DESC1*  pDesc,\n    const D3D11_SUBRESOURCE_DATA* pInitialData,\n          ID3D11Texture3D1**      ppTexture3D) {\n    D3D11_COMMON_TEXTURE_DESC desc;\n    desc.Width          = pDesc->Width;\n    desc.Height         = pDesc->Height;\n    desc.Depth          = pDesc->Depth;\n    desc.MipLevels      = pDesc->MipLevels;\n    desc.ArraySize      = 1;\n    desc.Format         = pDesc->Format;\n    desc.SampleDesc     = DXGI_SAMPLE_DESC { 1, 0 };\n    desc.Usage          = pDesc->Usage;\n    desc.BindFlags      = pDesc->BindFlags;\n    desc.CPUAccessFlags = pDesc->CPUAccessFlags;\n    desc.MiscFlags      = pDesc->MiscFlags;\n    desc.TextureLayout  = pDesc->TextureLayout;\n    \n    HRESULT hr = D3D11CommonTexture::NormalizeTextureProperties(&desc);\n\n    if (FAILED(hr))\n      return hr;\n\n    if ((desc.MiscFlags & D3D11_RESOURCE_MISC_TILED)\n     && (m_deviceFeatures.GetTiledResourcesTier() < D3D11_TILED_RESOURCES_TIER_3))\n      return E_INVALIDARG;\n\n    if (!ppTexture3D)\n      return S_FALSE;\n      \n    try {\n      Com<D3D11Texture3D> texture = new D3D11Texture3D(this, &desc, nullptr);\n      m_initializer->InitTexture(texture->GetCommonTexture(), pInitialData);\n      *ppTexture3D = texture.ref();\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateShaderResourceView(\n          ID3D11Resource*                   pResource,\n    const D3D11_SHADER_RESOURCE_VIEW_DESC*  pDesc,\n          ID3D11ShaderResourceView**        ppSRView) {\n    InitReturnPtr(ppSRView);\n\n    if (!pResource)\n      return E_INVALIDARG;\n\n    uint32_t plane = GetViewPlaneIndex(pResource, pDesc ? pDesc->Format : DXGI_FORMAT_UNKNOWN);\n\n    D3D11_SHADER_RESOURCE_VIEW_DESC1 desc = pDesc\n      ? D3D11ShaderResourceView::PromoteDesc(pDesc, plane)\n      : D3D11_SHADER_RESOURCE_VIEW_DESC1();\n    \n    ID3D11ShaderResourceView1* view = nullptr;\n\n    HRESULT hr = CreateShaderResourceViewBase(pResource,\n      pDesc    ? &desc : nullptr,\n      ppSRView ? &view : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppSRView = view;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateShaderResourceView1(\n          ID3D11Resource*                   pResource,\n    const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc,\n          ID3D11ShaderResourceView1**       ppSRView) {\n    InitReturnPtr(ppSRView);\n\n    if (!pResource)\n      return E_INVALIDARG;\n\n    return CreateShaderResourceViewBase(pResource, pDesc, ppSRView);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateShaderResourceViewBase(\n          ID3D11Resource*                   pResource,\n    const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc,\n          ID3D11ShaderResourceView1**       ppSRView) {\n    D3D11_COMMON_RESOURCE_DESC resourceDesc;\n    GetCommonResourceDesc(pResource, &resourceDesc);\n    \n    // The description is optional. If omitted, we'll create\n    // a view that covers all subresources of the image.\n    D3D11_SHADER_RESOURCE_VIEW_DESC1 desc;\n    \n    if (!pDesc) {\n      if (FAILED(D3D11ShaderResourceView::GetDescFromResource(pResource, &desc)))\n        return E_INVALIDARG;\n    } else {\n      desc = *pDesc;\n      \n      if (FAILED(D3D11ShaderResourceView::NormalizeDesc(pResource, &desc)))\n        return E_INVALIDARG;\n    }\n\n    uint32_t plane = D3D11ShaderResourceView::GetPlaneSlice(&desc);\n    \n    if (!CheckResourceViewCompatibility(pResource, D3D11_BIND_SHADER_RESOURCE, desc.Format, plane)) {\n      Logger::err(str::format(\"D3D11: Cannot create shader resource view:\",\n        \"\\n  Resource type:   \", resourceDesc.Dim,\n        \"\\n  Resource usage:  \", resourceDesc.BindFlags,\n        \"\\n  Resource format: \", resourceDesc.Format,\n        \"\\n  View format:     \", desc.Format,\n        \"\\n  View plane:      \", plane));\n      return E_INVALIDARG;\n    }\n    \n    if (!ppSRView)\n      return S_FALSE;\n    \n    try {\n      *ppSRView = ref(new D3D11ShaderResourceView(this, pResource, &desc));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateUnorderedAccessView(\n          ID3D11Resource*                   pResource,\n    const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc,\n          ID3D11UnorderedAccessView**       ppUAView) {\n    InitReturnPtr(ppUAView);\n\n    if (!pResource)\n      return E_INVALIDARG;\n\n    uint32_t plane = GetViewPlaneIndex(pResource, pDesc ? pDesc->Format : DXGI_FORMAT_UNKNOWN);\n\n    D3D11_UNORDERED_ACCESS_VIEW_DESC1 desc = pDesc\n      ? D3D11UnorderedAccessView::PromoteDesc(pDesc, plane)\n      : D3D11_UNORDERED_ACCESS_VIEW_DESC1();\n\n    ID3D11UnorderedAccessView1* view = nullptr;\n\n    HRESULT hr = CreateUnorderedAccessViewBase(pResource,\n      pDesc    ? &desc : nullptr,\n      ppUAView ? &view : nullptr);\n\n    if (hr != S_OK)\n      return hr;\n\n    *ppUAView = view;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateUnorderedAccessView1(\n          ID3D11Resource*                   pResource,\n    const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc,\n          ID3D11UnorderedAccessView1**      ppUAView) {\n    InitReturnPtr(ppUAView);\n    \n    if (!pResource)\n      return E_INVALIDARG;\n\n    return CreateUnorderedAccessViewBase(pResource, pDesc, ppUAView);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateUnorderedAccessViewBase(\n          ID3D11Resource*                   pResource,\n    const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc,\n          ID3D11UnorderedAccessView1**      ppUAView) {\n    D3D11_COMMON_RESOURCE_DESC resourceDesc;\n    GetCommonResourceDesc(pResource, &resourceDesc);\n\n    // The description is optional. If omitted, we'll create\n    // a view that covers all subresources of the image.\n    D3D11_UNORDERED_ACCESS_VIEW_DESC1 desc;\n    \n    if (!pDesc) {\n      if (FAILED(D3D11UnorderedAccessView::GetDescFromResource(pResource, &desc)))\n        return E_INVALIDARG;\n    } else {\n      desc = *pDesc;\n      \n      if (FAILED(D3D11UnorderedAccessView::NormalizeDesc(pResource, &desc)))\n        return E_INVALIDARG;\n    }\n    \n    uint32_t plane = D3D11UnorderedAccessView::GetPlaneSlice(&desc);\n\n    if (!CheckResourceViewCompatibility(pResource, D3D11_BIND_UNORDERED_ACCESS, desc.Format, plane)) {\n      Logger::err(str::format(\"D3D11: Cannot create unordered access view:\",\n        \"\\n  Resource type:   \", resourceDesc.Dim,\n        \"\\n  Resource usage:  \", resourceDesc.BindFlags,\n        \"\\n  Resource format: \", resourceDesc.Format,\n        \"\\n  View format:     \", desc.Format,\n        \"\\n  View plane:      \", plane));\n      return E_INVALIDARG;\n    }\n\n    if (!ppUAView)\n      return S_FALSE;\n    \n    try {\n      auto uav = new D3D11UnorderedAccessView(this, pResource, &desc);\n      m_initializer->InitUavCounter(uav);\n      *ppUAView = ref(uav);\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateRenderTargetView(\n          ID3D11Resource*                   pResource,\n    const D3D11_RENDER_TARGET_VIEW_DESC*    pDesc,\n          ID3D11RenderTargetView**          ppRTView) {\n    InitReturnPtr(ppRTView);\n\n    if (!pResource)\n      return E_INVALIDARG;\n\n    uint32_t plane = GetViewPlaneIndex(pResource, pDesc ? pDesc->Format : DXGI_FORMAT_UNKNOWN);\n\n    D3D11_RENDER_TARGET_VIEW_DESC1 desc = pDesc\n      ? D3D11RenderTargetView::PromoteDesc(pDesc, plane)\n      : D3D11_RENDER_TARGET_VIEW_DESC1();\n    \n    ID3D11RenderTargetView1* view = nullptr;\n\n    HRESULT hr = CreateRenderTargetViewBase(pResource,\n      pDesc    ? &desc : nullptr,\n      ppRTView ? &view : nullptr);\n    \n    if (hr != S_OK)\n      return hr;\n    \n    *ppRTView = view;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateRenderTargetView1(\n          ID3D11Resource*                   pResource,\n    const D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc,\n          ID3D11RenderTargetView1**         ppRTView) {\n    InitReturnPtr(ppRTView);\n\n    if (!pResource)\n      return E_INVALIDARG;\n\n    return CreateRenderTargetViewBase(pResource, pDesc, ppRTView);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateRenderTargetViewBase(\n          ID3D11Resource*                   pResource,\n    const D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc,\n          ID3D11RenderTargetView1**         ppRTView) {\n    // DXVK only supports render target views for image resources\n    D3D11_COMMON_RESOURCE_DESC resourceDesc;\n    GetCommonResourceDesc(pResource, &resourceDesc);\n    \n    if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      Logger::warn(\"D3D11: Cannot create render target view for a buffer\");\n      return S_OK; // It is required to run Battlefield 3 and Battlefield 4.\n    }\n    \n    // The view description is optional. If not defined, it\n    // will use the resource's format and all array layers.\n    D3D11_RENDER_TARGET_VIEW_DESC1 desc;\n    \n    if (!pDesc) {\n      if (FAILED(D3D11RenderTargetView::GetDescFromResource(pResource, &desc)))\n        return E_INVALIDARG;\n    } else {\n      desc = *pDesc;\n      \n      if (FAILED(D3D11RenderTargetView::NormalizeDesc(pResource, &desc)))\n        return E_INVALIDARG;\n    }\n    \n    uint32_t plane = D3D11RenderTargetView::GetPlaneSlice(&desc);\n\n    if (!CheckResourceViewCompatibility(pResource, D3D11_BIND_RENDER_TARGET, desc.Format, plane)) {\n      Logger::err(str::format(\"D3D11: Cannot create render target view:\",\n        \"\\n  Resource type:   \", resourceDesc.Dim,\n        \"\\n  Resource usage:  \", resourceDesc.BindFlags,\n        \"\\n  Resource format: \", resourceDesc.Format,\n        \"\\n  View format:     \", desc.Format,\n        \"\\n  View plane:      \", plane));\n      return E_INVALIDARG;\n    }\n\n    if (!ppRTView)\n      return S_FALSE;\n    \n    try {\n      *ppRTView = ref(new D3D11RenderTargetView(this, pResource, &desc));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateDepthStencilView(\n          ID3D11Resource*                   pResource,\n    const D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc,\n          ID3D11DepthStencilView**          ppDepthStencilView) {\n    InitReturnPtr(ppDepthStencilView);\n    \n    if (pResource == nullptr)\n      return E_INVALIDARG;\n    \n    D3D11_COMMON_RESOURCE_DESC resourceDesc;\n    GetCommonResourceDesc(pResource, &resourceDesc);\n\n    // The view description is optional. If not defined, it\n    // will use the resource's format and all array layers.\n    D3D11_DEPTH_STENCIL_VIEW_DESC desc;\n    \n    if (pDesc == nullptr) {\n      if (FAILED(D3D11DepthStencilView::GetDescFromResource(pResource, &desc)))\n        return E_INVALIDARG;\n    } else {\n      desc = *pDesc;\n      \n      if (FAILED(D3D11DepthStencilView::NormalizeDesc(pResource, &desc)))\n        return E_INVALIDARG;\n    }\n    \n    if (!CheckResourceViewCompatibility(pResource, D3D11_BIND_DEPTH_STENCIL, desc.Format, 0)) {\n      Logger::err(str::format(\"D3D11: Cannot create depth-stencil view:\",\n        \"\\n  Resource type:   \", resourceDesc.Dim,\n        \"\\n  Resource usage:  \", resourceDesc.BindFlags,\n        \"\\n  Resource format: \", resourceDesc.Format,\n        \"\\n  View format:     \", desc.Format));\n      return E_INVALIDARG;\n    }\n    \n    if (ppDepthStencilView == nullptr)\n      return S_FALSE;\n    \n    try {\n      *ppDepthStencilView = ref(new D3D11DepthStencilView(this, pResource, &desc));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateInputLayout(\n    const D3D11_INPUT_ELEMENT_DESC*   pInputElementDescs,\n          UINT                        NumElements,\n    const void*                       pShaderBytecodeWithInputSignature,\n          SIZE_T                      BytecodeLength,\n          ID3D11InputLayout**         ppInputLayout) {\n    InitReturnPtr(ppInputLayout);\n\n    // This check is somehow even correct, passing null with zero\n    // size will always fail but passing non-null with zero size\n    // works, provided the shader does not have any actual inputs\n    if (!pInputElementDescs)\n      return E_INVALIDARG;\n\n    try {\n      dxbc_spv::dxbc::Container container(pShaderBytecodeWithInputSignature, BytecodeLength);\n      auto isgnChunk = container.getInputSignatureChunk();\n\n      if (!isgnChunk)\n        return E_INVALIDARG;\n\n      dxbc_spv::dxbc::Signature inputSignature(std::move(isgnChunk));\n\n      uint32_t attrMask = 0;\n      uint32_t bindMask = 0;\n      uint32_t locationMask = 0;\n      uint32_t bindingsDefined = 0;\n\n      std::array<DxvkVertexAttribute, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> attrList = { };\n      std::array<DxvkVertexBinding,   D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> bindList = { };\n\n      for (uint32_t i = 0; i < NumElements; i++) {\n        auto entry = inputSignature.findSemantic(0u,\n          pInputElementDescs[i].SemanticName,\n          pInputElementDescs[i].SemanticIndex);\n\n        // Create vertex input attribute description\n        DxvkVertexAttribute attrib = { };\n        attrib.location = entry != inputSignature.end() ? entry->getRegisterIndex() : 0;\n        attrib.binding  = pInputElementDescs[i].InputSlot;\n        attrib.format   = LookupFormat(pInputElementDescs[i].Format, DXGI_VK_FORMAT_MODE_COLOR).Format;\n        attrib.offset   = pInputElementDescs[i].AlignedByteOffset;\n\n        // The application may choose to let the implementation\n        // generate the exact vertex layout. In that case we'll\n        // pack attributes on the same binding in the order they\n        // are declared, aligning each attribute to four bytes.\n        const DxvkFormatInfo* formatInfo = lookupFormatInfo(attrib.format);\n        VkDeviceSize alignment = std::min<VkDeviceSize>(formatInfo->elementSize, 4);\n\n        if (attrib.offset == D3D11_APPEND_ALIGNED_ELEMENT) {\n          attrib.offset = 0;\n\n          for (uint32_t j = 1; j <= i; j++) {\n            const DxvkVertexAttribute& prev = attrList.at(i - j);\n\n            if (prev.binding == attrib.binding) {\n              attrib.offset = align(prev.offset + lookupFormatInfo(prev.format)->elementSize, alignment);\n              break;\n            }\n          }\n        } else if (attrib.offset & (alignment - 1)) {\n          return E_INVALIDARG;\n        }\n\n        attrList.at(i) = attrib;\n\n        // Create vertex input binding description. The\n        // stride is dynamic state in D3D11 and will be\n        // set by D3D11DeviceContext::IASetVertexBuffers.\n        DxvkVertexBinding binding = { };\n        binding.binding   = pInputElementDescs[i].InputSlot;\n        binding.divisor   = pInputElementDescs[i].InstanceDataStepRate;\n        binding.inputRate = pInputElementDescs[i].InputSlotClass == D3D11_INPUT_PER_INSTANCE_DATA\n          ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;\n        binding.extent    = entry != inputSignature.end() ? uint32_t(attrib.offset + formatInfo->elementSize) : 0u;\n\n        // Check if the binding was already defined. If so, the\n        // parameters must be identical (namely, the input rate).\n        if (bindingsDefined & (1u << binding.binding)) {\n          if (bindList.at(binding.binding).inputRate != binding.inputRate)\n            return E_INVALIDARG;\n\n          bindList.at(binding.binding).extent = std::max(\n            bindList.at(binding.binding).extent, binding.extent);\n        } else {\n          bindList.at(binding.binding) = binding;\n          bindingsDefined |= 1u << binding.binding;\n        }\n\n        if (entry != inputSignature.end()) {\n          attrMask |= 1u << i;\n          bindMask |= 1u << binding.binding;\n          locationMask |= 1u << attrib.location;\n        }\n      }\n\n      // Ensure that all inputs used by the shader are defined\n      for (auto i = inputSignature.begin(); i != inputSignature.end(); i++) {\n        bool isBuiltIn = i->getSystemValue() != dxbc_spv::dxbc::SignatureSysval::eNone;\n\n        if (!isBuiltIn && !(locationMask & (1u << i->getRegisterIndex())))\n          return E_INVALIDARG;\n      }\n\n      // Compact the attribute and binding lists to filter\n      // out attributes and bindings not used by the shader\n      uint32_t attrCount = CompactSparseList(attrList.data(), attrMask);\n      uint32_t bindCount = CompactSparseList(bindList.data(), bindMask);\n\n      if (!ppInputLayout)\n        return S_FALSE;\n\n      *ppInputLayout = ref(\n        new D3D11InputLayout(this,\n          attrCount, attrList.data(),\n          bindCount, bindList.data()));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateVertexShader(\n    const void*                       pShaderBytecode,\n          SIZE_T                      BytecodeLength,\n          ID3D11ClassLinkage*         pClassLinkage,\n          ID3D11VertexShader**        ppVertexShader) {\n    InitReturnPtr(ppVertexShader);\n    D3D11CommonShader module;\n\n    DxvkIrShaderCreateInfo moduleInfo = { };\n    moduleInfo.options = m_shaderOptions;\n\n    HRESULT hr = CreateShaderModule(&module, pClassLinkage,\n      ComputeShaderKey(VK_SHADER_STAGE_VERTEX_BIT, pShaderBytecode, BytecodeLength),\n      pShaderBytecode, BytecodeLength, moduleInfo);\n    \n    if (FAILED(hr))\n      return hr;\n    \n    if (!ppVertexShader)\n      return S_FALSE;\n    \n    *ppVertexShader = ref(new D3D11VertexShader(this, module));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateGeometryShader(\n    const void*                       pShaderBytecode,\n          SIZE_T                      BytecodeLength,\n          ID3D11ClassLinkage*         pClassLinkage,\n          ID3D11GeometryShader**      ppGeometryShader) {\n    InitReturnPtr(ppGeometryShader);\n    D3D11CommonShader module;\n    \n    DxvkIrShaderCreateInfo moduleInfo = { };\n    moduleInfo.options = m_shaderOptions;\n\n    HRESULT hr = CreateShaderModule(&module, pClassLinkage,\n      ComputeShaderKey(VK_SHADER_STAGE_GEOMETRY_BIT, pShaderBytecode, BytecodeLength),\n      pShaderBytecode, BytecodeLength, moduleInfo);\n\n    if (FAILED(hr))\n      return hr;\n    \n    if (!ppGeometryShader)\n      return S_FALSE;\n    \n    *ppGeometryShader = ref(new D3D11GeometryShader(this, module));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateGeometryShaderWithStreamOutput(\n    const void*                       pShaderBytecode,\n          SIZE_T                      BytecodeLength,\n    const D3D11_SO_DECLARATION_ENTRY* pSODeclaration,\n          UINT                        NumEntries,\n    const UINT*                       pBufferStrides,\n          UINT                        NumStrides,\n          UINT                        RasterizedStream,\n          ID3D11ClassLinkage*         pClassLinkage,\n          ID3D11GeometryShader**      ppGeometryShader) {\n    InitReturnPtr(ppGeometryShader);\n    D3D11CommonShader module;\n\n    if (!m_dxvkDevice->features().extTransformFeedback.transformFeedback)\n      return DXGI_ERROR_INVALID_CALL;\n\n    // Zero-init some counterss so that we can increment\n    // them while walking over the stream output entries\n    std::array<uint32_t, D3D11_SO_BUFFER_SLOT_COUNT> xfbOffsets = { };\n\n    DxvkIrShaderCreateInfo moduleInfo = { };\n    moduleInfo.options = m_shaderOptions;\n    moduleInfo.rasterizedStream = RasterizedStream;\n\n    for (uint32_t i = 0; i < NumEntries; i++) {\n      const D3D11_SO_DECLARATION_ENTRY* so = &pSODeclaration[i];\n\n      if (so->OutputSlot >= D3D11_SO_BUFFER_SLOT_COUNT)\n        return E_INVALIDARG;\n\n      if (so->SemanticName != nullptr) {\n        if (so->Stream >= D3D11_SO_BUFFER_SLOT_COUNT\n         || so->StartComponent >= 4\n         || so->ComponentCount <  1\n         || so->ComponentCount >  4)\n          return E_INVALIDARG;\n\n        auto& entry = moduleInfo.xfbEntries.emplace_back();\n        entry.semanticName = so->SemanticName;\n        entry.semanticIndex = so->SemanticIndex;\n        entry.componentMask = ((1u << so->ComponentCount) - 1u) << so->StartComponent;\n        entry.stream = so->Stream;\n        entry.buffer = so->OutputSlot;\n        entry.offset = xfbOffsets.at(so->OutputSlot);\n      }\n\n      xfbOffsets.at(so->OutputSlot) += so->ComponentCount * sizeof(uint32_t);\n    }\n    \n    // If necessary, override the buffer strides\n    for (uint32_t i = 0; i < NumStrides; i++)\n      xfbOffsets.at(i) = pBufferStrides[i];\n\n    // Assign buffer stride to each entry\n    for (size_t i = 0u; i < moduleInfo.xfbEntries.size(); i++) {\n      auto& entry = moduleInfo.xfbEntries[i];\n      entry.stride = xfbOffsets.at(entry.buffer);\n    }\n\n    // Create the actual shader module\n    auto shaderKey = ComputeShaderKey(VK_SHADER_STAGE_GEOMETRY_BIT,\n      pShaderBytecode, BytecodeLength, pSODeclaration, NumEntries,\n      pBufferStrides, NumStrides, RasterizedStream);\n    \n    HRESULT hr = CreateShaderModule(&module, pClassLinkage, shaderKey,\n      pShaderBytecode, BytecodeLength, moduleInfo);\n\n    if (FAILED(hr))\n      return E_INVALIDARG;\n    \n    if (!ppGeometryShader)\n      return S_FALSE;\n    \n    *ppGeometryShader = ref(new D3D11GeometryShader(this, module));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreatePixelShader(\n    const void*                       pShaderBytecode,\n          SIZE_T                      BytecodeLength,\n          ID3D11ClassLinkage*         pClassLinkage,\n          ID3D11PixelShader**         ppPixelShader) {\n    InitReturnPtr(ppPixelShader);\n    D3D11CommonShader module;\n    \n    DxvkIrShaderCreateInfo moduleInfo = { };\n    moduleInfo.options = m_shaderOptions;\n\n    HRESULT hr = CreateShaderModule(&module, pClassLinkage,\n      ComputeShaderKey(VK_SHADER_STAGE_FRAGMENT_BIT, pShaderBytecode, BytecodeLength),\n      pShaderBytecode, BytecodeLength, moduleInfo);\n\n    if (FAILED(hr))\n      return hr;\n    \n    if (!ppPixelShader)\n      return S_FALSE;\n    \n    *ppPixelShader = ref(new D3D11PixelShader(this, module));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateHullShader(\n    const void*                       pShaderBytecode,\n          SIZE_T                      BytecodeLength,\n          ID3D11ClassLinkage*         pClassLinkage,\n          ID3D11HullShader**          ppHullShader) {\n    InitReturnPtr(ppHullShader);\n    D3D11CommonShader module;\n    \n    DxvkIrShaderCreateInfo moduleInfo = { };\n    moduleInfo.options = m_shaderOptions;\n\n    HRESULT hr = CreateShaderModule(&module, pClassLinkage,\n      ComputeShaderKey(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, pShaderBytecode, BytecodeLength),\n      pShaderBytecode, BytecodeLength, moduleInfo);\n\n    if (FAILED(hr))\n      return hr;\n    \n    if (!ppHullShader)\n      return S_FALSE;\n    \n    *ppHullShader = ref(new D3D11HullShader(this, module));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateDomainShader(\n    const void*                       pShaderBytecode,\n          SIZE_T                      BytecodeLength,\n          ID3D11ClassLinkage*         pClassLinkage,\n          ID3D11DomainShader**        ppDomainShader) {\n    InitReturnPtr(ppDomainShader);\n    D3D11CommonShader module;\n    \n    DxvkIrShaderCreateInfo moduleInfo = { };\n    moduleInfo.options = m_shaderOptions;\n\n    HRESULT hr = CreateShaderModule(&module, pClassLinkage,\n      ComputeShaderKey(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, pShaderBytecode, BytecodeLength),\n      pShaderBytecode, BytecodeLength, moduleInfo);\n\n    if (FAILED(hr))\n      return hr;\n    \n    if (ppDomainShader == nullptr)\n      return S_FALSE;\n    \n    *ppDomainShader = ref(new D3D11DomainShader(this, module));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateComputeShader(\n    const void*                       pShaderBytecode,\n          SIZE_T                      BytecodeLength,\n          ID3D11ClassLinkage*         pClassLinkage,\n          ID3D11ComputeShader**       ppComputeShader) {\n    InitReturnPtr(ppComputeShader);\n    D3D11CommonShader module;\n    \n    DxvkIrShaderCreateInfo moduleInfo = { };\n    moduleInfo.options = m_shaderOptions;\n\n    HRESULT hr = CreateShaderModule(&module, pClassLinkage,\n      ComputeShaderKey(VK_SHADER_STAGE_COMPUTE_BIT, pShaderBytecode, BytecodeLength),\n      pShaderBytecode, BytecodeLength, moduleInfo);\n\n    if (FAILED(hr))\n      return hr;\n    \n    if (!ppComputeShader)\n      return S_FALSE;\n    \n    *ppComputeShader = ref(new D3D11ComputeShader(this, module));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateClassLinkage(ID3D11ClassLinkage** ppLinkage) {\n    *ppLinkage = ref(new D3D11ClassLinkage(this));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateBlendState(\n    const D3D11_BLEND_DESC*           pBlendStateDesc,\n          ID3D11BlendState**          ppBlendState) {\n    InitReturnPtr(ppBlendState);\n\n    if (!pBlendStateDesc)\n      return E_INVALIDARG;\n    \n    D3D11_BLEND_DESC1 desc = D3D11BlendState::PromoteDesc(pBlendStateDesc);\n    \n    if (FAILED(D3D11BlendState::NormalizeDesc(&desc)))\n      return E_INVALIDARG;\n    \n    if (ppBlendState != nullptr) {\n      *ppBlendState = m_bsStateObjects.Create(this, desc);\n      return S_OK;\n    } return S_FALSE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateBlendState1(\n    const D3D11_BLEND_DESC1*          pBlendStateDesc, \n          ID3D11BlendState1**         ppBlendState) {\n    InitReturnPtr(ppBlendState);\n    \n    if (!pBlendStateDesc)\n      return E_INVALIDARG;\n\n    D3D11_BLEND_DESC1 desc = *pBlendStateDesc;\n    \n    if (FAILED(D3D11BlendState::NormalizeDesc(&desc)))\n      return E_INVALIDARG;\n    \n    if (ppBlendState != nullptr) {\n      *ppBlendState = m_bsStateObjects.Create(this, desc);\n      return S_OK;\n    } return S_FALSE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateDepthStencilState(\n    const D3D11_DEPTH_STENCIL_DESC*   pDepthStencilDesc,\n          ID3D11DepthStencilState**   ppDepthStencilState) {\n    InitReturnPtr(ppDepthStencilState);\n    \n    if (!pDepthStencilDesc)\n      return E_INVALIDARG;\n\n    D3D11_DEPTH_STENCIL_DESC desc = *pDepthStencilDesc;\n    \n    if (FAILED(D3D11DepthStencilState::NormalizeDesc(&desc)))\n      return E_INVALIDARG;\n    \n    if (ppDepthStencilState != nullptr) {\n      *ppDepthStencilState = m_dsStateObjects.Create(this, desc);\n      return S_OK;\n    } return S_FALSE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateRasterizerState(\n    const D3D11_RASTERIZER_DESC*      pRasterizerDesc,\n          ID3D11RasterizerState**     ppRasterizerState) {\n    InitReturnPtr(ppRasterizerState);\n\n    if (!pRasterizerDesc)\n      return E_INVALIDARG;\n\n    D3D11_RASTERIZER_DESC2 desc = D3D11RasterizerState::PromoteDesc(pRasterizerDesc);\n    \n    if (FAILED(D3D11RasterizerState::NormalizeDesc(&desc)))\n      return E_INVALIDARG;\n    \n    if (!ppRasterizerState)\n      return S_FALSE;\n    \n    *ppRasterizerState = m_rsStateObjects.Create(this, desc);\n    return S_OK;\n  }\n  \n  \n  HRESULT D3D11Device::CreateRasterizerState1(\n    const D3D11_RASTERIZER_DESC1*     pRasterizerDesc, \n          ID3D11RasterizerState1**    ppRasterizerState) {\n    InitReturnPtr(ppRasterizerState);\n    \n    if (!pRasterizerDesc)\n      return E_INVALIDARG;\n\n    D3D11_RASTERIZER_DESC2 desc = D3D11RasterizerState::PromoteDesc(pRasterizerDesc);\n    \n    if (FAILED(D3D11RasterizerState::NormalizeDesc(&desc)))\n      return E_INVALIDARG;\n    \n    if (!ppRasterizerState)\n      return S_FALSE;\n    \n    *ppRasterizerState = m_rsStateObjects.Create(this, desc);\n    return S_OK;\n  }\n  \n  \n  HRESULT D3D11Device::CreateRasterizerState2(\n    const D3D11_RASTERIZER_DESC2*     pRasterizerDesc, \n          ID3D11RasterizerState2**    ppRasterizerState) {\n    InitReturnPtr(ppRasterizerState);\n    \n    if (!pRasterizerDesc)\n      return E_INVALIDARG;\n\n    D3D11_RASTERIZER_DESC2 desc = *pRasterizerDesc;\n    \n    if (FAILED(D3D11RasterizerState::NormalizeDesc(&desc)))\n      return E_INVALIDARG;\n\n    if (desc.ConservativeRaster != D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF\n     && !m_deviceFeatures.GetConservativeRasterizationTier())\n      return E_INVALIDARG;\n\n    if (!ppRasterizerState)\n      return S_FALSE;\n    \n    *ppRasterizerState = m_rsStateObjects.Create(this, desc);\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateSamplerState(\n    const D3D11_SAMPLER_DESC*         pSamplerDesc,\n          ID3D11SamplerState**        ppSamplerState) {\n    InitReturnPtr(ppSamplerState);\n\n    if (pSamplerDesc == nullptr)\n      return E_INVALIDARG;\n\n    D3D11_SAMPLER_DESC desc = *pSamplerDesc;\n    \n    if (FAILED(D3D11SamplerState::NormalizeDesc(&desc)))\n      return E_INVALIDARG;\n\n    D3D11_TILED_RESOURCES_TIER tiledResourcesTier = m_deviceFeatures.GetTiledResourcesTier();\n\n    if (IsMinMaxFilter(desc.Filter) && tiledResourcesTier < D3D11_TILED_RESOURCES_TIER_2)\n      return E_INVALIDARG;\n\n    if (!ppSamplerState)\n      return S_FALSE;\n    \n    try {\n      *ppSamplerState = m_samplerObjects.Create(this, desc);\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateQuery(\n    const D3D11_QUERY_DESC*           pQueryDesc,\n          ID3D11Query**               ppQuery) {\n    InitReturnPtr(ppQuery);\n\n    if (!pQueryDesc)\n      return E_INVALIDARG;\n    \n    D3D11_QUERY_DESC1 desc;\n    desc.Query       = pQueryDesc->Query;\n    desc.MiscFlags   = pQueryDesc->MiscFlags;\n    desc.ContextType = D3D11_CONTEXT_TYPE_ALL;\n\n    ID3D11Query1* query = nullptr;\n    HRESULT hr = CreateQueryBase(&desc, ppQuery ? &query : nullptr);\n\n    if (hr != S_OK)\n      return hr;\n\n    *ppQuery = query;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateQuery1(\n    const D3D11_QUERY_DESC1*          pQueryDesc,\n          ID3D11Query1**              ppQuery) {\n    InitReturnPtr(ppQuery);\n\n    if (!pQueryDesc)\n      return E_INVALIDARG;\n\n    return CreateQueryBase(pQueryDesc, ppQuery);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateQueryBase(\n    const D3D11_QUERY_DESC1*          pQueryDesc,\n          ID3D11Query1**              ppQuery) {\n    HRESULT hr = D3D11Query::ValidateDesc(pQueryDesc);\n\n    if (FAILED(hr))\n      return hr;\n    \n    if (!ppQuery)\n      return S_FALSE;\n    \n    try {\n      *ppQuery = ref(new D3D11Query(this, *pQueryDesc));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreatePredicate(\n    const D3D11_QUERY_DESC*           pPredicateDesc,\n          ID3D11Predicate**           ppPredicate) {\n    InitReturnPtr(ppPredicate);\n    \n    if (!pPredicateDesc)\n      return E_INVALIDARG;\n\n    D3D11_QUERY_DESC1 desc;\n    desc.Query       = pPredicateDesc->Query;\n    desc.MiscFlags   = pPredicateDesc->MiscFlags;\n    desc.ContextType = D3D11_CONTEXT_TYPE_ALL;\n\n    if (desc.Query != D3D11_QUERY_OCCLUSION_PREDICATE) {\n      Logger::warn(str::format(\"D3D11: Unhandled predicate type: \", pPredicateDesc->Query));\n      return E_INVALIDARG;\n    }\n    \n    if (!ppPredicate)\n      return S_FALSE;\n    \n    try {\n      *ppPredicate = D3D11Query::AsPredicate(\n        ref(new D3D11Query(this, desc)));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateCounter(\n    const D3D11_COUNTER_DESC*         pCounterDesc,\n          ID3D11Counter**             ppCounter) {\n    InitReturnPtr(ppCounter);\n    \n    Logger::err(str::format(\"D3D11: Unsupported counter: \", pCounterDesc->Counter));\n    return E_INVALIDARG;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeferredContext(\n          UINT                        ContextFlags,\n          ID3D11DeviceContext**       ppDeferredContext) {\n    *ppDeferredContext = ref(new D3D11DeferredContext(this, m_dxvkDevice, ContextFlags));\n    return S_OK;\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeferredContext1(\n          UINT                        ContextFlags, \n          ID3D11DeviceContext1**      ppDeferredContext) {\n    *ppDeferredContext = ref(new D3D11DeferredContext(this, m_dxvkDevice, ContextFlags));\n    return S_OK;\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeferredContext2(\n          UINT                        ContextFlags, \n          ID3D11DeviceContext2**      ppDeferredContext) {\n    *ppDeferredContext = ref(new D3D11DeferredContext(this, m_dxvkDevice, ContextFlags));\n    return S_OK;\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeferredContext3(\n          UINT                        ContextFlags, \n          ID3D11DeviceContext3**      ppDeferredContext) {\n    *ppDeferredContext = ref(new D3D11DeferredContext(this, m_dxvkDevice, ContextFlags));\n    return S_OK;\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateDeviceContextState(\n          UINT                        Flags, \n    const D3D_FEATURE_LEVEL*          pFeatureLevels, \n          UINT                        FeatureLevels, \n          UINT                        SDKVersion, \n          REFIID                      EmulatedInterface, \n          D3D_FEATURE_LEVEL*          pChosenFeatureLevel, \n          ID3DDeviceContextState**    ppContextState) {\n    InitReturnPtr(ppContextState);\n\n    if (!pFeatureLevels || !FeatureLevels)\n      return E_INVALIDARG;\n\n    if (EmulatedInterface != __uuidof(ID3D10Device)\n     && EmulatedInterface != __uuidof(ID3D10Device1)\n     && EmulatedInterface != __uuidof(ID3D11Device)\n     && EmulatedInterface != __uuidof(ID3D11Device1))\n      return E_INVALIDARG;\n\n    D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL();\n\n    for (uint32_t flId = 0; flId < FeatureLevels; flId++) {\n      if (pFeatureLevels[flId] <= m_maxFeatureLevel) {\n        featureLevel = pFeatureLevels[flId];\n        break;\n      }\n    }\n\n    if (!featureLevel)\n      return E_INVALIDARG;\n\n    if (m_featureLevel < featureLevel) {\n      m_featureLevel = featureLevel;\n      m_deviceFeatures = D3D11DeviceFeatures(\n        m_dxvkDevice->instance(),\n        m_dxvkDevice->adapter(),\n        m_d3d11Options, m_featureLevel);\n    }\n\n    if (pChosenFeatureLevel)\n      *pChosenFeatureLevel = featureLevel;\n\n    if (!ppContextState)\n      return S_FALSE;\n\n    *ppContextState = ref(new D3D11DeviceContextState(this));\n    return S_OK;\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D11Device::CreateFence(\n          UINT64                      InitialValue,\n          D3D11_FENCE_FLAG            Flags,\n          REFIID                      riid,\n          void**                      ppFence) {\n    InitReturnPtr(ppFence);\n\n    try {\n      Com<D3D11Fence> fence = new D3D11Fence(this, InitialValue, Flags, INVALID_HANDLE_VALUE);\n      return fence->QueryInterface(riid, ppFence);\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_FAIL;\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D11Device::ReadFromSubresource(\n          void*                       pDstData,\n          UINT                        DstRowPitch,\n          UINT                        DstDepthPitch,\n          ID3D11Resource*             pSrcResource,\n          UINT                        SrcSubresource,\n    const D3D11_BOX*                  pSrcBox) {\n    auto texture = GetCommonTexture(pSrcResource);\n\n    if (!texture)\n      return;\n\n    if (texture->Desc()->Usage != D3D11_USAGE_DEFAULT\n     || texture->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_NONE\n     || texture->CountSubresources() <= SrcSubresource)\n      return;\n\n    uint32_t map = texture->GetMapType(SrcSubresource);\n\n    if (map != uint32_t(D3D11_MAP_READ)\n     && map != uint32_t(D3D11_MAP_READ_WRITE))\n      return;\n\n    CopySubresourceData(\n      pDstData, DstRowPitch, DstDepthPitch,\n      texture, SrcSubresource, pSrcBox);\n  }\n\n\n  void STDMETHODCALLTYPE D3D11Device::WriteToSubresource(\n          ID3D11Resource*             pDstResource,\n          UINT                        DstSubresource,\n    const D3D11_BOX*                  pDstBox,\n    const void*                       pSrcData,\n          UINT                        SrcRowPitch,\n          UINT                        SrcDepthPitch) {\n    auto texture = GetCommonTexture(pDstResource);\n\n    if (!texture)\n      return;\n\n    if (texture->Desc()->Usage != D3D11_USAGE_DEFAULT\n     || texture->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_NONE\n     || texture->CountSubresources() <= DstSubresource)\n      return;\n\n    uint32_t map = texture->GetMapType(DstSubresource);\n\n    if (map != uint32_t(D3D11_MAP_WRITE)\n     && map != uint32_t(D3D11_MAP_WRITE_NO_OVERWRITE)\n     && map != uint32_t(D3D11_MAP_READ_WRITE))\n      return;\n\n    CopySubresourceData(\n      pSrcData, SrcRowPitch, SrcRowPitch,\n      texture, DstSubresource, pDstBox);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11Device::OpenSharedResource(\n          HANDLE      hResource,\n          REFIID      ReturnedInterface,\n          void**      ppResource) {\n    InitReturnPtr(ppResource);\n\n    if (!(reinterpret_cast<uintptr_t>(hResource) & 0xc0000000)) {\n      Logger::warn(\"D3D11Device::OpenSharedResource: Invalid shared handle type\");\n      return E_INVALIDARG;\n    }\n\n    if (ppResource == nullptr)\n      return S_FALSE;\n\n    union d3dkmt_desc d3dkmt;\n\n    D3DKMT_QUERYRESOURCEINFO query = { };\n    query.hDevice = m_dxvkDevice->kmtLocal();\n    query.hGlobalShare = reinterpret_cast<uintptr_t>(hResource);\n    query.pPrivateRuntimeData = &d3dkmt;\n    query.PrivateRuntimeDataSize = sizeof(d3dkmt);\n\n    if (D3DKMTQueryResourceInfo(&query)) {\n      Logger::warn(str::format(\"D3D11Device::OpenSharedResource: Failed to query resource: \", hResource));\n    } else if (query.PrivateRuntimeDataSize < sizeof(d3dkmt.dxgi) || query.PrivateRuntimeDataSize > sizeof(d3dkmt)) {\n      Logger::warn(str::format(\"D3D11Device::OpenSharedResource: Unexpected size: \", query.PrivateRuntimeDataSize));\n    } else {\n      D3DDDI_OPENALLOCATIONINFO2 alloc = { };\n      D3DKMT_OPENRESOURCE open = { };\n      open.hDevice = m_dxvkDevice->kmtLocal();\n      open.hGlobalShare = reinterpret_cast<uintptr_t>(hResource);\n      open.NumAllocations = 1;\n      open.pOpenAllocationInfo2 = &alloc;\n      open.pPrivateRuntimeData = &d3dkmt;\n      open.PrivateRuntimeDataSize = query.PrivateRuntimeDataSize;\n\n      if (D3DKMTOpenResource2(&open)) {\n        Logger::warn(str::format(\"D3D11Device::OpenSharedResource: Failed to open resource: \", hResource));\n      } else {\n        D3DKMT_DESTROYALLOCATION destroy = { };\n        destroy.hDevice = m_dxvkDevice->kmtLocal();\n        destroy.hResource = open.hResource;\n        D3DKMTDestroyAllocation(&destroy);\n\n        Rc<DxvkFence> fence;\n        if (d3dkmt.dxgi.sync_handle) {\n          DxvkFenceCreateInfo fenceInfo = { };\n          fenceInfo.sharedType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;\n          fenceInfo.sharedHandle = reinterpret_cast<HANDLE>(d3dkmt.dxgi.sync_handle);\n          fence = this->GetDXVKDevice()->createFence(fenceInfo);\n        }\n\n        Rc<DxvkKeyedMutex> mutex;\n        if (d3dkmt.dxgi.keyed_mutex) {\n          D3DKMT_OPENKEYEDMUTEX openMutex = { };\n          openMutex.hSharedHandle = d3dkmt.dxgi.mutex_handle;\n\n          if (D3DKMTOpenKeyedMutex(&openMutex)) {\n            Logger::warn(str::format(\"D3D11Device::OpenSharedResource: Failed to open keyed mutex: \", d3dkmt.dxgi.keyed_mutex));\n          } else {\n            mutex = new DxvkKeyedMutex(m_dxvkDevice, std::move(fence), openMutex.hKeyedMutex, openMutex.hSharedHandle);\n          }\n        }\n\n        D3D11_COMMON_TEXTURE_DESC desc = { };\n        if (!ConvertRuntimeDescriptor(query.PrivateRuntimeDataSize, d3dkmt, &desc))\n          return E_INVALIDARG;\n\n        try {\n          const Com<D3D11Texture2D> texture = new D3D11Texture2D(this, &desc, nullptr, hResource);\n          texture->GetCommonTexture()->GetImage()->setKeyedMutex(std::move(mutex));\n          texture->QueryInterface(ReturnedInterface, ppResource);\n          return S_OK;\n        }\n        catch (const DxvkError& e) {\n          Logger::err(e.message());\n          return E_INVALIDARG;\n        }\n      }\n    }\n\n    /* try the legacy Proton shared resource implementation */\n    return OpenSharedResourceGeneric<true>(\n      hResource, ReturnedInterface, ppResource);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::OpenSharedResource1(\n          HANDLE      hResource,\n          REFIID      ReturnedInterface,\n          void**      ppResource) {\n    InitReturnPtr(ppResource);\n\n    if (reinterpret_cast<uintptr_t>(hResource) & 0xc0000000) {\n      Logger::warn(\"D3D11Device::OpenSharedResource1: Invalid shared handle type\");\n      return E_INVALIDARG;\n    }\n\n    if (ppResource == nullptr)\n      return S_FALSE;\n\n    union d3dkmt_desc d3dkmt;\n\n    D3DKMT_QUERYRESOURCEINFOFROMNTHANDLE query = { };\n    query.hDevice = m_dxvkDevice->kmtLocal();\n    query.hNtHandle = hResource;\n    query.pPrivateRuntimeData = &d3dkmt;\n    query.PrivateRuntimeDataSize = sizeof(d3dkmt);\n\n    if (D3DKMTQueryResourceInfoFromNtHandle(&query)) {\n      Logger::warn(str::format(\"D3D11Device::OpenSharedResource1: Failed to query resource: \", hResource));\n    } else if (query.PrivateRuntimeDataSize < sizeof(d3dkmt.dxgi) || query.PrivateRuntimeDataSize > sizeof(d3dkmt)) {\n      Logger::warn(str::format(\"D3D11Device::OpenSharedResource1: Unexpected size: \", query.PrivateRuntimeDataSize));\n    } else {\n      D3DDDI_OPENALLOCATIONINFO2 alloc = { };\n      D3DKMT_OPENRESOURCEFROMNTHANDLE open = { };\n      char dummy;\n\n      open.hDevice = m_dxvkDevice->kmtLocal();\n      open.hNtHandle = hResource;\n      open.NumAllocations = 1;\n      open.pOpenAllocationInfo2 = &alloc;\n      open.pPrivateRuntimeData = &d3dkmt;\n      open.PrivateRuntimeDataSize = query.PrivateRuntimeDataSize;\n      open.pTotalPrivateDriverDataBuffer = &dummy;\n      open.TotalPrivateDriverDataBufferSize = 0;\n\n      if (D3DKMTOpenResourceFromNtHandle(&open)) {\n        Logger::warn(str::format(\"D3D11Device::OpenSharedResource1: Failed to open resource: \", hResource));\n      } else {\n        D3DKMT_DESTROYALLOCATION destroy = { };\n        destroy.hDevice = m_dxvkDevice->kmtLocal();\n        destroy.hResource = open.hResource;\n        D3DKMTDestroyAllocation(&destroy);\n\n        Rc<DxvkFence> fence;\n        if (open.hSyncObject) {\n#ifdef _WIN32\n          DxvkFenceCreateInfo fenceInfo = { };\n\n          /* need to create a NT shared handle again to import the fence from it */\n          if (D3DKMTShareObjects(1, &open.hSyncObject, NULL, GENERIC_ALL, &fenceInfo.sharedHandle))\n            Logger::warn(str::format(\"D3D11Device::OpenSharedResource1: Failed to open sync object\"));\n          else {\n            fenceInfo.sharedType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT;\n            fence = m_dxvkDevice->createFence(fenceInfo);\n            CloseHandle(fenceInfo.sharedHandle);\n          }\n#else\n          Logger::warn(str::format(\"D3D11Device::OpenSharedResource1: Ignoring bundled sync object\"));\n#endif\n\n          D3DKMT_DESTROYSYNCHRONIZATIONOBJECT destroySync = { };\n          destroySync.hSyncObject = open.hSyncObject;\n          D3DKMTDestroySynchronizationObject(&destroySync);\n        }\n\n        Rc<DxvkKeyedMutex> mutex;\n        if (open.hKeyedMutex) {\n          mutex = new DxvkKeyedMutex(m_dxvkDevice, std::move(fence), open.hKeyedMutex, 0);\n        }\n\n        D3D11_COMMON_TEXTURE_DESC desc = { };\n        if (!ConvertRuntimeDescriptor(query.PrivateRuntimeDataSize, d3dkmt, &desc))\n          return E_INVALIDARG;\n\n        try {\n          const Com<D3D11Texture2D> texture = new D3D11Texture2D(this, &desc, nullptr, hResource);\n          texture->GetCommonTexture()->GetImage()->setKeyedMutex(std::move(mutex));\n          texture->QueryInterface(ReturnedInterface, ppResource);\n          return S_OK;\n        }\n        catch (const DxvkError& e) {\n          Logger::err(e.message());\n          return E_INVALIDARG;\n        }\n      }\n    }\n\n    /* try the legacy Proton shared resource implementation */\n    return OpenSharedResourceGeneric<false>(\n      hResource, ReturnedInterface, ppResource);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::OpenSharedResourceByName(\n          LPCWSTR     lpName, \n          DWORD       dwDesiredAccess, \n          REFIID      returnedInterface, \n          void**      ppResource) {\n    InitReturnPtr(ppResource);\n    \n    Logger::err(\"D3D11Device::OpenSharedResourceByName: Not implemented\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11Device::OpenSharedFence(\n          HANDLE      hFence,\n          REFIID      ReturnedInterface,\n          void**      ppFence) {\n    InitReturnPtr(ppFence);\n\n    if (ppFence == nullptr)\n      return S_FALSE;\n\n    try {\n      Com<D3D11Fence> fence = new D3D11Fence(this, 0, D3D11_FENCE_FLAG_SHARED, hFence);\n      return fence->QueryInterface(ReturnedInterface, ppFence);\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_FAIL;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11Device::CheckFormatSupport(\n          DXGI_FORMAT Format,\n          UINT*       pFormatSupport) {\n    return GetFormatSupportFlags(Format, pFormatSupport, nullptr);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CheckMultisampleQualityLevels(\n          DXGI_FORMAT Format,\n          UINT        SampleCount,\n          UINT*       pNumQualityLevels) {\n    return CheckMultisampleQualityLevels1(Format, SampleCount, 0, pNumQualityLevels);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CheckMultisampleQualityLevels1(\n          DXGI_FORMAT Format,\n          UINT        SampleCount,\n          UINT        Flags,\n          UINT*       pNumQualityLevels) {\n    // There are many error conditions, so we'll just assume\n    // that we will fail and return a non-zero value in case\n    // the device does actually support the format.\n    if (!pNumQualityLevels)\n      return E_INVALIDARG;\n    \n    // We don't support tiled resources, but it's unclear what\n    // we are supposed to return in this case. Be conservative.\n    if (Flags) {\n      *pNumQualityLevels = 0;\n      return E_FAIL;\n    }\n    \n    // For some reason, we can query DXGI_FORMAT_UNKNOWN\n    if (Format == DXGI_FORMAT_UNKNOWN) {\n      *pNumQualityLevels = SampleCount == 1 ? 1 : 0;\n      return SampleCount ? S_OK : E_FAIL;\n    }\n    \n    // All other unknown formats should result in an error return.\n    VkFormat format = LookupFormat(Format, DXGI_VK_FORMAT_MODE_ANY).Format;\n\n    if (format == VK_FORMAT_UNDEFINED)\n      return E_INVALIDARG;\n    \n    // Zero-init now, leave value undefined otherwise.\n    // This does actually match native D3D11 behaviour.\n    *pNumQualityLevels = 0;\n\n    // Non-power of two sample counts are not supported, but querying\n    // support for them is legal, so we return zero quality levels.\n    VkSampleCountFlagBits sampleCountFlag = VK_SAMPLE_COUNT_1_BIT;\n    \n    if (FAILED(DecodeSampleCount(SampleCount, &sampleCountFlag)))\n      return SampleCount && SampleCount <= 32 ? S_OK : E_FAIL;\n\n    // Get image create flags depending on function arguments\n    VkImageCreateFlags flags = 0;\n\n    if (Flags & D3D11_CHECK_MULTISAMPLE_QUALITY_LEVELS_TILED_RESOURCE) {\n      flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT\n            |  VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT\n            |  VK_IMAGE_CREATE_SPARSE_ALIASED_BIT;\n    }\n\n    // Check if the device supports the given combination of format\n    // and sample count. D3D exposes the opaque concept of quality\n    // levels to the application, we'll just define one such level.\n    DxvkFormatQuery formatQuery = { };\n    formatQuery.format = format;\n    formatQuery.type = VK_IMAGE_TYPE_2D;\n    formatQuery.tiling = VK_IMAGE_TILING_OPTIMAL;\n    formatQuery.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    formatQuery.flags = flags;\n\n    auto properties = m_dxvkDevice->getFormatLimits(formatQuery);\n\n    if (properties && (properties->sampleCounts & sampleCountFlag))\n      *pNumQualityLevels = 1;\n    return S_OK;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11Device::CheckCounterInfo(D3D11_COUNTER_INFO* pCounterInfo) {\n    // We basically don't support counters\n    pCounterInfo->LastDeviceDependentCounter  = D3D11_COUNTER(0);\n    pCounterInfo->NumSimultaneousCounters     = 0;\n    pCounterInfo->NumDetectableParallelUnits  = 0;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CheckCounter(\n    const D3D11_COUNTER_DESC* pDesc,\n          D3D11_COUNTER_TYPE* pType,\n          UINT*               pActiveCounters,\n          LPSTR               szName,\n          UINT*               pNameLength,\n          LPSTR               szUnits,\n          UINT*               pUnitsLength,\n          LPSTR               szDescription,\n          UINT*               pDescriptionLength) {\n    Logger::err(\"D3D11: Counters not supported\");\n    return E_INVALIDARG;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::CheckFeatureSupport(\n          D3D11_FEATURE Feature,\n          void*         pFeatureSupportData,\n          UINT          FeatureSupportDataSize) {\n    switch (Feature) {\n      // Format support queries are special in that they use in-out\n      // structs, and we need the Vulkan device to query them at all\n      case D3D11_FEATURE_FORMAT_SUPPORT: {\n        auto info = static_cast<D3D11_FEATURE_DATA_FORMAT_SUPPORT*>(pFeatureSupportData);\n\n        if (FeatureSupportDataSize != sizeof(*info))\n          return E_INVALIDARG;\n        \n        return GetFormatSupportFlags(info->InFormat, &info->OutFormatSupport, nullptr);\n      } return S_OK;\n\n      case D3D11_FEATURE_FORMAT_SUPPORT2: {\n        auto info = static_cast<D3D11_FEATURE_DATA_FORMAT_SUPPORT2*>(pFeatureSupportData);\n\n        if (FeatureSupportDataSize != sizeof(*info))\n          return E_INVALIDARG;\n        \n        return GetFormatSupportFlags(info->InFormat, nullptr, &info->OutFormatSupport2);\n      } return S_OK;\n\n      default:\n        // For everything else, we can use the device feature struct\n        // that we already initialized during device creation.\n        return m_deviceFeatures.GetFeatureData(Feature, FeatureSupportDataSize, pFeatureSupportData);\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::GetPrivateData(\n          REFGUID guid, UINT* pDataSize, void* pData) {\n    return m_container->GetPrivateData(guid, pDataSize, pData);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::SetPrivateData(\n          REFGUID guid, UINT DataSize, const void* pData) {\n    return m_container->SetPrivateData(guid, DataSize, pData);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::SetPrivateDataInterface(\n          REFGUID guid, const IUnknown* pData) {\n    return m_container->SetPrivateDataInterface(guid, pData);\n  }\n  \n  \n  D3D_FEATURE_LEVEL STDMETHODCALLTYPE D3D11Device::GetFeatureLevel() {\n    return m_featureLevel;\n  }\n  \n  \n  UINT STDMETHODCALLTYPE D3D11Device::GetCreationFlags() {\n    return m_featureFlags;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::GetDeviceRemovedReason() {\n    VkResult status = m_dxvkDevice->getDeviceStatus();\n\n    switch (status) {\n      case VK_SUCCESS: return S_OK;\n      default:         return DXGI_ERROR_DEVICE_RESET;\n    }\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Device::GetImmediateContext(ID3D11DeviceContext** ppImmediateContext) {\n    *ppImmediateContext = m_context.ref();\n  }\n\n\n  void STDMETHODCALLTYPE D3D11Device::GetImmediateContext1(ID3D11DeviceContext1** ppImmediateContext) {\n    *ppImmediateContext = m_context.ref();\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Device::GetImmediateContext2(ID3D11DeviceContext2** ppImmediateContext) {\n    *ppImmediateContext = m_context.ref();\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Device::GetImmediateContext3(ID3D11DeviceContext3** ppImmediateContext) {\n    *ppImmediateContext = m_context.ref();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::SetExceptionMode(UINT RaiseFlags) {\n    Logger::err(\"D3D11Device::SetExceptionMode: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  UINT STDMETHODCALLTYPE D3D11Device::GetExceptionMode() {\n    Logger::err(\"D3D11Device::GetExceptionMode: Not implemented\");\n    return 0;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11Device::GetResourceTiling(\n          ID3D11Resource*           pTiledResource,\n          UINT*                     pNumTilesForEntireResource,\n          D3D11_PACKED_MIP_DESC*    pPackedMipDesc,\n          D3D11_TILE_SHAPE*         pStandardTileShapeForNonPackedMips,\n          UINT*                     pNumSubresourceTilings,\n          UINT                      FirstSubresourceTilingToGet,\n          D3D11_SUBRESOURCE_TILING* pSubresourceTilingsForNonPackedMips) {\n    D3D11_COMMON_RESOURCE_DESC desc = { };\n    GetCommonResourceDesc(pTiledResource, &desc);\n\n    if (!(desc.MiscFlags & D3D11_RESOURCE_MISC_TILED)) {\n      if (pNumTilesForEntireResource)\n        *pNumTilesForEntireResource = 0;\n\n      if (pPackedMipDesc)\n        *pPackedMipDesc = D3D11_PACKED_MIP_DESC();\n\n      if (pStandardTileShapeForNonPackedMips)\n        *pStandardTileShapeForNonPackedMips = D3D11_TILE_SHAPE();\n\n      if (pNumSubresourceTilings) {\n        if (pSubresourceTilingsForNonPackedMips) {\n          for (uint32_t i = 0; i < *pNumSubresourceTilings; i++)\n            pSubresourceTilingsForNonPackedMips[i] = D3D11_SUBRESOURCE_TILING();\n        }\n\n        *pNumSubresourceTilings = 0;\n      }\n    } else {\n      DxvkSparsePageTable* sparseInfo = nullptr;\n      uint32_t mipCount = 0;\n\n      if (desc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) {\n        Rc<DxvkBuffer> buffer = static_cast<D3D11Buffer*>(pTiledResource)->GetBuffer();\n        sparseInfo = buffer->getSparsePageTable();\n      } else {\n        Rc<DxvkImage> image = GetCommonTexture(pTiledResource)->GetImage();\n        sparseInfo = image->getSparsePageTable();\n        mipCount = image->info().mipLevels;\n      }\n\n      if (pNumTilesForEntireResource)\n        *pNumTilesForEntireResource = sparseInfo->getPageCount();\n\n      if (pPackedMipDesc) {\n        auto properties = sparseInfo->getProperties();\n\n        if (properties.mipTailSize) {\n          pPackedMipDesc->NumStandardMips = properties.pagedMipCount;\n          pPackedMipDesc->NumPackedMips = mipCount - properties.pagedMipCount;\n          pPackedMipDesc->NumTilesForPackedMips = sparseInfo->getPageCount() - properties.mipTailPageIndex;\n          pPackedMipDesc->StartTileIndexInOverallResource = properties.mipTailPageIndex;\n        } else {\n          pPackedMipDesc->NumStandardMips = mipCount;\n          pPackedMipDesc->NumPackedMips = 0;\n          pPackedMipDesc->NumTilesForPackedMips = 0;\n          pPackedMipDesc->StartTileIndexInOverallResource = 0;\n        }\n      }\n\n      if (pStandardTileShapeForNonPackedMips) {\n        auto properties = sparseInfo->getProperties();\n        pStandardTileShapeForNonPackedMips->WidthInTexels = properties.pageRegionExtent.width;\n        pStandardTileShapeForNonPackedMips->HeightInTexels = properties.pageRegionExtent.height;\n        pStandardTileShapeForNonPackedMips->DepthInTexels = properties.pageRegionExtent.depth;\n      }\n\n      if (pNumSubresourceTilings) {\n        uint32_t subresourceCount = sparseInfo->getSubresourceCount();\n        uint32_t tilingCount = subresourceCount - std::min(FirstSubresourceTilingToGet, subresourceCount);\n        tilingCount = std::min(tilingCount, *pNumSubresourceTilings);\n\n        for (uint32_t i = 0; i < tilingCount; i++) {\n          auto subresourceInfo = sparseInfo->getSubresourceProperties(FirstSubresourceTilingToGet + i);\n          auto dstInfo = &pSubresourceTilingsForNonPackedMips[i];\n\n          if (subresourceInfo.isMipTail) {\n            dstInfo->WidthInTiles = 0u;\n            dstInfo->HeightInTiles = 0u;\n            dstInfo->DepthInTiles = 0u;\n            dstInfo->StartTileIndexInOverallResource = D3D11_PACKED_TILE;\n          } else {\n            dstInfo->WidthInTiles = subresourceInfo.pageCount.width;\n            dstInfo->HeightInTiles = subresourceInfo.pageCount.height;\n            dstInfo->DepthInTiles = subresourceInfo.pageCount.depth;\n            dstInfo->StartTileIndexInOverallResource = subresourceInfo.pageIndex;\n          }\n        }\n\n        *pNumSubresourceTilings = tilingCount;\n      }\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Device::RegisterDeviceRemovedEvent(\n          HANDLE                    hEvent,\n          DWORD*                    pdwCookie) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11Device::RegisterDeviceRemovedEvent: Stub\");\n\n    *pdwCookie = 0xdeadbeef;\n    return S_OK;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11Device::UnregisterDeviceRemoved(\n          DWORD                     dwCookie) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::err(\"D3D11Device::UnregisterDeviceRemovedEvent: Not implemented\");\n  }\n\n\n  DXGI_VK_FORMAT_INFO D3D11Device::LookupFormat(\n          DXGI_FORMAT           Format,\n          DXGI_VK_FORMAT_MODE   Mode) const {\n    return m_d3d11Formats.GetFormatInfo(Format, Mode);\n  }\n  \n  \n  DXGI_VK_FORMAT_INFO D3D11Device::LookupPackedFormat(\n          DXGI_FORMAT           Format,\n          DXGI_VK_FORMAT_MODE   Mode) const {\n    return m_d3d11Formats.GetPackedFormatInfo(Format, Mode);\n  }\n  \n  \n  DXGI_VK_FORMAT_FAMILY D3D11Device::LookupFamily(\n          DXGI_FORMAT           Format,\n          DXGI_VK_FORMAT_MODE   Mode) const {\n    return m_d3d11Formats.GetFormatFamily(Format, Mode);\n  }\n\n\n  bool D3D11Device::Is11on12Device() const {\n    return m_container->Is11on12Device();\n  }\n  \n  \n  D3D_FEATURE_LEVEL D3D11Device::GetMaxFeatureLevel(\n    const Rc<DxvkInstance>& Instance,\n    const Rc<DxvkAdapter>&  Adapter) {\n    // The feature level override always takes precedence\n    static const std::array<std::pair<std::string, D3D_FEATURE_LEVEL>, 9> s_featureLevels = {{\n      { \"12_1\", D3D_FEATURE_LEVEL_12_1 },\n      { \"12_0\", D3D_FEATURE_LEVEL_12_0 },\n      { \"11_1\", D3D_FEATURE_LEVEL_11_1 },\n      { \"11_0\", D3D_FEATURE_LEVEL_11_0 },\n      { \"10_1\", D3D_FEATURE_LEVEL_10_1 },\n      { \"10_0\", D3D_FEATURE_LEVEL_10_0 },\n      { \"9_3\",  D3D_FEATURE_LEVEL_9_3  },\n      { \"9_2\",  D3D_FEATURE_LEVEL_9_2  },\n      { \"9_1\",  D3D_FEATURE_LEVEL_9_1  },\n    }};\n    \n    std::string maxLevel = Instance->config().getOption<std::string>(\"d3d11.maxFeatureLevel\");\n\n    auto entry = std::find_if(s_featureLevels.begin(), s_featureLevels.end(),\n      [&] (const std::pair<std::string, D3D_FEATURE_LEVEL>& pair) {\n        return pair.first == maxLevel;\n      });\n\n    if (entry != s_featureLevels.end())\n      return entry->second;\n\n    // Otherwise, check the actually available device features\n    return D3D11DeviceFeatures::GetMaxFeatureLevel(Instance, Adapter);\n  }\n  \n  \n  HRESULT D3D11Device::CreateShaderModule(\n          D3D11CommonShader*      pShaderModule,\n          ID3D11ClassLinkage*     pLinkage,\n    const DxvkShaderHash&         ShaderKey,\n    const void*                   pShaderBytecode,\n          size_t                  BytecodeLength,\n    const DxvkIrShaderCreateInfo& ModuleInfo) {\n    if (!BytecodeLength || !pShaderBytecode)\n      return E_INVALIDARG;\n\n    // Ensure that the built-in hash is valid for the given binary.\n    // Somewhat relevant for us because we use the hash as a way to\n    // deduplicate and also tag shaders.\n    dxbc_spv::dxbc::Container container(pShaderBytecode, BytecodeLength);\n\n    if (!container.validateHash()) {\n      Logger::err(\"D3D11: Shader hash validation failed\");\n      return E_INVALIDARG;\n    }\n\n    // Parse dxbc binary and I/O signatures and validate that all\n    // features used by the shader are enabled on the device\n    dxbc_spv::dxbc::Parser parser(container.getCodeChunk());\n    dxbc_spv::dxbc::ShaderInfo shaderInfo = parser.getShaderInfo();\n\n    auto [hi, lo] = shaderInfo.getVersion();\n\n    if ((hi > 5u) || (hi == 5u && lo) || (hi == 4u && lo > 1u) || hi < 4u)\n      throw DxvkError(str::format(\"Invalid shader model: \", hi, \"_\", lo));\n\n    // Check whether the stage matches or if we can create a pass-through GS\n    auto shaderStage = [] (dxbc_spv::dxbc::ShaderType type) {\n      switch (type) {\n        case dxbc_spv::dxbc::ShaderType::eVertex:   return VK_SHADER_STAGE_VERTEX_BIT;\n        case dxbc_spv::dxbc::ShaderType::eHull:     return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;\n        case dxbc_spv::dxbc::ShaderType::eDomain:   return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;\n        case dxbc_spv::dxbc::ShaderType::eGeometry: return VK_SHADER_STAGE_GEOMETRY_BIT;\n        case dxbc_spv::dxbc::ShaderType::ePixel:    return VK_SHADER_STAGE_FRAGMENT_BIT;\n        case dxbc_spv::dxbc::ShaderType::eCompute:  return VK_SHADER_STAGE_COMPUTE_BIT;\n      }\n\n      return VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;\n    } (shaderInfo.getType());\n\n    if (ShaderKey.stage() != shaderStage) {\n      bool mismatch = !ShaderKey.hasXfb() || (\n        shaderStage != VK_SHADER_STAGE_VERTEX_BIT &&\n        shaderStage != VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);\n\n      if (mismatch) {\n        Logger::warn(str::format(\"D3D11: \", ShaderKey.toString(), \": Source shader is of incompatible type: \", shaderInfo.getType()));\n        return E_INVALIDARG;\n      }\n    }\n\n    dxbc_spv::dxbc::Instruction icbOp = { };\n\n    D3D11BindingMask bindingMask;\n\n    while (parser) {\n      auto op = parser.parseInstruction();\n\n      if (!op)\n        return E_INVALIDARG;\n\n      switch (op.getOpToken().getOpCode()) {\n        case dxbc_spv::dxbc::OpCode::eCustomData: {\n          if (op.getOpToken().getCustomDataType() == dxbc_spv::dxbc::CustomDataType::eDclIcb)\n            icbOp = std::move(op);\n        } break;\n\n        case dxbc_spv::dxbc::OpCode::eDclSampler: {\n          uint32_t index = op.getDst(0u).getIndex(0u);\n          bindingMask.setSampler(index);\n        } break;\n\n        case dxbc_spv::dxbc::OpCode::eDclConstantBuffer: {\n          uint32_t index = op.getDst(0u).getIndex(0u);\n          bindingMask.setCbv(index);\n        } break;\n\n        case dxbc_spv::dxbc::OpCode::eDclResource:\n        case dxbc_spv::dxbc::OpCode::eDclResourceRaw:\n        case dxbc_spv::dxbc::OpCode::eDclResourceStructured: {\n          uint32_t index = op.getDst(0u).getIndex(0u);\n          bindingMask.setSrv(index);\n        } break;\n\n        case dxbc_spv::dxbc::OpCode::eDclUavTyped:\n        case dxbc_spv::dxbc::OpCode::eDclUavRaw:\n        case dxbc_spv::dxbc::OpCode::eDclUavStructured: {\n          uint32_t index = op.getDst(0u).getIndex(0u);\n          bindingMask.setUav(index);\n        } break;\n\n        case dxbc_spv::dxbc::OpCode::eDclInput:\n        case dxbc_spv::dxbc::OpCode::eDclInputSgv:\n        case dxbc_spv::dxbc::OpCode::eDclInputSiv:\n        case dxbc_spv::dxbc::OpCode::eDclInputPs:\n        case dxbc_spv::dxbc::OpCode::eDclInputPsSgv:\n        case dxbc_spv::dxbc::OpCode::eDclInputPsSiv: {\n          const auto& dst = op.getDst(0u);\n\n          if (dst.getRegisterType() == dxbc_spv::dxbc::RegisterType::eInnerCoverage) {\n            if (m_deviceFeatures.GetConservativeRasterizationTier() < D3D11_CONSERVATIVE_RASTERIZATION_TIER_2) {\n              Logger::warn(str::format(\"D3D11: \", ShaderKey.toString(), \": Shader uses innser coverage, but feature is not supported.\"));\n              return E_INVALIDARG;\n            }\n          }\n        } break;\n\n        case dxbc_spv::dxbc::OpCode::eDclOutputSgv:\n        case dxbc_spv::dxbc::OpCode::eDclOutputSiv: {\n          auto sysval = op.getImm(0u).getImmediate<dxbc_spv::dxbc::Sysval>(0u);\n\n          if ((sysval == dxbc_spv::dxbc::Sysval::eRenderTargetId || sysval == dxbc_spv::dxbc::Sysval::eViewportId)\n           && (shaderInfo.getType() == dxbc_spv::dxbc::ShaderType::eVertex || shaderInfo.getType() == dxbc_spv::dxbc::ShaderType::eDomain)) {\n            D3D11_FEATURE_DATA_D3D11_OPTIONS3 options3 = { };\n            m_deviceFeatures.GetFeatureData(D3D11_FEATURE_D3D11_OPTIONS3, sizeof(options3), &options3);\n\n            if (!options3.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer) {\n              Logger::warn(str::format(\"D3D11: \", ShaderKey.toString(), \": Shader uses viewport / layer, but feature is not supported.\"));\n              return E_INVALIDARG;\n            }\n          }\n        } [[fallthrough]];\n\n        case dxbc_spv::dxbc::OpCode::eDclOutput: {\n          const auto& dst = op.getDst(0u);\n\n          if (dst.getRegisterType() == dxbc_spv::dxbc::RegisterType::eStencilRef) {\n            if (m_deviceFeatures.GetConservativeRasterizationTier() < D3D11_CONSERVATIVE_RASTERIZATION_TIER_2) {\n              Logger::warn(str::format(\"D3D11: \", ShaderKey.toString(), \": Shader exports stencil reference, but feature is not supported.\"));\n              return E_INVALIDARG;\n            }\n          }\n        } break;\n\n        case dxbc_spv::dxbc::OpCode::eDMov:\n        case dxbc_spv::dxbc::OpCode::eDMovc:\n        case dxbc_spv::dxbc::OpCode::eDAdd:\n        case dxbc_spv::dxbc::OpCode::eDMul:\n        case dxbc_spv::dxbc::OpCode::eDFma:\n        case dxbc_spv::dxbc::OpCode::eDDiv:\n        case dxbc_spv::dxbc::OpCode::eDRcp:\n        case dxbc_spv::dxbc::OpCode::eDMin:\n        case dxbc_spv::dxbc::OpCode::eDMax:\n        case dxbc_spv::dxbc::OpCode::eDtoF:\n        case dxbc_spv::dxbc::OpCode::eDtoI:\n        case dxbc_spv::dxbc::OpCode::eDtoU:\n        case dxbc_spv::dxbc::OpCode::eFtoD:\n        case dxbc_spv::dxbc::OpCode::eItoD:\n        case dxbc_spv::dxbc::OpCode::eUtoD:\n        case dxbc_spv::dxbc::OpCode::eDEq:\n        case dxbc_spv::dxbc::OpCode::eDNe:\n        case dxbc_spv::dxbc::OpCode::eDLt:\n        case dxbc_spv::dxbc::OpCode::eDGe: {\n          D3D11_FEATURE_DATA_DOUBLES doubles = { };\n          m_deviceFeatures.GetFeatureData(D3D11_FEATURE_DOUBLES, sizeof(doubles), &doubles);\n\n          if (!doubles.DoublePrecisionFloatShaderOps) {\n            Logger::warn(str::format(\"D3D11: \", ShaderKey.toString(), \": Shader uses fp64, but feature is not supported.\"));\n            return E_INVALIDARG;\n          }\n        } break;\n\n        case dxbc_spv::dxbc::OpCode::eGather4S:\n        case dxbc_spv::dxbc::OpCode::eGather4CS:\n        case dxbc_spv::dxbc::OpCode::eGather4PoS:\n        case dxbc_spv::dxbc::OpCode::eGather4PoCS:\n        case dxbc_spv::dxbc::OpCode::eLdS:\n        case dxbc_spv::dxbc::OpCode::eLdMsS:\n        case dxbc_spv::dxbc::OpCode::eLdUavTypedS:\n        case dxbc_spv::dxbc::OpCode::eLdRawS:\n        case dxbc_spv::dxbc::OpCode::eLdStructuredS:\n        case dxbc_spv::dxbc::OpCode::eSampleLS:\n        case dxbc_spv::dxbc::OpCode::eSampleClzS:\n        case dxbc_spv::dxbc::OpCode::eSampleClampS:\n        case dxbc_spv::dxbc::OpCode::eSampleBClampS:\n        case dxbc_spv::dxbc::OpCode::eSampleDClampS:\n        case dxbc_spv::dxbc::OpCode::eSampleCClampS:\n        case dxbc_spv::dxbc::OpCode::eCheckAccessFullyMapped: {\n          if (m_deviceFeatures.GetTiledResourcesTier() < D3D11_TILED_RESOURCES_TIER_2) {\n            Logger::warn(str::format(\"D3D11: \", ShaderKey.toString(), \": Shader uses sparse residency, but TILED_RESOURCES_TIER_2 is not supported.\"));\n            return E_INVALIDARG;\n          }\n        } break;\n\n        default:\n          break;\n      }\n    }\n\n    // Handle immediate constant buffer declaration\n    D3D11ShaderIcbInfo icbInfo = { };\n\n    if (icbOp) {\n      icbInfo.data = icbOp.getCustomData().first;\n      icbInfo.size = icbOp.getCustomData().second;\n    }\n\n    // Initialize the actual shader\n    D3D11CommonShader commonShader = { };\n\n    HRESULT hr = m_shaderModules.GetShaderModule(this,\n      static_cast<D3D11ClassLinkage*>(pLinkage),\n      ShaderKey, ModuleInfo, pShaderBytecode, BytecodeLength,\n      icbInfo, bindingMask, &commonShader);\n\n    if (FAILED(hr))\n      return hr;\n\n    *pShaderModule = std::move(commonShader);\n    return S_OK;\n  }\n\n\n  DxvkShaderHash D3D11Device::ComputeShaderKey(\n          VkShaderStageFlagBits   Stage,\n    const void*                   pShaderBytecode,\n          size_t                  BytecodeLength) {\n    return ComputeShaderKey(Stage, pShaderBytecode, BytecodeLength, nullptr, 0u, nullptr, 0u, 0u);\n  }\n\n\n  DxvkShaderHash D3D11Device::ComputeShaderKey(\n          VkShaderStageFlagBits   Stage,\n    const void*                   pShaderBytecode,\n          size_t                  BytecodeLength,\n    const D3D11_SO_DECLARATION_ENTRY* pSODeclaration,\n          UINT                    NumEntries,\n    const UINT*                   pBufferStrides,\n          UINT                    NumStrides,\n          UINT                    RasterizedStream) {\n    dxbc_spv::dxbc::Container container(pShaderBytecode, BytecodeLength);\n    auto binHash = container.getHash();\n\n    if (!NumEntries) {\n      return DxvkShaderHash(Stage,\n        BytecodeLength, binHash.data.data(), binHash.data.size());\n    }\n\n    dxbc_spv::util::md5::Hasher xfbHasher;\n\n    for (uint32_t i = 0u; i < NumEntries; i++) {\n      xfbHasher.update(&pSODeclaration[i].Stream, sizeof(pSODeclaration[i].Stream));\n\n      if (pSODeclaration[i].SemanticName)\n        xfbHasher.update(pSODeclaration[i].SemanticName, std::strlen(pSODeclaration[i].SemanticName));\n\n      xfbHasher.update(&pSODeclaration[i].SemanticIndex, sizeof(pSODeclaration[i].SemanticIndex));\n      xfbHasher.update(&pSODeclaration[i].StartComponent, sizeof(pSODeclaration[i].StartComponent));\n      xfbHasher.update(&pSODeclaration[i].ComponentCount, sizeof(pSODeclaration[i].ComponentCount));\n      xfbHasher.update(&pSODeclaration[i].OutputSlot, sizeof(pSODeclaration[i].OutputSlot));\n    }\n\n    xfbHasher.update(pBufferStrides, sizeof(*pBufferStrides) * NumStrides);\n    xfbHasher.update(&RasterizedStream, sizeof(RasterizedStream));\n    xfbHasher.finalize();\n\n    auto xfbHash = xfbHasher.getDigest();\n\n    return DxvkShaderHash(Stage, BytecodeLength,\n      binHash.data.data(), binHash.data.size(),\n      xfbHash.data.data(), xfbHash.data.size());\n  }\n\n\n  HRESULT D3D11Device::GetFormatSupportFlags(DXGI_FORMAT Format, UINT* pFlags1, UINT* pFlags2) const {\n    const DXGI_VK_FORMAT_INFO fmtMapping = LookupFormat(Format, DXGI_VK_FORMAT_MODE_ANY);\n\n    // Reset output flags preemptively\n    if (pFlags1 != nullptr) *pFlags1 = 0;\n    if (pFlags2 != nullptr) *pFlags2 = 0;\n\n    // Unsupported or invalid format\n    if (Format && fmtMapping.Format == VK_FORMAT_UNDEFINED)\n      return E_FAIL;\n    \n    // Query Vulkan format properties and supported features for it\n    const DxvkFormatInfo* fmtProperties = lookupFormatInfo(fmtMapping.Format);\n\n    DxvkFormatFeatures fmtSupport = fmtMapping.Format != VK_FORMAT_UNDEFINED\n      ? m_dxvkDevice->getFormatFeatures(fmtMapping.Format)\n      : DxvkFormatFeatures();\n    \n    VkFormatFeatureFlags2 bufFeatures = fmtSupport.buffer;\n    VkFormatFeatureFlags2 imgFeatures = fmtSupport.optimal | fmtSupport.linear;\n\n    // For multi-plane images, we want to check available view formats as well\n    if (fmtProperties->flags.test(DxvkFormatFlag::MultiPlane)) {\n      const VkFormatFeatureFlags2 featureMask\n        = VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT\n        | VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT\n        | VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT\n        | VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT\n        | VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT;\n\n      DXGI_VK_FORMAT_FAMILY formatFamily = LookupFamily(Format, DXGI_VK_FORMAT_MODE_ANY);\n\n      for (uint32_t i = 0; i < formatFamily.FormatCount; i++) {\n        DxvkFormatFeatures viewFmtSupport = m_dxvkDevice->getFormatFeatures(formatFamily.Formats[i]);\n        imgFeatures |= (viewFmtSupport.optimal | viewFmtSupport.linear) & featureMask;\n      }\n    }\n    \n    UINT flags1 = 0;\n    UINT flags2 = 0;\n\n    // Format can be used for shader resource views with buffers\n    if ((bufFeatures & VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT) || !Format)\n      flags1 |= D3D11_FORMAT_SUPPORT_BUFFER;\n    \n    // Format can be used for vertex data\n    if (bufFeatures & VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT)\n      flags1 |= D3D11_FORMAT_SUPPORT_IA_VERTEX_BUFFER;\n    \n    // Format can be used for index data. Only\n    // these two formats are supported by D3D11.\n    if (Format == DXGI_FORMAT_R16_UINT\n     || Format == DXGI_FORMAT_R32_UINT)\n      flags1 |= D3D11_FORMAT_SUPPORT_IA_INDEX_BUFFER;\n    \n    // These formats are technically irrelevant since\n    // SO buffers are passed in as raw buffers and not\n    // as views, but the feature flag exists regardless\n    if (Format == DXGI_FORMAT_R32_FLOAT\n     || Format == DXGI_FORMAT_R32_UINT\n     || Format == DXGI_FORMAT_R32_SINT\n     || Format == DXGI_FORMAT_R32G32_FLOAT\n     || Format == DXGI_FORMAT_R32G32_UINT\n     || Format == DXGI_FORMAT_R32G32_SINT\n     || Format == DXGI_FORMAT_R32G32B32_FLOAT\n     || Format == DXGI_FORMAT_R32G32B32_UINT\n     || Format == DXGI_FORMAT_R32G32B32_SINT\n     || Format == DXGI_FORMAT_R32G32B32A32_FLOAT\n     || Format == DXGI_FORMAT_R32G32B32A32_UINT\n     || Format == DXGI_FORMAT_R32G32B32A32_SINT)\n      flags1 |= D3D11_FORMAT_SUPPORT_SO_BUFFER;\n    \n    if (imgFeatures & (VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT)) {\n      const VkFormat depthFormat = LookupFormat(Format, DXGI_VK_FORMAT_MODE_DEPTH).Format;\n      \n      if (GetImageTypeSupport(fmtMapping.Format, VK_IMAGE_TYPE_1D, 0)) flags1 |= D3D11_FORMAT_SUPPORT_TEXTURE1D;\n      if (GetImageTypeSupport(fmtMapping.Format, VK_IMAGE_TYPE_2D, 0)) flags1 |= D3D11_FORMAT_SUPPORT_TEXTURE2D;\n      if (GetImageTypeSupport(fmtMapping.Format, VK_IMAGE_TYPE_3D, 0)) flags1 |= D3D11_FORMAT_SUPPORT_TEXTURE3D;\n\n      // We only support tiled resources with a single aspect\n      D3D11_TILED_RESOURCES_TIER tiledResourcesTier = m_deviceFeatures.GetTiledResourcesTier();\n      VkImageAspectFlags sparseAspects = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT;\n\n      if (tiledResourcesTier && !(fmtProperties->aspectMask & ~sparseAspects)) {\n        VkImageCreateFlags flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT\n                                 | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT\n                                 | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT;\n\n        if (GetImageTypeSupport(fmtMapping.Format, VK_IMAGE_TYPE_2D, flags))\n          flags2 |= D3D11_FORMAT_SUPPORT2_TILED;\n      }\n\n      flags1 |= D3D11_FORMAT_SUPPORT_MIP\n             |  D3D11_FORMAT_SUPPORT_CAST_WITHIN_BIT_LAYOUT;\n\n      // Format can be read \n      if (imgFeatures & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT) {\n        flags1 |= D3D11_FORMAT_SUPPORT_TEXTURECUBE\n               |  D3D11_FORMAT_SUPPORT_SHADER_LOAD\n               |  D3D11_FORMAT_SUPPORT_SHADER_GATHER\n               |  D3D11_FORMAT_SUPPORT_SHADER_SAMPLE\n               |  D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_INPUT;\n        \n        if (depthFormat != VK_FORMAT_UNDEFINED) {\n          flags1 |= D3D11_FORMAT_SUPPORT_SHADER_GATHER_COMPARISON\n                 |  D3D11_FORMAT_SUPPORT_SHADER_SAMPLE_COMPARISON;\n        }\n      }\n      \n      // Format is a color format that can be used for rendering\n      if (imgFeatures & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT) {\n        flags1 |= D3D11_FORMAT_SUPPORT_RENDER_TARGET\n               |  D3D11_FORMAT_SUPPORT_MIP_AUTOGEN\n               |  D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT;\n        \n        if (m_dxvkDevice->features().core.features.logicOp)\n          flags2 |= D3D11_FORMAT_SUPPORT2_OUTPUT_MERGER_LOGIC_OP;\n      }\n      \n      // Format supports blending when used for rendering\n      if (imgFeatures & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT)\n        flags1 |= D3D11_FORMAT_SUPPORT_BLENDABLE;\n      \n      // Format is a depth-stencil format that can be used for rendering\n      if (imgFeatures & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT)\n        flags1 |= D3D11_FORMAT_SUPPORT_DEPTH_STENCIL;\n      \n      // Report supported swap chain formats\n      if (Format == DXGI_FORMAT_R8G8B8A8_UNORM\n       || Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB\n       || Format == DXGI_FORMAT_B8G8R8A8_UNORM\n       || Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB\n       || Format == DXGI_FORMAT_R16G16B16A16_FLOAT\n       || Format == DXGI_FORMAT_R10G10B10A2_UNORM\n       || Format == DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM)\n        flags1 |= D3D11_FORMAT_SUPPORT_DISPLAY;\n      \n      // Query multisample support for this format\n      VkImageUsageFlags usage = (fmtProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT)\n        ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n        : VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n      DxvkFormatQuery formatQuery = { };\n      formatQuery.format = fmtMapping.Format;\n      formatQuery.type = VK_IMAGE_TYPE_2D;\n      formatQuery.tiling = VK_IMAGE_TILING_OPTIMAL;\n      formatQuery.usage = usage;\n\n      auto limits = m_dxvkDevice->getFormatLimits(formatQuery);\n\n      if (limits && limits->sampleCounts > VK_SAMPLE_COUNT_1_BIT) {\n        flags1 |= D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET\n               |  D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE\n               |  D3D11_FORMAT_SUPPORT_MULTISAMPLE_LOAD;\n      }\n\n      // Query whether the format is shareable\n      if ((fmtProperties->aspectMask & (VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_PLANE_0_BIT))\n       && (m_dxvkDevice->features().khrExternalMemoryWin32)) {\n        constexpr VkExternalMemoryFeatureFlags featureMask\n          = VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT\n          | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;\n\n        formatQuery.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;\n        limits = m_dxvkDevice->getFormatLimits(formatQuery);\n\n        if (limits && (limits->externalFeatures & featureMask))\n          flags2 |= D3D11_FORMAT_SUPPORT2_SHAREABLE;\n      }\n    }\n    \n    // Format can be used for storage images or storage texel buffers\n    if ((bufFeatures & VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT)\n     && (imgFeatures & VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT)\n     && (imgFeatures & VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT)) {\n      flags1 |= D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW;\n      flags2 |= D3D11_FORMAT_SUPPORT2_UAV_TYPED_STORE;\n      \n      if (!m_shaderOptions.flags.test(DxvkShaderCompileFlag::TypedR32LoadRequiresFormat)) {\n        // If the R32 formats are supported without format declarations,\n        // we can optionally support additional formats for typed loads\n        if (imgFeatures & VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT)\n          flags2 |= D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD;\n      } else {\n        // Otherwise, we need to emit format declarations, so we can\n        // only support the basic set of R32 formats for typed loads\n        if (Format == DXGI_FORMAT_R32_FLOAT\n         || Format == DXGI_FORMAT_R32_UINT\n         || Format == DXGI_FORMAT_R32_SINT)\n          flags2 |= D3D11_FORMAT_SUPPORT2_UAV_TYPED_LOAD;\n      }\n      \n      if (Format == DXGI_FORMAT_R32_UINT || Format == DXGI_FORMAT_R32_SINT) {\n        flags2 |= D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_ADD\n               |  D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_BITWISE_OPS\n               |  D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_COMPARE_STORE_OR_COMPARE_EXCHANGE\n               |  D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_EXCHANGE;\n      }\n      \n      if (Format == DXGI_FORMAT_R32_SINT)\n        flags2 |= D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_SIGNED_MIN_OR_MAX;\n      \n      if (Format == DXGI_FORMAT_R32_UINT)\n        flags2 |= D3D11_FORMAT_SUPPORT2_UAV_ATOMIC_UNSIGNED_MIN_OR_MAX;\n    }\n\n    // Mark everyting as CPU lockable\n    if (flags1 | flags2)\n      flags1 |= D3D11_FORMAT_SUPPORT_CPU_LOCKABLE;\n    \n    // Write back format support flags\n    if (pFlags1 != nullptr) *pFlags1 = flags1;\n    if (pFlags2 != nullptr) *pFlags2 = flags2;\n    return (pFlags1 && flags1) || (pFlags2 && flags2) ? S_OK : E_FAIL;\n  }\n  \n  \n  BOOL D3D11Device::GetImageTypeSupport(VkFormat Format, VkImageType Type, VkImageCreateFlags Flags) const {\n    DxvkFormatQuery formatQuery = { };\n    formatQuery.format = Format;\n    formatQuery.type = Type;\n    formatQuery.tiling = VK_IMAGE_TILING_OPTIMAL;\n    formatQuery.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    formatQuery.flags = Flags;\n\n    auto properties = m_dxvkDevice->getFormatLimits(formatQuery);\n\n    if (!properties) {\n      formatQuery.tiling = VK_IMAGE_TILING_LINEAR;\n      properties = m_dxvkDevice->getFormatLimits(formatQuery);\n    }\n\n    return properties.has_value();\n  }\n  \n  \n  uint32_t D3D11Device::GetViewPlaneIndex(\n          ID3D11Resource*         pResource,\n          DXGI_FORMAT             ViewFormat) {\n    auto texture = GetCommonTexture(pResource);\n\n    if (!texture)\n      return 0;\n\n    uint32_t planeCount = texture->GetPlaneCount();\n\n    if (planeCount == 1)\n      return 0;\n\n    auto formatMode   = texture->GetFormatMode();\n    auto formatFamily = LookupFamily(texture->Desc()->Format, formatMode);\n    auto viewFormat   = LookupFormat(ViewFormat, formatMode);\n\n    for (uint32_t i = 0; i < formatFamily.FormatCount; i++) {\n      if (formatFamily.Formats[i] == viewFormat.Format)\n        return i % planeCount;\n    }\n\n    return ~0u;\n  }\n\n\n  template<bool IsKmtHandle>\n  HRESULT D3D11Device::OpenSharedResourceGeneric(\n          HANDLE      hResource,\n          REFIID      ReturnedInterface,\n          void**      ppResource) {\n#ifdef _WIN32\n    HANDLE ntHandle = IsKmtHandle ? openKmtHandle(hResource) : hResource;\n\n    if (ntHandle == INVALID_HANDLE_VALUE) {\n      Logger::warn(str::format(\"D3D11Device::OpenSharedResourceGeneric: Handle not found: \", hResource));\n      return E_INVALIDARG;\n    }\n\n    DxvkSharedTextureMetadata metadata;\n    bool ret = getSharedMetadata(ntHandle, &metadata, sizeof(metadata), NULL);\n\n    if (IsKmtHandle)\n      ::CloseHandle(ntHandle);\n\n    if (!ret) {\n      Logger::warn(\"D3D11Device::OpenSharedResourceGeneric: Failed to get shared resource info for a texture\");\n      return E_INVALIDARG;\n    }\n\n    D3D11_COMMON_TEXTURE_DESC d3d11Desc;\n    d3d11Desc.Width          = metadata.Width;\n    d3d11Desc.Height         = metadata.Height;\n    d3d11Desc.Depth          = 1,\n    d3d11Desc.MipLevels      = metadata.MipLevels;\n    d3d11Desc.ArraySize      = metadata.ArraySize;\n    d3d11Desc.Format         = metadata.Format;\n    d3d11Desc.SampleDesc     = metadata.SampleDesc;\n    d3d11Desc.Usage          = metadata.Usage;\n    d3d11Desc.BindFlags      = metadata.BindFlags;\n    d3d11Desc.CPUAccessFlags = metadata.CPUAccessFlags;\n    d3d11Desc.MiscFlags      = metadata.MiscFlags;\n    d3d11Desc.TextureLayout  = metadata.TextureLayout;\n    if ((d3d11Desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE) && !(d3d11Desc.MiscFlags & (D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX))) {\n      Logger::warn(\"Fixing up wrong MiscFlags\");\n      d3d11Desc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;\n    }\n\n    // Only 2D textures may be shared\n    try {\n      const Com<D3D11Texture2D> texture = new D3D11Texture2D(this, &d3d11Desc, nullptr, hResource);\n      texture->QueryInterface(ReturnedInterface, ppResource);\n      return S_OK;\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n#else\n    Logger::warn(\"D3D11Device::OpenSharedResourceGeneric: Not supported on this platform.\");\n    return E_INVALIDARG;\n#endif\n  }\n\n\n  template<typename Void>\n  void D3D11Device::CopySubresourceData(\n          Void*                       pData,\n          UINT                        RowPitch,\n          UINT                        DepthPitch,\n          D3D11CommonTexture*         pTexture,\n          UINT                        Subresource,\n    const D3D11_BOX*                  pBox) {\n    // Validate box against subresource dimensions\n    auto formatInfo = lookupFormatInfo(pTexture->GetPackedFormat());\n    auto subresource = pTexture->GetSubresourceFromIndex(\n      formatInfo->aspectMask, Subresource);\n\n    VkOffset3D offset = { 0, 0, 0 };\n    VkExtent3D extent = pTexture->MipLevelExtent(subresource.mipLevel);\n\n    if (pBox) {\n      if (pBox->left >= pBox->right\n       || pBox->top >= pBox->bottom\n       || pBox->front >= pBox->back)\n        return;  // legal, but no-op\n      \n      if (pBox->right > extent.width\n       || pBox->bottom > extent.height\n       || pBox->back > extent.depth)\n        return;  // out of bounds\n      \n      offset = VkOffset3D {\n        int32_t(pBox->left),\n        int32_t(pBox->top),\n        int32_t(pBox->front) };\n\n      extent = VkExtent3D {\n        pBox->right - pBox->left,\n        pBox->bottom - pBox->top,\n        pBox->back - pBox->front };\n    }\n\n    // Copy image data, one plane at a time for multi-plane formats\n    Rc<DxvkImage> image = pTexture->GetImage();\n    VkDeviceSize dataOffset = 0;\n\n    for (uint32_t i = 0; i < pTexture->GetPlaneCount(); i++) {\n      // Find current image aspects to process\n      VkImageAspectFlags aspect = formatInfo->aspectMask;\n\n      if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane))\n        aspect = vk::getPlaneAspect(i);\n\n      // Compute data layout of the current subresource\n      D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT layout = pTexture->GetSubresourceLayout(aspect, Subresource);\n\n      // Compute actual map pointer, accounting for the region offset\n      void* mapPtr = pTexture->GetMapPtr(Subresource, pTexture->ComputeMappedOffset(Subresource, i, offset));\n\n      if constexpr (std::is_const<Void>::value) {\n        // WriteToSubresource\n        auto srcData = reinterpret_cast<const char*>(pData) + dataOffset;\n\n        util::packImageData(mapPtr, srcData, RowPitch, DepthPitch,\n          layout.RowPitch, layout.DepthPitch, image->info().type,\n          extent, 1, formatInfo, aspect);\n      } else {\n        // ReadFromSubresource\n        auto dstData = reinterpret_cast<char*>(pData) + dataOffset;\n\n        util::packImageData(dstData, mapPtr,\n          layout.RowPitch, layout.DepthPitch,\n          RowPitch, DepthPitch, image->info().type,\n          extent, 1, formatInfo, aspect);\n      }\n\n      // Advance linear data pointer by the size of the current aspect\n      dataOffset += util::computeImageDataSize(\n        pTexture->GetPackedFormat(), extent, aspect);\n    }\n\n    // Track dirty texture region if necessary\n    if constexpr (std::is_const<Void>::value)\n      pTexture->AddDirtyRegion(Subresource, offset, extent);\n  }\n\n\n  bool D3D11Device::LockImage(\n    const Rc<DxvkImage>&            Image,\n          VkImageUsageFlags         Usage) {\n    bool feedback = false;\n\n    auto chunk = AllocCsChunk(DxvkCsChunkFlag::SingleUse);\n\n    chunk->push([\n      cImage  = Image,\n      cUsage  = Usage,\n      &feedback\n    ] (DxvkContext* ctx) {\n      DxvkImageUsageInfo usageInfo;\n      usageInfo.usage = cUsage;\n      usageInfo.stableGpuAddress = VK_TRUE;\n\n      feedback = ctx->ensureImageCompatibility(cImage, usageInfo);\n    });\n\n    m_context->InjectCsChunk(DxvkCsQueue::HighPriority, std::move(chunk), true);\n\n    if (!feedback) {\n      Logger::err(str::format(\"Failed to lock image:\"\n        \"\\n  Image format:  \", Image->info().format,\n        \"\\n  Image usage:   \", std::hex, Image->info().usage,\n        \"\\n  Desired usage: \", std::hex, Usage));\n    }\n\n    return feedback;\n  }\n\n\n  DxvkShaderOptions D3D11Device::GetShaderOptions(\n    const Rc<DxvkDevice>&             Device,\n    const D3D11Options&               Options) {\n    auto result = Device->getShaderCompileOptions();\n\n    if (Options.disableMsaa)\n      result.flags.set(DxvkShaderCompileFlag::DisableMsaa);\n\n    if (Options.forceComputeLdsBarriers)\n      result.flags.set(DxvkShaderCompileFlag::InsertSharedMemoryBarriers);\n\n    if (Options.forceComputeUavBarriers)\n      result.flags.set(DxvkShaderCompileFlag::InsertResourceBarriers);\n\n    if (Options.forceSampleRateShading)\n      result.flags.set(DxvkShaderCompileFlag::EnableSampleRateShading);\n\n    return result;\n  }\n\n\n  bool D3D11Device::ConvertRuntimeDescriptor(\n       UINT                       size,\n       const union d3dkmt_desc&   d3dkmt,\n       D3D11_COMMON_TEXTURE_DESC* desc) {\n\n    if (size == sizeof(d3dkmt.d3d12) && d3dkmt.d3d12.d3d11.dxgi.size == sizeof(d3dkmt.d3d12.d3d11) && d3dkmt.d3d12.d3d11.dxgi.version == 0) {\n      Logger::warn(str::format(\"D3D11Device::ConvertRuntimeDescriptor: D3D12 descriptor conversion not implemented\"));\n      return false;\n    }\n\n    if (size >= sizeof(d3dkmt.d3d11) && d3dkmt.dxgi.size == sizeof(d3dkmt.d3d11) && d3dkmt.dxgi.version == 4) {\n      Logger::debug(str::format(\"D3D11Device::ConvertRuntimeDescriptor: Found D3D11 desc with dimension: \", d3dkmt.d3d11.dimension));\n\n      switch (d3dkmt.d3d11.dimension) {\n        case D3D11_RESOURCE_DIMENSION_TEXTURE2D:\n          desc->Width = d3dkmt.d3d11.d3d11_2d.Width;\n          desc->Height = d3dkmt.d3d11.d3d11_2d.Height;\n          desc->Depth = 1;\n          desc->MipLevels = d3dkmt.d3d11.d3d11_2d.MipLevels;\n          desc->ArraySize = d3dkmt.d3d11.d3d11_2d.ArraySize;\n          desc->Format = d3dkmt.d3d11.d3d11_2d.Format;\n          desc->SampleDesc = d3dkmt.d3d11.d3d11_2d.SampleDesc;\n          desc->Usage = d3dkmt.d3d11.d3d11_2d.Usage;\n          desc->BindFlags = d3dkmt.d3d11.d3d11_2d.BindFlags;\n          desc->CPUAccessFlags = d3dkmt.d3d11.d3d11_2d.CPUAccessFlags;\n          desc->MiscFlags = d3dkmt.d3d11.d3d11_2d.MiscFlags;\n          desc->TextureLayout = D3D11_TEXTURE_LAYOUT_UNDEFINED;\n          break;\n        default:\n          Logger::warn(str::format(\"D3D11Device::ConvertRuntimeDescriptor: Unsupported dimension: \", d3dkmt.d3d11.dimension));\n          return false;\n      }\n\n      Logger::debug(str::format(\"D3D11Device::ConvertRuntimeDescriptor: Translated D3D11 desc:\"));\n      Logger::debug(str::format(\"  Width: \", desc->Width));\n      Logger::debug(str::format(\"  Height: \", desc->Height));\n      Logger::debug(str::format(\"  Depth: \", desc->Depth));\n      Logger::debug(str::format(\"  MipLevels: \", desc->MipLevels));\n      Logger::debug(str::format(\"  ArraySize: \", desc->ArraySize));\n      Logger::debug(str::format(\"  Format: \", desc->Format));\n      Logger::debug(str::format(\"  SampleDesc.Count: \", desc->SampleDesc.Count));\n      Logger::debug(str::format(\"  SampleDesc.Quality: \", desc->SampleDesc.Quality));\n      Logger::debug(str::format(\"  Usage: \", desc->Usage));\n      Logger::debug(str::format(\"  BindFlags: \", desc->BindFlags));\n      Logger::debug(str::format(\"  CPUAccessFlags: \", desc->CPUAccessFlags));\n      Logger::debug(str::format(\"  MiscFlags: \", desc->MiscFlags));\n      Logger::debug(str::format(\"  TextureLayout: \", desc->TextureLayout));\n      return true;\n    }\n\n    if (size >= sizeof(d3dkmt.d3d9) && d3dkmt.dxgi.size == sizeof(d3dkmt.d3d9) && d3dkmt.dxgi.version == 1) {\n      Logger::debug(str::format(\"D3D11Device::ConvertRuntimeDescriptor: Found D3D9 desc: \", d3dkmt.d3d9.type));\n      Logger::debug(str::format(\"  dxgi.width: \", d3dkmt.d3d9.dxgi.width));\n      Logger::debug(str::format(\"  dxgi.height: \", d3dkmt.d3d9.dxgi.height));\n      Logger::debug(str::format(\"  format: \", d3dkmt.d3d9.format));\n      Logger::debug(str::format(\"  usage: \", d3dkmt.d3d9.usage));\n      if (d3dkmt.d3d9.type == D3DRTYPE_TEXTURE) {\n        Logger::debug(str::format(\"  texture.width: \", d3dkmt.d3d9.texture.width));\n        Logger::debug(str::format(\"  texture.height: \", d3dkmt.d3d9.texture.height));\n        Logger::debug(str::format(\"  texture.depth: \", d3dkmt.d3d9.texture.depth));\n        Logger::debug(str::format(\"  texture.levels: \", d3dkmt.d3d9.texture.levels));\n      } else if (d3dkmt.d3d9.type == D3DRTYPE_SURFACE) {\n        Logger::debug(str::format(\"  surface.width: \", d3dkmt.d3d9.surface.width));\n        Logger::debug(str::format(\"  surface.height: \", d3dkmt.d3d9.surface.height));\n      } else {\n        Logger::warn(str::format(\"D3D11Device::ConvertRuntimeDescriptor: Unsupported D3D9 type: \", d3dkmt.d3d9.type));\n        return false;\n      }\n\n      desc->Width = d3dkmt.d3d9.dxgi.width;\n      desc->Height = d3dkmt.d3d9.dxgi.height;\n      desc->Depth = 1;\n      desc->MipLevels = 1;\n      desc->ArraySize = 1;\n      desc->Format = d3dkmt.d3d9.dxgi.format;\n      desc->SampleDesc.Count = 1;\n      desc->SampleDesc.Quality = 0;\n      desc->Usage = D3D11_USAGE_DEFAULT;\n      desc->BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;\n      desc->CPUAccessFlags = 0;\n      desc->MiscFlags = D3D11_RESOURCE_MISC_SHARED;\n      desc->TextureLayout = D3D11_TEXTURE_LAYOUT_UNDEFINED;\n\n      switch (d3dkmt.d3d9.type) {\n        case D3DRTYPE_TEXTURE:\n          desc->Width = d3dkmt.d3d9.texture.width;\n          desc->Height = d3dkmt.d3d9.texture.height;\n          desc->MipLevels = d3dkmt.d3d9.texture.levels;\n          desc->ArraySize = d3dkmt.d3d9.texture.depth ? d3dkmt.d3d9.texture.depth : 1;\n          break;\n        case D3DRTYPE_SURFACE:\n          desc->Width = d3dkmt.d3d9.surface.width;\n          desc->Height = d3dkmt.d3d9.surface.height;\n          break;\n        default:\n          break;\n      }\n\n      Logger::debug(str::format(\"D3D11Device::ConvertRuntimeDescriptor: Translated D3D9 desc:\"));\n      Logger::debug(str::format(\"  Width: \", desc->Width));\n      Logger::debug(str::format(\"  Height: \", desc->Height));\n      Logger::debug(str::format(\"  Depth: \", desc->Depth));\n      Logger::debug(str::format(\"  MipLevels: \", desc->MipLevels));\n      Logger::debug(str::format(\"  ArraySize: \", desc->ArraySize));\n      Logger::debug(str::format(\"  Format: \", desc->Format));\n      Logger::debug(str::format(\"  SampleDesc.Count: \", desc->SampleDesc.Count));\n      Logger::debug(str::format(\"  SampleDesc.Quality: \", desc->SampleDesc.Quality));\n      Logger::debug(str::format(\"  Usage: \", desc->Usage));\n      Logger::debug(str::format(\"  BindFlags: \", desc->BindFlags));\n      Logger::debug(str::format(\"  CPUAccessFlags: \", desc->CPUAccessFlags));\n      Logger::debug(str::format(\"  MiscFlags: \", desc->MiscFlags));\n      Logger::debug(str::format(\"  TextureLayout: \", desc->TextureLayout));\n      return true;\n    }\n\n    Logger::warn(str::format(\"D3D11Device::ConvertRuntimeDescriptor: Unsupported runtime desc size: \",\n                             size, \"/\", d3dkmt.dxgi.size, \" version: \", d3dkmt.dxgi.version));\n    return false;\n  }\n\n\n\n  D3D11DeviceExt::D3D11DeviceExt(\n          D3D11DXGIDevice*        pContainer,\n          D3D11Device*            pDevice)\n  : m_container(pContainer), m_device(pDevice) {\n    \n  }\n  \n  \n  ULONG STDMETHODCALLTYPE D3D11DeviceExt::AddRef() {\n    return m_container->AddRef();\n  }\n  \n  \n  ULONG STDMETHODCALLTYPE D3D11DeviceExt::Release() {\n    return m_container->Release();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DeviceExt::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_container->QueryInterface(riid, ppvObject);\n  }\n  \n  \n  BOOL STDMETHODCALLTYPE D3D11DeviceExt::GetExtensionSupport(\n          D3D11_VK_EXTENSION      Extension) {\n    const auto& deviceFeatures = m_device->GetDXVKDevice()->features();\n    \n    switch (Extension) {\n      case D3D11_VK_EXT_BARRIER_CONTROL:\n        return true;\n      \n      case D3D11_VK_EXT_MULTI_DRAW_INDIRECT:\n        return deviceFeatures.core.features.multiDrawIndirect;\n        \n      case D3D11_VK_EXT_MULTI_DRAW_INDIRECT_COUNT:\n        return deviceFeatures.core.features.multiDrawIndirect\n            && deviceFeatures.vk12.drawIndirectCount;\n      \n      case D3D11_VK_EXT_DEPTH_BOUNDS:\n        return deviceFeatures.core.features.depthBounds;\n\n      case D3D11_VK_NVX_IMAGE_VIEW_HANDLE:\n        return deviceFeatures.nvxImageViewHandle;\n\n      case D3D11_VK_NVX_BINARY_IMPORT:\n        return deviceFeatures.nvxBinaryImport;\n\n      default:\n        return false;\n    }\n  }\n  \n  \n  bool STDMETHODCALLTYPE D3D11DeviceExt::GetCudaTextureObjectNVX(uint32_t srvDriverHandle, uint32_t samplerDriverHandle, uint32_t* pCudaTextureHandle) {\n    ID3D11ShaderResourceView* srv = HandleToSrvNVX(srvDriverHandle);\n\n    if (!srv) {\n      Logger::warn(str::format(\"GetCudaTextureObjectNVX() failure - srv handle wasn't found: \", srvDriverHandle));\n      return false;\n    }\n\n    ID3D11SamplerState* samplerState = HandleToSamplerNVX(samplerDriverHandle);\n\n    if (!samplerState) {\n      Logger::warn(str::format(\"GetCudaTextureObjectNVX() failure - sampler handle wasn't found: \", samplerDriverHandle));\n      return false;\n    }\n\n    D3D11SamplerState* pSS = static_cast<D3D11SamplerState*>(samplerState);\n    Rc<DxvkSampler> pDSS = pSS->GetDXVKSampler();\n\n    D3D11ShaderResourceView* pSRV = static_cast<D3D11ShaderResourceView*>(srv);\n    Rc<DxvkImageView> pIV = pSRV->GetImageView();\n\n    LockImage(pIV->image(), 0u);\n\n    VkImageViewHandleInfoNVX imageViewHandleInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_HANDLE_INFO_NVX };\n    imageViewHandleInfo.imageView = pIV->handle();\n    imageViewHandleInfo.sampler = pDSS->getDescriptor().samplerObject;\n    imageViewHandleInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n\n    // note: there's no implicit lifetime management here; it's up to the\n    // app to keep the sampler and SRV alive as long as it wants to use this\n    // derived handle.\n    VkDevice vkDevice = m_device->GetDXVKDevice()->handle();\n    *pCudaTextureHandle = m_device->GetDXVKDevice()->vkd()->vkGetImageViewHandleNVX(vkDevice, &imageViewHandleInfo);\n\n    if (!*pCudaTextureHandle) {\n      Logger::warn(\"GetCudaTextureObjectNVX() handle==0 - failed\");\n      return false;\n    }\n\n    return true;\n  }\n  \n\n  bool STDMETHODCALLTYPE D3D11DeviceExt::CreateCubinComputeShaderWithNameNVX(const void* pCubin, uint32_t size,\n      uint32_t blockX, uint32_t blockY, uint32_t blockZ, const char* pShaderName, IUnknown** phShader) {\n    Rc<DxvkDevice> dxvkDevice = m_device->GetDXVKDevice();\n    VkDevice vkDevice = dxvkDevice->handle();\n\n    VkCuModuleCreateInfoNVX moduleCreateInfo = { VK_STRUCTURE_TYPE_CU_MODULE_CREATE_INFO_NVX };\n    moduleCreateInfo.pData = pCubin;\n    moduleCreateInfo.dataSize = size;\n\n    VkCuModuleNVX cuModule;\n    VkCuFunctionNVX cuFunction;\n    VkResult result;\n\n    if ((result = dxvkDevice->vkd()->vkCreateCuModuleNVX(vkDevice, &moduleCreateInfo, nullptr, &cuModule))) {\n      Logger::warn(str::format(\"CreateCubinComputeShaderWithNameNVX() - failure to create module - result=\", result, \" pcubindata=\", pCubin, \" cubinsize=\", size));\n      return false; // failure\n    }\n\n    VkCuFunctionCreateInfoNVX functionCreateInfo = { VK_STRUCTURE_TYPE_CU_FUNCTION_CREATE_INFO_NVX };\n    functionCreateInfo.module = cuModule;\n    functionCreateInfo.pName = pShaderName;\n\n    if ((result = dxvkDevice->vkd()->vkCreateCuFunctionNVX(vkDevice, &functionCreateInfo, nullptr, &cuFunction))) {\n      dxvkDevice->vkd()->vkDestroyCuModuleNVX(vkDevice, cuModule, nullptr);\n      Logger::warn(str::format(\"CreateCubinComputeShaderWithNameNVX() - failure to create function - result=\", result));\n      return false;\n    }\n\n    *phShader = ref(new CubinShaderWrapper(dxvkDevice,\n      cuModule, cuFunction, { blockX, blockY, blockZ }));\n    return true;\n  }\n\n\n  bool STDMETHODCALLTYPE D3D11DeviceExt::GetResourceHandleGPUVirtualAddressAndSizeNVX(void* hObject, uint64_t* gpuVAStart, uint64_t* gpuVASize) {\n    // The hObject 'opaque driver handle' is really just a straight cast\n    // of the corresponding ID3D11Resource* in dxvk/dxvknvapi\n    ID3D11Resource* pResource = static_cast<ID3D11Resource*>(hObject);\n\n    D3D11_COMMON_RESOURCE_DESC resourceDesc;\n\n    if (FAILED(GetCommonResourceDesc(pResource, &resourceDesc))) {\n      Logger::warn(\"GetResourceHandleGPUVirtualAddressAndSize: Invalid resource\");\n      return false;\n    }\n\n    Rc<DxvkDevice> dxvkDevice = m_device->GetDXVKDevice();\n    VkDevice vkDevice = dxvkDevice->handle();\n\n    if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_TEXTURE2D) {\n      D3D11CommonTexture *texture = GetCommonTexture(pResource);\n\n      // Ensure that the image has a stable GPU address and\n      // won't be relocated by the backend going forward\n      Rc<DxvkImage> dxvkImage = texture->GetImage();\n\n      if (!LockImage(dxvkImage, VK_IMAGE_USAGE_SAMPLED_BIT))\n        return false;\n\n      // The d3d11 nvapi provides us a texture, but vulkan only lets us\n      // get the GPU address from an image view. So, make a private image\n      // view and get the address from that.\n      DxvkImageViewKey viewInfo;\n      viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n      viewInfo.format = dxvkImage->info().format;\n      viewInfo.aspects = dxvkImage->formatInfo()->aspectMask;\n      viewInfo.mipIndex = 0;\n      viewInfo.mipCount = dxvkImage->info().mipLevels;\n      viewInfo.layerIndex = 0;\n      viewInfo.layerCount = 1;\n      viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n\n      auto dxvkView = dxvkImage->createView(viewInfo);\n      VkImageViewAddressPropertiesNVX imageViewAddressProperties = { VK_STRUCTURE_TYPE_IMAGE_VIEW_ADDRESS_PROPERTIES_NVX };\n\n      VkResult vr = dxvkDevice->vkd()->vkGetImageViewAddressNVX(vkDevice,\n        dxvkView->handle(), &imageViewAddressProperties);\n\n      if (vr != VK_SUCCESS) {\n        Logger::warn(str::format(\"GetResourceHandleGPUVirtualAddressAndSize(): Failed: vr = \", vr));\n        return false;\n      }\n\n      *gpuVAStart = imageViewAddressProperties.deviceAddress;\n      *gpuVASize = imageViewAddressProperties.size;\n    } else if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      Rc<DxvkBuffer> dxvkBuffer = GetCommonBuffer(pResource)->GetBuffer();\n      LockBuffer(dxvkBuffer);\n\n      *gpuVAStart = dxvkBuffer->getSliceInfo().gpuAddress;\n      *gpuVASize = dxvkBuffer->info().size;\n    } else {\n      Logger::warn(str::format(\"GetResourceHandleGPUVirtualAddressAndSize(): Unsupported resource type: \", resourceDesc.Dim));\n      return false;\n    }\n\n    if (!*gpuVAStart)\n        Logger::warn(\"GetResourceHandleGPUVirtualAddressAndSize() addr==0 - unexpected\"); // ... but not explicitly a failure; continue\n\n    return true;\n  }\n\n\n  bool STDMETHODCALLTYPE D3D11DeviceExt::CreateUnorderedAccessViewAndGetDriverHandleNVX(\n          ID3D11Resource*                     pResource,\n    const D3D11_UNORDERED_ACCESS_VIEW_DESC*   pDesc,\n          ID3D11UnorderedAccessView**         ppUAV,\n          uint32_t*                           pDriverHandle) {\n    D3D11_COMMON_RESOURCE_DESC resourceDesc = { };\n    GetCommonResourceDesc(pResource, &resourceDesc);\n\n    if (resourceDesc.Dim != D3D11_RESOURCE_DIMENSION_TEXTURE2D) {\n      Logger::warn(str::format(\"CreateUnorderedAccessViewAndGetDriverHandleNVX(): Unsupported dimension: \", resourceDesc.Dim));\n      return false;\n    }\n\n    Rc<DxvkImage> dxvkImage = GetCommonTexture(pResource)->GetImage();\n\n    if (!(dxvkImage->info().usage & VK_IMAGE_USAGE_STORAGE_BIT)) {\n      Logger::warn(str::format(\"CreateUnorderedAccessViewAndGetDriverHandleNVX(res=\", pResource, \"): Image not UAV compatible\"));\n      return false;\n    }\n\n    Com<ID3D11UnorderedAccessView> uav;\n\n    if (FAILED(m_device->CreateUnorderedAccessView(pResource, pDesc, &uav)))\n      return false;\n\n    Rc<DxvkImageView> dxvkImageView = static_cast<D3D11UnorderedAccessView*>(uav.ptr())->GetImageView();\n    LockImage(dxvkImageView->image(), 0u);\n\n    VkImageViewHandleInfoNVX imageViewHandleInfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_HANDLE_INFO_NVX};\n    imageViewHandleInfo.imageView = dxvkImageView->handle();\n    imageViewHandleInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n\n    Rc<DxvkDevice> dxvkDevice = m_device->GetDXVKDevice();\n    *pDriverHandle = dxvkDevice->vkd()->vkGetImageViewHandleNVX(\n      dxvkDevice->handle(), &imageViewHandleInfo);\n\n    if (!*pDriverHandle) {\n      Logger::warn(\"CreateUnorderedAccessViewAndGetDriverHandleNVX(): Handle is 0\");\n      return false;\n    }\n\n    *ppUAV = uav.ref();\n    return true;\n  }\n\n\n  bool STDMETHODCALLTYPE D3D11DeviceExt::CreateShaderResourceViewAndGetDriverHandleNVX(ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC*  pDesc, ID3D11ShaderResourceView** ppSRV, uint32_t* pDriverHandle) {\n    D3D11_COMMON_RESOURCE_DESC resourceDesc = { };\n    GetCommonResourceDesc(pResource, &resourceDesc);\n\n    if (resourceDesc.Dim != D3D11_RESOURCE_DIMENSION_TEXTURE2D) {\n      Logger::warn(str::format(\"CreateShaderResourceViewAndGetDriverHandleNVX(): Unsupported dimension: \", resourceDesc.Dim));\n      return false;\n    }\n\n    Rc<DxvkImage> dxvkImage = GetCommonTexture(pResource)->GetImage();\n\n    if (!(dxvkImage->info().usage & VK_IMAGE_USAGE_SAMPLED_BIT)) {\n      Logger::warn(str::format(\"CreateShaderResourceViewAndGetDriverHandleNVX(res=\", pResource, \"): Image not SRV compatible\"));\n      return false;\n    }\n\n    Com<ID3D11ShaderResourceView> srv;\n\n    if (FAILED(m_device->CreateShaderResourceView(pResource, pDesc, &srv)))\n      return false;\n\n    Rc<DxvkImageView> dxvkImageView = static_cast<D3D11ShaderResourceView*>(srv.ptr())->GetImageView();\n    LockImage(dxvkImageView->image(), 0u);\n\n    VkImageViewHandleInfoNVX imageViewHandleInfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_HANDLE_INFO_NVX};\n    imageViewHandleInfo.imageView = dxvkImageView->handle();\n    imageViewHandleInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\n    Rc<DxvkDevice> dxvkDevice = m_device->GetDXVKDevice();\n    *pDriverHandle = dxvkDevice->vkd()->vkGetImageViewHandleNVX(\n      dxvkDevice->handle(), &imageViewHandleInfo);\n\n    if (!*pDriverHandle) {\n      Logger::warn(\"CreateShaderResourceViewAndGetDriverHandleNVX(): Handle is 0\");\n      return false;\n    }\n\n    // will need to look-up resource from uint32 handle later\n    *ppSRV = srv.ref();\n    AddSrvAndHandleNVX(srv.ptr(), *pDriverHandle);\n    return true;\n  }\n\n\n  bool STDMETHODCALLTYPE D3D11DeviceExt::CreateSamplerStateAndGetDriverHandleNVX(const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState, uint32_t* pDriverHandle) {\n    if (FAILED(m_device->CreateSamplerState(pSamplerDesc, ppSamplerState)))\n      return false;\n\n    // for our purposes the actual value doesn't matter, only its uniqueness\n    static std::atomic<ULONG> s_seqNum = 0;\n    *pDriverHandle = ++s_seqNum;\n\n    // will need to look-up sampler from uint32 handle later\n    AddSamplerAndHandleNVX(*ppSamplerState, *pDriverHandle);\n    return true;\n  }\n\n\n  void D3D11DeviceExt::AddSamplerAndHandleNVX(ID3D11SamplerState* pSampler, uint32_t Handle) {\n    std::lock_guard lock(m_mapLock);\n    m_samplerHandleToPtr[Handle] = pSampler;\n  }\n\n\n  ID3D11SamplerState* D3D11DeviceExt::HandleToSamplerNVX(uint32_t Handle) {\n    std::lock_guard lock(m_mapLock);\n    auto got = m_samplerHandleToPtr.find(Handle);\n\n    if (got == m_samplerHandleToPtr.end())\n      return nullptr;\n\n    return static_cast<ID3D11SamplerState*>(got->second);\n  }\n\n\n  void D3D11DeviceExt::AddSrvAndHandleNVX(ID3D11ShaderResourceView* pSrv, uint32_t Handle) {\n    std::lock_guard lock(m_mapLock);\n    m_srvHandleToPtr[Handle] = pSrv;\n  }\n\n\n  ID3D11ShaderResourceView* D3D11DeviceExt::HandleToSrvNVX(uint32_t Handle) {\n    std::lock_guard lock(m_mapLock);\n    auto got = m_srvHandleToPtr.find(Handle);\n\n    if (got == m_srvHandleToPtr.end())\n      return nullptr;\n\n    return static_cast<ID3D11ShaderResourceView*>(got->second);\n  }\n\n\n  bool D3D11DeviceExt::LockImage(\n    const Rc<DxvkImage>&            Image,\n          VkImageUsageFlags         Usage) {\n    if (!Image->canRelocate() && (Image->info().usage & Usage))\n      return true;\n\n    return m_device->LockImage(Image, Usage);\n  }\n\n\n  void D3D11DeviceExt::LockBuffer(\n    const Rc<DxvkBuffer>&           Buffer) {\n    if (!Buffer->canRelocate())\n      return;\n\n    auto chunk = m_device->AllocCsChunk(DxvkCsChunkFlag::SingleUse);\n\n    chunk->push([cBuffer = Buffer] (DxvkContext* ctx) {\n      ctx->ensureBufferAddress(cBuffer);\n    });\n\n    m_device->GetContext()->InjectCsChunk(DxvkCsQueue::HighPriority, std::move(chunk), true);\n  }\n\n\n\n  \n  D3D11VideoDevice::D3D11VideoDevice(\n          D3D11DXGIDevice*        pContainer,\n          D3D11Device*            pDevice)\n  : m_container(pContainer), m_device(pDevice) {\n\n  }\n\n\n  D3D11VideoDevice::~D3D11VideoDevice() {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11VideoDevice::AddRef() {\n    return m_container->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11VideoDevice::Release() {\n    return m_container->Release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_container->QueryInterface(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoDecoder(\n    const D3D11_VIDEO_DECODER_DESC*                     pVideoDesc,\n    const D3D11_VIDEO_DECODER_CONFIG*                   pConfig,\n          ID3D11VideoDecoder**                          ppDecoder) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoDevice::CreateVideoDecoder: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoProcessor(\n          ID3D11VideoProcessorEnumerator*               pEnum,\n          UINT                                          RateConversionIndex,\n          ID3D11VideoProcessor**                        ppVideoProcessor) {\n    try {\n      auto enumerator = static_cast<D3D11VideoProcessorEnumerator*>(pEnum);\n      *ppVideoProcessor = ref(new D3D11VideoProcessor(m_device, enumerator, RateConversionIndex));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_FAIL;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateAuthenticatedChannel(\n          D3D11_AUTHENTICATED_CHANNEL_TYPE              ChannelType,\n          ID3D11AuthenticatedChannel**                  ppAuthenticatedChannel) {\n    Logger::warn(\"D3D11VideoDevice::CreateAuthenticatedChannel: Stub\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateCryptoSession(\n    const GUID*                                         pCryptoType,\n    const GUID*                                         pDecoderProfile,\n    const GUID*                                         pKeyExchangeType,\n          ID3D11CryptoSession**                         ppCryptoSession) {\n    Logger::warn(\"D3D11VideoDevice::CreateCryptoSession: Stub\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoDecoderOutputView(\n          ID3D11Resource*                               pResource,\n    const D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC*         pDesc,\n          ID3D11VideoDecoderOutputView**                ppVDOVView) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoDevice::CreateVideoDecoderOutputView: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoProcessorInputView(\n          ID3D11Resource*                               pResource,\n          ID3D11VideoProcessorEnumerator*               pEnum,\n    const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC*        pDesc,\n          ID3D11VideoProcessorInputView**               ppVPIView) {\n    try {\n      *ppVPIView = ref(new D3D11VideoProcessorInputView(m_device, pResource, *pDesc));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_FAIL;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoProcessorOutputView(\n          ID3D11Resource*                               pResource,\n          ID3D11VideoProcessorEnumerator*               pEnum,\n    const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC*       pDesc,\n          ID3D11VideoProcessorOutputView**              ppVPOView) {\n    try {\n      *ppVPOView = ref(new D3D11VideoProcessorOutputView(m_device, pResource, *pDesc));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_FAIL;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CreateVideoProcessorEnumerator(\n    const D3D11_VIDEO_PROCESSOR_CONTENT_DESC*           pDesc,\n          ID3D11VideoProcessorEnumerator**              ppEnum)  {\n    try {\n      *ppEnum = ref(new D3D11VideoProcessorEnumerator(m_device, *pDesc));\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_FAIL;\n    }\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D11VideoDevice::GetVideoDecoderProfileCount() {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoDevice::GetVideoDecoderProfileCount: Stub\");\n\n    return 0;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::GetVideoDecoderProfile(\n          UINT                                          Index,\n          GUID*                                         pDecoderProfile) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoDevice::GetVideoDecoderProfile: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CheckVideoDecoderFormat(\n    const GUID*                                         pDecoderProfile,\n          DXGI_FORMAT                                   Format,\n          BOOL*                                         pSupported) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoDevice::CheckVideoDecoderFormat: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::GetVideoDecoderConfigCount(\n    const D3D11_VIDEO_DECODER_DESC*                     pDesc,\n          UINT*                                         pCount) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoDevice::GetVideoDecoderConfigCount: Stub\");\n\n    if (!pCount)\n      return E_INVALIDARG;\n\n    *pCount = 0;\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::GetVideoDecoderConfig(\n    const D3D11_VIDEO_DECODER_DESC*                     pDesc,\n          UINT                                          Index,\n          D3D11_VIDEO_DECODER_CONFIG*                   pConfig) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoDevice::GetVideoDecoderConfig: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::GetContentProtectionCaps(\n    const GUID*                                         pCryptoType,\n    const GUID*                                         pDecoderProfile,\n          D3D11_VIDEO_CONTENT_PROTECTION_CAPS*          pCaps) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoDevice::GetContentProtectionCaps: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::CheckCryptoKeyExchange(\n    const GUID*                                         pCryptoType,\n    const GUID*                                         pDecoderProfile,\n          UINT                                          Index,\n          GUID*                                         pKeyExchangeType) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoDevice::CheckCryptoKeyExchange: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::SetPrivateData(\n          REFGUID                                       Name,\n          UINT                                          DataSize,\n    const void*                                         pData) {\n    return m_container->SetPrivateData(Name, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoDevice::SetPrivateDataInterface(\n          REFGUID                                       Name,\n    const IUnknown*                                     pData) {\n    return m_container->SetPrivateDataInterface(Name, pData);\n  }\n\n\n  D3D11ReflexDevice::D3D11ReflexDevice(\n          D3D11DXGIDevice*        pContainer,\n          D3D11Device*            pDevice)\n  : m_container(pContainer), m_device(pDevice) {\n    auto dxvkDevice = pDevice->GetDXVKDevice();\n\n    m_reflexEnabled = dxvkDevice->features().nvLowLatency2\n                   && dxvkDevice->config().latencySleep == Tristate::Auto;\n  }\n\n\n  D3D11ReflexDevice::~D3D11ReflexDevice() {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11ReflexDevice::AddRef() {\n    return m_container->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11ReflexDevice::Release() {\n    return m_container->Release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::QueryInterface(\n          REFIID                        riid,\n          void**                        ppvObject) {\n    return m_container->QueryInterface(riid, ppvObject);\n  }\n\n\n  BOOL STDMETHODCALLTYPE D3D11ReflexDevice::SupportsLowLatency() {\n    return m_reflexEnabled;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::LatencySleep() {\n    if (!m_reflexEnabled)\n      return DXGI_ERROR_INVALID_CALL;\n\n    // Don't keep object locked while sleeping\n    Rc<DxvkReflexLatencyTrackerNv> tracker;\n\n    { std::lock_guard lock(m_mutex);\n      tracker = m_tracker;\n    }\n\n    if (tracker)\n      tracker->latencySleep();\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::SetLatencySleepMode(\n          BOOL                          LowLatencyEnable,\n          BOOL                          LowLatencyBoost,\n          UINT32                        MinIntervalUs) {\n    if (!m_reflexEnabled)\n      return DXGI_ERROR_INVALID_CALL;\n\n    std::lock_guard lock(m_mutex);\n\n    if (m_tracker) {\n      m_tracker->setLatencySleepMode(\n        LowLatencyEnable, LowLatencyBoost, MinIntervalUs);\n    }\n\n    // Write back in case we have no swapchain yet\n    m_enableLowLatency = LowLatencyEnable;\n    m_enableBoost      = LowLatencyBoost;\n    m_minIntervalUs    = MinIntervalUs;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::SetLatencyMarker(\n          UINT64                        FrameId,\n          UINT32                        MarkerType) {\n    if (!m_reflexEnabled)\n      return DXGI_ERROR_INVALID_CALL;\n\n    std::lock_guard lock(m_mutex);\n\n    if (m_tracker) {\n      auto marker = VkLatencyMarkerNV(MarkerType);\n      m_tracker->setLatencyMarker(FrameId, marker);\n\n      if (marker == VK_LATENCY_MARKER_RENDERSUBMIT_START_NV) {\n        m_device->GetContext()->InjectCs(DxvkCsQueue::Ordered, [\n          cTracker  = m_tracker,\n          cFrameId  = FrameId\n        ] (DxvkContext* ctx) {\n          uint64_t frameId = cTracker->frameIdFromAppFrameId(cFrameId);\n\n          if (frameId)\n            ctx->beginLatencyTracking(cTracker, frameId);\n        });\n      } else if (marker == VK_LATENCY_MARKER_RENDERSUBMIT_END_NV) {\n        m_device->GetContext()->InjectCs(DxvkCsQueue::Ordered, [\n          cTracker  = m_tracker\n        ] (DxvkContext* ctx) {\n          ctx->endLatencyTracking(cTracker);\n        });\n      }\n    }\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11ReflexDevice::GetLatencyInfo(\n          D3D_LOW_LATENCY_RESULTS*      pLowLatencyResults) {\n    constexpr static size_t FrameCount = 64;\n\n    if (!pLowLatencyResults)\n      return E_INVALIDARG;\n\n    for (size_t i = 0; i < FrameCount; i++)\n      pLowLatencyResults->frameReports[i] = D3D_LOW_LATENCY_FRAME_REPORT();\n\n    if (!m_reflexEnabled)\n      return DXGI_ERROR_INVALID_CALL;\n\n    std::lock_guard lock(m_mutex);\n\n    if (!m_tracker)\n      return S_OK;\n\n    // Apparently we have to report all 64 frames, or nothing\n    std::array<DxvkReflexFrameReport, FrameCount> reports = { };\n    uint32_t reportCount = m_tracker->getFrameReports(FrameCount, reports.data());\n\n    if (reportCount < FrameCount)\n      return S_OK;\n\n    for (uint32_t i = 0; i < FrameCount; i++) {\n      auto& src = reports[i];\n      auto& dst = pLowLatencyResults->frameReports[i];\n\n      dst.frameID = src.report.presentID;\n      dst.inputSampleTime = src.report.inputSampleTimeUs;\n      dst.simStartTime = src.report.simStartTimeUs;\n      dst.simEndTime = src.report.simEndTimeUs;\n      dst.renderSubmitStartTime = src.report.renderSubmitStartTimeUs;\n      dst.renderSubmitEndTime = src.report.renderSubmitEndTimeUs;\n      dst.presentStartTime = src.report.presentStartTimeUs;\n      dst.presentEndTime = src.report.presentEndTimeUs;\n      dst.driverStartTime = src.report.driverStartTimeUs;\n      dst.driverEndTime = src.report.driverEndTimeUs;\n      dst.osRenderQueueStartTime = src.report.osRenderQueueStartTimeUs;\n      dst.osRenderQueueEndTime = src.report.osRenderQueueEndTimeUs;\n      dst.gpuRenderStartTime = src.report.gpuRenderStartTimeUs;\n      dst.gpuRenderEndTime = src.report.gpuRenderEndTimeUs;\n      dst.gpuActiveRenderTimeUs = src.gpuActiveTimeUs;\n      dst.gpuFrameTimeUs = 0;\n\n      if (i) {\n        dst.gpuFrameTimeUs = reports[i - 0].report.gpuRenderEndTimeUs\n                           - reports[i - 1].report.gpuRenderEndTimeUs;\n      }\n    }\n\n    return S_OK;\n  }\n\n\n  void D3D11ReflexDevice::RegisterLatencyTracker(\n          Rc<DxvkLatencyTracker>          Tracker) {\n    std::lock_guard lock(m_mutex);\n\n    if (m_tracker)\n      return;\n\n    if ((m_tracker = dynamic_cast<DxvkReflexLatencyTrackerNv*>(Tracker.ptr())))\n      m_tracker->setLatencySleepMode(m_enableLowLatency, m_enableBoost, m_minIntervalUs);\n  }\n\n\n  void D3D11ReflexDevice::UnregisterLatencyTracker(\n          Rc<DxvkLatencyTracker>          Tracker) {\n    std::lock_guard lock(m_mutex);\n\n    if (m_tracker == Tracker)\n      m_tracker = nullptr;\n  }\n\n\n\n\n  DXGIVkSwapChainFactory::DXGIVkSwapChainFactory(\n          D3D11DXGIDevice*        pContainer,\n          D3D11Device*            pDevice)\n  : m_container(pContainer), m_device(pDevice) {\n    \n  }\n\n\n  ULONG STDMETHODCALLTYPE DXGIVkSwapChainFactory::AddRef() {\n    return m_device->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE DXGIVkSwapChainFactory::Release() {\n    return m_device->Release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DXGIVkSwapChainFactory::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_device->QueryInterface(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DXGIVkSwapChainFactory::CreateSwapChain(\n          IDXGIVkSurfaceFactory*    pSurfaceFactory,\n    const DXGI_SWAP_CHAIN_DESC1*    pDesc,\n          IDXGIVkSwapChain**        ppSwapChain) {\n    InitReturnPtr(ppSwapChain);\n\n    try {\n      auto vki = m_device->GetDXVKDevice()->adapter()->vki();\n\n      Com<D3D11SwapChain> presenter = new D3D11SwapChain(\n        m_container, m_device, pSurfaceFactory, pDesc);\n      \n      *ppSwapChain = presenter.ref();\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_FAIL;\n    }\n  }\n\n\n\n\n  DXGIDXVKDevice::DXGIDXVKDevice(D3D11DXGIDevice* pContainer)\n  : m_container(pContainer), m_apiVersion(11) {\n\n  }\n  \n\n  ULONG STDMETHODCALLTYPE DXGIDXVKDevice::AddRef() {\n    return m_container->AddRef();\n  }\n  \n\n  ULONG STDMETHODCALLTYPE DXGIDXVKDevice::Release() {\n    return m_container->Release();\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE DXGIDXVKDevice::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_container->QueryInterface(riid, ppvObject);\n  }\n\n\n  void STDMETHODCALLTYPE DXGIDXVKDevice::SetAPIVersion(\n            UINT                    Version) {\n    m_apiVersion = Version;\n  }\n\n\n  UINT STDMETHODCALLTYPE DXGIDXVKDevice::GetAPIVersion() {\n    return m_apiVersion;\n  }\n\n  \n\n\n  D3D11DXGIDevice::D3D11DXGIDevice(\n          IDXGIAdapter*       pAdapter,\n          ID3D12Device*       pD3D12Device,\n          ID3D12CommandQueue* pD3D12Queue,\n          Rc<DxvkInstance>    pDxvkInstance,\n          Rc<DxvkAdapter>     pDxvkAdapter,\n          Rc<DxvkDevice>      pDxvkDevice,\n          D3D_FEATURE_LEVEL   FeatureLevel,\n          UINT                FeatureFlags)\n  : m_dxgiAdapter   (pAdapter),\n    m_dxvkInstance  (pDxvkInstance),\n    m_dxvkAdapter   (pDxvkAdapter),\n    m_dxvkDevice    (pDxvkDevice),\n    m_d3d11Device   (this, FeatureLevel, FeatureFlags),\n    m_d3d11DeviceExt(this, &m_d3d11Device),\n    m_d3d11Interop  (this, &m_d3d11Device),\n    m_d3d11Video    (this, &m_d3d11Device),\n    m_d3d11Reflex   (this, &m_d3d11Device),\n    m_d3d11on12     (this, &m_d3d11Device, pD3D12Device, pD3D12Queue),\n    m_metaDevice    (this),\n    m_dxvkFactory   (this, &m_d3d11Device),\n    m_destructionNotifier(this) {\n\n  }\n  \n  \n  D3D11DXGIDevice::~D3D11DXGIDevice() {\n\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDXGIObject)\n     || riid == __uuidof(IDXGIDevice)\n     || riid == __uuidof(IDXGIDevice1)\n     || riid == __uuidof(IDXGIDevice2)\n     || riid == __uuidof(IDXGIDevice3)\n     || riid == __uuidof(IDXGIDevice4)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(IDXGIVkInteropDevice)\n     || riid == __uuidof(IDXGIVkInteropDevice1)) {\n      *ppvObject = ref(&m_d3d11Interop);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10Device)\n     || riid == __uuidof(ID3D10Device1)) {\n      *ppvObject = ref(m_d3d11Device.GetD3D10Interface());\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D11Device)\n     || riid == __uuidof(ID3D11Device1)\n     || riid == __uuidof(ID3D11Device2)\n     || riid == __uuidof(ID3D11Device3)\n     || riid == __uuidof(ID3D11Device4)\n     || riid == __uuidof(ID3D11Device5)) {\n      *ppvObject = ref(&m_d3d11Device);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D11VkExtDevice)\n     || riid == __uuidof(ID3D11VkExtDevice1)) {\n      *ppvObject = ref(&m_d3d11DeviceExt);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(IDXGIDXVKDevice)) {\n      *ppvObject = ref(&m_metaDevice);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(IDXGIVkSwapChainFactory)) {\n      *ppvObject = ref(&m_dxvkFactory);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D11VideoDevice)) {\n      *ppvObject = ref(&m_d3d11Video);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DLowLatencyDevice)) {\n      *ppvObject = ref(&m_d3d11Reflex);\n      return S_OK;\n    }\n\n    if (m_d3d11on12.Is11on12Device()) {\n      if (riid == __uuidof(ID3D11On12Device)\n       || riid == __uuidof(ID3D11On12Device1_DXVK)) {\n        *ppvObject = ref(&m_d3d11on12);\n        return S_OK;\n      }\n    }\n\n    if (riid == __uuidof(ID3D10Multithread)) {\n      Com<ID3D11DeviceContext> context;\n      m_d3d11Device.GetImmediateContext(&context);\n      return context->QueryInterface(riid, ppvObject);\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D11Debug))\n      return E_NOINTERFACE;      \n    \n    // Undocumented interfaces that are queried by some games\n    if (riid == GUID{0xd56e2a4c,0x5127,0x8437,{0x65,0x8a,0x98,0xc5,0xbb,0x78,0x94,0x98}})\n      return E_NOINTERFACE;\n    \n    if (logQueryInterfaceError(__uuidof(IDXGIDXVKDevice), riid)) {\n      Logger::warn(\"D3D11DXGIDevice::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::GetParent(\n          REFIID                  riid,\n          void**                  ppParent) {\n    return m_dxgiAdapter->QueryInterface(riid, ppParent);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::CreateSurface(\n    const DXGI_SURFACE_DESC*    pDesc,\n          UINT                  NumSurfaces,\n          DXGI_USAGE            Usage,\n    const DXGI_SHARED_RESOURCE* pSharedResource,\n          IDXGISurface**        ppSurface) {\n    if (!pDesc || (NumSurfaces && !ppSurface))\n      return E_INVALIDARG;\n    \n    D3D11_TEXTURE2D_DESC desc;\n    desc.Width          = pDesc->Width;\n    desc.Height         = pDesc->Height;\n    desc.MipLevels      = 1;\n    desc.ArraySize      = 1;\n    desc.Format         = pDesc->Format;\n    desc.SampleDesc     = pDesc->SampleDesc;\n    desc.BindFlags      = 0;\n    desc.MiscFlags      = 0;\n\n    // Handle bind flags\n    if (Usage & DXGI_USAGE_RENDER_TARGET_OUTPUT)\n      desc.BindFlags |= D3D11_BIND_RENDER_TARGET;\n\n    if (Usage & DXGI_USAGE_SHADER_INPUT)\n      desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;\n\n    if (Usage & DXGI_USAGE_UNORDERED_ACCESS)\n      desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;\n\n    // Handle CPU access flags\n    switch (Usage & DXGI_CPU_ACCESS_FIELD) {\n      case DXGI_CPU_ACCESS_NONE:\n        desc.Usage          = D3D11_USAGE_DEFAULT;\n        desc.CPUAccessFlags = 0;\n        break;\n\n      case DXGI_CPU_ACCESS_DYNAMIC:\n        desc.Usage          = D3D11_USAGE_DYNAMIC;\n        desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;\n        break;\n\n      case DXGI_CPU_ACCESS_READ_WRITE:\n      case DXGI_CPU_ACCESS_SCRATCH:\n        desc.Usage          = D3D11_USAGE_STAGING;\n        desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;\n        break;\n\n      default:\n        return E_INVALIDARG;\n    }\n\n    // Restrictions and limitations of CreateSurface are not\n    // well-documented, so we'll be a lenient on validation.\n    HRESULT hr = m_d3d11Device.CreateTexture2D(&desc, nullptr, nullptr);\n\n    if (FAILED(hr))\n      return hr;\n\n    // We don't support shared resources\n    if (NumSurfaces && pSharedResource)\n      Logger::err(\"D3D11: CreateSurface: Shared surfaces not supported\");\n\n    // Try to create the given number of surfaces\n    uint32_t surfacesCreated = 0;\n    hr = S_OK;\n\n    for (uint32_t i = 0; i < NumSurfaces; i++) {\n      Com<ID3D11Texture2D> texture;\n\n      hr = m_d3d11Device.CreateTexture2D(&desc, nullptr, &texture);\n\n      if (SUCCEEDED(hr)) {\n        hr = texture->QueryInterface(__uuidof(IDXGISurface),\n          reinterpret_cast<void**>(&ppSurface[i]));\n        surfacesCreated = i + 1;\n      }\n\n      if (FAILED(hr))\n        break;\n    }\n\n    // Don't leak surfaces if we failed to create one\n    if (FAILED(hr)) {\n      for (uint32_t i = 0; i < surfacesCreated; i++)\n        ppSurface[i]->Release();\n    }\n\n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::GetAdapter(\n          IDXGIAdapter**        pAdapter) {\n    if (pAdapter == nullptr)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    *pAdapter = m_dxgiAdapter.ref();\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::GetGPUThreadPriority(\n          INT*                  pPriority) {\n    *pPriority = 0;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::QueryResourceResidency(\n          IUnknown* const*      ppResources,\n          DXGI_RESIDENCY*       pResidencyStatus,\n          UINT                  NumResources) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::err(\"D3D11DXGIDevice::QueryResourceResidency: Stub\");\n    \n    if (!ppResources || !pResidencyStatus)\n      return E_INVALIDARG;\n\n    for (uint32_t i = 0; i < NumResources; i++)\n      pResidencyStatus[i] = DXGI_RESIDENCY_FULLY_RESIDENT;\n\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::SetGPUThreadPriority(\n          INT                   Priority) {\n    if (Priority < -7 || Priority > 7)\n      return E_INVALIDARG;\n    \n    Logger::err(\"DXGI: SetGPUThreadPriority: Ignoring\");\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::GetMaximumFrameLatency(\n          UINT*                 pMaxLatency) {\n    if (!pMaxLatency)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    *pMaxLatency = m_frameLatency;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::SetMaximumFrameLatency(\n          UINT                  MaxLatency) {\n    if (MaxLatency == 0)\n      MaxLatency = DefaultFrameLatency;\n    \n    if (MaxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    m_frameLatency = MaxLatency;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::OfferResources( \n          UINT                          NumResources,\n          IDXGIResource* const*         ppResources,\n          DXGI_OFFER_RESOURCE_PRIORITY  Priority) {\n    return OfferResources1(NumResources, ppResources, Priority, 0);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::OfferResources1( \n          UINT                          NumResources,\n          IDXGIResource* const*         ppResources,\n          DXGI_OFFER_RESOURCE_PRIORITY  Priority,\n          UINT                          Flags) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11DXGIDevice::OfferResources1: Stub\");\n\n    return S_OK;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::ReclaimResources( \n          UINT                          NumResources,\n          IDXGIResource* const*         ppResources,\n          BOOL*                         pDiscarded) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11DXGIDevice::ReclaimResources: Stub\");\n\n    if (pDiscarded)\n      *pDiscarded = false;\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::ReclaimResources1(\n          UINT                          NumResources,\n          IDXGIResource* const*         ppResources,\n          DXGI_RECLAIM_RESOURCE_RESULTS* pResults) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11DXGIDevice::ReclaimResources1: Stub\");\n\n    if (pResults) {\n      for (uint32_t i = 0; i < NumResources; i++)\n        pResults[i] = DXGI_RECLAIM_RESOURCE_RESULT_OK;\n    }\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIDevice::EnqueueSetEvent(HANDLE hEvent) {\n    auto immediateContext = m_d3d11Device.GetContext();\n    immediateContext->Flush1(D3D11_CONTEXT_TYPE_ALL, hEvent);\n    return S_OK;            \n  }\n\n\n  void STDMETHODCALLTYPE D3D11DXGIDevice::Trim() {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11DXGIDevice::Trim: Stub\");\n  }\n  \n  \n  Rc<DxvkDevice> STDMETHODCALLTYPE D3D11DXGIDevice::GetDXVKDevice() {\n    return m_dxvkDevice;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_device.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <vector>\n\n#include \"../dxgi/dxgi_object.h\"\n#include \"../dxgi/dxgi_interfaces.h\"\n\n#include \"../dxvk/dxvk_cs.h\"\n#include \"../dxvk/dxvk_latency_reflex.h\"\n\n#include \"../d3d10/d3d10_device.h\"\n\n#include \"../util/com/com_private_data.h\"\n\n#include \"d3d11_cmdlist.h\"\n#include \"d3d11_cuda.h\"\n#include \"d3d11_features.h\"\n#include \"d3d11_initializer.h\"\n#include \"d3d11_interfaces.h\"\n#include \"d3d11_interop.h\"\n#include \"d3d11_on_12.h\"\n#include \"d3d11_options.h\"\n#include \"d3d11_shader.h\"\n#include \"d3d11_state.h\"\n#include \"d3d11_util.h\"\n\nnamespace dxvk {\n  class DxgiAdapter;\n  \n  class D3D11Buffer;\n  class D3D11CommonShader;\n  class D3D11CommonTexture;\n  class D3D11Counter;\n  class D3D11DXGIDevice;\n  class D3D11ImmediateContext;\n  class D3D11Predicate;\n  class D3D11Query;\n  class D3D11Texture1D;\n  class D3D11Texture2D;\n  class D3D11Texture3D;\n  \n  /**\n   * \\brief D3D11 device implementation\n   * \n   * Implements the ID3D11Device interfaces\n   * as part of a \\ref D3D11DeviceContainer.\n   */\n  class D3D11Device final : public ID3D11Device5 {\n    /// Maximum number of resource init commands per command buffer\n    constexpr static uint64_t InitCommandThreshold = 50;\n  public:\n    \n    D3D11Device(\n            D3D11DXGIDevice*        pContainer,\n            D3D_FEATURE_LEVEL       FeatureLevel,\n            UINT                    FeatureFlags);\n    \n    ~D3D11Device();\n    \n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n    \n    HRESULT STDMETHODCALLTYPE CreateBuffer(\n      const D3D11_BUFFER_DESC*      pDesc,\n      const D3D11_SUBRESOURCE_DATA* pInitialData,\n            ID3D11Buffer**          ppBuffer);\n    \n    HRESULT STDMETHODCALLTYPE CreateTexture1D(\n      const D3D11_TEXTURE1D_DESC*   pDesc,\n      const D3D11_SUBRESOURCE_DATA* pInitialData,\n            ID3D11Texture1D**       ppTexture1D);\n    \n    HRESULT STDMETHODCALLTYPE CreateTexture2D(\n      const D3D11_TEXTURE2D_DESC*   pDesc,\n      const D3D11_SUBRESOURCE_DATA* pInitialData,\n            ID3D11Texture2D**       ppTexture2D);\n    \n    HRESULT STDMETHODCALLTYPE CreateTexture2D1(\n      const D3D11_TEXTURE2D_DESC1*  pDesc,\n      const D3D11_SUBRESOURCE_DATA* pInitialData,\n            ID3D11Texture2D1**      ppTexture2D);\n    \n    HRESULT STDMETHODCALLTYPE CreateTexture2DBase(\n      const D3D11_TEXTURE2D_DESC1*  pDesc,\n      const D3D11_SUBRESOURCE_DATA* pInitialData,\n            ID3D11Texture2D1**      ppTexture2D);\n    \n    HRESULT STDMETHODCALLTYPE CreateTexture3D(\n      const D3D11_TEXTURE3D_DESC*   pDesc,\n      const D3D11_SUBRESOURCE_DATA* pInitialData,\n            ID3D11Texture3D**       ppTexture3D);\n    \n    HRESULT STDMETHODCALLTYPE CreateTexture3D1(\n      const D3D11_TEXTURE3D_DESC1*  pDesc,\n      const D3D11_SUBRESOURCE_DATA* pInitialData,\n            ID3D11Texture3D1**      ppTexture3D);\n    \n    HRESULT STDMETHODCALLTYPE CreateTexture3DBase(\n      const D3D11_TEXTURE3D_DESC1*  pDesc,\n      const D3D11_SUBRESOURCE_DATA* pInitialData,\n            ID3D11Texture3D1**      ppTexture3D);\n    \n    HRESULT STDMETHODCALLTYPE CreateShaderResourceView(\n            ID3D11Resource*                   pResource,\n      const D3D11_SHADER_RESOURCE_VIEW_DESC*  pDesc,\n            ID3D11ShaderResourceView**        ppSRView);\n    \n    HRESULT STDMETHODCALLTYPE CreateShaderResourceView1(\n            ID3D11Resource*                   pResource,\n      const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc,\n            ID3D11ShaderResourceView1**       ppSRView);\n    \n    HRESULT STDMETHODCALLTYPE CreateShaderResourceViewBase(\n            ID3D11Resource*                   pResource,\n      const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc,\n            ID3D11ShaderResourceView1**       ppSRView);\n    \n    HRESULT STDMETHODCALLTYPE CreateUnorderedAccessView(\n            ID3D11Resource*                   pResource,\n      const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc,\n            ID3D11UnorderedAccessView**       ppUAView);\n    \n    HRESULT STDMETHODCALLTYPE CreateUnorderedAccessView1(\n            ID3D11Resource*                   pResource,\n      const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc,\n            ID3D11UnorderedAccessView1**      ppUAView);\n    \n    HRESULT STDMETHODCALLTYPE CreateUnorderedAccessViewBase(\n            ID3D11Resource*                   pResource,\n      const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc,\n            ID3D11UnorderedAccessView1**      ppUAView);\n    \n    HRESULT STDMETHODCALLTYPE CreateRenderTargetView(\n            ID3D11Resource*                   pResource,\n      const D3D11_RENDER_TARGET_VIEW_DESC*    pDesc,\n            ID3D11RenderTargetView**          ppRTView);\n    \n    HRESULT STDMETHODCALLTYPE CreateRenderTargetView1(\n            ID3D11Resource*                   pResource,\n      const D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc,\n            ID3D11RenderTargetView1**         ppRTView);\n    \n    HRESULT STDMETHODCALLTYPE CreateRenderTargetViewBase(\n            ID3D11Resource*                   pResource,\n      const D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc,\n            ID3D11RenderTargetView1**         ppRTView);\n    \n    HRESULT STDMETHODCALLTYPE CreateDepthStencilView(\n            ID3D11Resource*                   pResource,\n      const D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc,\n            ID3D11DepthStencilView**          ppDepthStencilView);\n    \n    HRESULT STDMETHODCALLTYPE CreateInputLayout(\n      const D3D11_INPUT_ELEMENT_DESC*   pInputElementDescs,\n            UINT                        NumElements,\n      const void*                       pShaderBytecodeWithInputSignature,\n            SIZE_T                      BytecodeLength,\n            ID3D11InputLayout**         ppInputLayout);\n    \n    HRESULT STDMETHODCALLTYPE CreateVertexShader(\n      const void*                       pShaderBytecode,\n            SIZE_T                      BytecodeLength,\n            ID3D11ClassLinkage*         pClassLinkage,\n            ID3D11VertexShader**        ppVertexShader);\n    \n    HRESULT STDMETHODCALLTYPE CreateGeometryShader(\n      const void*                       pShaderBytecode,\n            SIZE_T                      BytecodeLength,\n            ID3D11ClassLinkage*         pClassLinkage,\n            ID3D11GeometryShader**      ppGeometryShader);\n    \n    HRESULT STDMETHODCALLTYPE CreateGeometryShaderWithStreamOutput(\n      const void*                       pShaderBytecode,\n            SIZE_T                      BytecodeLength,\n      const D3D11_SO_DECLARATION_ENTRY* pSODeclaration,\n            UINT                        NumEntries,\n      const UINT*                       pBufferStrides,\n            UINT                        NumStrides,\n            UINT                        RasterizedStream,\n            ID3D11ClassLinkage*         pClassLinkage,\n            ID3D11GeometryShader**      ppGeometryShader);\n    \n    HRESULT STDMETHODCALLTYPE CreatePixelShader(\n      const void*                       pShaderBytecode,\n            SIZE_T                      BytecodeLength,\n            ID3D11ClassLinkage*         pClassLinkage,\n            ID3D11PixelShader**         ppPixelShader);\n    \n    HRESULT STDMETHODCALLTYPE CreateHullShader(\n      const void*                       pShaderBytecode,\n            SIZE_T                      BytecodeLength,\n            ID3D11ClassLinkage*         pClassLinkage,\n            ID3D11HullShader**          ppHullShader);\n    \n    HRESULT STDMETHODCALLTYPE CreateDomainShader(\n      const void*                       pShaderBytecode,\n            SIZE_T                      BytecodeLength,\n            ID3D11ClassLinkage*         pClassLinkage,\n            ID3D11DomainShader**        ppDomainShader);\n    \n    HRESULT STDMETHODCALLTYPE CreateComputeShader(\n      const void*                       pShaderBytecode,\n            SIZE_T                      BytecodeLength,\n            ID3D11ClassLinkage*         pClassLinkage,\n            ID3D11ComputeShader**       ppComputeShader);\n    \n    HRESULT STDMETHODCALLTYPE CreateClassLinkage(\n            ID3D11ClassLinkage**        ppLinkage);\n    \n    HRESULT STDMETHODCALLTYPE CreateBlendState(\n      const D3D11_BLEND_DESC*           pBlendStateDesc,\n            ID3D11BlendState**          ppBlendState);\n    \n    HRESULT STDMETHODCALLTYPE CreateBlendState1(\n      const D3D11_BLEND_DESC1*          pBlendStateDesc,\n            ID3D11BlendState1**         ppBlendState);\n\n    HRESULT STDMETHODCALLTYPE CreateDepthStencilState(\n      const D3D11_DEPTH_STENCIL_DESC*   pDepthStencilDesc,\n            ID3D11DepthStencilState**   ppDepthStencilState);\n    \n    HRESULT STDMETHODCALLTYPE CreateRasterizerState(\n      const D3D11_RASTERIZER_DESC*      pRasterizerDesc,\n            ID3D11RasterizerState**     ppRasterizerState);\n    \n    HRESULT STDMETHODCALLTYPE CreateRasterizerState1(\n      const D3D11_RASTERIZER_DESC1*     pRasterizerDesc,\n            ID3D11RasterizerState1**    ppRasterizerState);\n    \n    HRESULT STDMETHODCALLTYPE CreateRasterizerState2(\n      const D3D11_RASTERIZER_DESC2*     pRasterizerDesc,\n            ID3D11RasterizerState2**    ppRasterizerState);\n    \n    HRESULT STDMETHODCALLTYPE CreateSamplerState(\n      const D3D11_SAMPLER_DESC*         pSamplerDesc,\n            ID3D11SamplerState**        ppSamplerState);\n    \n    HRESULT STDMETHODCALLTYPE CreateQuery(\n      const D3D11_QUERY_DESC*           pQueryDesc,\n            ID3D11Query**               ppQuery);\n    \n    HRESULT STDMETHODCALLTYPE CreateQuery1(\n      const D3D11_QUERY_DESC1*          pQueryDesc,\n            ID3D11Query1**              ppQuery);\n    \n    HRESULT STDMETHODCALLTYPE CreateQueryBase(\n      const D3D11_QUERY_DESC1*          pQueryDesc,\n            ID3D11Query1**              ppQuery);\n    \n    HRESULT STDMETHODCALLTYPE CreatePredicate(\n      const D3D11_QUERY_DESC*           pPredicateDesc,\n            ID3D11Predicate**           ppPredicate);\n    \n    HRESULT STDMETHODCALLTYPE CreateCounter(\n      const D3D11_COUNTER_DESC*         pCounterDesc,\n            ID3D11Counter**             ppCounter);\n    \n    HRESULT STDMETHODCALLTYPE CreateDeferredContext(\n            UINT                        ContextFlags,\n            ID3D11DeviceContext**       ppDeferredContext);\n\n    HRESULT STDMETHODCALLTYPE CreateDeferredContext1(\n            UINT                        ContextFlags,\n            ID3D11DeviceContext1**      ppDeferredContext);\n\n    HRESULT STDMETHODCALLTYPE CreateDeferredContext2(\n            UINT                        ContextFlags,\n            ID3D11DeviceContext2**      ppDeferredContext);\n\n    HRESULT STDMETHODCALLTYPE CreateDeferredContext3(\n            UINT                        ContextFlags,\n            ID3D11DeviceContext3**      ppDeferredContext);\n\n    HRESULT STDMETHODCALLTYPE CreateDeviceContextState(\n            UINT                        Flags,\n      const D3D_FEATURE_LEVEL*          pFeatureLevels,\n            UINT                        FeatureLevels,\n            UINT                        SDKVersion,\n            REFIID                      EmulatedInterface,\n            D3D_FEATURE_LEVEL*          pChosenFeatureLevel,\n            ID3DDeviceContextState**    ppContextState);\n\n    HRESULT STDMETHODCALLTYPE CreateFence(\n            UINT64                      InitialValue,\n            D3D11_FENCE_FLAG            Flags,\n            REFIID                      riid,\n            void**                      ppFence);\n\n    void STDMETHODCALLTYPE ReadFromSubresource(\n            void*                       pDstData,\n            UINT                        DstRowPitch,\n            UINT                        DstDepthPitch,\n            ID3D11Resource*             pSrcResource,\n            UINT                        SrcSubresource,\n      const D3D11_BOX*                  pSrcBox);\n\n    void STDMETHODCALLTYPE WriteToSubresource(\n            ID3D11Resource*             pDstResource,\n            UINT                        DstSubresource,\n      const D3D11_BOX*                  pDstBox,\n      const void*                       pSrcData,\n            UINT                        SrcRowPitch,\n            UINT                        SrcDepthPitch);\n\n    HRESULT STDMETHODCALLTYPE OpenSharedResource(\n            HANDLE      hResource,\n            REFIID      ReturnedInterface,\n            void**      ppResource);\n\n    HRESULT STDMETHODCALLTYPE OpenSharedResource1(\n            HANDLE      hResource,\n            REFIID      returnedInterface,\n            void**      ppResource);\n\n    HRESULT STDMETHODCALLTYPE OpenSharedResourceByName(\n            LPCWSTR     lpName,\n            DWORD       dwDesiredAccess,\n            REFIID      returnedInterface,\n            void**      ppResource);\n    \n    HRESULT STDMETHODCALLTYPE OpenSharedFence(\n            HANDLE      hFence,\n            REFIID      ReturnedInterface,\n            void**      ppFence);\n\n    HRESULT STDMETHODCALLTYPE CheckFormatSupport(\n            DXGI_FORMAT Format,\n            UINT*       pFormatSupport);\n    \n    HRESULT STDMETHODCALLTYPE CheckMultisampleQualityLevels(\n            DXGI_FORMAT Format,\n            UINT        SampleCount,\n            UINT*       pNumQualityLevels);\n    \n    HRESULT STDMETHODCALLTYPE CheckMultisampleQualityLevels1(\n            DXGI_FORMAT Format,\n            UINT        SampleCount,\n            UINT        Flags,\n            UINT*       pNumQualityLevels);\n    \n    void STDMETHODCALLTYPE CheckCounterInfo(\n            D3D11_COUNTER_INFO* pCounterInfo);\n    \n    HRESULT STDMETHODCALLTYPE CheckCounter(\n      const D3D11_COUNTER_DESC* pDesc,\n            D3D11_COUNTER_TYPE* pType,\n            UINT*               pActiveCounters,\n            LPSTR               szName,\n            UINT*               pNameLength,\n            LPSTR               szUnits,\n            UINT*               pUnitsLength,\n            LPSTR               szDescription,\n            UINT*               pDescriptionLength);\n    \n    HRESULT STDMETHODCALLTYPE CheckFeatureSupport(\n            D3D11_FEATURE Feature,\n            void*         pFeatureSupportData,\n            UINT          FeatureSupportDataSize);\n    \n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID Name,\n            UINT    *pDataSize,\n            void    *pData);\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID Name,\n            UINT    DataSize,\n      const void    *pData);\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID  Name,\n      const IUnknown *pUnknown);\n    \n    D3D_FEATURE_LEVEL STDMETHODCALLTYPE GetFeatureLevel();\n    \n    UINT STDMETHODCALLTYPE GetCreationFlags();\n    \n    HRESULT STDMETHODCALLTYPE GetDeviceRemovedReason();\n    \n    void STDMETHODCALLTYPE GetImmediateContext(\n            ID3D11DeviceContext** ppImmediateContext);\n\n    void STDMETHODCALLTYPE GetImmediateContext1(\n            ID3D11DeviceContext1** ppImmediateContext);\n    \n    void STDMETHODCALLTYPE GetImmediateContext2(\n            ID3D11DeviceContext2** ppImmediateContext);\n    \n    void STDMETHODCALLTYPE GetImmediateContext3(\n            ID3D11DeviceContext3** ppImmediateContext);\n    \n    HRESULT STDMETHODCALLTYPE SetExceptionMode(UINT RaiseFlags);\n    \n    UINT STDMETHODCALLTYPE GetExceptionMode();\n    \n    void STDMETHODCALLTYPE GetResourceTiling(\n            ID3D11Resource*           pTiledResource,\n            UINT*                     pNumTilesForEntireResource,\n            D3D11_PACKED_MIP_DESC*    pPackedMipDesc,\n            D3D11_TILE_SHAPE*         pStandardTileShapeForNonPackedMips,\n            UINT*                     pNumSubresourceTilings,\n            UINT                      FirstSubresourceTilingToGet,\n            D3D11_SUBRESOURCE_TILING* pSubresourceTilingsForNonPackedMips);\n\n    HRESULT STDMETHODCALLTYPE RegisterDeviceRemovedEvent(\n            HANDLE                    hEvent,\n            DWORD*                    pdwCookie);\n\n    void STDMETHODCALLTYPE UnregisterDeviceRemoved(\n            DWORD                     dwCookie);\n\n    Rc<DxvkDevice> GetDXVKDevice() {\n      return m_dxvkDevice;\n    }\n    \n    void FlushInitCommands() {\n      m_initializer->FlushCsChunk();\n    }\n\n    void NotifyContextFlush() {\n      m_initializer->NotifyContextFlush();\n    }\n    \n    void InitShaderIcb(\n            D3D11CommonShader*          pShader,\n            size_t                      IcbSize,\n      const void*                       pIcbData) {\n      return m_initializer->InitShaderIcb(pShader, IcbSize, pIcbData);\n    }\n\n    VkPipelineStageFlags GetEnabledShaderStages() const {\n      return m_dxvkDevice->getShaderPipelineStages();\n    }\n    \n    DXGI_VK_FORMAT_INFO LookupFormat(\n            DXGI_FORMAT           Format,\n            DXGI_VK_FORMAT_MODE   Mode) const;\n    \n    DXGI_VK_FORMAT_INFO LookupPackedFormat(\n            DXGI_FORMAT           Format,\n            DXGI_VK_FORMAT_MODE   Mode) const;\n    \n    DXGI_VK_FORMAT_FAMILY LookupFamily(\n            DXGI_FORMAT           Format,\n            DXGI_VK_FORMAT_MODE   Mode) const;\n    \n    DxvkCsChunkRef AllocCsChunk(DxvkCsChunkFlags flags) {\n      DxvkCsChunk* chunk = m_csChunkPool.allocChunk(flags);\n      return DxvkCsChunkRef(chunk, &m_csChunkPool);\n    }\n    \n    const D3D11Options* GetOptions() const {\n      return &m_d3d11Options;\n    }\n\n    D3D10Device* GetD3D10Interface() const {\n      return m_d3d10Device;\n    }\n\n    D3D11ImmediateContext* GetContext() const {\n      return m_context.ptr();\n    }\n\n    bool Is11on12Device() const;\n\n    bool LockImage(\n      const Rc<DxvkImage>&            Image,\n            VkImageUsageFlags         Usage);\n\n    static D3D_FEATURE_LEVEL GetMaxFeatureLevel(\n      const Rc<DxvkInstance>& Instance,\n      const Rc<DxvkAdapter>&  Adapter);\n    \n    DxvkBarrierControlFlags GetOptionsBarrierControlFlags() {\n      DxvkBarrierControlFlags barrierControl = 0u;\n\n      if (m_d3d11Options.relaxedBarriers)\n        barrierControl.set(DxvkBarrierControl::ComputeAllowWriteOnlyOverlap);\n\n      if (m_d3d11Options.relaxedBarriers || m_d3d11Options.relaxedGraphicsBarriers)\n        barrierControl.set(DxvkBarrierControl::GraphicsAllowReadWriteOverlap);\n\n      return barrierControl;\n    }\n    \n  private:\n    \n    D3D11DXGIDevice*                m_container;\n\n    D3D_FEATURE_LEVEL               m_featureLevel;\n    UINT                            m_featureFlags;\n    \n    const Rc<DxvkDevice>            m_dxvkDevice;\n    const Rc<DxvkAdapter>           m_dxvkAdapter;\n    \n    const DXGIVkFormatTable         m_d3d11Formats;\n    const D3D11Options              m_d3d11Options;\n\n    DxvkShaderOptions               m_shaderOptions = { };\n\n    DxvkCsChunkPool                 m_csChunkPool;\n\n    D3D11Initializer*               m_initializer = nullptr;\n    D3D10Device*                    m_d3d10Device = nullptr;\n\n    D3D11StateObjectSet<D3D11BlendState>        m_bsStateObjects;\n    D3D11StateObjectSet<D3D11DepthStencilState> m_dsStateObjects;\n    D3D11StateObjectSet<D3D11RasterizerState>   m_rsStateObjects;\n    D3D11StateObjectSet<D3D11SamplerState>      m_samplerObjects;\n    D3D11ShaderModuleSet                        m_shaderModules;\n\n    D3D_FEATURE_LEVEL               m_maxFeatureLevel;\n    D3D11DeviceFeatures             m_deviceFeatures;\n\n    Com<D3D11ImmediateContext, false> m_context;\n\n    HRESULT CreateShaderModule(\n            D3D11CommonShader*      pShaderModule,\n            ID3D11ClassLinkage*     pLinkage,\n      const DxvkShaderHash&         ShaderKey,\n      const void*                   pShaderBytecode,\n            size_t                  BytecodeLength,\n      const DxvkIrShaderCreateInfo& ModuleInfo);\n\n    DxvkShaderHash ComputeShaderKey(\n            VkShaderStageFlagBits   Stage,\n      const void*                   pShaderBytecode,\n            size_t                  BytecodeLength);\n\n    DxvkShaderHash ComputeShaderKey(\n            VkShaderStageFlagBits   Stage,\n      const void*                   pShaderBytecode,\n            size_t                  BytecodeLength,\n      const D3D11_SO_DECLARATION_ENTRY* pSODeclaration,\n            UINT                    NumEntries,\n      const UINT*                   pBufferStrides,\n            UINT                    NumStrides,\n            UINT                    RasterizedStream);\n\n    HRESULT GetFormatSupportFlags(\n            DXGI_FORMAT             Format,\n            UINT*                   pFlags1,\n            UINT*                   pFlags2) const;\n    \n    BOOL GetImageTypeSupport(\n            VkFormat                Format,\n            VkImageType             Type,\n            VkImageCreateFlags      Flags) const;\n\n    template<bool IsKmtHandle>\n    HRESULT OpenSharedResourceGeneric(\n            HANDLE      hResource,\n            REFIID      ReturnedInterface,\n            void**      ppResource);\n\n    uint32_t GetViewPlaneIndex(\n            ID3D11Resource*         pResource,\n            DXGI_FORMAT             ViewFormat);\n    \n    template<typename Void>\n    void CopySubresourceData(\n            Void*                       pData,\n            UINT                        RowPitch,\n            UINT                        DepthPitch,\n            D3D11CommonTexture*         pTexture,\n            UINT                        Subresource,\n      const D3D11_BOX*                  pBox);\n\n    static DxvkShaderOptions GetShaderOptions(\n      const Rc<DxvkDevice>&             Device,\n      const D3D11Options&               Options);\n\n    static bool ConvertRuntimeDescriptor(\n      UINT                       size,\n      const union d3dkmt_desc&   d3dkmt,\n      D3D11_COMMON_TEXTURE_DESC* desc);\n\n  };\n  \n  \n  /**\n   * \\brief Extended D3D11 device\n   */\n  class D3D11DeviceExt : public ID3D11VkExtDevice1 {\n    \n  public:\n    \n    D3D11DeviceExt(\n            D3D11DXGIDevice*        pContainer,\n            D3D11Device*            pDevice);\n    \n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n    \n    BOOL STDMETHODCALLTYPE GetExtensionSupport(\n            D3D11_VK_EXTENSION      Extension);\n    \n    bool STDMETHODCALLTYPE GetCudaTextureObjectNVX(\n            uint32_t                srvDriverHandle,\n            uint32_t                samplerDriverHandle,\n            uint32_t*               pCudaTextureHandle);\n\n    bool STDMETHODCALLTYPE CreateCubinComputeShaderWithNameNVX(\n            const void*             pCubin,\n            uint32_t                size,\n            uint32_t                blockX,\n            uint32_t                blockY,\n            uint32_t                blockZ,\n            const char*             pShaderName,\n            IUnknown**              phShader);\n\n    bool STDMETHODCALLTYPE GetResourceHandleGPUVirtualAddressAndSizeNVX(\n            void*                   hObject,\n            uint64_t*               gpuVAStart,\n            uint64_t*               gpuVASize);\n\n     bool STDMETHODCALLTYPE CreateUnorderedAccessViewAndGetDriverHandleNVX(\n            ID3D11Resource*                         pResource,\n            const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc,\n            ID3D11UnorderedAccessView**             ppUAV,\n            uint32_t*                               pDriverHandle);\n\n     bool STDMETHODCALLTYPE CreateShaderResourceViewAndGetDriverHandleNVX(\n            ID3D11Resource*                        pResource,\n            const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc,\n            ID3D11ShaderResourceView**             ppSRV,\n            uint32_t*                              pDriverHandle);\n\n     bool STDMETHODCALLTYPE CreateSamplerStateAndGetDriverHandleNVX(\n            const D3D11_SAMPLER_DESC* pSamplerDesc,\n            ID3D11SamplerState**      ppSamplerState,\n            uint32_t*                 pDriverHandle);\n    \n  private:\n    \n    D3D11DXGIDevice* m_container;\n    D3D11Device*     m_device;\n    \n    void AddSamplerAndHandleNVX(\n            ID3D11SamplerState*       pSampler,\n            uint32_t                  Handle);\n\n    ID3D11SamplerState* HandleToSamplerNVX(\n            uint32_t                  Handle);\n\n    void AddSrvAndHandleNVX(\n            ID3D11ShaderResourceView* pSrv,\n            uint32_t                  Handle);\n\n    ID3D11ShaderResourceView* HandleToSrvNVX(\n            uint32_t                  Handle);\n\n    bool LockImage(\n      const Rc<DxvkImage>&            Image,\n            VkImageUsageFlags         Usage);\n\n    void LockBuffer(\n      const Rc<DxvkBuffer>&           Buffer);\n\n    dxvk::mutex m_mapLock;\n    std::unordered_map<uint32_t, ID3D11SamplerState*> m_samplerHandleToPtr;\n    std::unordered_map<uint32_t, ID3D11ShaderResourceView*> m_srvHandleToPtr;\n  };\n\n\n  /**\n   * \\brief D3D11 video device\n   */\n  class D3D11VideoDevice : public ID3D11VideoDevice {\n\n  public:\n\n    D3D11VideoDevice(\n            D3D11DXGIDevice*        pContainer,\n            D3D11Device*            pDevice);\n\n    ~D3D11VideoDevice();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    HRESULT STDMETHODCALLTYPE CreateVideoDecoder(\n      const D3D11_VIDEO_DECODER_DESC*                     pVideoDesc,\n      const D3D11_VIDEO_DECODER_CONFIG*                   pConfig,\n            ID3D11VideoDecoder**                          ppDecoder);\n\n    HRESULT STDMETHODCALLTYPE CreateVideoProcessor(\n            ID3D11VideoProcessorEnumerator*               pEnum,\n            UINT                                          RateConversionIndex,\n            ID3D11VideoProcessor**                        ppVideoProcessor);\n\n    HRESULT STDMETHODCALLTYPE CreateAuthenticatedChannel(\n            D3D11_AUTHENTICATED_CHANNEL_TYPE              ChannelType,\n            ID3D11AuthenticatedChannel**                  ppAuthenticatedChannel);\n\n    HRESULT STDMETHODCALLTYPE CreateCryptoSession(\n      const GUID*                                         pCryptoType,\n      const GUID*                                         pDecoderProfile,\n      const GUID*                                         pKeyExchangeType,\n            ID3D11CryptoSession**                         ppCryptoSession);\n\n    HRESULT STDMETHODCALLTYPE CreateVideoDecoderOutputView(\n            ID3D11Resource*                               pResource,\n      const D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC*         pDesc,\n            ID3D11VideoDecoderOutputView**                ppVDOVView);\n\n    HRESULT STDMETHODCALLTYPE CreateVideoProcessorInputView(\n            ID3D11Resource*                               pResource,\n            ID3D11VideoProcessorEnumerator*               pEnum,\n      const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC*        pDesc,\n            ID3D11VideoProcessorInputView**               ppVPIView);\n\n    HRESULT STDMETHODCALLTYPE CreateVideoProcessorOutputView(\n            ID3D11Resource*                               pResource,\n            ID3D11VideoProcessorEnumerator*               pEnum,\n      const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC*       pDesc,\n            ID3D11VideoProcessorOutputView**              ppVPOView);\n\n    HRESULT STDMETHODCALLTYPE CreateVideoProcessorEnumerator(\n      const D3D11_VIDEO_PROCESSOR_CONTENT_DESC*           pDesc,\n            ID3D11VideoProcessorEnumerator**              ppEnum);\n\n    UINT STDMETHODCALLTYPE GetVideoDecoderProfileCount();\n\n    HRESULT STDMETHODCALLTYPE GetVideoDecoderProfile(\n            UINT                                          Index,\n            GUID*                                         pDecoderProfile);\n\n    HRESULT STDMETHODCALLTYPE CheckVideoDecoderFormat(\n      const GUID*                                         pDecoderProfile,\n            DXGI_FORMAT                                   Format,\n            BOOL*                                         pSupported);\n\n    HRESULT STDMETHODCALLTYPE GetVideoDecoderConfigCount(\n      const D3D11_VIDEO_DECODER_DESC*                     pDesc,\n            UINT*                                         pCount);\n\n    HRESULT STDMETHODCALLTYPE GetVideoDecoderConfig(\n      const D3D11_VIDEO_DECODER_DESC*                     pDesc,\n            UINT                                          Index,\n            D3D11_VIDEO_DECODER_CONFIG*                   pConfig);\n\n    HRESULT STDMETHODCALLTYPE GetContentProtectionCaps(\n      const GUID*                                         pCryptoType,\n      const GUID*                                         pDecoderProfile,\n            D3D11_VIDEO_CONTENT_PROTECTION_CAPS*          pCaps);\n\n    HRESULT STDMETHODCALLTYPE CheckCryptoKeyExchange(\n      const GUID*                                         pCryptoType,\n      const GUID*                                         pDecoderProfile,\n            UINT                                          Index,\n            GUID*                                         pKeyExchangeType);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                                       Name,\n            UINT                                          DataSize,\n      const void*                                         pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                                       Name,\n      const IUnknown*                                     pData);\n\n  private:\n\n    D3D11DXGIDevice* m_container;\n    D3D11Device*     m_device;\n\n  };\n\n\n  /**\n   * \\brief Nvidia Reflex interop\n   */\n  class D3D11ReflexDevice : public ID3DLowLatencyDevice {\n\n  public:\n\n    D3D11ReflexDevice(\n            D3D11DXGIDevice*        pContainer,\n            D3D11Device*            pDevice);\n\n    ~D3D11ReflexDevice();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                        riid,\n            void**                        ppvObject);\n\n    BOOL STDMETHODCALLTYPE SupportsLowLatency();\n\n    HRESULT STDMETHODCALLTYPE LatencySleep();\n\n    HRESULT STDMETHODCALLTYPE SetLatencySleepMode(\n            BOOL                          LowLatencyEnable,\n            BOOL                          LowLatencyBoost,\n            UINT32                        MinIntervalUs);\n\n    HRESULT STDMETHODCALLTYPE SetLatencyMarker(\n            UINT64                        FrameId,\n            UINT32                        MarkerType);\n\n    HRESULT STDMETHODCALLTYPE GetLatencyInfo(\n            D3D_LOW_LATENCY_RESULTS*      pLowLatencyResults);\n\n    void RegisterLatencyTracker(\n            Rc<DxvkLatencyTracker>          Tracker);\n\n    void UnregisterLatencyTracker(\n            Rc<DxvkLatencyTracker>          Tracker);\n\n  private:\n\n    D3D11DXGIDevice*  m_container;\n    D3D11Device*      m_device;\n\n    bool              m_reflexEnabled = false;\n\n    dxvk::mutex       m_mutex;\n\n    bool              m_enableLowLatency  = false;\n    bool              m_enableBoost       = false;\n    uint64_t          m_minIntervalUs     = 0u;\n\n    Rc<DxvkReflexLatencyTrackerNv>  m_tracker;\n  };\n\n\n  /**\n   * \\brief DXVK swap chain factory\n   */\n  class DXGIVkSwapChainFactory : public IDXGIVkSwapChainFactory {\n\n  public:\n\n    DXGIVkSwapChainFactory(\n            D3D11DXGIDevice*        pContainer,\n            D3D11Device*            pDevice);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    HRESULT STDMETHODCALLTYPE CreateSwapChain(\n            IDXGIVkSurfaceFactory*    pSurfaceFactory,\n      const DXGI_SWAP_CHAIN_DESC1*    pDesc,\n            IDXGIVkSwapChain**        ppSwapChain);\n\n  private:\n\n    D3D11DXGIDevice* m_container;\n    D3D11Device*     m_device;\n\n  };\n\n\n  /**\n   * \\brief D3D11 device metadata shenanigans\n   */\n  class DXGIDXVKDevice : public IDXGIDXVKDevice {\n\n  public:\n\n    DXGIDXVKDevice(D3D11DXGIDevice* pContainer);\n    \n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    void STDMETHODCALLTYPE SetAPIVersion(\n              UINT                    Version);\n\n    UINT STDMETHODCALLTYPE GetAPIVersion();\n\n  private:\n\n    D3D11DXGIDevice* m_container;\n    UINT             m_apiVersion;\n\n  };\n\n\n  /**\n   * \\brief D3D11 device container\n   * \n   * Stores all the objects that contribute to the D3D11\n   * device implementation, including the DXGI device.\n   */\n  class D3D11DXGIDevice : public DxgiObject<IDXGIDevice4> {\n    constexpr static uint32_t DefaultFrameLatency = 3;\n  public:\n    \n    D3D11DXGIDevice(\n            IDXGIAdapter*       pAdapter,\n            ID3D12Device*       pD3D12Device,\n            ID3D12CommandQueue* pD3D12Queue,\n            Rc<DxvkInstance>    pDxvkInstance,\n            Rc<DxvkAdapter>     pDxvkAdapter,\n            Rc<DxvkDevice>      pDxvkDevice,\n            D3D_FEATURE_LEVEL   FeatureLevel,\n            UINT                FeatureFlags);\n    \n    ~D3D11DXGIDevice();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject);\n    \n    HRESULT STDMETHODCALLTYPE GetParent(\n            REFIID                riid,\n            void**                ppParent);\n    \n    HRESULT STDMETHODCALLTYPE CreateSurface(\n      const DXGI_SURFACE_DESC*    pDesc,\n            UINT                  NumSurfaces,\n            DXGI_USAGE            Usage,\n      const DXGI_SHARED_RESOURCE* pSharedResource,\n            IDXGISurface**        ppSurface) final;\n    \n    HRESULT STDMETHODCALLTYPE GetAdapter(\n            IDXGIAdapter**        pAdapter) final;\n    \n    HRESULT STDMETHODCALLTYPE GetGPUThreadPriority(\n            INT*                  pPriority) final;\n    \n    HRESULT STDMETHODCALLTYPE QueryResourceResidency(\n            IUnknown* const*      ppResources,\n            DXGI_RESIDENCY*       pResidencyStatus,\n            UINT                  NumResources) final;\n    \n    HRESULT STDMETHODCALLTYPE SetGPUThreadPriority(\n            INT                   Priority) final;\n    \n    HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(\n            UINT*                 pMaxLatency) final;\n    \n    HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(\n            UINT                  MaxLatency) final;\n\n    HRESULT STDMETHODCALLTYPE OfferResources( \n            UINT                          NumResources,\n            IDXGIResource* const*         ppResources,\n            DXGI_OFFER_RESOURCE_PRIORITY  Priority) final;\n        \n    HRESULT STDMETHODCALLTYPE OfferResources1( \n            UINT                          NumResources,\n            IDXGIResource* const*         ppResources,\n            DXGI_OFFER_RESOURCE_PRIORITY  Priority,\n            UINT                          Flags) final;\n    \n    HRESULT STDMETHODCALLTYPE ReclaimResources( \n            UINT                          NumResources,\n            IDXGIResource* const*         ppResources,\n            BOOL*                         pDiscarded) final;\n    \n    HRESULT STDMETHODCALLTYPE ReclaimResources1(\n            UINT                          NumResources,\n            IDXGIResource* const*         ppResources,\n            DXGI_RECLAIM_RESOURCE_RESULTS* pResults) final;\n        \n    HRESULT STDMETHODCALLTYPE EnqueueSetEvent( \n            HANDLE                hEvent) final;\n    \n    void STDMETHODCALLTYPE Trim() final;\n    \n    Rc<DxvkDevice> STDMETHODCALLTYPE GetDXVKDevice();\n\n    BOOL Is11on12Device() const {\n      return m_d3d11on12.Is11on12Device();\n    }\n\n  private:\n\n    Com<IDXGIAdapter>   m_dxgiAdapter;\n\n    Rc<DxvkInstance>    m_dxvkInstance;\n    Rc<DxvkAdapter>     m_dxvkAdapter;\n    Rc<DxvkDevice>      m_dxvkDevice;\n\n    D3D11Device         m_d3d11Device;\n    D3D11DeviceExt      m_d3d11DeviceExt;\n    D3D11VkInterop      m_d3d11Interop;\n    D3D11VideoDevice    m_d3d11Video;\n    D3D11ReflexDevice   m_d3d11Reflex;\n    D3D11on12Device     m_d3d11on12;\n    DXGIDXVKDevice      m_metaDevice;\n    \n    DXGIVkSwapChainFactory   m_dxvkFactory;\n\n    D3DDestructionNotifier   m_destructionNotifier;\n    \n    uint32_t m_frameLatency = DefaultFrameLatency;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_device_child.h",
    "content": "#pragma once\n\n#include \"d3d11_include.h\"\n#include \"d3d11_state.h\"\n\n#include \"../util/com/com_private_data.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n\n  template<typename Base>\n  class D3D11DeviceObject : public Base {\n    \n  public:\n\n    D3D11DeviceObject(D3D11Device* pDevice)\n    : m_parent(pDevice) {\n\n    }\n    \n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID               guid,\n            UINT*                 pDataSize,\n            void*                 pData) final {\n      return m_privateData.getData(\n        guid, pDataSize, pData);\n    }\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID               guid,\n            UINT                  DataSize,\n      const void*                 pData) final {\n      // WKPDID_D3DDebugObjectName, can't use directly due to MSVC link errors\n      if (guid == GUID{0x429b8c22,0x9188,0x4b0c,0x87,0x42,0xac,0xb0,0xbf,0x85,0xc2,0x00})\n        SetDebugName(static_cast<const char*>(pData));\n\n      return m_privateData.setData(\n        guid, DataSize, pData);\n    }\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID               guid,\n      const IUnknown*             pUnknown) final {\n      return m_privateData.setInterface(\n        guid, pUnknown);\n    }\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D11Device**        ppDevice) final {\n      *ppDevice = ref(GetParentInterface());\n    }\n\n    virtual void STDMETHODCALLTYPE SetDebugName(const char* pName) {\n      // No-op by default\n    }\n\n  protected:\n\n    ID3D11Device* GetParentInterface() const {\n      // We don't know the definition of ID3D11Device\n      // here, because D3D11Device includes this file.\n      return reinterpret_cast<ID3D11Device*>(m_parent);\n    }\n\n    D3D11Device* const m_parent;\n    \n  private:\n    \n    ComPrivateData m_privateData;\n    \n  };\n\n  \n  template<typename Base>\n  class D3D11DeviceChild : public D3D11DeviceObject<ComObject<Base>> {\n    \n  public:\n\n    D3D11DeviceChild(D3D11Device* pDevice)\n    : D3D11DeviceObject<ComObject<Base>>(pDevice) {\n\n    }\n\n    ULONG STDMETHODCALLTYPE AddRef() {\n      uint32_t refCount = this->m_refCount++;\n      if (unlikely(!refCount)) {\n        this->AddRefPrivate();\n        this->GetParentInterface()->AddRef();\n      }\n\n      return refCount + 1;\n    }\n    \n    ULONG STDMETHODCALLTYPE Release() {\n      uint32_t refCount = --this->m_refCount;\n      if (unlikely(!refCount)) {\n        auto* parent = this->GetParentInterface();\n        this->ReleasePrivate();\n        parent->Release();\n      }\n      return refCount;\n    }\n    \n  };\n\n  template<typename Base, typename Self>\n  class D3D11StateObject : public D3D11DeviceObject<Base> {\n    using Container = D3D11StateObjectSet<Self>;\n\n    constexpr static uint32_t AddRefValue = 1u;\n    constexpr static uint32_t ReleaseShift = 16u;\n    constexpr static uint32_t ReleaseValue = 1u << ReleaseShift;\n    constexpr static uint32_t RefMask = ReleaseValue - 1u;\n  public:\n\n    D3D11StateObject(D3D11Device* pDevice, Container* pContainer)\n    : D3D11DeviceObject<Base>(pDevice), m_container(pContainer) {\n\n    }\n\n    ULONG STDMETHODCALLTYPE AddRef() {\n      uint32_t refCount = m_refCount.fetch_add(1u, std::memory_order_acquire);\n\n      if (unlikely(!refCount)) {\n        AddRefPrivate();\n        this->GetParentInterface()->AddRef();\n      }\n\n      return refCount + 1;\n    }\n\n    ULONG STDMETHODCALLTYPE Release() {\n      uint32_t refCount = m_refCount.fetch_sub(1u, std::memory_order_release) - 1u;\n\n      if (unlikely(!refCount)) {\n        ID3D11Device* device = this->GetParentInterface();\n        ReleasePrivate();\n        device->Release();\n      }\n\n      return refCount;\n    }\n\n    void AddRefPrivate() {\n      // Since state objects manage themselves inside a look-up table, we need to\n      // atomically count both release and addrefs to support the following sequence\n      // of events:\n      // - Thread 0: Calls StateObjectSet::Create and takes lock\n      // - Thread 1: Calls StateObjectSet::Destroy, is now blocked\n      // - Thread 0: StateObjectSet::Create returns\n      // - Thread 0: Calls StateObjectSet::Destroy immmediately and takes lock\n      // - Thread 0: StateObjectSet::Destroy returns\n      // - Thread 1: Gets unblocked\n      // - Thread 1: StateObjectSet::Destroy returns\n      // In this scenario, only one thread can safely destroy the object.\n      uint32_t expected = m_refPrivate.load(std::memory_order_relaxed);\n      uint32_t desired;\n\n      do {\n        desired = ((expected + 1u) & RefMask) | (expected & ~RefMask);\n      } while (!m_refPrivate.compare_exchange_strong(expected, desired, std::memory_order_acquire));\n    }\n\n    void ReleasePrivate() {\n      uint32_t refCount = m_refPrivate.fetch_add(ReleaseValue, std::memory_order_release) + ReleaseValue;\n\n      uint32_t addRefCount = (refCount & RefMask) / AddRefValue;\n      uint32_t releaseCount = (refCount & ~RefMask) / ReleaseValue;\n\n      if (unlikely(addRefCount == releaseCount))\n        m_container->Destroy(static_cast<Self*>(this), refCount);\n    }\n\n    BOOL IsCurrent(uint32_t version) {\n      return m_refPrivate.load(std::memory_order_relaxed) == version;\n    }\n\n  private:\n\n    std::atomic<uint32_t> m_refCount = { 0u };\n    std::atomic<uint32_t> m_refPrivate = { 0u };\n    Container*            m_container = nullptr;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_enums.cpp",
    "content": "#include \"d3d11_enums.h\"\n\nstd::ostream& operator << (std::ostream& os, D3D_FEATURE_LEVEL e) {\n  switch (e) {\n    ENUM_NAME(D3D_FEATURE_LEVEL_9_1);\n    ENUM_NAME(D3D_FEATURE_LEVEL_9_2);\n    ENUM_NAME(D3D_FEATURE_LEVEL_9_3);\n    ENUM_NAME(D3D_FEATURE_LEVEL_10_0);\n    ENUM_NAME(D3D_FEATURE_LEVEL_10_1);\n    ENUM_NAME(D3D_FEATURE_LEVEL_11_0);\n    ENUM_NAME(D3D_FEATURE_LEVEL_11_1);\n    ENUM_NAME(D3D_FEATURE_LEVEL_12_0);\n    ENUM_NAME(D3D_FEATURE_LEVEL_12_1);\n    ENUM_DEFAULT(e);\n  }\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_enums.h",
    "content": "#pragma once\n\n#include <ostream>\n\n#include \"d3d11_include.h\"\n\nstd::ostream& operator << (std::ostream& os, D3D_FEATURE_LEVEL e);"
  },
  {
    "path": "src/d3d11/d3d11_features.cpp",
    "content": "#include <array>\n\n#include \"d3d11_features.h\"\n\nnamespace dxvk {\n\n  D3D11DeviceFeatures::D3D11DeviceFeatures() {\n\n  }\n\n\n  D3D11DeviceFeatures::D3D11DeviceFeatures(\n    const Rc<DxvkInstance>&     Instance,\n    const Rc<DxvkAdapter>&      Adapter,\n    const D3D11Options&         Options,\n          D3D_FEATURE_LEVEL     FeatureLevel)\n  : m_features    (Adapter->features()),\n    m_properties  (Adapter->deviceProperties()) {\n    // Assume no TBDR. DXVK does not optimize for TBDR architectures\n    // anyway, and D3D11 does not really provide meaningful support.\n    m_architectureInfo.TileBasedDeferredRenderer          = FALSE;\n\n    // D3D9 options. We unconditionally support all of these.\n    m_d3d9Options.FullNonPow2TextureSupport               = TRUE;\n\n    m_d3d9Options1.FullNonPow2TextureSupported            = TRUE;\n    m_d3d9Options1.DepthAsTextureWithLessEqualComparisonFilterSupported = TRUE;\n    m_d3d9Options1.SimpleInstancingSupported              = TRUE;\n    m_d3d9Options1.TextureCubeFaceRenderTargetWithNonCubeDepthStencilSupported = TRUE;\n\n    m_d3d9Shadow.SupportsDepthAsTextureWithLessEqualComparisonFilter = TRUE;\n\n    m_d3d9SimpleInstancing.SimpleInstancingSupported      = TRUE;\n\n    // D3D10 options. We unconditionally support compute shaders.\n    m_d3d10Options.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x = TRUE;\n\n    // D3D11.1 options. All of these are required for Feature Level 11_1.\n    auto sharedResourceTier = DetermineSharedResourceTier(Adapter, FeatureLevel);\n\n    bool hasDoublePrecisionSupport = m_features.core.features.shaderFloat64\n                                  && m_features.core.features.shaderInt64;\n\n    m_d3d11Options.DiscardAPIsSeenByDriver                = TRUE;\n    m_d3d11Options.FlagsForUpdateAndCopySeenByDriver      = TRUE;\n    m_d3d11Options.ClearView                              = TRUE;\n    m_d3d11Options.CopyWithOverlap                        = TRUE;\n    m_d3d11Options.ConstantBufferPartialUpdate            = TRUE;\n    m_d3d11Options.ConstantBufferOffsetting               = TRUE;\n    m_d3d11Options.MapNoOverwriteOnDynamicConstantBuffer  = TRUE;\n    m_d3d11Options.MapNoOverwriteOnDynamicBufferSRV       = TRUE;\n    m_d3d11Options.ExtendedResourceSharing                = sharedResourceTier > D3D11_SHARED_RESOURCE_TIER_0;\n\n    if (FeatureLevel >= D3D_FEATURE_LEVEL_10_0) {\n      m_d3d11Options.OutputMergerLogicOp                  = m_features.core.features.logicOp;\n      m_d3d11Options.MultisampleRTVWithForcedSampleCountOne = TRUE; // Not really\n    }\n\n    if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0) {\n      m_d3d11Options.UAVOnlyRenderingForcedSampleCount    = TRUE;\n      m_d3d11Options.SAD4ShaderInstructions               = TRUE;\n      m_d3d11Options.ExtendedDoublesShaderInstructions    = hasDoublePrecisionSupport;\n    }\n\n    // D3D11.2 options.\n    auto tiledResourcesTier = DetermineTiledResourcesTier(FeatureLevel);\n    m_d3d11Options1.TiledResourcesTier                    = tiledResourcesTier;\n    m_d3d11Options1.MinMaxFiltering                       = tiledResourcesTier >= D3D11_TILED_RESOURCES_TIER_2;\n    m_d3d11Options1.ClearViewAlsoSupportsDepthOnlyFormats = TRUE;\n\n    if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0)\n      m_d3d11Options1.MapOnDefaultBuffers                 = TRUE;\n\n    // D3D11.3 options\n    m_d3d11Options2.TypedUAVLoadAdditionalFormats         = DetermineUavExtendedTypedLoadSupport(Adapter, FeatureLevel);\n    m_d3d11Options2.ConservativeRasterizationTier         = DetermineConservativeRasterizationTier(FeatureLevel);\n    m_d3d11Options2.TiledResourcesTier                    = tiledResourcesTier;\n    m_d3d11Options2.StandardSwizzle                       = FALSE;\n    m_d3d11Options2.UnifiedMemoryArchitecture             = FALSE;\n\n    if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0)\n      m_d3d11Options2.MapOnDefaultTextures                = TRUE;\n\n    if (FeatureLevel >= D3D_FEATURE_LEVEL_11_1) {\n      m_d3d11Options2.ROVsSupported                       = m_features.extFragmentShaderInterlock.fragmentShaderPixelInterlock;\n      m_d3d11Options2.PSSpecifiedStencilRefSupported      = m_features.extShaderStencilExport;\n    }\n\n    // More D3D11.3 options\n    if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0) {\n      m_d3d11Options3.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer =\n        m_features.vk12.shaderOutputViewportIndex &&\n        m_features.vk12.shaderOutputLayer;\n    }\n\n    // D3D11.4 options\n    m_d3d11Options4.ExtendedNV12SharedTextureSupported    = sharedResourceTier > D3D11_SHARED_RESOURCE_TIER_0;\n\n    // More D3D11.4 options\n    m_d3d11Options5.SharedResourceTier                    = sharedResourceTier;\n\n    // Double-precision support\n    if (FeatureLevel >= D3D_FEATURE_LEVEL_11_0)\n      m_doubles.DoublePrecisionFloatShaderOps             = hasDoublePrecisionSupport;\n\n    // These numbers are not accurate, but we have no real way to query these\n    m_gpuVirtualAddress.MaxGPUVirtualAddressBitsPerResource = 32;\n    m_gpuVirtualAddress.MaxGPUVirtualAddressBitsPerProcess = 40;\n\n    // Marker support only depends on the debug utils extension\n    m_marker.Profile = !Instance->debugFlags().isClear();\n\n    // DXVK will keep all shaders in memory once created, and all Vulkan\n    // drivers that we know of that can run DXVK have an on-disk cache.\n    m_shaderCache.SupportFlags = D3D11_SHADER_CACHE_SUPPORT_AUTOMATIC_INPROC_CACHE\n                               | D3D11_SHADER_CACHE_SUPPORT_AUTOMATIC_DISK_CACHE;\n\n    // 16-bit precision is supported on capable devices\n    auto minPrecision = Adapter->features().core.features.shaderInt16 && Adapter->features().vk12.shaderFloat16\n      ? D3D11_SHADER_MIN_PRECISION_16_BIT\n      : D3D11_SHADER_MIN_PRECISION_SUPPORT(0u);\n\n    m_shaderMinPrecision.PixelShaderMinPrecision          = minPrecision;\n    m_shaderMinPrecision.AllOtherShaderStagesMinPrecision = minPrecision;\n\n    // Report native support for command lists by default. Deferred context\n    // usage can be beneficial for us as ExecuteCommandList has low overhead,\n    // and we avoid having to deal with known UpdateSubresource bugs this way.\n    m_threading.DriverConcurrentCreates                   = TRUE;\n    m_threading.DriverCommandLists                        = Options.exposeDriverCommandLists;\n  }\n\n\n  D3D11DeviceFeatures::~D3D11DeviceFeatures() {\n\n  }\n\n\n  HRESULT D3D11DeviceFeatures::GetFeatureData(\n          D3D11_FEATURE         Feature,\n          UINT                  FeatureDataSize,\n          void*                 pFeatureData) const {\n    switch (Feature) {\n      case D3D11_FEATURE_ARCHITECTURE_INFO:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_architectureInfo);\n      case D3D11_FEATURE_D3D9_OPTIONS:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d9Options);\n      case D3D11_FEATURE_D3D9_OPTIONS1:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d9Options1);\n      case D3D11_FEATURE_D3D9_SHADOW_SUPPORT:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d9Shadow);\n      case D3D11_FEATURE_D3D9_SIMPLE_INSTANCING_SUPPORT:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d9SimpleInstancing);\n      case D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d10Options);\n      case D3D11_FEATURE_D3D11_OPTIONS:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options);\n      case D3D11_FEATURE_D3D11_OPTIONS1:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options1);\n      case D3D11_FEATURE_D3D11_OPTIONS2:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options2);\n      case D3D11_FEATURE_D3D11_OPTIONS3:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options3);\n      case D3D11_FEATURE_D3D11_OPTIONS4:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options4);\n      case D3D11_FEATURE_D3D11_OPTIONS5:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_d3d11Options5);\n      case D3D11_FEATURE_DOUBLES:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_doubles);\n      case D3D11_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_gpuVirtualAddress);\n      case D3D11_FEATURE_MARKER_SUPPORT:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_marker);\n      case D3D11_FEATURE_SHADER_CACHE:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_shaderCache);\n      case D3D11_FEATURE_SHADER_MIN_PRECISION_SUPPORT:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_shaderMinPrecision);\n      case D3D11_FEATURE_THREADING:\n        return GetTypedFeatureData(FeatureDataSize, pFeatureData, &m_threading);\n      default:\n        Logger::err(str::format(\"D3D11: Unknown feature: \", Feature));\n        return E_INVALIDARG;\n    }\n  }\n\n\n  D3D_FEATURE_LEVEL D3D11DeviceFeatures::GetMaxFeatureLevel(\n    const Rc<DxvkInstance>&     Instance,\n    const Rc<DxvkAdapter>&      Adapter) {\n    D3D11Options options(Instance->config());\n    D3D11DeviceFeatures features(Instance, Adapter, options, D3D_FEATURE_LEVEL_12_1);\n    return features.GetMaxFeatureLevel();\n  }\n\n\n  D3D11_CONSERVATIVE_RASTERIZATION_TIER D3D11DeviceFeatures::DetermineConservativeRasterizationTier(\n          D3D_FEATURE_LEVEL     FeatureLevel) {\n    if (FeatureLevel < D3D_FEATURE_LEVEL_11_1\n     || !m_features.extConservativeRasterization)\n      return D3D11_CONSERVATIVE_RASTERIZATION_NOT_SUPPORTED;\n\n    // We don't really have a way to query uncertainty regions,\n    // so just check degenerate triangle behaviour\n    if (!m_properties.extConservativeRasterization.degenerateTrianglesRasterized)\n      return D3D11_CONSERVATIVE_RASTERIZATION_TIER_1;\n\n    // Inner coverage is required for Tier 3 support\n    if (!m_properties.extConservativeRasterization.fullyCoveredFragmentShaderInputVariable)\n      return D3D11_CONSERVATIVE_RASTERIZATION_TIER_2;\n\n    return D3D11_CONSERVATIVE_RASTERIZATION_TIER_3;\n  }\n\n\n  D3D11_SHARED_RESOURCE_TIER D3D11DeviceFeatures::DetermineSharedResourceTier(\n    const Rc<DxvkAdapter>&      Adapter,\n          D3D_FEATURE_LEVEL     FeatureLevel) {\n    static std::atomic<bool> s_errorShown = { false };\n\n    // Lie about supporting Tier 1 since that's the\n    // minimum required tier for Feature Level 11_1\n    if (!Adapter->features().khrExternalMemoryWin32) {\n      if (!s_errorShown.exchange(true))\n        Logger::warn(\"D3D11DeviceFeatures: External memory features not supported\");\n\n      return D3D11_SHARED_RESOURCE_TIER_1;\n    }\n\n    // Check support for extended formats. Ignore multi-plane\n    // formats here since driver support varies too much.\n    std::array<VkFormat, 30> requiredFormats = {{\n      VK_FORMAT_R16G16B16A16_SFLOAT,\n      VK_FORMAT_R32G32B32A32_SFLOAT,\n      VK_FORMAT_R32G32B32A32_UINT,\n      VK_FORMAT_R32G32B32A32_SINT,\n      VK_FORMAT_R16G16B16A16_SFLOAT,\n      VK_FORMAT_R16G16B16A16_UNORM,\n      VK_FORMAT_R16G16B16A16_UINT,\n      VK_FORMAT_R16G16B16A16_SNORM,\n      VK_FORMAT_R16G16B16A16_SINT,\n      VK_FORMAT_A2B10G10R10_UNORM_PACK32,\n      VK_FORMAT_A2B10G10R10_UINT_PACK32,\n      VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_R8G8B8A8_SRGB,\n      VK_FORMAT_R8G8B8A8_UINT,\n      VK_FORMAT_R8G8B8A8_SNORM,\n      VK_FORMAT_R8G8B8A8_SINT,\n      VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_B8G8R8A8_SRGB,\n      VK_FORMAT_R32_SFLOAT,\n      VK_FORMAT_R32_UINT,\n      VK_FORMAT_R32_SINT,\n      VK_FORMAT_R16_SFLOAT,\n      VK_FORMAT_R16_UNORM,\n      VK_FORMAT_R16_UINT,\n      VK_FORMAT_R16_SNORM,\n      VK_FORMAT_R16_SINT,\n      VK_FORMAT_R8_UNORM,\n      VK_FORMAT_R8_UINT,\n      VK_FORMAT_R8_SNORM,\n      VK_FORMAT_R8_SINT,\n    }};\n\n    bool allKmtHandlesSupported = true;\n    bool allNtHandlesSupported = true;\n\n    for (auto f : requiredFormats) {\n      allKmtHandlesSupported &= CheckFormatSharingSupport(Adapter, f, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT);\n      allNtHandlesSupported &= CheckFormatSharingSupport(Adapter, f, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT);\n    }\n\n    // Again, lie about at least tier 1 support\n    if (!allKmtHandlesSupported) {\n      if (!s_errorShown.exchange(true))\n        Logger::warn(\"D3D11DeviceFeatures: Some formats not supported for resource sharing\");\n      return D3D11_SHARED_RESOURCE_TIER_1;\n    }\n\n    // Tier 2 requires all the above formats to be shareable\n    // with NT handles in order to support D3D12 interop\n    if (!allNtHandlesSupported)\n      return D3D11_SHARED_RESOURCE_TIER_1;\n\n    // Tier 3 additionally requires R11G11B10 to be\n    // shareable with D3D12\n    if (!CheckFormatSharingSupport(Adapter, VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT))\n      return D3D11_SHARED_RESOURCE_TIER_2;\n\n    return D3D11_SHARED_RESOURCE_TIER_3;\n  }\n\n\n  D3D11_TILED_RESOURCES_TIER D3D11DeviceFeatures::DetermineTiledResourcesTier(\n          D3D_FEATURE_LEVEL     FeatureLevel) {\n    if (FeatureLevel < D3D_FEATURE_LEVEL_11_0\n     || !m_features.core.features.sparseBinding\n     || !m_features.core.features.sparseResidencyBuffer\n     || !m_features.core.features.sparseResidencyImage2D\n     || !m_features.core.features.sparseResidencyAliased\n     || !m_properties.core.properties.sparseProperties.residencyStandard2DBlockShape)\n      return D3D11_TILED_RESOURCES_NOT_SUPPORTED;\n\n    if (FeatureLevel < D3D_FEATURE_LEVEL_11_1\n     || !m_features.core.features.shaderResourceResidency\n     || !m_features.core.features.shaderResourceMinLod\n     || !m_features.vk12.samplerFilterMinmax\n     || !m_properties.vk12.filterMinmaxSingleComponentFormats\n     || !m_properties.core.properties.sparseProperties.residencyNonResidentStrict\n     || m_properties.core.properties.sparseProperties.residencyAlignedMipSize)\n      return D3D11_TILED_RESOURCES_TIER_1;\n\n    if (!m_features.core.features.sparseResidencyImage3D\n     || !m_properties.core.properties.sparseProperties.residencyStandard3DBlockShape)\n      return D3D11_TILED_RESOURCES_TIER_2;\n\n    return D3D11_TILED_RESOURCES_TIER_3;\n  }\n\n\n  BOOL D3D11DeviceFeatures::DetermineUavExtendedTypedLoadSupport(\n    const Rc<DxvkAdapter>&      Adapter,\n          D3D_FEATURE_LEVEL     FeatureLevel) {\n    static const std::array<VkFormat, 18> s_formats = {{\n      VK_FORMAT_R32_SFLOAT,\n      VK_FORMAT_R32_UINT,\n      VK_FORMAT_R32_SINT,\n      VK_FORMAT_R32G32B32A32_SFLOAT,\n      VK_FORMAT_R32G32B32A32_UINT,\n      VK_FORMAT_R32G32B32A32_SINT,\n      VK_FORMAT_R16G16B16A16_SFLOAT,\n      VK_FORMAT_R16G16B16A16_UINT,\n      VK_FORMAT_R16G16B16A16_SINT,\n      VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_R8G8B8A8_UINT,\n      VK_FORMAT_R8G8B8A8_SINT,\n      VK_FORMAT_R16_SFLOAT,\n      VK_FORMAT_R16_UINT,\n      VK_FORMAT_R16_SINT,\n      VK_FORMAT_R8_UNORM,\n      VK_FORMAT_R8_UINT,\n      VK_FORMAT_R8_SINT,\n    }};\n\n    if (FeatureLevel < D3D_FEATURE_LEVEL_11_0)\n      return FALSE;\n\n    for (auto f : s_formats) {\n      DxvkFormatFeatures features = Adapter->getFormatFeatures(f);\n      VkFormatFeatureFlags2 imgFeatures = features.optimal | features.linear;\n\n      if (!(imgFeatures & VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT))\n        return FALSE;\n    }\n\n    return TRUE;\n  }\n\n\n  BOOL D3D11DeviceFeatures::CheckFormatSharingSupport(\n    const Rc<DxvkAdapter>&      Adapter,\n          VkFormat              Format,\n          VkExternalMemoryHandleTypeFlagBits HandleType) {\n    DxvkFormatQuery query = { };\n    query.format = Format;\n    query.type = VK_IMAGE_TYPE_2D;\n    query.tiling = VK_IMAGE_TILING_OPTIMAL;\n    query.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    query.handleType = HandleType;\n\n    constexpr VkExternalMemoryFeatureFlags featureMask\n      = VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT\n      | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;\n\n    auto limits = Adapter->getFormatLimits(query);\n    return limits && (limits->externalFeatures & featureMask);\n  }\n\n\n  D3D_FEATURE_LEVEL D3D11DeviceFeatures::GetMaxFeatureLevel() const {\n    // Check Feature Level 11_0 features\n    if (!m_features.core.features.drawIndirectFirstInstance\n     || !m_features.core.features.fragmentStoresAndAtomics\n     || !m_features.core.features.multiDrawIndirect\n     || !m_features.core.features.tessellationShader)\n      return D3D_FEATURE_LEVEL_10_1;\n\n    // Check Feature Level 11_1 features\n    if (!m_d3d11Options.OutputMergerLogicOp\n     || !m_features.core.features.vertexPipelineStoresAndAtomics)\n      return D3D_FEATURE_LEVEL_11_0;\n\n    // Check Feature Level 12_0 features\n    if (m_d3d11Options2.TiledResourcesTier < D3D11_TILED_RESOURCES_TIER_2\n     || !m_d3d11Options2.TypedUAVLoadAdditionalFormats)\n      return D3D_FEATURE_LEVEL_11_1;\n\n    // Check Feature Level 12_1 features\n    if (!m_d3d11Options2.ConservativeRasterizationTier\n     || !m_d3d11Options2.ROVsSupported)\n      return D3D_FEATURE_LEVEL_12_0;\n\n    return D3D_FEATURE_LEVEL_12_1;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_features.h",
    "content": "#pragma once\n\n#include \"d3d11_include.h\"\n#include \"d3d11_options.h\"\n\n#include \"../dxvk/dxvk_adapter.h\"\n#include \"../dxvk/dxvk_instance.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Device features\n   *\n   * Stores D3D device feature structs.\n   */\n  class D3D11DeviceFeatures {\n\n  public:\n\n    D3D11DeviceFeatures();\n\n    D3D11DeviceFeatures(\n      const Rc<DxvkInstance>&     Instance,\n      const Rc<DxvkAdapter>&      Adapter,\n      const D3D11Options&         Options,\n            D3D_FEATURE_LEVEL     FeatureLevel);\n\n    ~D3D11DeviceFeatures();\n\n    /**\n     * \\brief Retrieves feature support data\n     *\n     * \\param [in] Feature D3D feature to query\n     * \\param [in] FeatureDataSize Data size, in bytes\n     * \\param [out] pFeatureData Data\n     * \\returns Status of the operation\n     */\n    HRESULT GetFeatureData(\n            D3D11_FEATURE         Feature,\n            UINT                  FeatureDataSize,\n            void*                 pFeatureData) const;\n\n    /**\n     * \\brief Queries tiled resources tier\n     * \\returns Tiled resources tier\n     */\n    D3D11_TILED_RESOURCES_TIER GetTiledResourcesTier() const {\n      return m_d3d11Options2.TiledResourcesTier;\n    }\n\n    /**\n     * \\brief Queries conservative rasterization tier\n     * \\returns Conservative rasterization tier\n     */\n    D3D11_CONSERVATIVE_RASTERIZATION_TIER GetConservativeRasterizationTier() const {\n      return m_d3d11Options2.ConservativeRasterizationTier;\n    }\n\n    /**\n     * \\brief Tests maximum supported feature level\n     *\n     * \\param [in] Instance DXVK instance\n     * \\param [in] Adapter DXVK adapter\n     * \\returns Highest supported feature level\n     */\n    static D3D_FEATURE_LEVEL GetMaxFeatureLevel(\n      const Rc<DxvkInstance>&     Instance,\n      const Rc<DxvkAdapter>&      Adapter);\n\n  private:\n\n    DxvkDeviceFeatures  m_features;\n    DxvkDeviceInfo      m_properties;\n\n    D3D11_FEATURE_DATA_ARCHITECTURE_INFO              m_architectureInfo            = { };\n    D3D11_FEATURE_DATA_D3D9_OPTIONS                   m_d3d9Options                 = { };\n    D3D11_FEATURE_DATA_D3D9_OPTIONS1                  m_d3d9Options1                = { };\n    D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT            m_d3d9Shadow                  = { };\n    D3D11_FEATURE_DATA_D3D9_SIMPLE_INSTANCING_SUPPORT m_d3d9SimpleInstancing        = { };\n    D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS       m_d3d10Options                = { };\n    D3D11_FEATURE_DATA_D3D11_OPTIONS                  m_d3d11Options                = { };\n    D3D11_FEATURE_DATA_D3D11_OPTIONS1                 m_d3d11Options1               = { };\n    D3D11_FEATURE_DATA_D3D11_OPTIONS2                 m_d3d11Options2               = { };\n    D3D11_FEATURE_DATA_D3D11_OPTIONS3                 m_d3d11Options3               = { };\n    D3D11_FEATURE_DATA_D3D11_OPTIONS4                 m_d3d11Options4               = { };\n    D3D11_FEATURE_DATA_D3D11_OPTIONS5                 m_d3d11Options5               = { };\n    D3D11_FEATURE_DATA_DOUBLES                        m_doubles                     = { };\n    D3D11_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT    m_gpuVirtualAddress           = { };\n    D3D11_FEATURE_DATA_MARKER_SUPPORT                 m_marker                      = { };\n    D3D11_FEATURE_DATA_SHADER_CACHE                   m_shaderCache                 = { };\n    D3D11_FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT   m_shaderMinPrecision          = { };\n    D3D11_FEATURE_DATA_THREADING                      m_threading                   = { };\n\n    template<typename T>\n    static HRESULT GetTypedFeatureData(UINT Size, void* pDstData, const T* pSrcData) {\n      if (Size != sizeof(T))\n        return E_INVALIDARG;\n\n      *(reinterpret_cast<T*>(pDstData)) = *pSrcData;\n      return S_OK;\n    }\n\n    D3D11_CONSERVATIVE_RASTERIZATION_TIER DetermineConservativeRasterizationTier(\n            D3D_FEATURE_LEVEL     FeatureLevel);\n\n    D3D11_SHARED_RESOURCE_TIER DetermineSharedResourceTier(\n      const Rc<DxvkAdapter>&      Adapter,\n            D3D_FEATURE_LEVEL     FeatureLevel);\n\n    D3D11_TILED_RESOURCES_TIER DetermineTiledResourcesTier(\n            D3D_FEATURE_LEVEL     FeatureLevel);\n\n    BOOL DetermineUavExtendedTypedLoadSupport(\n      const Rc<DxvkAdapter>&      Adapter,\n            D3D_FEATURE_LEVEL     FeatureLevel);\n\n    BOOL CheckFormatSharingSupport(\n      const Rc<DxvkAdapter>&      Adapter,\n            VkFormat              Format,\n            VkExternalMemoryHandleTypeFlagBits HandleType);\n\n    D3D_FEATURE_LEVEL GetMaxFeatureLevel() const;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d11/d3d11_fence.cpp",
    "content": "#include \"d3d11_fence.h\"\n#include \"d3d11_device.h\"\n#include \"../util/util_win32_compat.h\"\n\nnamespace dxvk {\n  \n  D3D11Fence::D3D11Fence(\n          D3D11Device*        pDevice,\n          UINT64              InitialValue,\n          D3D11_FENCE_FLAG    Flags,\n          HANDLE              hFence)\n  : D3D11DeviceChild<ID3D11Fence>(pDevice),\n    m_flags(Flags), m_destructionNotifier(this) {\n    DxvkFenceCreateInfo fenceInfo = { };\n    fenceInfo.initialValue = InitialValue;\n\n    if (Flags & D3D11_FENCE_FLAG_SHARED) {\n      fenceInfo.sharedType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D11_FENCE_BIT;\n\n      if (!hFence)\n        hFence = INVALID_HANDLE_VALUE;\n\n      fenceInfo.sharedHandle = hFence;\n    }\n\n    if (Flags & ~D3D11_FENCE_FLAG_SHARED)\n      Logger::err(str::format(\"Fence flags 0x\", std::hex, Flags, \" not supported\"));\n\n    m_fence = pDevice->GetDXVKDevice()->createFence(fenceInfo);\n  }\n\n\n  D3D11Fence::~D3D11Fence() {\n\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11Fence::QueryInterface(\n          REFIID              riid,\n          void**              ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11Fence)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11Fence), riid)) {\n      Logger::warn(\"D3D11Fence: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11Fence::CreateSharedHandle(\n    const SECURITY_ATTRIBUTES* pAttributes,\n          DWORD               dwAccess,\n          LPCWSTR             lpName,\n          HANDLE*             pHandle) {\n    InitReturnPtr(pHandle);\n    if (!(m_flags & D3D11_FENCE_FLAG_SHARED))\n      return E_INVALIDARG;\n\n    OBJECT_ATTRIBUTES attr = { };\n    attr.Length = sizeof(attr);\n    attr.SecurityDescriptor = const_cast<SECURITY_ATTRIBUTES*>(pAttributes);\n\n    WCHAR buffer[MAX_PATH];\n    UNICODE_STRING name_str;\n    if (lpName) {\n        DWORD session, len, name_len = wcslen(lpName);\n\n        ProcessIdToSessionId(GetCurrentProcessId(), &session);\n        len = swprintf(buffer, ARRAYSIZE(buffer), L\"\\\\Sessions\\\\%u\\\\BaseNamedObjects\\\\\", session);\n        memcpy(buffer + len, lpName, (name_len + 1) * sizeof(WCHAR));\n        name_str.MaximumLength = name_str.Length = (len + name_len) * sizeof(WCHAR);\n        name_str.MaximumLength += sizeof(WCHAR);\n        name_str.Buffer = buffer;\n\n        attr.ObjectName = &name_str;\n        attr.Attributes = OBJ_CASE_INSENSITIVE;\n    }\n\n    D3DKMT_HANDLE local = m_fence->kmtLocal();\n    if (!D3DKMTShareObjects(1, &local, &attr, dwAccess, pHandle))\n      return S_OK;\n\n    /* try legacy Proton shared resource implementation */\n\n    if (pAttributes)\n      Logger::warn(str::format(\"CreateSharedHandle: attributes \", pAttributes, \" not handled\"));\n    if (dwAccess)\n      Logger::warn(str::format(\"CreateSharedHandle: access \", dwAccess, \" not handled\"));\n    if (lpName)\n      Logger::warn(str::format(\"CreateSharedHandle: name \", dxvk::str::fromws(lpName), \" not handled\"));\n\n    HANDLE sharedHandle = m_fence->sharedHandle();\n    if (sharedHandle == INVALID_HANDLE_VALUE)\n      return E_INVALIDARG;\n\n    *pHandle = sharedHandle;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11Fence::SetEventOnCompletion(\n          UINT64              Value,\n          HANDLE              hEvent) {\n    if (hEvent) {\n      m_fence->enqueueWait(Value, [hEvent] {\n        SetEvent(hEvent);\n      });\n    } else {\n      m_fence->wait(Value);\n    }\n    return S_OK;\n  }\n\n\n  UINT64 STDMETHODCALLTYPE D3D11Fence::GetCompletedValue() {\n    // TODO in the case of rewinds, the stored value may be higher.\n    // For shared fences, calling vkGetSemaphoreCounterValue here could alleviate the issue.\n\n    return m_fence->getValue();\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_fence.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_fence.h\"\n#include \"../dxvk/dxvk_gpu_query.h\"\n\n#include \"d3d11_device_child.h\"\n\nnamespace dxvk {\n  \n  class D3D11Fence : public D3D11DeviceChild<ID3D11Fence> {\n\n  public:\n    \n    D3D11Fence(\n            D3D11Device*        pDevice,\n            UINT64              InitialValue,\n            D3D11_FENCE_FLAG    Flags,\n            HANDLE              hFence);\n\n    ~D3D11Fence();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID              riid,\n            void**              ppvObject);\n\n    HRESULT STDMETHODCALLTYPE CreateSharedHandle(\n      const SECURITY_ATTRIBUTES* pAttributes,\n            DWORD               dwAccess,\n            LPCWSTR             lpName,\n            HANDLE*             pHandle);\n\n    HRESULT STDMETHODCALLTYPE SetEventOnCompletion(\n            UINT64              Value,\n            HANDLE              hEvent);\n\n    UINT64 STDMETHODCALLTYPE GetCompletedValue();\n\n    Rc<DxvkFence> GetFence() const {\n      return m_fence;\n    }\n    \n  private:\n    \n    Rc<DxvkFence>           m_fence;\n    D3D11_FENCE_FLAG        m_flags = D3D11_FENCE_FLAG_NONE;\n\n    D3DDestructionNotifier  m_destructionNotifier;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_gdi.cpp",
    "content": "#include \"d3d11_context.h\"\n#include \"d3d11_device.h\"\n#include \"d3d11_gdi.h\"\n\n#include \"../util/util_gdi.h\"\n#include \"../util/util_win32_compat.h\"\n\nnamespace dxvk {\n  \n  D3D11GDISurface::D3D11GDISurface(\n          ID3D11Resource*     pResource,\n          UINT                Subresource)\n  : m_resource    (pResource),\n    m_subresource (Subresource),\n    m_readback    (nullptr),\n    m_hdc         (nullptr),\n    m_hbitmap     (nullptr),\n    m_acquired    (false) {\n    // Allocate memory for the bitmap\n    auto tex = GetCommonTexture(m_resource)->Desc();\n    m_data.resize(tex->Width * tex->Height);\n\n    // Create GDI DC\n    D3DKMT_CREATEDCFROMMEMORY desc;\n    desc.pMemory     = m_data.data();\n    desc.Format      = D3DFMT_A8R8G8B8;\n    desc.Width       = tex->Width;\n    desc.Height      = tex->Height;\n    desc.Pitch       = tex->Width * sizeof(uint32_t);\n    desc.hDeviceDc   = CreateCompatibleDC(nullptr);\n    desc.pColorTable = nullptr;\n    desc.hDc         = nullptr;\n    desc.hBitmap     = nullptr;\n\n    if (D3DKMTCreateDCFromMemory(&desc))\n      Logger::err(str::format(\"D3D11: Failed to create GDI DC\"));\n    \n    m_hdc     = desc.hDc;\n    m_hbitmap = desc.hBitmap;\n  }\n\n\n  D3D11GDISurface::~D3D11GDISurface() {\n    if (m_readback)\n      m_readback->Release();\n\n    D3DKMT_DESTROYDCFROMMEMORY desc;\n    desc.hDc     = m_hdc;\n    desc.hBitmap = m_hbitmap;\n    D3DKMTDestroyDCFromMemory(&desc);\n  }\n\n  \n  HRESULT D3D11GDISurface::Acquire(BOOL Discard, HDC* phdc) {\n    if (!phdc)\n      return E_INVALIDARG;\n    \n    *phdc = nullptr;\n\n    if (m_acquired)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    if (!Discard) {\n      // Create a staging resource that we can map\n      if (!m_readback && FAILED(CreateReadbackResource())) {\n        Logger::err(\"D3D11: Failed to create GDI readback resource\");\n        return E_FAIL;\n      }\n\n      // Copy subresource to staging image\n      Com<ID3D11Device>         device;\n      Com<ID3D11DeviceContext>  context;\n\n      m_resource->GetDevice(&device);\n      device->GetImmediateContext(&context);\n\n      context->CopySubresourceRegion(m_readback, 0,\n        0, 0, 0, m_resource, m_subresource, nullptr);\n\n      // Copy staging image to DC memory\n      auto tex       = GetCommonTexture(m_resource)->Desc();\n      auto rowData   = reinterpret_cast<char*>(m_data.data());\n      auto rowLength = sizeof(uint32_t) * tex->Width;\n\n      D3D11_MAPPED_SUBRESOURCE sr;\n      context->Map(m_readback, 0, D3D11_MAP_READ, 0, &sr);\n\n      for (uint32_t i = 0; i < tex->Height; i++) {\n        std::memcpy(rowData + rowLength * i,\n          reinterpret_cast<const char*>(sr.pData) + sr.RowPitch * i,\n          rowLength);\n      }\n\n      context->Unmap(m_readback, 0);\n    }\n    \n    m_acquired = true;\n    *phdc      = m_hdc;\n    return S_OK;\n  }\n\n  \n  HRESULT D3D11GDISurface::Release(const RECT* pDirtyRect) {\n    if (!m_acquired)\n      return DXGI_ERROR_INVALID_CALL;\n\n    Com<ID3D11Device>         device;\n    Com<ID3D11DeviceContext>  context;\n\n    m_resource->GetDevice(&device);\n    device->GetImmediateContext(&context);\n\n    // Commit changes made to the DC\n    auto tex = GetCommonTexture(m_resource)->Desc();\n\n    RECT rect;\n\n    if (pDirtyRect) {\n      rect.left   = std::max<LONG>(pDirtyRect->left,   0);\n      rect.top    = std::max<LONG>(pDirtyRect->top,    0);\n      rect.right  = std::min<LONG>(pDirtyRect->right,  tex->Width);\n      rect.bottom = std::min<LONG>(pDirtyRect->bottom, tex->Height);\n    } else {\n      rect.left   = 0;\n      rect.top    = 0;\n      rect.right  = tex->Width;\n      rect.bottom = tex->Height;\n    }\n\n    if (rect.left < rect.right && rect.top < rect.bottom) {\n      D3D11_BOX box;\n      box.left    = rect.left;\n      box.top     = rect.top;\n      box.front   = 0;\n      box.right   = rect.right;\n      box.bottom  = rect.bottom;\n      box.back    = 1;\n\n      context->UpdateSubresource(m_resource, m_subresource,\n        &box, m_data.data() + rect.left,\n        sizeof(uint32_t) * tex->Width,\n        sizeof(uint32_t) * tex->Width * tex->Height);\n    }\n    \n    m_acquired = false;\n    return S_OK;\n  }\n\n\n  HRESULT D3D11GDISurface::CreateReadbackResource() {\n    auto tex = GetCommonTexture(m_resource);\n\n    Com<ID3D11Device> device;\n    m_resource->GetDevice(&device);\n\n    D3D11_RESOURCE_DIMENSION dim = { };\n    m_resource->GetType(&dim);\n\n    VkImageSubresource sr = tex->GetSubresourceFromIndex(\n      VK_IMAGE_ASPECT_COLOR_BIT, m_subresource);\n\n    switch (dim) {\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        D3D11_TEXTURE1D_DESC desc;\n        desc.Width     = std::max<UINT>(tex->Desc()->Width >> sr.mipLevel, 1);\n        desc.MipLevels = 1;\n        desc.ArraySize = 1;\n        desc.Format    = tex->Desc()->Format;\n        desc.Usage     = D3D11_USAGE_STAGING;\n        desc.BindFlags = 0;\n        desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;\n        desc.MiscFlags = 0;\n        \n        ID3D11Texture1D* tex1D = nullptr;\n        HRESULT hr = device->CreateTexture1D(&desc, nullptr, &tex1D);\n        m_readback = tex1D;\n        return hr;\n      } break;\n\n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        D3D11_TEXTURE2D_DESC desc;\n        desc.Width     = std::max<UINT>(tex->Desc()->Width  >> sr.mipLevel, 1);\n        desc.Height    = std::max<UINT>(tex->Desc()->Height >> sr.mipLevel, 1);\n        desc.MipLevels = 1;\n        desc.ArraySize = 1;\n        desc.Format    = tex->Desc()->Format;\n        desc.SampleDesc= { 1, 0 };\n        desc.Usage     = D3D11_USAGE_STAGING;\n        desc.BindFlags = 0;\n        desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;\n        desc.MiscFlags = 0;\n        \n        ID3D11Texture2D* tex2D = nullptr;\n        HRESULT hr = device->CreateTexture2D(&desc, nullptr, &tex2D);\n        m_readback = tex2D;\n        return hr;\n      } break;\n\n      default:\n        return E_INVALIDARG;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_gdi.h",
    "content": "#pragma once\n\n#include <vector>\n\n#include \"d3d11_include.h\"\n\nnamespace dxvk {\n  \n  class D3D11GDISurface {\n\n  public:\n\n    D3D11GDISurface(\n            ID3D11Resource*     pResource,\n            UINT                Subresource);\n\n    ~D3D11GDISurface();\n\n    HRESULT Acquire(\n            BOOL                Discard,\n            HDC*                phdc);\n\n    HRESULT Release(\n      const RECT*               pDirtyRect);\n\n  private:\n\n    ID3D11Resource* m_resource;\n    uint32_t        m_subresource;\n    ID3D11Resource* m_readback;\n    HDC             m_hdc;\n    HANDLE          m_hbitmap;\n    bool            m_acquired;\n\n    std::vector<uint32_t> m_data;\n\n    HRESULT CreateReadbackResource();\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_include.h",
    "content": "#pragma once\n\n#include \"../dxgi/dxgi_include.h\"\n\n#include <d3d11_4.h>\n"
  },
  {
    "path": "src/d3d11/d3d11_initializer.cpp",
    "content": "#include <cstring>\n\n#include \"d3d11_context_imm.h\"\n#include \"d3d11_device.h\"\n#include \"d3d11_initializer.h\"\n\nnamespace dxvk {\n\n  D3D11Initializer::D3D11Initializer(\n          D3D11Device*                pParent)\n  : m_parent(pParent),\n    m_device(pParent->GetDXVKDevice()),\n    m_stagingBuffer(m_device, StagingBufferSize),\n    m_stagingSignal(new sync::Fence(0)),\n    m_csChunk(m_parent->AllocCsChunk(DxvkCsChunkFlag::SingleUse)) {\n\n  }\n\n  \n  D3D11Initializer::~D3D11Initializer() {\n\n  }\n\n\n  void D3D11Initializer::NotifyContextFlush() {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    NotifyContextFlushLocked();\n  }\n\n\n  void D3D11Initializer::InitBuffer(\n          D3D11Buffer*                pBuffer,\n    const D3D11_SUBRESOURCE_DATA*     pInitialData) {\n    if (!(pBuffer->Desc()->MiscFlags & D3D11_RESOURCE_MISC_TILED)) {\n      VkMemoryPropertyFlags memFlags = pBuffer->GetBuffer()->memFlags();\n\n      (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)\n        ? InitHostVisibleBuffer(pBuffer, pInitialData)\n        : InitDeviceLocalBuffer(pBuffer, pInitialData);\n    }\n  }\n  \n\n  void D3D11Initializer::InitTexture(\n          D3D11CommonTexture*         pTexture,\n    const D3D11_SUBRESOURCE_DATA*     pInitialData) {\n    if (pTexture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_TILED)\n      InitTiledTexture(pTexture);\n    else if (pTexture->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT)\n      InitHostVisibleTexture(pTexture, pInitialData);\n    else\n      InitDeviceLocalTexture(pTexture, pInitialData);\n\n    SyncSharedTexture(pTexture);\n  }\n\n\n  void D3D11Initializer::InitUavCounter(\n          D3D11UnorderedAccessView*   pUav) {\n    auto counterView = pUav->GetCounterView();\n\n    if (counterView == nullptr)\n      return;\n\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    m_transferCommands += 1;\n\n    EmitCs([\n      cCounterSlice = DxvkBufferSlice(counterView)\n    ] (DxvkContext* ctx) {\n      const uint32_t zero = 0;\n      ctx->updateBuffer(\n        cCounterSlice.buffer(),\n        cCounterSlice.offset(),\n        sizeof(zero), &zero);\n    });\n  }\n\n\n  void D3D11Initializer::InitShaderIcb(\n          D3D11CommonShader*          pShader,\n          size_t                      IcbSize,\n    const void*                       pIcbData) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    m_transferCommands += 1;\n\n    auto icbSlice = pShader->GetIcb();\n    auto srcSlice = m_stagingBuffer.alloc(icbSlice.length());\n\n    std::memcpy(srcSlice.mapPtr(0), pIcbData, IcbSize);\n\n    if (IcbSize < icbSlice.length())\n      std::memset(srcSlice.mapPtr(IcbSize), 0, icbSlice.length() - IcbSize);\n\n    EmitCs([\n      cIcbSlice = std::move(icbSlice),\n      cSrcSlice = std::move(srcSlice)\n    ] (DxvkContext* ctx) {\n      ctx->copyBuffer(cIcbSlice.buffer(), cIcbSlice.offset(),\n        cSrcSlice.buffer(), cSrcSlice.offset(), cIcbSlice.length());\n    });\n\n    ThrottleAllocationLocked();\n  }\n\n\n  void D3D11Initializer::InitDeviceLocalBuffer(\n          D3D11Buffer*                pBuffer,\n    const D3D11_SUBRESOURCE_DATA*     pInitialData) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    Rc<DxvkBuffer> buffer = pBuffer->GetBuffer();\n\n    if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) {\n      auto stagingSlice = m_stagingBuffer.alloc(buffer->info().size);\n      std::memcpy(stagingSlice.mapPtr(0), pInitialData->pSysMem, stagingSlice.length());\n\n      m_transferCommands += 1;\n\n      EmitCs([\n        cBuffer       = buffer,\n        cStagingSlice = std::move(stagingSlice)\n      ] (DxvkContext* ctx) {\n        ctx->uploadBuffer(cBuffer,\n          cStagingSlice.buffer(),\n          cStagingSlice.offset());\n      });\n    } else {\n      m_transferCommands += 1;\n\n      EmitCs([\n        cBuffer       = buffer\n      ] (DxvkContext* ctx) {\n        ctx->initBuffer(cBuffer);\n      });\n    }\n\n    ThrottleAllocationLocked();\n  }\n\n\n  void D3D11Initializer::InitHostVisibleBuffer(\n          D3D11Buffer*                pBuffer,\n    const D3D11_SUBRESOURCE_DATA*     pInitialData) {\n    // If the buffer is mapped, we can write data directly\n    // to the mapped memory region instead of doing it on\n    // the GPU. Same goes for zero-initialization.\n    if (pInitialData && pInitialData->pSysMem)\n      std::memcpy(pBuffer->GetMapPtr(), pInitialData->pSysMem, pBuffer->Desc()->ByteWidth);\n    else\n      std::memset(pBuffer->GetMapPtr(), 0, pBuffer->Desc()->ByteWidth);\n  }\n\n\n  void D3D11Initializer::InitDeviceLocalTexture(\n          D3D11CommonTexture*         pTexture,\n    const D3D11_SUBRESOURCE_DATA*     pInitialData) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    \n    // Image migt be null if this is a staging resource\n    Rc<DxvkImage> image = pTexture->GetImage();\n    auto desc = pTexture->Desc();\n\n    VkFormat packedFormat = m_parent->LookupPackedFormat(desc->Format, pTexture->GetFormatMode()).Format;\n    auto formatInfo = lookupFormatInfo(packedFormat);\n\n    if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) {\n      // Compute data size for all subresources and allocate staging buffer memory\n      DxvkBufferSlice stagingSlice;\n\n      if (pTexture->HasImage()) {\n        VkDeviceSize dataSize = 0u;\n\n        for (uint32_t mip = 0; mip < image->info().mipLevels; mip++) {\n          dataSize += image->info().numLayers * align(util::computeImageDataSize(\n            packedFormat, image->mipLevelExtent(mip), formatInfo->aspectMask), CACHE_LINE_SIZE);\n        }\n\n        stagingSlice = m_stagingBuffer.alloc(dataSize);\n      }\n\n      // Copy initial data for each subresource into the staging buffer,\n      // as well as the mapped per-subresource buffers if available.\n      VkDeviceSize dataOffset = 0u;\n\n      for (uint32_t mip = 0; mip < desc->MipLevels; mip++) {\n        for (uint32_t layer = 0; layer < desc->ArraySize; layer++) {\n          uint32_t index = D3D11CalcSubresource(mip, layer, desc->MipLevels);\n          VkExtent3D mipLevelExtent = pTexture->MipLevelExtent(mip);\n\n          if (pTexture->HasImage()) {\n            VkDeviceSize mipSizePerLayer = util::computeImageDataSize(\n              packedFormat, image->mipLevelExtent(mip), formatInfo->aspectMask);\n\n            m_transferCommands += 1;\n\n            util::packImageData(stagingSlice.mapPtr(dataOffset),\n              pInitialData[index].pSysMem, pInitialData[index].SysMemPitch, pInitialData[index].SysMemSlicePitch,\n              0, 0, pTexture->GetVkImageType(), mipLevelExtent, 1, formatInfo, formatInfo->aspectMask);\n\n            dataOffset += align(mipSizePerLayer, CACHE_LINE_SIZE);\n          }\n\n          if (pTexture->HasPersistentBuffers()) {\n            util::packImageData(pTexture->GetMapPtr(index, 0),\n              pInitialData[index].pSysMem, pInitialData[index].SysMemPitch, pInitialData[index].SysMemSlicePitch,\n              0, 0, pTexture->GetVkImageType(), mipLevelExtent, 1, formatInfo, formatInfo->aspectMask);\n          }\n        }\n      }\n\n      // Upload all subresources of the image in one go\n      if (pTexture->HasImage()) {\n        EmitCs([\n          cImage        = std::move(image),\n          cStagingSlice = std::move(stagingSlice),\n          cFormat       = packedFormat\n        ] (DxvkContext* ctx) {\n          ctx->uploadImage(cImage,\n            cStagingSlice.buffer(),\n            cStagingSlice.offset(),\n            CACHE_LINE_SIZE, cFormat);\n        });\n      }\n    } else {\n      if (pTexture->HasImage()) {\n        m_transferCommands += 1;\n        \n        // While the Microsoft docs state that resource contents are\n        // undefined if no initial data is provided, some applications\n        // expect a resource to be pre-cleared.\n        EmitCs([\n          cImage = std::move(image)\n        ] (DxvkContext* ctx) {\n          ctx->initImage(cImage, VK_IMAGE_LAYOUT_UNDEFINED);\n        });\n      }\n\n      if (pTexture->HasPersistentBuffers()) {\n        for (uint32_t i = 0; i < pTexture->CountSubresources(); i++) {\n          auto layout = pTexture->GetSubresourceLayout(formatInfo->aspectMask, i);\n          std::memset(pTexture->GetMapPtr(i, layout.Offset), 0, layout.Size);\n        }\n      }\n    }\n\n    ThrottleAllocationLocked();\n  }\n\n\n  void D3D11Initializer::InitHostVisibleTexture(\n          D3D11CommonTexture*         pTexture,\n    const D3D11_SUBRESOURCE_DATA*     pInitialData) {\n    Rc<DxvkImage> image = pTexture->GetImage();\n    auto formatInfo = image->formatInfo();\n\n    for (uint32_t layer = 0; layer < pTexture->Desc()->ArraySize; layer++) {\n      for (uint32_t level = 0; level < pTexture->Desc()->MipLevels; level++) {\n        uint32_t subresourceIndex = D3D11CalcSubresource(level, layer, pTexture->Desc()->MipLevels);\n\n        VkImageSubresource subresource;\n        subresource.aspectMask = formatInfo->aspectMask;\n        subresource.mipLevel   = level;\n        subresource.arrayLayer = layer;\n\n        VkExtent3D blockCount = util::computeBlockCount(\n          image->mipLevelExtent(level), formatInfo->blockSize);\n\n        auto layout = pTexture->GetSubresourceLayout(\n          subresource.aspectMask, subresourceIndex);\n\n        if (pInitialData && pInitialData[subresourceIndex].pSysMem) {\n          const auto& initialData = pInitialData[subresourceIndex];\n\n          for (uint32_t z = 0; z < blockCount.depth; z++) {\n            for (uint32_t y = 0; y < blockCount.height; y++) {\n              auto size = blockCount.width * formatInfo->elementSize;\n\n              auto dst = pTexture->GetMapPtr(subresourceIndex, layout.Offset\n                      + y * layout.RowPitch\n                      + z * layout.DepthPitch);\n\n              auto src = reinterpret_cast<const char*>(initialData.pSysMem)\n                      + y * initialData.SysMemPitch\n                      + z * initialData.SysMemSlicePitch;\n\n              std::memcpy(dst, src, size);\n\n              if (size < layout.RowPitch)\n                std::memset(reinterpret_cast<char*>(dst) + size, 0, layout.RowPitch - size);\n            }\n          }\n        } else {\n          void* dst = pTexture->GetMapPtr(subresourceIndex, layout.Offset);\n          std::memset(dst, 0, layout.Size);\n        }\n      }\n    }\n\n    // Initialize the image on the GPU\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    EmitCs([\n      cImage = std::move(image)\n    ] (DxvkContext* ctx) {\n      ctx->initImage(cImage, VK_IMAGE_LAYOUT_PREINITIALIZED);\n    });\n\n    m_transferCommands += 1;\n    ThrottleAllocationLocked();\n  }\n\n\n  void D3D11Initializer::InitTiledTexture(\n          D3D11CommonTexture*         pTexture) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    EmitCs([\n      cImage = pTexture->GetImage()\n    ] (DxvkContext* ctx) {\n      ctx->initSparseImage(cImage);\n    });\n\n    m_transferCommands += 1;\n    ThrottleAllocationLocked();\n  }\n\n\n  void D3D11Initializer::ThrottleAllocationLocked() {\n    DxvkStagingBufferStats stats = m_stagingBuffer.getStatistics();\n\n    // If the amount of memory in flight exceeds the limit, stall the\n    // calling thread and wait for some memory to actually get released.\n    VkDeviceSize stagingMemoryInFlight = stats.allocatedTotal - m_stagingSignal->value();\n\n    if (stagingMemoryInFlight > MaxMemoryInFlight) {\n      ExecuteFlushLocked();\n\n      m_stagingSignal->wait(stats.allocatedTotal - MaxMemoryInFlight);\n    } else if (m_transferCommands >= MaxCommandsPerSubmission || stats.allocatedSinceLastReset >= MaxMemoryPerSubmission) {\n      // Flush pending commands if there are a lot of updates in flight\n      // to keep both execution time and staging memory in check.\n      ExecuteFlushLocked();\n    }\n  }\n\n\n  void D3D11Initializer::ExecuteFlush() {\n    std::lock_guard lock(m_mutex);\n\n    ExecuteFlushLocked();\n  }\n\n\n  void D3D11Initializer::ExecuteFlushLocked() {\n    DxvkStagingBufferStats stats = m_stagingBuffer.getStatistics();\n\n    EmitCs([\n      cSignal       = m_stagingSignal,\n      cSignalValue  = stats.allocatedTotal\n    ] (DxvkContext* ctx) {\n      ctx->signal(cSignal, cSignalValue);\n      ctx->flushCommandList(nullptr, nullptr);\n    });\n\n    FlushCsChunk();\n\n    NotifyContextFlushLocked();\n  }\n\n\n  void D3D11Initializer::SyncSharedTexture(D3D11CommonTexture* pResource) {\n    if (!(pResource->Desc()->MiscFlags & (D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_SHARED_NTHANDLE)))\n      return;\n\n    // Ensure that initialization commands are submitted and waited on before\n    // returning control to the application in order to avoid race conditions\n    // in case the texture is used immediately on a secondary device.\n    if (pResource->HasImage()) {\n      ExecuteFlush();\n\n      m_device->waitForResource(*pResource->GetImage(), DxvkAccess::Write);\n    }\n\n    // If a keyed mutex is used, initialize that to the correct state as well.\n    Com<IDXGIKeyedMutex> keyedMutex;\n\n    if (SUCCEEDED(pResource->GetInterface()->QueryInterface(\n        __uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&keyedMutex)))) {\n      keyedMutex->AcquireSync(0, 0);\n      keyedMutex->ReleaseSync(0);\n    }\n  }\n\n\n  void D3D11Initializer::FlushCsChunkLocked() {\n    m_parent->GetContext()->InjectCsChunk(DxvkCsQueue::HighPriority, std::move(m_csChunk), false);\n    m_csChunk = m_parent->AllocCsChunk(DxvkCsChunkFlag::SingleUse);\n  }\n\n\n  void D3D11Initializer::NotifyContextFlushLocked() {\n    m_stagingBuffer.reset();\n    m_transferCommands = 0;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_initializer.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_staging.h\"\n\n#include \"d3d11_buffer.h\"\n#include \"d3d11_shader.h\"\n#include \"d3d11_texture.h\"\n#include \"d3d11_view_uav.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n\n  /**\n   * \\brief Resource initialization context\n   * \n   * Manages a context which is used for resource\n   * initialization. This includes initialization\n   * with application-defined data, as well as\n   * zero-initialization for buffers and images.\n   */\n  class D3D11Initializer {\n    // Use a staging buffer with a linear allocator to service small uploads\n    constexpr static VkDeviceSize StagingBufferSize = 1ull << 20;\n  public:\n\n    // Maximum number of copy and clear commands to record before flushing\n    constexpr static size_t MaxCommandsPerSubmission = 512u;\n\n    // Maximum amount of staging memory to allocate before flushing\n    constexpr static size_t MaxMemoryPerSubmission = (env::is32BitHostPlatform() ? 12u : 48u) << 20;\n\n    // Maximum amount of memory in flight. If there are pending uploads while\n    // this limit is exceeded, further initialization will be stalled.\n    constexpr static size_t MaxMemoryInFlight = 3u * MaxMemoryPerSubmission;\n\n    D3D11Initializer(\n            D3D11Device*                pParent);\n    \n    ~D3D11Initializer();\n\n    void FlushCsChunk() {\n      std::lock_guard<dxvk::mutex> lock(m_csMutex);\n\n      if (!m_csChunk->empty())\n        FlushCsChunkLocked();\n    }\n\n    void NotifyContextFlush();\n\n    void InitBuffer(\n            D3D11Buffer*                pBuffer,\n      const D3D11_SUBRESOURCE_DATA*     pInitialData);\n    \n    void InitTexture(\n            D3D11CommonTexture*         pTexture,\n      const D3D11_SUBRESOURCE_DATA*     pInitialData);\n\n    void InitUavCounter(\n            D3D11UnorderedAccessView*   pUav);\n    \n    void InitShaderIcb(\n            D3D11CommonShader*          pShader,\n            size_t                      IcbSize,\n      const void*                       pIcbData);\n\n  private:\n\n    dxvk::mutex       m_mutex;\n\n    D3D11Device*      m_parent;\n    Rc<DxvkDevice>    m_device;\n    \n    DxvkStagingBuffer m_stagingBuffer;\n    Rc<sync::Fence>   m_stagingSignal;\n\n    size_t            m_transferCommands  = 0;\n\n    dxvk::mutex       m_csMutex;\n    DxvkCsChunkRef    m_csChunk;\n\n    void InitDeviceLocalBuffer(\n            D3D11Buffer*                pBuffer,\n      const D3D11_SUBRESOURCE_DATA*     pInitialData);\n\n    void InitHostVisibleBuffer(\n            D3D11Buffer*                pBuffer,\n      const D3D11_SUBRESOURCE_DATA*     pInitialData);\n\n    void InitDeviceLocalTexture(\n            D3D11CommonTexture*         pTexture,\n      const D3D11_SUBRESOURCE_DATA*     pInitialData);\n\n    void InitHostVisibleTexture(\n            D3D11CommonTexture*         pTexture,\n      const D3D11_SUBRESOURCE_DATA*     pInitialData);\n\n    void InitTiledTexture(\n            D3D11CommonTexture*         pTexture);\n\n    void ThrottleAllocationLocked();\n\n    void ExecuteFlush();\n\n    void ExecuteFlushLocked();\n\n    void SyncSharedTexture(\n            D3D11CommonTexture*         pResource);\n\n    void FlushCsChunkLocked();\n\n    void NotifyContextFlushLocked();\n\n    template<typename Cmd>\n    void EmitCs(Cmd&& command) {\n      std::lock_guard<dxvk::mutex> lock(m_csMutex);\n\n      if (unlikely(!m_csChunk->push(command))) {\n        FlushCsChunkLocked();\n\n        m_csChunk->push(command);\n      }\n    }\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_input_layout.cpp",
    "content": "#include \"d3d11_device.h\"\n#include \"d3d11_input_layout.h\"\n\nnamespace dxvk {\n  \n  D3D11InputLayout::D3D11InputLayout(\n          D3D11Device*          pDevice,\n          uint32_t              numAttributes,\n    const DxvkVertexAttribute*  pAttributes,\n          uint32_t              numBindings,\n    const DxvkVertexBinding*    pBindings)\n  : D3D11DeviceChild<ID3D11InputLayout>(pDevice),\n    m_attributeCount  (numAttributes),\n    m_bindingCount    (numBindings),\n    m_d3d10           (this),\n    m_destructionNotifier(this) {\n    for (uint32_t i = 0; i < numAttributes; i++)\n      m_inputs[i] = DxvkVertexInput(pAttributes[i]);\n\n    for (uint32_t i = 0; i < numBindings; i++)\n      m_inputs[i + numAttributes] = DxvkVertexInput(pBindings[i]);\n  }\n\n\n  D3D11InputLayout::~D3D11InputLayout() {\n\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11InputLayout::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11InputLayout)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10InputLayout)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11InputLayout), riid)) {\n      Logger::warn(\"D3D11InputLayout::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  bool D3D11InputLayout::Compare(const D3D11InputLayout* pOther) const {\n    if (m_attributeCount != pOther->m_attributeCount || m_bindingCount != pOther->m_bindingCount)\n      return false;\n\n    // Try to vectorize at least a little bit here. We can't use bcmpeq here\n    // since there is no way at all to guaratee alignment for the array.\n    for (uint32_t i = 0; i < m_attributeCount + m_bindingCount; i += 4u) {\n      if (std::memcmp(&m_inputs[i], &pOther->m_inputs[i], 4u * sizeof(DxvkVertexInput)))\n        return false;\n    }\n\n    return true;\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_input_layout.h",
    "content": "#pragma once\n\n#include \"d3d11_device_child.h\"\n\n#include \"../d3d10/d3d10_input_layout.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n\n  class D3D11InputLayout : public D3D11DeviceChild<ID3D11InputLayout> {\n    \n  public:\n    \n    D3D11InputLayout(\n            D3D11Device*          pDevice,\n            uint32_t              numAttributes,\n      const DxvkVertexAttribute*  pAttributes,\n            uint32_t              numBindings,\n      const DxvkVertexBinding*    pBindings);\n    \n    ~D3D11InputLayout();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject) final;\n\n    uint32_t GetAttributeCount() const {\n      return m_attributeCount;\n    }\n\n    uint32_t GetBindingCount() const {\n      return m_bindingCount;\n    }\n\n    DxvkVertexInput GetInput(uint32_t Index) const {\n      return m_inputs[Index];\n    }\n\n    bool Compare(\n      const D3D11InputLayout*     pOther) const;\n    \n    D3D10InputLayout* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n    \n  private:\n\n    uint32_t m_attributeCount = 0;\n    uint32_t m_bindingCount = 0;\n\n    std::array<DxvkVertexInput, MaxNumVertexAttributes + MaxNumVertexBindings> m_inputs = { };\n\n    D3D10InputLayout m_d3d10;\n\n    D3DDestructionNotifier m_destructionNotifier;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_interfaces.h",
    "content": "#pragma once\n\n#include \"../dxgi/dxgi_interfaces.h\"\n\n#include \"d3d11_include.h\"\n\n/**\n * \\brief D3D11 extension\n * \n * Lists D3D11 extensions supported by DXVK.\n */\nenum D3D11_VK_EXTENSION : uint32_t {\n  D3D11_VK_EXT_MULTI_DRAW_INDIRECT        = 0,\n  D3D11_VK_EXT_MULTI_DRAW_INDIRECT_COUNT  = 1,\n  D3D11_VK_EXT_DEPTH_BOUNDS               = 2,\n  D3D11_VK_EXT_BARRIER_CONTROL            = 3,\n  D3D11_VK_NVX_BINARY_IMPORT              = 4,\n  D3D11_VK_NVX_IMAGE_VIEW_HANDLE          = 5,\n};\n\n\n/**\n * \\brief Barrier control flags\n */\nenum D3D11_VK_BARRIER_CONTROL : uint32_t {\n  D3D11_VK_BARRIER_CONTROL_IGNORE_WRITE_AFTER_WRITE   = 1 << 0,\n\n  // Removed:\n  // D3D11_VK_BARRIER_CONTROL_IGNORE_GRAPHICS_UAV        = 1 << 1,\n};\n\n\n/**\n * \\brief Extended D3D11 device\n * \n * Introduces a method to check for extension support.\n */\nMIDL_INTERFACE(\"8a6e3c42-f74c-45b7-8265-a231b677ca17\")\nID3D11VkExtDevice : public IUnknown {\n  /**\n   * \\brief Checks whether an extension is supported\n   * \n   * \\param [in] Extension The extension to check\n   * \\returns \\c TRUE if the extension is supported\n   */\n  virtual BOOL STDMETHODCALLTYPE GetExtensionSupport(\n          D3D11_VK_EXTENSION      Extension) = 0;\n  \n};\n\n\n/**\n * \\brief Extended extended D3D11 device\n * \n * Introduces methods to get virtual addresses and driver\n * handles for resources, and create and destroy objects\n * for D3D11-Cuda interop.\n */\nMIDL_INTERFACE(\"cfcf64ef-9586-46d0-bca4-97cf2ca61b06\")\nID3D11VkExtDevice1 : public ID3D11VkExtDevice {\n\n  virtual bool STDMETHODCALLTYPE GetResourceHandleGPUVirtualAddressAndSizeNVX(\n          void*                   hObject,\n          uint64_t*               gpuVAStart,\n          uint64_t*               gpuVASize) = 0;\n\n  virtual bool STDMETHODCALLTYPE CreateUnorderedAccessViewAndGetDriverHandleNVX(\n          ID3D11Resource*         pResource,\n          const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc,\n          ID3D11UnorderedAccessView** ppUAV,\n          uint32_t*               pDriverHandle) = 0;\n\n  virtual bool STDMETHODCALLTYPE CreateShaderResourceViewAndGetDriverHandleNVX(\n          ID3D11Resource*         pResource,\n          const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc,\n          ID3D11ShaderResourceView** ppSRV,\n          uint32_t*               pDriverHandle) = 0;\n\n  virtual bool STDMETHODCALLTYPE CreateSamplerStateAndGetDriverHandleNVX(\n          const D3D11_SAMPLER_DESC* pSamplerDesc,\n          ID3D11SamplerState**    ppSamplerState,\n          uint32_t*               pDriverHandle) = 0;\n\n  virtual bool STDMETHODCALLTYPE CreateCubinComputeShaderWithNameNVX(\n          const void*             pCubin,\n          uint32_t                size,\n          uint32_t                blockX,\n          uint32_t                blockY,\n          uint32_t                blockZ,\n          const char*             pShaderName,\n          IUnknown**              phShader) = 0;\n\n  virtual bool STDMETHODCALLTYPE GetCudaTextureObjectNVX(\n          uint32_t                srvDriverHandle,\n          uint32_t                samplerDriverHandle,\n          uint32_t*               pCudaTextureHandle) = 0;\n};\n\n\n/**\n * \\brief Extended D3D11 context\n * \n * Provides functionality for various D3D11\n * extensions.\n */\nMIDL_INTERFACE(\"fd0bca13-5cb6-4c3a-987e-4750de2ca791\")\nID3D11VkExtContext : public IUnknown {\n  virtual void STDMETHODCALLTYPE MultiDrawIndirect(\n          UINT                    DrawCount,\n          ID3D11Buffer*           pBufferForArgs,\n          UINT                    ByteOffsetForArgs,\n          UINT                    ByteStrideForArgs) = 0;\n  \n  virtual void STDMETHODCALLTYPE MultiDrawIndexedIndirect(\n          UINT                    DrawCount,\n          ID3D11Buffer*           pBufferForArgs,\n          UINT                    ByteOffsetForArgs,\n          UINT                    ByteStrideForArgs) = 0;\n  \n  virtual void STDMETHODCALLTYPE MultiDrawIndirectCount(\n          UINT                    MaxDrawCount,\n          ID3D11Buffer*           pBufferForCount,\n          UINT                    ByteOffsetForCount,\n          ID3D11Buffer*           pBufferForArgs,\n          UINT                    ByteOffsetForArgs,\n          UINT                    ByteStrideForArgs) = 0;\n  \n  virtual void STDMETHODCALLTYPE MultiDrawIndexedIndirectCount(\n          UINT                    MaxDrawCount,\n          ID3D11Buffer*           pBufferForCount,\n          UINT                    ByteOffsetForCount,\n          ID3D11Buffer*           pBufferForArgs,\n          UINT                    ByteOffsetForArgs,\n          UINT                    ByteStrideForArgs) = 0;\n  \n  virtual void STDMETHODCALLTYPE SetDepthBoundsTest(\n          BOOL                    Enable,\n          FLOAT                   MinDepthBounds,\n          FLOAT                   MaxDepthBounds) = 0;\n  \n  virtual void STDMETHODCALLTYPE SetBarrierControl(\n          UINT                    ControlFlags) = 0;\n};\n\n\n/**\n * \\brief Extended extended D3D11 context\n * \n * Provides functionality to launch a Cuda kernel\n */\nMIDL_INTERFACE(\"874b09b2-ae0b-41d8-8476-5f3b7a0e879d\")\nID3D11VkExtContext1 : public ID3D11VkExtContext {\n\n  virtual bool STDMETHODCALLTYPE LaunchCubinShaderNVX(\n          IUnknown*               hShader,\n          uint32_t                gridX,\n          uint32_t                gridY,\n          uint32_t                gridZ,\n          const void*             pParams,\n          uint32_t                paramSize,\n          void* const*            pReadResources,\n          uint32_t                numReadResources,\n          void* const*            pWriteResources,\n          uint32_t                numWriteResources) = 0;\n};\n\n\n/**\n * \\brief Frame reports used for Reflex interop\n */\nstruct D3D_LOW_LATENCY_FRAME_REPORT\n{\n    UINT64 frameID;\n    UINT64 inputSampleTime;\n    UINT64 simStartTime;\n    UINT64 simEndTime;\n    UINT64 renderSubmitStartTime;\n    UINT64 renderSubmitEndTime;\n    UINT64 presentStartTime;\n    UINT64 presentEndTime;\n    UINT64 driverStartTime;\n    UINT64 driverEndTime;\n    UINT64 osRenderQueueStartTime;\n    UINT64 osRenderQueueEndTime;\n    UINT64 gpuRenderStartTime;\n    UINT64 gpuRenderEndTime;\n    UINT32 gpuActiveRenderTimeUs;\n    UINT32 gpuFrameTimeUs;\n    UINT8 rsvd[120];\n};\n\n\n/**\n * \\brief Data structure used for Reflex interop\n */\nstruct D3D_LOW_LATENCY_RESULTS\n{\n    UINT32 version;\n    D3D_LOW_LATENCY_FRAME_REPORT frameReports[64];\n    UINT8 rsvd[32];\n};\n\n\n/**\n * \\brief D3D interop interface for Nvidia Reflex\n */\nMIDL_INTERFACE(\"f3112584-41f9-348d-a59b-00b7e1d285d6\")\nID3DLowLatencyDevice : public IUnknown {\n  virtual BOOL STDMETHODCALLTYPE SupportsLowLatency() = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE LatencySleep() = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetLatencySleepMode(\n          BOOL                          LowLatencyEnable,\n          BOOL                          LowLatencyBoost,\n          UINT32                        MinIntervalUs) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetLatencyMarker(\n          UINT64                        FrameId,\n          UINT32                        MarkerType) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetLatencyInfo(\n          D3D_LOW_LATENCY_RESULTS*      pLowLatencyResults) = 0;\n};\n\n\n#ifndef _MSC_VER\n__CRT_UUID_DECL(ID3D11VkExtDevice,         0x8a6e3c42,0xf74c,0x45b7,0x82,0x65,0xa2,0x31,0xb6,0x77,0xca,0x17);\n__CRT_UUID_DECL(ID3D11VkExtDevice1,        0xcfcf64ef,0x9586,0x46d0,0xbc,0xa4,0x97,0xcf,0x2c,0xa6,0x1b,0x06);\n__CRT_UUID_DECL(ID3D11VkExtContext,        0xfd0bca13,0x5cb6,0x4c3a,0x98,0x7e,0x47,0x50,0xde,0x2c,0xa7,0x91);\n__CRT_UUID_DECL(ID3D11VkExtContext1,       0x874b09b2,0xae0b,0x41d8,0x84,0x76,0x5f,0x3b,0x7a,0x0e,0x87,0x9d);\n__CRT_UUID_DECL(ID3DLowLatencyDevice,      0xf3112584,0x41f9,0x348d,0xa5,0x9b,0x00,0xb7,0xe1,0xd2,0x85,0xd6);\n#endif\n"
  },
  {
    "path": "src/d3d11/d3d11_interop.cpp",
    "content": "#include \"d3d11_context_imm.h\"\n#include \"d3d11_interop.h\"\n#include \"d3d11_device.h\"\n\n#include \"../dxvk/dxvk_adapter.h\"\n#include \"../dxvk/dxvk_device.h\"\n#include \"../dxvk/dxvk_instance.h\"\n\nnamespace dxvk {\n  \n  D3D11VkInterop::D3D11VkInterop(\n          IDXGIObject*          pContainer,\n          D3D11Device*          pDevice)\n  : m_container (pContainer),\n    m_device    (pDevice) { }\n  \n  \n  D3D11VkInterop::~D3D11VkInterop() {\n    \n  }\n  \n  \n  ULONG STDMETHODCALLTYPE D3D11VkInterop::AddRef() {\n    return m_container->AddRef();\n  }\n  \n  \n  ULONG STDMETHODCALLTYPE D3D11VkInterop::Release() {\n    return m_container->Release();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11VkInterop::QueryInterface(\n          REFIID                riid,\n          void**                ppvObject) {\n    return m_container->QueryInterface(riid, ppvObject);\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11VkInterop::GetVulkanHandles(\n          VkInstance*           pInstance,\n          VkPhysicalDevice*     pPhysDev,\n          VkDevice*             pDevice) {\n    auto device   = m_device->GetDXVKDevice();\n    auto adapter  = device->adapter();\n    auto instance = device->instance();\n    \n    if (pDevice != nullptr)\n      *pDevice = device->handle();\n    \n    if (pPhysDev != nullptr)\n      *pPhysDev = adapter->handle();\n    \n    if (pInstance != nullptr)\n      *pInstance = instance->handle();\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11VkInterop::GetSubmissionQueue(\n          VkQueue*              pQueue,\n          uint32_t*             pQueueFamilyIndex) {\n    auto device = static_cast<D3D11Device*>(m_device)->GetDXVKDevice();\n    DxvkDeviceQueue queue = device->queues().graphics;\n    \n    if (pQueue != nullptr)\n      *pQueue = queue.queueHandle;\n    \n    if (pQueueFamilyIndex != nullptr)\n      *pQueueFamilyIndex = queue.queueFamily;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11VkInterop::TransitionSurfaceLayout(\n          IDXGIVkInteropSurface*    pSurface,\n    const VkImageSubresourceRange*  pSubresources,\n          VkImageLayout             OldLayout,\n          VkImageLayout             NewLayout) {\n    auto immediateContext = m_device->GetContext();\n\n    immediateContext->TransitionSurfaceLayout(\n      pSurface, pSubresources, OldLayout, NewLayout);\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11VkInterop::FlushRenderingCommands() {\n    auto immediateContext = m_device->GetContext();\n    immediateContext->Flush();\n    immediateContext->SynchronizeCsThread(DxvkCsThread::SynchronizeAll);\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11VkInterop::LockSubmissionQueue() {\n    m_device->GetDXVKDevice()->lockSubmission();\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11VkInterop::ReleaseSubmissionQueue() {\n    m_device->GetDXVKDevice()->unlockSubmission();\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11VkInterop::GetSubmissionQueue1(\n          VkQueue*              pQueue,\n          uint32_t*             pQueueIndex,\n          uint32_t*             pQueueFamilyIndex) {\n    auto device = static_cast<D3D11Device*>(m_device)->GetDXVKDevice();\n    DxvkDeviceQueue queue = device->queues().graphics;\n    \n    if (pQueue != nullptr)\n      *pQueue = queue.queueHandle;\n    \n    if (pQueueIndex != nullptr)\n      *pQueueIndex = queue.queueIndex;\n    \n    if (pQueueFamilyIndex != nullptr)\n      *pQueueFamilyIndex = queue.queueFamily;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D11VkInterop::CreateTexture2DFromVkImage(\n          const D3D11_TEXTURE2D_DESC1 *pDesc,\n          VkImage vkImage,\n          ID3D11Texture2D **ppTexture2D) {\n\n    InitReturnPtr(ppTexture2D);\n\n    if (!pDesc)\n      return E_INVALIDARG;\n    \n    D3D11_COMMON_TEXTURE_DESC desc;\n    desc.Width          = pDesc->Width;\n    desc.Height         = pDesc->Height;\n    desc.Depth          = 1;\n    desc.MipLevels      = pDesc->MipLevels;\n    desc.ArraySize      = pDesc->ArraySize;\n    desc.Format         = pDesc->Format;\n    desc.SampleDesc     = pDesc->SampleDesc;\n    desc.Usage          = pDesc->Usage;\n    desc.BindFlags      = pDesc->BindFlags;\n    desc.CPUAccessFlags = pDesc->CPUAccessFlags;\n    desc.MiscFlags      = pDesc->MiscFlags;\n    desc.TextureLayout  = pDesc->TextureLayout;\n    \n    HRESULT hr = D3D11CommonTexture::NormalizeTextureProperties(&desc);\n\n    if (FAILED(hr))\n      return hr;\n    \n    if (!ppTexture2D)\n      return S_FALSE;\n    \n    try {\n      Com<D3D11Texture2D> texture = new D3D11Texture2D(m_device, &desc, 0, vkImage);\n      *ppTexture2D = texture.ref();\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n  }\n}"
  },
  {
    "path": "src/d3d11/d3d11_interop.h",
    "content": "#pragma once\n\n#include \"../dxgi/dxgi_interfaces.h\"\n\n#include \"d3d11_include.h\"\n\nnamespace dxvk {\n\n  class D3D11Device;\n  \n  class D3D11VkInterop : public ComObject<IDXGIVkInteropDevice1> {\n    \n  public:\n    \n    D3D11VkInterop(\n            IDXGIObject*          pContainer,\n            D3D11Device*          pDevice);\n\n    ~D3D11VkInterop();\n    \n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject);\n    \n    void STDMETHODCALLTYPE GetVulkanHandles(\n            VkInstance*           pInstance,\n            VkPhysicalDevice*     pPhysDev,\n            VkDevice*             pDevice);\n    \n    void STDMETHODCALLTYPE GetSubmissionQueue(\n            VkQueue*              pQueue,\n            uint32_t*             pQueueFamilyIndex);\n    \n    void STDMETHODCALLTYPE TransitionSurfaceLayout(\n            IDXGIVkInteropSurface*    pSurface,\n      const VkImageSubresourceRange*  pSubresources,\n            VkImageLayout             OldLayout,\n            VkImageLayout             NewLayout);\n    \n    void STDMETHODCALLTYPE FlushRenderingCommands();\n    \n    void STDMETHODCALLTYPE LockSubmissionQueue();\n    \n    void STDMETHODCALLTYPE ReleaseSubmissionQueue();\n    \n    void STDMETHODCALLTYPE GetSubmissionQueue1(\n            VkQueue*              pQueue,\n            uint32_t*             pQueueIndex,\n            uint32_t*             pQueueFamilyIndex);\n    \n    HRESULT STDMETHODCALLTYPE CreateTexture2DFromVkImage(\n            const D3D11_TEXTURE2D_DESC1* pDesc,\n            VkImage                      vkImage,\n            ID3D11Texture2D**            ppTexture2D);\n    \n  private:\n    \n    IDXGIObject* m_container;\n    D3D11Device* m_device;\n    \n  };\n  \n}"
  },
  {
    "path": "src/d3d11/d3d11_main.cpp",
    "content": "#include <array>\n\n#include \"../dxgi/dxgi_adapter.h\"\n\n#include \"../dxvk/dxvk_instance.h\"\n\n#include \"d3d11_device.h\"\n#include \"d3d11_enums.h\"\n#include \"d3d11_interop.h\"\n\nnamespace dxvk {\n  Logger Logger::s_instance(\"d3d11.log\");\n}\n  \nextern \"C\" {\n  using namespace dxvk;\n  \n  HRESULT D3D11InternalCreateDevice(\n          IDXGIFactory*       pFactory,\n          IDXGIAdapter*       pAdapter,\n          UINT                Flags,\n    const D3D_FEATURE_LEVEL*  pFeatureLevels,\n          UINT                FeatureLevels,\n          ID3D11Device**      ppDevice) {\n    InitReturnPtr(ppDevice);\n\n    Rc<DxvkAdapter>  dxvkAdapter;\n    Rc<DxvkInstance> dxvkInstance;\n\n    Com<IDXGIDXVKAdapter> dxgiVkAdapter;\n    \n    // Try to find the corresponding Vulkan device for the DXGI adapter\n    if (SUCCEEDED(pAdapter->QueryInterface(__uuidof(IDXGIDXVKAdapter), reinterpret_cast<void**>(&dxgiVkAdapter)))) {\n      dxvkAdapter  = dxgiVkAdapter->GetDXVKAdapter();\n      dxvkInstance = dxgiVkAdapter->GetDXVKInstance();\n    } else {\n      Logger::warn(\"D3D11InternalCreateDevice: Adapter is not a DXVK adapter\");\n      DXGI_ADAPTER_DESC desc;\n      pAdapter->GetDesc(&desc);\n\n      dxvkInstance = new DxvkInstance(0);\n      dxvkAdapter  = dxvkInstance->findAdapterByLuid(&desc.AdapterLuid);\n\n      if (dxvkAdapter == nullptr)\n        dxvkAdapter = dxvkInstance->findAdapterByDeviceId(desc.VendorId, desc.DeviceId);\n      \n      if (dxvkAdapter == nullptr)\n        dxvkAdapter = dxvkInstance->enumAdapters(0);\n\n      if (dxvkAdapter == nullptr)\n        return E_FAIL;\n    }\n    \n    // Feature levels to probe if the\n    // application does not specify any.\n    std::array<D3D_FEATURE_LEVEL, 6> defaultFeatureLevels = {\n      D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1,\n      D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3,\n      D3D_FEATURE_LEVEL_9_2,  D3D_FEATURE_LEVEL_9_1,\n    };\n    \n    if (!pFeatureLevels || !FeatureLevels) {\n      pFeatureLevels = defaultFeatureLevels.data();\n      FeatureLevels  = defaultFeatureLevels.size();\n    }\n    \n    // Find the highest feature level supported by the device.\n    // This works because the feature level array is ordered.\n    D3D_FEATURE_LEVEL maxFeatureLevel = D3D11Device::GetMaxFeatureLevel(dxvkInstance, dxvkAdapter);\n    D3D_FEATURE_LEVEL minFeatureLevel = D3D_FEATURE_LEVEL();\n    D3D_FEATURE_LEVEL devFeatureLevel = D3D_FEATURE_LEVEL();\n\n    Logger::info(str::format(\"D3D11InternalCreateDevice: Maximum supported feature level: \", maxFeatureLevel));\n\n    for (uint32_t flId = 0 ; flId < FeatureLevels; flId++) {\n      minFeatureLevel = pFeatureLevels[flId];\n\n      if (minFeatureLevel <= maxFeatureLevel) {\n        devFeatureLevel = minFeatureLevel;\n        break;\n      }\n    }\n\n    if (!devFeatureLevel) {\n      Logger::err(str::format(\"D3D11InternalCreateDevice: Minimum required feature level \", minFeatureLevel, \" not supported\"));\n      return E_INVALIDARG;\n    }\n\n    try {\n      Logger::info(str::format(\"D3D11InternalCreateDevice: Using feature level \", devFeatureLevel));\n\n      Rc<DxvkDevice> dxvkDevice = dxvkAdapter->createDevice();\n\n      Com<D3D11DXGIDevice> device = new D3D11DXGIDevice(\n        pAdapter, nullptr, nullptr,\n        dxvkInstance, dxvkAdapter, dxvkDevice,\n        devFeatureLevel, Flags);\n\n      return device->QueryInterface(\n        __uuidof(ID3D11Device),\n        reinterpret_cast<void**>(ppDevice));\n    } catch (const DxvkError& e) {\n      Logger::err(\"D3D11InternalCreateDevice: Failed to create D3D11 device\");\n      return E_FAIL;\n    }\n  }\n  \n  \n  static HRESULT D3D11InternalCreateDeviceAndSwapChain(\n          IDXGIAdapter*         pAdapter,\n          D3D_DRIVER_TYPE       DriverType,\n          HMODULE               Software,\n          UINT                  Flags,\n    const D3D_FEATURE_LEVEL*    pFeatureLevels,\n          UINT                  FeatureLevels,\n          UINT                  SDKVersion,\n    const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc,\n          IDXGISwapChain**      ppSwapChain,\n          ID3D11Device**        ppDevice,\n          D3D_FEATURE_LEVEL*    pFeatureLevel,\n          ID3D11DeviceContext** ppImmediateContext) {\n    InitReturnPtr(ppDevice);\n    InitReturnPtr(ppSwapChain);\n    InitReturnPtr(ppImmediateContext);\n\n    if (pFeatureLevel)\n      *pFeatureLevel = D3D_FEATURE_LEVEL(0);\n\n    HRESULT hr;\n\n    Com<IDXGIFactory> dxgiFactory = nullptr;\n    Com<IDXGIAdapter> dxgiAdapter = pAdapter;\n    Com<ID3D11Device> device      = nullptr;\n    \n    if (ppSwapChain && !pSwapChainDesc)\n      return E_INVALIDARG;\n    \n    if (!pAdapter) {\n      // We'll treat everything as hardware, even if the\n      // Vulkan device is actually a software device.\n      if (DriverType != D3D_DRIVER_TYPE_HARDWARE)\n        Logger::warn(\"D3D11CreateDevice: Unsupported driver type\");\n      \n      // We'll use the first adapter returned by a DXGI factory\n      hr = CreateDXGIFactory1(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&dxgiFactory));\n\n      if (FAILED(hr)) {\n        Logger::err(\"D3D11CreateDevice: Failed to create a DXGI factory\");\n        return hr;\n      }\n\n      hr = dxgiFactory->EnumAdapters(0, &dxgiAdapter);\n\n      if (FAILED(hr)) {\n        Logger::err(\"D3D11CreateDevice: No default adapter available\");\n        return hr;\n      }\n    } else {\n      // We should be able to query the DXGI factory from the adapter\n      if (FAILED(dxgiAdapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&dxgiFactory)))) {\n        Logger::err(\"D3D11CreateDevice: Failed to query DXGI factory from DXGI adapter\");\n        return E_INVALIDARG;\n      }\n      \n      // In theory we could ignore these, but the Microsoft docs explicitly\n      // state that we need to return E_INVALIDARG in case the arguments are\n      // invalid. Both the driver type and software parameter can only be\n      // set if the adapter itself is unspecified.\n      // See: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476082(v=vs.85).aspx\n      if (DriverType != D3D_DRIVER_TYPE_UNKNOWN || Software)\n        return E_INVALIDARG;\n    }\n    \n    // Create the actual device\n    hr = D3D11InternalCreateDevice(\n      dxgiFactory.ptr(), dxgiAdapter.ptr(),\n      Flags, pFeatureLevels, FeatureLevels,\n      &device);\n    \n    if (FAILED(hr))\n      return hr;\n    \n    // Create the swap chain, if requested\n    if (ppSwapChain) {\n      DXGI_SWAP_CHAIN_DESC desc = *pSwapChainDesc;\n      hr = dxgiFactory->CreateSwapChain(device.ptr(), &desc, ppSwapChain);\n\n      if (FAILED(hr)) {\n        Logger::err(\"D3D11CreateDevice: Failed to create swap chain\");\n        return hr;\n      }\n    }\n    \n    // Write back whatever info the application requested\n    if (pFeatureLevel)\n      *pFeatureLevel = device->GetFeatureLevel();\n    \n    if (ppDevice)\n      *ppDevice = device.ref();\n    \n    if (ppImmediateContext)\n      device->GetImmediateContext(ppImmediateContext);\n\n    // If we were unable to write back the device and the\n    // swap chain, the application has no way of working\n    // with the device so we should report S_FALSE here.\n    if (!ppDevice && !ppImmediateContext && !ppSwapChain)\n      return S_FALSE;\n    \n    return S_OK;\n  }\n  \n\n    DLLEXPORT HRESULT __stdcall D3D11CoreCreateDevice(\n            IDXGIFactory*       pFactory,\n            IDXGIAdapter*       pAdapter,\n            D3D_DRIVER_TYPE     DriverType,\n            HMODULE             Software,\n            UINT                Flags,\n      const D3D_FEATURE_LEVEL*  pFeatureLevels,\n            UINT                FeatureLevels,\n            UINT                SDKVersion,\n            ID3D11Device**      ppDevice,\n            D3D_FEATURE_LEVEL*  pFeatureLevel) {\n    return D3D11InternalCreateDeviceAndSwapChain(\n      pAdapter, DriverType, Software, Flags,\n      pFeatureLevels, FeatureLevels, SDKVersion,\n      nullptr, nullptr,\n      ppDevice, pFeatureLevel, nullptr);\n  }\n\n\n  DLLEXPORT HRESULT __stdcall D3D11CreateDevice(\n          IDXGIAdapter*         pAdapter,\n          D3D_DRIVER_TYPE       DriverType,\n          HMODULE               Software,\n          UINT                  Flags,\n    const D3D_FEATURE_LEVEL*    pFeatureLevels,\n          UINT                  FeatureLevels,\n          UINT                  SDKVersion,\n          ID3D11Device**        ppDevice,\n          D3D_FEATURE_LEVEL*    pFeatureLevel,\n          ID3D11DeviceContext** ppImmediateContext) {\n    return D3D11InternalCreateDeviceAndSwapChain(\n      pAdapter, DriverType, Software, Flags,\n      pFeatureLevels, FeatureLevels, SDKVersion,\n      nullptr, nullptr,\n      ppDevice, pFeatureLevel, ppImmediateContext);\n  }\n  \n  \n  DLLEXPORT HRESULT __stdcall D3D11CreateDeviceAndSwapChain(\n          IDXGIAdapter*         pAdapter,\n          D3D_DRIVER_TYPE       DriverType,\n          HMODULE               Software,\n          UINT                  Flags,\n    const D3D_FEATURE_LEVEL*    pFeatureLevels,\n          UINT                  FeatureLevels,\n          UINT                  SDKVersion,\n    const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc,\n          IDXGISwapChain**      ppSwapChain,\n          ID3D11Device**        ppDevice,\n          D3D_FEATURE_LEVEL*    pFeatureLevel,\n          ID3D11DeviceContext** ppImmediateContext) {\n    return D3D11InternalCreateDeviceAndSwapChain(\n      pAdapter, DriverType, Software, Flags,\n      pFeatureLevels, FeatureLevels, SDKVersion,\n      pSwapChainDesc, ppSwapChain,\n      ppDevice, pFeatureLevel, ppImmediateContext);\n  }\n  \n\n  DLLEXPORT HRESULT __stdcall D3D11On12CreateDevice(\n          IUnknown*             pDevice,\n          UINT                  Flags,\n    const D3D_FEATURE_LEVEL*    pFeatureLevels,\n          UINT                  FeatureLevels,\n          IUnknown* const*      ppCommandQueues,\n          UINT                  NumQueues,\n          UINT                  NodeMask,\n          ID3D11Device**        ppDevice,\n          ID3D11DeviceContext** ppImmediateContext,\n          D3D_FEATURE_LEVEL*    pChosenFeatureLevel) {\n    InitReturnPtr(ppDevice);\n    InitReturnPtr(ppImmediateContext);\n\n    if (pChosenFeatureLevel)\n      *pChosenFeatureLevel = D3D_FEATURE_LEVEL(0);\n\n    if (!pDevice)\n      return E_INVALIDARG;\n\n    // Figure out D3D12 objects\n    Com<ID3D12Device> d3d12Device;\n    Com<ID3D12CommandQueue> d3d12Queue;\n\n    if (FAILED(pDevice->QueryInterface(__uuidof(ID3D12Device), reinterpret_cast<void**>(&d3d12Device)))) {\n      Logger::err(\"D3D11On12CreateDevice: Device is not a valid D3D12 device\");\n      return E_INVALIDARG;\n    }\n\n    if (NodeMask & (NodeMask - 1)) {\n      Logger::err(\"D3D11On12CreateDevice: Invalid node mask\");\n      return E_INVALIDARG;\n    }\n\n    if (!NumQueues || !ppCommandQueues || !ppCommandQueues[0]) {\n      Logger::err(\"D3D11On12CreateDevice: No command queue specified\");\n      return E_INVALIDARG;\n    }\n\n    if (NumQueues > 1) {\n      // Not sure what to do with more than one graphics queue\n      Logger::warn(\"D3D11On12CreateDevice: Only one queue supported\");\n    }\n\n    if (FAILED(ppCommandQueues[0]->QueryInterface(__uuidof(ID3D12CommandQueue), reinterpret_cast<void**>(&d3d12Queue)))) {\n      Logger::err(\"D3D11On12CreateDevice: Queue is not a valid D3D12 command queue\");\n      return E_INVALIDARG;\n    }\n\n    // Determine feature level for the D3D11 device\n    std::array<D3D_FEATURE_LEVEL, 4> defaultFeatureLevels = {{\n      D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1,\n      D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_12_1,\n    }};\n\n    D3D12_FEATURE_DATA_FEATURE_LEVELS featureLevel = { };\n\n    if (!FeatureLevels || !pFeatureLevels) {\n      featureLevel.NumFeatureLevels = defaultFeatureLevels.size();\n      featureLevel.pFeatureLevelsRequested = defaultFeatureLevels.data();\n    } else {\n      featureLevel.NumFeatureLevels = FeatureLevels;\n      featureLevel.pFeatureLevelsRequested = pFeatureLevels;\n    }\n\n    HRESULT hr = d3d12Device->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featureLevel, sizeof(featureLevel));\n\n    if (FAILED(hr) || !featureLevel.MaxSupportedFeatureLevel) {\n      Logger::err(str::format(\"D3D11On12CreateDevice: Minimum required feature level not supported\"));\n      return hr;\n    }\n\n    Logger::info(str::format(\"D3D11On12CreateDevice: Chosen feature level: \", featureLevel.MaxSupportedFeatureLevel));\n\n    Com<ID3D12DXVKInteropDevice> interopDevice;\n\n    if (FAILED(d3d12Device->QueryInterface(__uuidof(ID3D12DXVKInteropDevice), reinterpret_cast<void**>(&interopDevice)))) {\n      Logger::err(\"D3D11On12CreateDevice: Device not a vkd3d-proton device.\");\n      return E_INVALIDARG;\n    }\n\n    Com<IDXGIAdapter> dxgiAdapter;\n\n    if (FAILED(interopDevice->GetDXGIAdapter(IID_PPV_ARGS(&dxgiAdapter)))) {\n      Logger::err(\"D3D11On12CreateDevice: Failed to query DXGI adapter.\");\n      return E_INVALIDARG;\n    }\n\n    try {\n      // Initialize DXVK instance\n      DxvkInstanceImportInfo instanceInfo = { };\n      DxvkDeviceImportInfo deviceInfo = { };\n      VkPhysicalDevice vulkanAdapter = VK_NULL_HANDLE;\n\n      interopDevice->GetVulkanHandles(&instanceInfo.instance, &vulkanAdapter, &deviceInfo.device);\n\n      uint32_t instanceExtensionCount = 0;\n      interopDevice->GetInstanceExtensions(&instanceExtensionCount, nullptr);\n\n      std::vector<const char*> instanceExtensions(instanceExtensionCount);\n      interopDevice->GetInstanceExtensions(&instanceExtensionCount, instanceExtensions.data());\n\n      instanceInfo.extensionCount = instanceExtensions.size();\n      instanceInfo.extensionNames = instanceExtensions.data();\n\n      Rc<DxvkInstance> dxvkInstance = new DxvkInstance(instanceInfo, 0);\n\n      // Find adapter by physical device handle\n      Rc<DxvkAdapter> dxvkAdapter;\n\n      for (uint32_t i = 0; i < dxvkInstance->adapterCount(); i++) {\n        Rc<DxvkAdapter> curr = dxvkInstance->enumAdapters(i);\n\n        if (curr->handle() == vulkanAdapter)\n          dxvkAdapter = std::move(curr);\n      }\n\n      if (dxvkAdapter == nullptr) {\n        Logger::err(\"D3D11On12CreateDevice: No matching adapter found\");\n        return E_INVALIDARG;\n      }\n\n      interopDevice->GetVulkanQueueInfo(d3d12Queue.ptr(), &deviceInfo.queue, &deviceInfo.queueFamily);\n      interopDevice->GetDeviceFeatures(&deviceInfo.features);\n\n      uint32_t deviceExtensionCount = 0;\n      interopDevice->GetDeviceExtensions(&deviceExtensionCount, nullptr);\n\n      std::vector<const char*> deviceExtensions(deviceExtensionCount);\n      interopDevice->GetDeviceExtensions(&deviceExtensionCount, deviceExtensions.data());\n\n      deviceInfo.extensionCount = deviceExtensions.size();\n      deviceInfo.extensionNames = deviceExtensions.data();\n\n      deviceInfo.queueCallback = [\n        cDevice = interopDevice,\n        cQueue = d3d12Queue\n      ] (bool doLock) {\n        HRESULT hr = doLock\n          ? cDevice->LockCommandQueue(cQueue.ptr())\n          : cDevice->UnlockCommandQueue(cQueue.ptr());\n\n        if (FAILED(hr))\n          Logger::err(str::format(\"Failed to lock vkd3d-proton device queue: \", hr));\n      };\n\n      Rc<DxvkDevice> dxvkDevice = dxvkAdapter->importDevice(deviceInfo);\n\n      // Create and return the actual D3D11 device\n      Com<D3D11DXGIDevice> device = new D3D11DXGIDevice(\n        dxgiAdapter.ptr(), d3d12Device.ptr(), d3d12Queue.ptr(),\n        dxvkInstance, dxvkAdapter, dxvkDevice,\n        featureLevel.MaxSupportedFeatureLevel, Flags);\n\n      Com<ID3D11Device> d3d11Device;\n      device->QueryInterface(__uuidof(ID3D11Device), reinterpret_cast<void**>(&d3d11Device));\n\n      if (ppDevice)\n        *ppDevice = d3d11Device.ref();\n\n      if (ppImmediateContext)\n        d3d11Device->GetImmediateContext(ppImmediateContext);\n\n      if (pChosenFeatureLevel)\n        *pChosenFeatureLevel = d3d11Device->GetFeatureLevel();\n\n      if (!ppDevice && !ppImmediateContext)\n        return S_FALSE;\n\n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(\"D3D11On12CreateDevice: Failed to create D3D11 device\");\n      return E_FAIL;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_on_12.cpp",
    "content": "#include \"d3d11_context_imm.h\"\n#include \"d3d11_device.h\"\n#include \"d3d11_on_12.h\"\n\nnamespace dxvk {\n\n  D3D11on12Device::D3D11on12Device(\n          D3D11DXGIDevice*        pContainer,\n          D3D11Device*            pDevice,\n          ID3D12Device*           pD3D12Device,\n          ID3D12CommandQueue*     pD3D12Queue)\n  : m_container   (pContainer),\n    m_device      (pDevice),\n    m_d3d12Device (pD3D12Device),\n    m_d3d12Queue  (pD3D12Queue) {\n\n  }\n\n\n  D3D11on12Device::~D3D11on12Device() {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11on12Device::AddRef() {\n    return m_container->AddRef();\n  }\n  \n\n  ULONG STDMETHODCALLTYPE D3D11on12Device::Release() {\n    return m_container->Release();\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE D3D11on12Device::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_container->QueryInterface(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11on12Device::CreateWrappedResource(\n          IUnknown*               pResource12,\n    const D3D11_RESOURCE_FLAGS*   pResourceFlags,\n          D3D12_RESOURCE_STATES   InputState,\n          D3D12_RESOURCE_STATES   OutputState,\n          REFIID                  riid,\n          void**                  ppResource11) {\n    Com<ID3D12DXVKInteropDevice> interopDevice;\n    m_d3d12Device->QueryInterface(__uuidof(ID3D12DXVKInteropDevice), reinterpret_cast<void**>(&interopDevice));\n\n    D3D11_ON_12_RESOURCE_INFO info = { };\n    info.InputState = InputState;\n    info.OutputState = OutputState;\n    info.IsWrappedResource = TRUE;\n\n    // 11on12 technically allows importing D3D12 heaps as tile pools,\n    // but we don't support importing sparse resources at this time.\n    if (FAILED(pResource12->QueryInterface(__uuidof(ID3D12Resource), reinterpret_cast<void**>(&info.Resource)))) {\n      Logger::err(\"D3D11on12Device::CreateWrappedResource: Resource not a valid D3D12 resource\");\n      return E_INVALIDARG;\n    }\n\n    // Query Vulkan resource handle and buffer offset as necessary\n    if (FAILED(interopDevice->GetVulkanResourceInfo(info.Resource.ptr(), &info.VulkanHandle, &info.VulkanOffset))) {\n      Logger::err(\"D3D11on12Device::CreateWrappedResource: Failed to retrieve Vulkan resource info\");\n      return E_INVALIDARG;\n    }\n\n    Com<ID3D11Resource> resource;\n    D3D12_RESOURCE_DESC desc = info.Resource->GetDesc();\n\n    if (desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) {\n      D3D11_BUFFER_DESC bufferDesc;\n\n      if (FAILED(D3D11Buffer::GetDescFromD3D12(info.Resource.ptr(), pResourceFlags, &bufferDesc)))\n        return E_INVALIDARG;\n\n      resource = new D3D11Buffer(m_device, &bufferDesc, &info);\n    } else {\n      D3D11_COMMON_TEXTURE_DESC textureDesc;\n\n      if (FAILED(D3D11CommonTexture::GetDescFromD3D12(info.Resource.ptr(), pResourceFlags, &textureDesc)))\n        return E_INVALIDARG;\n\n      switch (desc.Dimension) {\n        case D3D12_RESOURCE_DIMENSION_TEXTURE1D:\n          resource = new D3D11Texture1D(m_device, &textureDesc, &info);\n          break;\n\n        case D3D12_RESOURCE_DIMENSION_TEXTURE2D:\n          resource = new D3D11Texture2D(m_device, &textureDesc, &info, nullptr);\n          break;\n\n        case D3D12_RESOURCE_DIMENSION_TEXTURE3D:\n          resource = new D3D11Texture3D(m_device, &textureDesc, &info);\n          break;\n\n        default:\n          Logger::err(\"D3D11on12Device::CreateWrappedResource: Unhandled resource dimension\");\n          return E_INVALIDARG;\n      }\n    }\n\n    return resource->QueryInterface(riid, ppResource11);\n  }\n\n\n  void STDMETHODCALLTYPE D3D11on12Device::ReleaseWrappedResources(\n          ID3D11Resource* const*  ppResources,\n          UINT                    ResourceCount) {\n    Com<ID3D12DXVKInteropDevice> interopDevice;\n    m_d3d12Device->QueryInterface(__uuidof(ID3D12DXVKInteropDevice), reinterpret_cast<void**>(&interopDevice));\n\n    for (uint32_t i = 0; i < ResourceCount; i++) {\n      D3D11_ON_12_RESOURCE_INFO info;\n\n      if (FAILED(GetResource11on12Info(ppResources[i], &info)) || !info.IsWrappedResource) {\n        Logger::warn(\"D3D11on12Device::ReleaseWrappedResources: Resource not a wrapped resource, skipping\");\n        continue;\n      }\n\n      VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;\n      interopDevice->GetVulkanImageLayout(info.Resource.ptr(), info.OutputState, &layout);\n      m_device->GetContext()->Release11on12Resource(ppResources[i], layout);\n    }\n  }\n\n\n  void STDMETHODCALLTYPE D3D11on12Device::AcquireWrappedResources(\n          ID3D11Resource* const*  ppResources,\n          UINT                    ResourceCount) {\n    Com<ID3D12DXVKInteropDevice> interopDevice;\n    m_d3d12Device->QueryInterface(__uuidof(ID3D12DXVKInteropDevice), reinterpret_cast<void**>(&interopDevice));\n\n    for (uint32_t i = 0; i < ResourceCount; i++) {\n      D3D11_ON_12_RESOURCE_INFO info;\n\n      if (FAILED(GetResource11on12Info(ppResources[i], &info)) || !info.IsWrappedResource) {\n        Logger::warn(\"D3D11on12Device::AcquireWrappedResources: Resource not a wrapped resource, skipping\");\n        continue;\n      }\n\n      VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;\n      interopDevice->GetVulkanImageLayout(info.Resource.ptr(), info.InputState, &layout);\n      m_device->GetContext()->Acquire11on12Resource(ppResources[i], layout);\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11on12Device::GetD3D12Device(\n          REFIID                  riid,\n          void**                  ppvDevice) {\n    return m_d3d12Queue->GetDevice(riid, ppvDevice);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_on_12.h",
    "content": "#pragma once\n\n#include \"d3d11_on_12_interfaces.h\"\n\n#include \"../util/log/log.h\"\n\n/**\n * \\brief Declaration of the ID3D11On12Device1 interface\n *\n * Various different headers that we need to be compatible with\n * can't seem to agree on the signature of GetD3D12Device, and\n * older wine/mingw headers don't support this interface at all.\n */\nMIDL_INTERFACE(\"bdb64df4-ea2f-4c70-b861-aaab1258bb5d\")\nID3D11On12Device1_DXVK : public ID3D11On12Device {\n  virtual HRESULT STDMETHODCALLTYPE GetD3D12Device(\n          REFIID                  riid,\n          void**                  ppvDevice) = 0;\n};\n\n\nnamespace dxvk {\n\n  class D3D11Device;\n  class D3D11DXGIDevice;\n\n  /**\n   * \\brief Resource info for 11on12 resources\n   */\n  struct D3D11_ON_12_RESOURCE_INFO {\n    Com<ID3D12Resource> Resource;\n    UINT64 VulkanHandle = 0;\n    UINT64 VulkanOffset = 0;\n    BOOL IsWrappedResource = FALSE;\n    D3D12_RESOURCE_STATES InputState = D3D12_RESOURCE_STATE_COMMON;\n    D3D12_RESOURCE_STATES OutputState = D3D12_RESOURCE_STATE_COMMON;\n  };\n\n\n  class D3D11on12Device : public ID3D11On12Device1_DXVK {\n\n  public:\n\n    D3D11on12Device(\n            D3D11DXGIDevice*        pContainer,\n            D3D11Device*            pDevice,\n            ID3D12Device*           pD3D12Device,\n            ID3D12CommandQueue*     pD3D12Queue);\n\n    ~D3D11on12Device();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    HRESULT STDMETHODCALLTYPE CreateWrappedResource(\n            IUnknown*               pResource12,\n      const D3D11_RESOURCE_FLAGS*   pResourceFlags,\n            D3D12_RESOURCE_STATES   InputState,\n            D3D12_RESOURCE_STATES   OutputState,\n            REFIID                  riid,\n            void**                  ppResource11);\n\n    void STDMETHODCALLTYPE ReleaseWrappedResources(\n            ID3D11Resource* const*  ppResources,\n            UINT                    ResourceCount);\n\n    void STDMETHODCALLTYPE AcquireWrappedResources(\n            ID3D11Resource* const*  ppResources,\n            UINT                    ResourceCount);\n\n    HRESULT STDMETHODCALLTYPE GetD3D12Device(\n            REFIID                  riid,\n            void**                  ppvDevice);\n\n    bool Is11on12Device() const {\n      return m_d3d12Device != nullptr;\n    }\n\n  private:\n\n    D3D11DXGIDevice*        m_container;\n    D3D11Device*            m_device;\n\n    Com<ID3D12Device>       m_d3d12Device;\n    Com<ID3D12CommandQueue> m_d3d12Queue;\n\n  };\n\n}\n\n#ifndef _MSC_VER\n__CRT_UUID_DECL(ID3D11On12Device1_DXVK, 0xbdb64df4,0xea2f,0x4c70,0xb8,0x61,0xaa,0xab,0x12,0x58,0xbb,0x5d);\n#endif\n"
  },
  {
    "path": "src/d3d11/d3d11_on_12_interfaces.h",
    "content": "#pragma once\n\n#include \"../vulkan/vulkan_loader.h\"\n\n#include <d3d11on12.h>\n\nMIDL_INTERFACE(\"39da4e09-bd1c-4198-9fae-86bbe3be41fd\")\nID3D12DXVKInteropDevice : public IUnknown {\n  virtual HRESULT STDMETHODCALLTYPE GetDXGIAdapter(\n          REFIID                      iid,\n          void**                      ppvObject) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetInstanceExtensions(\n          UINT*                       pExtensionCount,\n    const char**                      ppExtensions) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetDeviceExtensions(\n          UINT*                       pExtensionCount,\n    const char**                      ppExtensions) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetDeviceFeatures(\n    const VkPhysicalDeviceFeatures2** ppFeatures) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetVulkanHandles(\n          VkInstance*                 pVkInstance,\n          VkPhysicalDevice*           pVkPhysicalDevice,\n          VkDevice*                   pVkDevice) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetVulkanQueueInfo(\n          ID3D12CommandQueue*         pCommandQueue,\n          VkQueue*                    pVkQueue,\n          UINT32*                     pVkQueueFamily) = 0;\n\n  virtual void STDMETHODCALLTYPE GetVulkanImageLayout(\n          ID3D12Resource*             pResource,\n          D3D12_RESOURCE_STATES       State,\n          VkImageLayout*              pVkLayout) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetVulkanResourceInfo(\n          ID3D12Resource*             pResource,\n          UINT64*                     pVkHandle,\n          UINT64*                     pBufferOffset) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE LockCommandQueue(\n          ID3D12CommandQueue*         pCommandQueue) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE UnlockCommandQueue(\n          ID3D12CommandQueue*         pCommandQueue) = 0;\n\n};\n\n#ifndef _MSC_VER\n__CRT_UUID_DECL(ID3D12DXVKInteropDevice, 0x39da4e09, 0xbd1c, 0x4198, 0x9f,0xae, 0x86,0xbb,0xe3,0xbe,0x41,0xfd)\n#endif\n"
  },
  {
    "path": "src/d3d11/d3d11_options.cpp",
    "content": "#include \"../util/util_math.h\"\n\n#include \"d3d11_options.h\"\n\nnamespace dxvk {\n  \n  static bool IsAPITracingDXGI() {\n#ifdef _WIN32\n    return !!::GetModuleHandle(\"dxgitrace.dll\");\n#else\n    return false;\n#endif\n  }\n\n  D3D11Options::D3D11Options(const Config& config) {\n    this->forceComputeLdsBarriers = config.getOption<bool>(\"d3d11.forceComputeLdsBarriers\", false);\n    this->forceComputeUavBarriers = config.getOption<bool>(\"d3d11.forceComputeUavBarriers\", false);\n    this->relaxedBarriers       = config.getOption<bool>(\"d3d11.relaxedBarriers\", false);\n    this->relaxedGraphicsBarriers = config.getOption<bool>(\"d3d11.relaxedGraphicsBarriers\", false);\n    this->maxTessFactor         = config.getOption<int32_t>(\"d3d11.maxTessFactor\", 0);\n    this->samplerAnisotropy     = config.getOption<int32_t>(\"d3d11.samplerAnisotropy\", -1);\n    this->samplerLodBias        = config.getOption<float>(\"d3d11.samplerLodBias\", 0.0f);\n    this->clampNegativeLodBias  = config.getOption<bool>(\"d3d11.clampNegativeLodBias\", false);\n    this->forceSampleRateShading = config.getOption<bool>(\"d3d11.forceSampleRateShading\", false);\n    this->disableMsaa           = config.getOption<bool>(\"d3d11.disableMsaa\", false);\n    this->enableContextLock     = config.getOption<bool>(\"d3d11.enableContextLock\", false);\n    this->deferSurfaceCreation  = config.getOption<bool>(\"dxgi.deferSurfaceCreation\", false);\n    this->maxFrameLatency       = config.getOption<int32_t>(\"dxgi.maxFrameLatency\", 0);\n    this->exposeDriverCommandLists = config.getOption<bool>(\"d3d11.exposeDriverCommandLists\", true);\n    this->reproducibleCommandStream = config.getOption<bool>(\"d3d11.reproducibleCommandStream\", false);\n    this->disableDirectImageMapping = config.getOption<bool>(\"d3d11.disableDirectImageMapping\", false);\n\n    // Clamp LOD bias so that people don't abuse this in unintended ways\n    this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);\n\n    auto cachedDynamicResources = config.getOption<std::string>(\"d3d11.cachedDynamicResources\", std::string());\n\n    if (IsAPITracingDXGI()) {\n      // apitrace reads back all mapped resources on the CPU, so\n      // allocating everything in cached memory is necessary to\n      // achieve acceptable performance\n      this->cachedDynamicResources = ~0u;\n    } else {\n      this->cachedDynamicResources = 0u;\n\n      for (char c : cachedDynamicResources) {\n        switch (c) {\n          case 'c': this->cachedDynamicResources |= D3D11_BIND_CONSTANT_BUFFER;   break;\n          case 'v': this->cachedDynamicResources |= D3D11_BIND_VERTEX_BUFFER;     break;\n          case 'i': this->cachedDynamicResources |= D3D11_BIND_INDEX_BUFFER;      break;\n          case 'r': this->cachedDynamicResources |= D3D11_BIND_SHADER_RESOURCE;   break;\n          case 'a': this->cachedDynamicResources  = ~0u;                          break;\n          default:  Logger::warn(str::format(\"Unknown flag for d3d11.cachedDynamicResources option: \", c));\n        }\n      }\n    }\n\n    // Shader dump path is only available via an environment variable\n    this->shaderDumpPath = env::getEnvVar(\"DXVK_SHADER_DUMP_PATH\");\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_options.h",
    "content": "#pragma once\n\n#include \"../util/config/config.h\"\n\n#include \"../dxgi/dxgi_options.h\"\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"d3d11_include.h\"\n\nnamespace dxvk {\n\n  struct D3D11Options {\n    D3D11Options(const Config& config);\n\n    /// Force thread-group shared memory accesses to be volatile\n    ///\n    /// Workaround for compute shaders that read and\n    /// write from the same shared memory location\n    /// without explicit synchronization.\n    bool forceComputeLdsBarriers = false;\n\n    /// Force UAV synchronization insided compute shaders\n    ///\n    /// Workaround for compute shaders that access overlapping\n    /// memory regions within a UAV without proper workgroup\n    /// synchroniation. Will have a negative performance impact.\n    bool forceComputeUavBarriers = false;\n\n    /// Use relaxed memory barriers\n    ///\n    /// May improve performance in some games,\n    /// but might also cause rendering issues.\n    bool relaxedBarriers = false;\n\n    /// Ignore graphics barriers\n    ///\n    /// May improve performance in some games,\n    /// but might also cause rendering issues.\n    bool relaxedGraphicsBarriers = false;\n\n    /// Maximum tessellation factor.\n    ///\n    /// Limits tessellation factors in tessellation\n    /// control shaders. Values from 8 to 64 are\n    /// supported, other values will be ignored.\n    int32_t maxTessFactor = 0;\n\n    /// Anisotropic filter override\n    ///\n    /// Enforces anisotropic filtering with the\n    /// given anisotropy value for all samplers.\n    int32_t samplerAnisotropy = -1;\n\n    /// Mipmap LOD bias\n    ///\n    /// Enforces the given LOD bias for all samplers.\n    float samplerLodBias = 0.0f;\n\n    /// Clamps negative LOD bias\n    bool clampNegativeLodBias = false;\n\n    /// Override maximum frame latency if the app specifies\n    /// a higher value. May help with frame timing issues.\n    int32_t maxFrameLatency = 0;\n\n    /// Defer surface creation until first present call. This\n    /// fixes issues with games that create multiple swap chains\n    /// for a single window that may interfere with each other.\n    bool deferSurfaceCreation = false;\n\n    /// Enables sample rate shading by interpolating fragment shader\n    /// inputs at the sample location rather than pixel center,\n    /// unless otherwise specified by the application.\n    bool forceSampleRateShading = false;\n\n    /// Forces the sample count of all textures to be 1, and\n    /// performs the required shader and resolve fixups.\n    bool disableMsaa = false;\n\n    /// Dynamic resources with the given bind flags will be allocated\n    /// in cached system memory. Enabled automatically when recording\n    /// an api trace.\n    uint32_t cachedDynamicResources = 0;\n\n    /// Always lock immediate context on every API call. May be\n    /// useful for debugging purposes or when applications have\n    /// race conditions.\n    bool enableContextLock = false;\n\n    /// Whether to expose the driver command list feature. Enabled by\n    /// default and generally beneficial, but some games may assume that\n    /// this is not supported when running on an AMD GPU.\n    bool exposeDriverCommandLists = true;\n\n    /// Ensure that for the same D3D commands the output VK commands\n    /// don't change between runs. Useful for comparative benchmarking,\n    /// can negatively affect performance.\n    bool reproducibleCommandStream = false;\n\n    /// Whether to force a staging buffer for mapped images.\n    /// Some games are broken and ignore row pitch.\n    bool disableDirectImageMapping = false;\n\n    /// Shader dump path\n    std::string shaderDumpPath;\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_query.cpp",
    "content": "#include \"d3d11_device.h\"\n#include \"d3d11_query.h\"\n\nnamespace dxvk {\n  \n  D3D11Query::D3D11Query(\n          D3D11Device*       device,\n    const D3D11_QUERY_DESC1& desc)\n  : D3D11DeviceChild<ID3D11Query1>(device),\n    m_desc(desc),\n    m_state(D3D11_VK_QUERY_INITIAL),\n    m_d3d10(this),\n    m_destructionNotifier(this) {\n    Rc<DxvkDevice> dxvkDevice = m_parent->GetDXVKDevice();\n\n    switch (m_desc.Query) {\n      case D3D11_QUERY_EVENT:\n        m_event[0] = dxvkDevice->createGpuEvent();\n        break;\n        \n      case D3D11_QUERY_OCCLUSION:\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_OCCLUSION,\n          VK_QUERY_CONTROL_PRECISE_BIT, 0);\n        break;\n      \n      case D3D11_QUERY_OCCLUSION_PREDICATE:\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_OCCLUSION, 0, 0);\n        break;\n        \n      case D3D11_QUERY_TIMESTAMP:\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_TIMESTAMP, 0, 0);\n        break;\n      \n      case D3D11_QUERY_TIMESTAMP_DISJOINT:\n        for (uint32_t i = 0; i < 2; i++) {\n          m_query[i] = dxvkDevice->createGpuQuery(\n            VK_QUERY_TYPE_TIMESTAMP, 0, 0);\n        }\n        break;\n      \n      case D3D11_QUERY_PIPELINE_STATISTICS:\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_PIPELINE_STATISTICS, 0, 0);\n        break;\n      \n      case D3D11_QUERY_SO_STATISTICS:\n      case D3D11_QUERY_SO_STATISTICS_STREAM0:\n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE:\n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0:\n        // FIXME it is technically incorrect to map\n        // SO_OVERFLOW_PREDICATE to the first stream,\n        // but this is good enough for D3D10 behaviour\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 0);\n        break;\n      \n      case D3D11_QUERY_SO_STATISTICS_STREAM1:\n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1:\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 1);\n        break;\n      \n      case D3D11_QUERY_SO_STATISTICS_STREAM2:\n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2:\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 2);\n        break;\n      \n      case D3D11_QUERY_SO_STATISTICS_STREAM3:\n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3:\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 0, 3);\n        break;\n      \n      default:\n        throw DxvkError(str::format(\"D3D11: Unhandled query type: \", desc.Query));\n    }\n  }\n  \n  \n  D3D11Query::~D3D11Query() {\n\n  }\n  \n    \n  HRESULT STDMETHODCALLTYPE D3D11Query::QueryInterface(REFIID  riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11Asynchronous)\n     || riid == __uuidof(ID3D11Query)\n     || riid == __uuidof(ID3D11Query1)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10Asynchronous)\n     || riid == __uuidof(ID3D10Query)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n    \n    if (m_desc.Query == D3D11_QUERY_OCCLUSION_PREDICATE) {\n      if (riid == __uuidof(ID3D11Predicate)) {\n        *ppvObject = AsPredicate(ref(this));\n        return S_OK;\n      }\n\n      if (riid == __uuidof(ID3D10Predicate)) {\n        *ppvObject = ref(&m_d3d10);\n        return S_OK;\n      }\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11Query), riid)) {\n      Logger::warn(\"D3D11Query: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  UINT STDMETHODCALLTYPE D3D11Query::GetDataSize() {\n    switch (m_desc.Query) {\n      case D3D11_QUERY_EVENT:\n        return sizeof(BOOL);\n      \n      case D3D11_QUERY_OCCLUSION:\n        return sizeof(UINT64);\n      \n      case D3D11_QUERY_TIMESTAMP:\n        return sizeof(UINT64);\n      \n      case D3D11_QUERY_TIMESTAMP_DISJOINT:\n        return sizeof(D3D11_QUERY_DATA_TIMESTAMP_DISJOINT);\n      \n      case D3D11_QUERY_PIPELINE_STATISTICS:\n        return sizeof(D3D11_QUERY_DATA_PIPELINE_STATISTICS);\n      \n      case D3D11_QUERY_OCCLUSION_PREDICATE:\n        return sizeof(BOOL);\n      \n      case D3D11_QUERY_SO_STATISTICS:\n      case D3D11_QUERY_SO_STATISTICS_STREAM0:\n      case D3D11_QUERY_SO_STATISTICS_STREAM1:\n      case D3D11_QUERY_SO_STATISTICS_STREAM2:\n      case D3D11_QUERY_SO_STATISTICS_STREAM3:\n        return sizeof(D3D11_QUERY_DATA_SO_STATISTICS);\n      \n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE:\n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0:\n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1:\n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2:\n      case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3:\n        return sizeof(BOOL);\n    }\n    \n    Logger::err(\"D3D11Query: Failed to query data size\");\n    return 0;\n  }\n  \n    \n  void STDMETHODCALLTYPE D3D11Query::GetDesc(D3D11_QUERY_DESC* pDesc) {\n    pDesc->Query     = m_desc.Query;\n    pDesc->MiscFlags = m_desc.MiscFlags;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Query::GetDesc1(D3D11_QUERY_DESC1* pDesc) {\n    *pDesc = m_desc;\n  }\n  \n  \n  void D3D11Query::Begin(DxvkContext* ctx) {\n    switch (m_desc.Query) {\n      case D3D11_QUERY_EVENT:\n      case D3D11_QUERY_TIMESTAMP:\n        break;\n\n      case D3D11_QUERY_TIMESTAMP_DISJOINT:\n        ctx->writeTimestamp(m_query[1]);\n        break;\n      \n      default:\n        ctx->beginQuery(m_query[0]);\n    }\n  }\n  \n  \n  void D3D11Query::End(DxvkContext* ctx) {\n    switch (m_desc.Query) {\n      case D3D11_QUERY_EVENT:\n        ctx->signalGpuEvent(m_event[0]);\n        break;\n      \n      case D3D11_QUERY_TIMESTAMP:\n      case D3D11_QUERY_TIMESTAMP_DISJOINT:\n        ctx->writeTimestamp(m_query[0]);\n        break;\n      \n      default:\n        ctx->endQuery(m_query[0]);\n    }\n\n    m_resetCtr.fetch_sub(1, std::memory_order_release);\n  }\n  \n  \n  bool STDMETHODCALLTYPE D3D11Query::DoBegin() {\n    if (!IsScoped() || m_state == D3D11_VK_QUERY_BEGUN)\n      return false;\n\n    m_state = D3D11_VK_QUERY_BEGUN;\n    return true;\n  }\n\n  bool STDMETHODCALLTYPE D3D11Query::DoEnd() {\n    // Apparently the D3D11 runtime implicitly begins the query\n    // if it is in the wrong state at the time End is called, so\n    // let the caller react to it instead of just failing here.\n    bool result = m_state == D3D11_VK_QUERY_BEGUN || !IsScoped();\n\n    m_state = D3D11_VK_QUERY_ENDED;\n    m_resetCtr.fetch_add(1, std::memory_order_acquire);\n    return result;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11Query::GetData(\n          void*                             pData,\n          UINT                              GetDataFlags) {\n    if (m_state != D3D11_VK_QUERY_ENDED)\n      return DXGI_ERROR_INVALID_CALL;\n\n    if (m_resetCtr != 0u)\n      return S_FALSE;\n\n    if (m_desc.Query == D3D11_QUERY_EVENT) {\n      DxvkGpuEventStatus status = m_event[0]->test();\n\n      if (status == DxvkGpuEventStatus::Invalid)\n        return DXGI_ERROR_INVALID_CALL;\n      \n      bool signaled = status == DxvkGpuEventStatus::Signaled;\n\n      if (pData != nullptr)\n        *static_cast<BOOL*>(pData) = signaled;\n      \n      return signaled ? S_OK : S_FALSE;\n    } else {\n      std::array<DxvkQueryData, MaxGpuQueries> queryData = { };\n      \n      for (uint32_t i = 0; i < MaxGpuQueries && m_query[i] != nullptr; i++) {\n        DxvkGpuQueryStatus status = m_query[i]->getData(queryData[i]);\n\n        if (status == DxvkGpuQueryStatus::Invalid\n         || status == DxvkGpuQueryStatus::Failed)\n          return DXGI_ERROR_INVALID_CALL;\n        \n        if (status == DxvkGpuQueryStatus::Pending)\n          return S_FALSE;\n      }\n      \n      if (pData == nullptr)\n        return S_OK;\n      \n      switch (m_desc.Query) {\n        case D3D11_QUERY_OCCLUSION:\n          *static_cast<UINT64*>(pData) = queryData[0].occlusion.samplesPassed;\n          return S_OK;\n        \n        case D3D11_QUERY_OCCLUSION_PREDICATE:\n          *static_cast<BOOL*>(pData) = queryData[0].occlusion.samplesPassed != 0;\n          return S_OK;\n        \n        case D3D11_QUERY_TIMESTAMP:\n          *static_cast<UINT64*>(pData) = queryData[0].timestamp.time;\n          return S_OK;\n        \n        case D3D11_QUERY_TIMESTAMP_DISJOINT: {\n          auto data = static_cast<D3D11_QUERY_DATA_TIMESTAMP_DISJOINT*>(pData);\n          data->Frequency = GetTimestampQueryFrequency();\n          data->Disjoint  = queryData[0].timestamp.time < queryData[1].timestamp.time;\n        } return S_OK;\n        \n        case D3D11_QUERY_PIPELINE_STATISTICS: {\n          auto data = static_cast<D3D11_QUERY_DATA_PIPELINE_STATISTICS*>(pData);\n          data->IAVertices    = queryData[0].statistic.iaVertices;\n          data->IAPrimitives  = queryData[0].statistic.iaPrimitives;\n          data->VSInvocations = queryData[0].statistic.vsInvocations;\n          data->GSInvocations = queryData[0].statistic.gsInvocations;\n          data->GSPrimitives  = queryData[0].statistic.gsPrimitives;\n          data->CInvocations  = queryData[0].statistic.clipInvocations;\n          data->CPrimitives   = queryData[0].statistic.clipPrimitives;\n          data->PSInvocations = queryData[0].statistic.fsInvocations;\n          data->HSInvocations = queryData[0].statistic.tcsPatches;\n          data->DSInvocations = queryData[0].statistic.tesInvocations;\n          data->CSInvocations = queryData[0].statistic.csInvocations;\n        } return S_OK;\n\n        case D3D11_QUERY_SO_STATISTICS:\n        case D3D11_QUERY_SO_STATISTICS_STREAM0:\n        case D3D11_QUERY_SO_STATISTICS_STREAM1:\n        case D3D11_QUERY_SO_STATISTICS_STREAM2:\n        case D3D11_QUERY_SO_STATISTICS_STREAM3: {\n          auto data = static_cast<D3D11_QUERY_DATA_SO_STATISTICS*>(pData);\n          data->NumPrimitivesWritten    = queryData[0].xfbStream.primitivesWritten;\n          data->PrimitivesStorageNeeded = queryData[0].xfbStream.primitivesNeeded;\n        } return S_OK;\n          \n        case D3D11_QUERY_SO_OVERFLOW_PREDICATE:\n        case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0:\n        case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM1:\n        case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM2:\n        case D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM3: {\n          auto data = static_cast<BOOL*>(pData);\n          *data = queryData[0].xfbStream.primitivesNeeded\n                > queryData[0].xfbStream.primitivesWritten;\n        } return S_OK;\n\n        default:\n          Logger::err(str::format(\"D3D11: Unhandled query type in GetData: \", m_desc.Query));\n          return E_INVALIDARG;\n      }\n    }\n  }\n  \n  \n  UINT64 D3D11Query::GetTimestampQueryFrequency() const {\n    Rc<DxvkDevice>  device  = m_parent->GetDXVKDevice();\n    Rc<DxvkAdapter> adapter = device->adapter();\n\n    const auto& limits = adapter->deviceProperties().core.properties.limits;\n    return uint64_t(1'000'000'000.0f / limits.timestampPeriod);\n  }\n\n\n  HRESULT D3D11Query::ValidateDesc(const D3D11_QUERY_DESC1* pDesc) {\n    if (pDesc->Query       >= D3D11_QUERY_PIPELINE_STATISTICS\n     && pDesc->ContextType >  D3D11_CONTEXT_TYPE_3D)\n      return E_INVALIDARG;\n    \n    return S_OK;\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_query.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_gpu_event.h\"\n#include \"../dxvk/dxvk_gpu_query.h\"\n\n#include \"../d3d10/d3d10_query.h\"\n\n#include \"d3d11_device_child.h\"\n\nnamespace dxvk {\n  \n  enum D3D11_VK_QUERY_STATE : uint32_t {\n    D3D11_VK_QUERY_INITIAL,\n    D3D11_VK_QUERY_BEGUN,\n    D3D11_VK_QUERY_ENDED,\n  };\n  \n  class D3D11Query : public D3D11DeviceChild<ID3D11Query1> {\n    constexpr static uint32_t MaxGpuQueries = 2;\n    constexpr static uint32_t MaxGpuEvents  = 1;\n  public:\n    \n    D3D11Query(\n            D3D11Device*        device,\n      const D3D11_QUERY_DESC1&  desc);\n    \n    ~D3D11Query();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    UINT STDMETHODCALLTYPE GetDataSize();\n    \n    void STDMETHODCALLTYPE GetDesc(D3D11_QUERY_DESC* pDesc) final;\n\n    void STDMETHODCALLTYPE GetDesc1(D3D11_QUERY_DESC1* pDesc) final;\n\n    void Begin(DxvkContext* ctx);\n    \n    void End(DxvkContext* ctx);\n    \n    bool STDMETHODCALLTYPE DoBegin();\n\n    bool STDMETHODCALLTYPE DoEnd();\n\n    HRESULT STDMETHODCALLTYPE GetData(\n            void*                             pData,\n            UINT                              GetDataFlags);\n    \n    void DoDeferredEnd() {\n      m_state = D3D11_VK_QUERY_ENDED;\n      m_resetCtr.fetch_add(1, std::memory_order_acquire);\n    }\n\n    bool IsScoped() const {\n      return m_desc.Query != D3D11_QUERY_EVENT\n          && m_desc.Query != D3D11_QUERY_TIMESTAMP;\n    }\n\n    bool IsEvent() const {\n      return m_desc.Query == D3D11_QUERY_EVENT;\n    }\n\n    bool TrackStalls() const {\n      return m_desc.Query == D3D11_QUERY_EVENT\n          || m_desc.Query == D3D11_QUERY_TIMESTAMP\n          || m_desc.Query == D3D11_QUERY_TIMESTAMP_DISJOINT;\n    }\n\n    bool IsStalling() const {\n      return m_stallFlag;\n    }\n\n    void NotifyEnd() {\n      m_stallMask <<= 1;\n    }\n\n    void NotifyStall() {\n      m_stallMask |= 1;\n      m_stallFlag |= bit::popcnt(m_stallMask) >= 16;\n    }\n    \n    D3D10Query* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n\n    static HRESULT ValidateDesc(const D3D11_QUERY_DESC1* pDesc);\n\n    static ID3D11Predicate* AsPredicate(ID3D11Query* pQuery) {\n      // ID3D11Predicate and ID3D11Query have the same vtable. This\n      // saves us some headache in all query-related functions.\n      return static_cast<ID3D11Predicate*>(pQuery);\n    }\n    \n    static D3D11Query* FromPredicate(ID3D11Predicate* pPredicate) {\n      return static_cast<D3D11Query*>(static_cast<ID3D11Query*>(pPredicate));\n    }\n    \n  private:\n    \n    D3D11_QUERY_DESC1  m_desc;\n\n    D3D11_VK_QUERY_STATE m_state;\n    \n    std::array<Rc<DxvkQuery>, MaxGpuQueries> m_query;\n    std::array<Rc<DxvkEvent>, MaxGpuEvents>  m_event;\n\n    D3D10Query m_d3d10;\n\n    uint32_t m_stallMask = 0;\n    bool     m_stallFlag = false;\n\n    std::atomic<uint32_t> m_resetCtr = { 0u };\n\n    D3DDestructionNotifier m_destructionNotifier;\n\n    UINT64 GetTimestampQueryFrequency() const;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_rasterizer.cpp",
    "content": "#include \"d3d11_device.h\"\n#include \"d3d11_rasterizer.h\"\n\nnamespace dxvk {\n  \n  D3D11RasterizerState::D3D11RasterizerState(\n          D3D11Device*                    device,\n    const D3D11_RASTERIZER_DESC2&         desc,\n          Container*                      container)\n  : D3D11StateObject<ID3D11RasterizerState2, D3D11RasterizerState>(device, container),\n    m_desc(desc), m_d3d10(this), m_destructionNotifier(this) {\n    // Polygon mode. Determines whether the rasterizer fills\n    // a polygon or renders lines connecting the vertices.\n    switch (desc.FillMode) {\n      default:\n      case D3D11_FILL_SOLID:     m_state.setPolygonMode(VK_POLYGON_MODE_FILL); break;\n      case D3D11_FILL_WIREFRAME: m_state.setPolygonMode(VK_POLYGON_MODE_LINE); break;\n    }\n    \n    // Face culling properties. The rasterizer may discard\n    // polygons that are facing towards or away from the\n    // viewer, depending on the options below.\n    switch (desc.CullMode) {\n      default:\n      case D3D11_CULL_NONE:  m_state.setCullMode(VK_CULL_MODE_NONE);      break;\n      case D3D11_CULL_FRONT: m_state.setCullMode(VK_CULL_MODE_FRONT_BIT); break;\n      case D3D11_CULL_BACK:  m_state.setCullMode(VK_CULL_MODE_BACK_BIT);  break;\n    }\n\n    m_state.setFrontFace(desc.FrontCounterClockwise\n      ? VK_FRONT_FACE_COUNTER_CLOCKWISE\n      : VK_FRONT_FACE_CLOCKWISE);\n\n    // In the backend we treat depth bias as a dynamic state because\n    // some games like to put random/uninitialized numbers here, but\n    // we do not need to enable it in case the parameters are both 0.\n    m_state.setDepthClip(desc.DepthClipEnable);\n    m_state.setConservativeMode(DecodeConservativeRasterizationMode(desc.ConservativeRaster));\n    m_state.setSampleCount(desc.ForcedSampleCount);\n    m_state.setFlatShading(false);\n    m_state.setLineMode(VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT);\n\n    m_depthBias.depthBiasConstant = float(desc.DepthBias);\n    m_depthBias.depthBiasSlope    = desc.SlopeScaledDepthBias;\n    m_depthBias.depthBiasClamp    = desc.DepthBiasClamp;\n\n    // Set up line rasterization mode\n    const auto& features = device->GetDXVKDevice()->features();\n\n    if (desc.MultisampleEnable) {\n      if (features.extLineRasterization.rectangularLines)\n        m_state.setLineMode(VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT);\n    } else if (desc.AntialiasedLineEnable) {\n      if (features.extLineRasterization.smoothLines)\n        m_state.setLineMode(VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT);\n    }\n  }\n  \n  \n  D3D11RasterizerState::~D3D11RasterizerState() {\n    \n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11RasterizerState::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11RasterizerState)\n     || riid == __uuidof(ID3D11RasterizerState1)\n     || riid == __uuidof(ID3D11RasterizerState2)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10RasterizerState)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11RasterizerState), riid)) {\n      Logger::warn(\"D3D11RasterizerState::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11RasterizerState::GetDesc(D3D11_RASTERIZER_DESC* pDesc) {\n    pDesc->FillMode               = m_desc.FillMode;\n    pDesc->CullMode               = m_desc.CullMode;\n    pDesc->FrontCounterClockwise  = m_desc.FrontCounterClockwise;\n    pDesc->DepthBias              = m_desc.DepthBias;\n    pDesc->DepthBiasClamp         = m_desc.DepthBiasClamp;\n    pDesc->SlopeScaledDepthBias   = m_desc.SlopeScaledDepthBias;\n    pDesc->DepthClipEnable        = m_desc.DepthClipEnable;\n    pDesc->ScissorEnable          = m_desc.ScissorEnable;\n    pDesc->MultisampleEnable      = m_desc.MultisampleEnable;\n    pDesc->AntialiasedLineEnable  = m_desc.AntialiasedLineEnable;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11RasterizerState::GetDesc1(D3D11_RASTERIZER_DESC1* pDesc) {\n    pDesc->FillMode               = m_desc.FillMode;\n    pDesc->CullMode               = m_desc.CullMode;\n    pDesc->FrontCounterClockwise  = m_desc.FrontCounterClockwise;\n    pDesc->DepthBias              = m_desc.DepthBias;\n    pDesc->DepthBiasClamp         = m_desc.DepthBiasClamp;\n    pDesc->SlopeScaledDepthBias   = m_desc.SlopeScaledDepthBias;\n    pDesc->DepthClipEnable        = m_desc.DepthClipEnable;\n    pDesc->ScissorEnable          = m_desc.ScissorEnable;\n    pDesc->MultisampleEnable      = m_desc.MultisampleEnable;\n    pDesc->AntialiasedLineEnable  = m_desc.AntialiasedLineEnable;\n    pDesc->ForcedSampleCount      = m_desc.ForcedSampleCount;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11RasterizerState::GetDesc2(D3D11_RASTERIZER_DESC2* pDesc) {\n    *pDesc = m_desc;\n  }\n\n  \n  D3D11_RASTERIZER_DESC2 D3D11RasterizerState::PromoteDesc(\n    const D3D11_RASTERIZER_DESC*  pSrcDesc) {\n    D3D11_RASTERIZER_DESC2 dstDesc;\n    dstDesc.FillMode              = pSrcDesc->FillMode;\n    dstDesc.CullMode              = pSrcDesc->CullMode;\n    dstDesc.FrontCounterClockwise = pSrcDesc->FrontCounterClockwise;\n    dstDesc.DepthBias             = pSrcDesc->DepthBias;\n    dstDesc.DepthBiasClamp        = pSrcDesc->DepthBiasClamp;\n    dstDesc.SlopeScaledDepthBias  = pSrcDesc->SlopeScaledDepthBias;\n    dstDesc.DepthClipEnable       = pSrcDesc->DepthClipEnable;\n    dstDesc.ScissorEnable         = pSrcDesc->ScissorEnable;\n    dstDesc.MultisampleEnable     = pSrcDesc->MultisampleEnable;\n    dstDesc.AntialiasedLineEnable = pSrcDesc->AntialiasedLineEnable;\n    dstDesc.ForcedSampleCount     = 0;\n    dstDesc.ConservativeRaster    = D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF;\n    return dstDesc;\n  }\n  \n  \n  D3D11_RASTERIZER_DESC2 D3D11RasterizerState::PromoteDesc(\n    const D3D11_RASTERIZER_DESC1*  pSrcDesc) {\n    D3D11_RASTERIZER_DESC2 dstDesc;\n    dstDesc.FillMode              = pSrcDesc->FillMode;\n    dstDesc.CullMode              = pSrcDesc->CullMode;\n    dstDesc.FrontCounterClockwise = pSrcDesc->FrontCounterClockwise;\n    dstDesc.DepthBias             = pSrcDesc->DepthBias;\n    dstDesc.DepthBiasClamp        = pSrcDesc->DepthBiasClamp;\n    dstDesc.SlopeScaledDepthBias  = pSrcDesc->SlopeScaledDepthBias;\n    dstDesc.DepthClipEnable       = pSrcDesc->DepthClipEnable;\n    dstDesc.ScissorEnable         = pSrcDesc->ScissorEnable;\n    dstDesc.MultisampleEnable     = pSrcDesc->MultisampleEnable;\n    dstDesc.AntialiasedLineEnable = pSrcDesc->AntialiasedLineEnable;\n    dstDesc.ForcedSampleCount     = 0;\n    dstDesc.ConservativeRaster    = D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF;\n    return dstDesc;\n  }\n  \n  \n  HRESULT D3D11RasterizerState::NormalizeDesc(\n          D3D11_RASTERIZER_DESC2* pDesc) {\n    if (pDesc->FillMode < D3D11_FILL_WIREFRAME\n     || pDesc->FillMode > D3D11_FILL_SOLID)\n      return E_INVALIDARG;\n    \n    if (pDesc->CullMode < D3D11_CULL_NONE\n     || pDesc->CullMode > D3D11_CULL_BACK)\n      return E_INVALIDARG;\n    \n    if (pDesc->FrontCounterClockwise)\n      pDesc->FrontCounterClockwise = TRUE;\n    \n    if (pDesc->DepthClipEnable)\n      pDesc->DepthClipEnable = TRUE;\n    \n    if (pDesc->ScissorEnable)\n      pDesc->ScissorEnable = TRUE;\n    \n    if (pDesc->MultisampleEnable)\n      pDesc->MultisampleEnable = TRUE;\n    \n    if (pDesc->AntialiasedLineEnable)\n      pDesc->AntialiasedLineEnable = TRUE;\n    \n    if (pDesc->ForcedSampleCount != 0) {\n      if (FAILED(DecodeSampleCount(pDesc->ForcedSampleCount, nullptr)))\n        return E_INVALIDARG;\n    }\n\n    return S_OK;\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_rasterizer.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../d3d10/d3d10_rasterizer.h\"\n\n#include \"d3d11_device_child.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  \n  class D3D11RasterizerState : public D3D11StateObject<ID3D11RasterizerState2, D3D11RasterizerState> {\n    using Container = D3D11StateObjectSet<D3D11RasterizerState>;\n  public:\n    \n    using DescType = D3D11_RASTERIZER_DESC2;\n    \n    D3D11RasterizerState(\n            D3D11Device*                    device,\n      const D3D11_RASTERIZER_DESC2&         desc,\n            Container*                      container);\n    ~D3D11RasterizerState();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_RASTERIZER_DESC* pDesc) final;\n    \n    void STDMETHODCALLTYPE GetDesc1(\n            D3D11_RASTERIZER_DESC1* pDesc) final;\n    \n    void STDMETHODCALLTYPE GetDesc2(\n            D3D11_RASTERIZER_DESC2* pDesc) final;\n    \n    const D3D11_RASTERIZER_DESC2& Desc() const {\n      return m_desc;\n    }\n    \n    DxvkRasterizerState GetState() const {\n      return m_state;\n    }\n\n    DxvkDepthBias GetDepthBias() const {\n      return m_depthBias;\n    }\n    \n    D3D10RasterizerState* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n    \n    static D3D11_RASTERIZER_DESC2 PromoteDesc(\n      const D3D11_RASTERIZER_DESC*  pDesc);\n    \n    static D3D11_RASTERIZER_DESC2 PromoteDesc(\n      const D3D11_RASTERIZER_DESC1* pDesc);\n    \n    static HRESULT NormalizeDesc(\n            D3D11_RASTERIZER_DESC2* pDesc);\n    \n  private:\n    \n    D3D11_RASTERIZER_DESC2 m_desc;\n    DxvkRasterizerState    m_state      = { };\n    DxvkDepthBias          m_depthBias  = { };\n    D3D10RasterizerState   m_d3d10;\n\n    D3DDestructionNotifier m_destructionNotifier;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_resource.cpp",
    "content": "#include \"d3d11_buffer.h\"\n#include \"d3d11_texture.h\"\n#include \"d3d11_resource.h\"\n#include \"d3d11_context_imm.h\"\n#include \"d3d11_device.h\"\n\n#include \"../util/util_win32_compat.h\"\n#include \"../util/util_shared_res.h\"\n\nnamespace dxvk {\n\n  D3D11DXGIKeyedMutex::D3D11DXGIKeyedMutex(\n          ID3D11Resource* pResource,\n          D3D11Device*    pDevice)\n  : m_resource(pResource),\n    m_device(pDevice) {\n  }\n\n\n  D3D11DXGIKeyedMutex::~D3D11DXGIKeyedMutex() {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11DXGIKeyedMutex::AddRef() {\n    return m_resource->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11DXGIKeyedMutex::Release() {\n    return m_resource->Release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_resource->QueryInterface(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::GetPrivateData(\n          REFGUID                 Name,\n          UINT*                   pDataSize,\n          void*                   pData) {\n    return m_resource->GetPrivateData(Name, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::SetPrivateData(\n          REFGUID                 Name,\n          UINT                    DataSize,\n    const void*                   pData) {\n    return m_resource->SetPrivateData(Name, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::SetPrivateDataInterface(\n          REFGUID                 Name,\n    const IUnknown*               pUnknown) {\n    return m_resource->SetPrivateDataInterface(Name, pUnknown);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::GetParent(\n          REFIID                  riid,\n          void**                  ppParent) {\n    return GetDevice(riid, ppParent);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::GetDevice(\n          REFIID                  riid,\n          void**                  ppDevice) {\n    Com<ID3D11Device> device;\n    m_resource->GetDevice(&device);\n    return device->QueryInterface(riid, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::AcquireSync(\n          UINT64                  Key,\n          DWORD                   dwMilliseconds) {\n    D3D11CommonTexture* texture = GetCommonTexture(m_resource);\n    Rc<DxvkDevice> dxvkDevice = m_device->GetDXVKDevice();\n\n    auto keyedMutex = texture->GetImage()->getKeyedMutex();\n    if (keyedMutex)\n      return keyedMutex->AcquireSync(Key, dwMilliseconds);\n\n    /* try legacy Proton shared resource implementation */\n\n    if (!m_device->GetDXVKDevice()->features().khrWin32KeyedMutex\n        || m_device->GetDXVKDevice()->vkd()->wine_vkAcquireKeyedMutex == nullptr) {\n      if (!m_warned) {\n        m_warned = true;\n        Logger::err(\"D3D11DXGIKeyedMutex::AcquireSync: Not supported\");\n      }\n      return S_OK;\n    }\n\n    VkResult vr = dxvkDevice->vkd()->wine_vkAcquireKeyedMutex(\n      dxvkDevice->handle(), texture->GetImage()->getMemoryInfo().memory, Key, dwMilliseconds);\n\n    switch (vr) {\n      case VK_SUCCESS: return S_OK;\n      case VK_TIMEOUT: return WAIT_TIMEOUT;\n      default:         return DXGI_ERROR_INVALID_CALL;\n    }\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIKeyedMutex::ReleaseSync(\n          UINT64                  Key) {\n    D3D11CommonTexture* texture = GetCommonTexture(m_resource);\n    Rc<DxvkDevice> dxvkDevice = m_device->GetDXVKDevice();\n\n    {\n      D3D11ImmediateContext* context = m_device->GetContext();\n      D3D10Multithread& multithread = context->GetMultithread();\n      static bool s_errorShown = false;\n\n      if (!multithread.GetMultithreadProtected() && !std::exchange(s_errorShown, true))\n        Logger::warn(\"D3D11DXGIKeyedMutex::ReleaseSync: Called without context locking enabled.\");\n\n      D3D10DeviceLock lock = context->LockContext();\n      context->WaitForResource(*texture->GetImage(), DxvkCsThread::SynchronizeAll, D3D11_MAP_READ_WRITE, 0);\n    }\n\n    auto keyedMutex = texture->GetImage()->getKeyedMutex();\n    if (keyedMutex)\n      return keyedMutex->ReleaseSync(Key);\n\n    /* try legacy Proton shared resource implementation */\n\n    if (!m_device->GetDXVKDevice()->features().khrWin32KeyedMutex\n        || m_device->GetDXVKDevice()->vkd()->wine_vkReleaseKeyedMutex == nullptr) {\n      return S_OK;\n    }\n\n    VkResult vr = dxvkDevice->vkd()->wine_vkReleaseKeyedMutex(\n      dxvkDevice->handle(), texture->GetImage()->getMemoryInfo().memory, Key);\n\n    return vr == VK_SUCCESS ? S_OK : DXGI_ERROR_INVALID_CALL;\n  }\n\n  D3D11DXGIResource::D3D11DXGIResource(\n          ID3D11Resource*         pResource,\n          D3D11Device*            pDevice)\n  : m_resource(pResource),\n    m_keyedMutex(pResource, pDevice) {\n\n  }\n\n\n  D3D11DXGIResource::~D3D11DXGIResource() {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11DXGIResource::AddRef() {\n    return m_resource->AddRef();\n  }\n  \n\n  ULONG STDMETHODCALLTYPE D3D11DXGIResource::Release() {\n    return m_resource->Release();\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_resource->QueryInterface(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetPrivateData(\n          REFGUID                 Name,\n          UINT*                   pDataSize,\n          void*                   pData) {\n    return m_resource->GetPrivateData(Name, pDataSize, pData);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::SetPrivateData(\n          REFGUID                 Name,\n          UINT                    DataSize,\n    const void*                   pData) {\n    return m_resource->SetPrivateData(Name, DataSize, pData);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::SetPrivateDataInterface(\n          REFGUID                 Name,\n    const IUnknown*               pUnknown) {\n    return m_resource->SetPrivateDataInterface(Name, pUnknown);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetParent(\n          REFIID                  riid,\n          void**                  ppParent) {\n    return GetDevice(riid, ppParent);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetDevice(\n          REFIID                  riid,\n          void**                  ppDevice) {\n    Com<ID3D11Device> device;\n    m_resource->GetDevice(&device);\n    return device->QueryInterface(riid, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetEvictionPriority(\n          UINT*                   pEvictionPriority) {\n    *pEvictionPriority = m_resource->GetEvictionPriority();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetSharedHandle(\n          HANDLE*                 pSharedHandle) {\n    auto texture = GetCommonTexture(m_resource);\n    if (texture == nullptr || pSharedHandle == nullptr ||\n        (texture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE))\n      return E_INVALIDARG;\n\n    if (!(texture->Desc()->MiscFlags & (D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX))) {\n      *pSharedHandle = NULL;\n      return S_OK;\n    }\n\n    D3DKMT_HANDLE global = texture->GetImage()->storage()->kmtGlobal();\n    if (global) {\n      *pSharedHandle = reinterpret_cast<HANDLE>(global);\n      return S_OK;\n    }\n\n    /* try legacy Proton shared resource implementation */\n\n    HANDLE kmtHandle = texture->GetImage()->sharedHandle();\n\n    if (kmtHandle == INVALID_HANDLE_VALUE)\n      return E_INVALIDARG;\n\n    *pSharedHandle = kmtHandle;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::GetUsage(\n          DXGI_USAGE*             pUsage) {\n    D3D11_COMMON_RESOURCE_DESC desc;\n\n    HRESULT hr = GetCommonResourceDesc(m_resource, &desc);\n\n    if (FAILED(hr))\n      return hr;\n    \n    DXGI_USAGE usage = desc.DxgiUsage;\n\n    switch (desc.Usage) {\n      case D3D11_USAGE_IMMUTABLE: usage |= DXGI_CPU_ACCESS_NONE;       break;\n      case D3D11_USAGE_DEFAULT:   usage |= DXGI_CPU_ACCESS_NONE;       break;\n      case D3D11_USAGE_DYNAMIC:   usage |= DXGI_CPU_ACCESS_DYNAMIC;    break;\n      case D3D11_USAGE_STAGING:   usage |= DXGI_CPU_ACCESS_READ_WRITE; break;\n    }\n\n    // TODO add flags for swap chain back buffers\n    if (desc.BindFlags & (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_CONSTANT_BUFFER))\n      usage |= DXGI_USAGE_SHADER_INPUT;\n    \n    if (desc.BindFlags & D3D11_BIND_RENDER_TARGET)\n      usage |= DXGI_USAGE_RENDER_TARGET_OUTPUT;\n    \n    if (desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS)\n      usage |= DXGI_USAGE_UNORDERED_ACCESS;\n\n    *pUsage = usage;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::SetEvictionPriority(\n          UINT                    EvictionPriority) {\n    m_resource->SetEvictionPriority(EvictionPriority);\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::CreateSharedHandle(\n    const SECURITY_ATTRIBUTES*    pAttributes,\n          DWORD                   dwAccess,\n          LPCWSTR                 lpName,\n          HANDLE*                 pHandle) {\n    auto texture = GetCommonTexture(m_resource);\n    if (pHandle) *pHandle = nullptr;\n    if (texture == nullptr || pHandle == nullptr ||\n        !(texture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE))\n      return E_INVALIDARG;\n\n    OBJECT_ATTRIBUTES attr = { };\n    attr.Length = sizeof(attr);\n    attr.SecurityDescriptor = const_cast<SECURITY_ATTRIBUTES*>(pAttributes);\n\n    WCHAR buffer[MAX_PATH];\n    UNICODE_STRING name_str;\n    if (lpName) {\n        DWORD session, len, name_len = wcslen(lpName);\n\n        ProcessIdToSessionId(GetCurrentProcessId(), &session);\n        len = swprintf(buffer, ARRAYSIZE(buffer), L\"\\\\Sessions\\\\%u\\\\BaseNamedObjects\\\\\", session);\n        memcpy(buffer + len, lpName, (name_len + 1) * sizeof(WCHAR));\n        name_str.MaximumLength = name_str.Length = (len + name_len) * sizeof(WCHAR);\n        name_str.MaximumLength += sizeof(WCHAR);\n        name_str.Buffer = buffer;\n\n        attr.ObjectName = &name_str;\n        attr.Attributes = OBJ_CASE_INSENSITIVE;\n    }\n\n    D3DKMT_HANDLE local = texture->GetImage()->storage()->kmtLocal();\n    auto keyedMutex = texture->GetImage()->getKeyedMutex();\n    if (keyedMutex) {\n      D3DKMT_HANDLE handles[] = {local, keyedMutex->kmtLocal(), keyedMutex->getSyncObject()->kmtLocal()};\n      if (!D3DKMTShareObjects(3, handles, &attr, dwAccess, pHandle))\n        return S_OK;\n    } else if (!D3DKMTShareObjects(1, &local, &attr, dwAccess, pHandle)) {\n      return S_OK;\n    }\n\n    /* try legacy Proton shared resource implementation */\n\n    if (lpName)\n      Logger::warn(\"Naming shared resources not supported\");\n\n    HANDLE handle = texture->GetImage()->sharedHandle();\n\n    if (handle == INVALID_HANDLE_VALUE)\n      return E_INVALIDARG;\n\n    *pHandle = handle;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGIResource::CreateSubresourceSurface(\n          UINT                    index,\n          IDXGISurface2**         ppSurface) {\n    auto buffer  = GetCommonBuffer (m_resource);\n    auto texture = GetCommonTexture(m_resource);\n    UINT subresourceCount;\n\n    if (buffer) {\n      subresourceCount = 1;\n    } else if (texture) {\n      D3D11_RESOURCE_DIMENSION resourceDim;\n\n      m_resource->GetType(&resourceDim);\n      if (resourceDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D && texture->Desc()->Depth > 1)\n        return E_INVALIDARG;\n\n      subresourceCount = texture->CountSubresources();\n    } else {\n      return DXGI_ERROR_INVALID_CALL;\n    }\n\n    if (index >= subresourceCount)\n      return E_INVALIDARG;\n\n    const Com<D3D11DXGISurface> surface = new D3D11DXGISurface(m_resource, index);\n    *ppSurface = surface.ref();\n    return S_OK;\n  }\n  \n\n  HRESULT D3D11DXGIResource::GetKeyedMutex(\n          void **ppvObject) {\n    auto texture = GetCommonTexture(m_resource);\n    if (texture == nullptr || !(texture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX))\n      return E_NOINTERFACE;\n    *ppvObject = ref(&m_keyedMutex);\n    return S_OK;\n  }\n\n\n  HRESULT GetResource11on12Info(\n          ID3D11Resource*             pResource,\n          D3D11_ON_12_RESOURCE_INFO*  p11on12Info) {\n    auto buffer   = GetCommonBuffer (pResource);\n    auto texture  = GetCommonTexture(pResource);\n\n    if (buffer != nullptr)\n      *p11on12Info = buffer->Get11on12Info();\n    else if (texture != nullptr)\n      *p11on12Info = texture->Get11on12Info();\n    else\n      return E_INVALIDARG;\n\n    if (p11on12Info->Resource == nullptr)\n      return E_INVALIDARG;\n\n    return S_OK;\n  }\n\n\n  HRESULT GetCommonResourceDesc(\n          ID3D11Resource*             pResource,\n          D3D11_COMMON_RESOURCE_DESC* pDesc) {\n    auto buffer   = GetCommonBuffer (pResource);\n    auto texture  = GetCommonTexture(pResource);\n\n    if (buffer != nullptr) {\n      pDesc->Dim            = D3D11_RESOURCE_DIMENSION_BUFFER;\n      pDesc->Format         = DXGI_FORMAT_UNKNOWN;\n      pDesc->Usage          = buffer->Desc()->Usage;\n      pDesc->BindFlags      = buffer->Desc()->BindFlags;\n      pDesc->CPUAccessFlags = buffer->Desc()->CPUAccessFlags;\n      pDesc->MiscFlags      = buffer->Desc()->MiscFlags;\n      pDesc->DxgiUsage      = 0;\n      return S_OK;\n    } else if (texture != nullptr) {\n      pResource->GetType(&pDesc->Dim);\n      pDesc->Format         = texture->Desc()->Format;\n      pDesc->Usage          = texture->Desc()->Usage;\n      pDesc->BindFlags      = texture->Desc()->BindFlags;\n      pDesc->CPUAccessFlags = texture->Desc()->CPUAccessFlags;\n      pDesc->MiscFlags      = texture->Desc()->MiscFlags;\n      pDesc->DxgiUsage      = texture->GetDxgiUsage();\n      return S_OK;\n    } else {\n      pDesc->Dim            = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n      pDesc->Format         = DXGI_FORMAT_UNKNOWN;\n      pDesc->Usage          = D3D11_USAGE_DEFAULT;\n      pDesc->BindFlags      = 0;\n      pDesc->CPUAccessFlags = 0;\n      pDesc->MiscFlags      = 0;\n      pDesc->DxgiUsage      = 0;\n      return E_INVALIDARG;\n    }\n  }\n\n\n  Rc<DxvkPagedResource> GetPagedResource(\n          ID3D11Resource*             pResource) {\n    auto texture = GetCommonTexture(pResource);\n\n    if (texture)\n      return texture->GetImage();\n\n    return static_cast<D3D11Buffer*>(pResource)->GetBuffer();\n  }\n\n\n  BOOL CheckResourceViewCompatibility(\n          ID3D11Resource*             pResource,\n          UINT                        BindFlags,\n          DXGI_FORMAT                 Format,\n          UINT                        Plane) {\n    auto texture = GetCommonTexture(pResource);\n    auto buffer  = GetCommonBuffer (pResource);\n    \n    return texture != nullptr\n      ? texture->CheckViewCompatibility(BindFlags, Format, Plane)\n      : buffer ->CheckViewCompatibility(BindFlags, Format);\n  }\n\n\n  HRESULT ResourceAddRefPrivate(ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION Type) {\n    switch (Type) {\n      case D3D11_RESOURCE_DIMENSION_BUFFER:    static_cast<D3D11Buffer*>   (pResource)->AddRefPrivate(); return S_OK;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: static_cast<D3D11Texture1D*>(pResource)->AddRefPrivate(); return S_OK;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: static_cast<D3D11Texture2D*>(pResource)->AddRefPrivate(); return S_OK;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: static_cast<D3D11Texture3D*>(pResource)->AddRefPrivate(); return S_OK;\n      default: return E_INVALIDARG;\n    }\n  }\n  \n\n  HRESULT ResourceAddRefPrivate(ID3D11Resource* pResource) {\n    D3D11_RESOURCE_DIMENSION dim;\n    pResource->GetType(&dim);\n\n    return ResourceAddRefPrivate(pResource, dim);\n  }\n\n\n  HRESULT ResourceReleasePrivate(ID3D11Resource* pResource, D3D11_RESOURCE_DIMENSION Type) {\n    switch (Type) {\n      case D3D11_RESOURCE_DIMENSION_BUFFER:    static_cast<D3D11Buffer*>   (pResource)->ReleasePrivate(); return S_OK;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: static_cast<D3D11Texture1D*>(pResource)->ReleasePrivate(); return S_OK;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: static_cast<D3D11Texture2D*>(pResource)->ReleasePrivate(); return S_OK;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: static_cast<D3D11Texture3D*>(pResource)->ReleasePrivate(); return S_OK;\n      default: return E_INVALIDARG;\n    }\n  }\n\n\n  HRESULT ResourceReleasePrivate(ID3D11Resource* pResource) {\n    D3D11_RESOURCE_DIMENSION dim;\n    pResource->GetType(&dim);\n\n    return ResourceReleasePrivate(pResource, dim);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_resource.h",
    "content": "#pragma once\n\n#include \"d3d11_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Common resource description\n   * \n   * Stores the usage and bind flags of a resource\n   * Can be used to quickly determine whether it is\n   * legal to create a view for a given resource.\n   */\n  struct D3D11_COMMON_RESOURCE_DESC {\n    D3D11_RESOURCE_DIMENSION  Dim;\n    DXGI_FORMAT               Format;\n    D3D11_USAGE               Usage;\n    UINT                      BindFlags;\n    UINT                      CPUAccessFlags;\n    UINT                      MiscFlags;\n    UINT                      DxgiUsage;\n  };\n  \n\n  /**\n   * \\brief IDXGIKeyedMutex implementation\n   */\n  class D3D11DXGIKeyedMutex : public IDXGIKeyedMutex {\n\n  public:\n\n    D3D11DXGIKeyedMutex(\n            ID3D11Resource*         pResource,\n            D3D11Device*            pDevice);\n\n    ~D3D11DXGIKeyedMutex();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                 Name,\n            UINT*                   pDataSize,\n            void*                   pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                 Name,\n            UINT                    DataSize,\n      const void*                   pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                 Name,\n      const IUnknown*               pUnknown);\n\n    HRESULT STDMETHODCALLTYPE GetParent(\n            REFIID                  riid,\n            void**                  ppParent);\n\n    HRESULT STDMETHODCALLTYPE GetDevice(\n            REFIID                  riid,\n            void**                  ppDevice);\n\n    HRESULT STDMETHODCALLTYPE AcquireSync(\n            UINT64                  Key,\n            DWORD                   dwMilliseconds);\n\n    HRESULT STDMETHODCALLTYPE ReleaseSync(\n            UINT64                  Key);\n\n  private:\n\n    ID3D11Resource* m_resource;\n    D3D11Device* m_device;\n    bool m_warned = false;\n  };\n\n\n  /**\n   * \\brief IDXGIResource implementation for D3D11 resources\n   */\n  class D3D11DXGIResource : public IDXGIResource1 {\n\n  public:\n    \n    D3D11DXGIResource(\n            ID3D11Resource*         pResource,\n            D3D11Device*            pDevice);\n\n    ~D3D11DXGIResource();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                 Name,\n            UINT*                   pDataSize,\n            void*                   pData);\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                 Name,\n            UINT                    DataSize,\n      const void*                   pData);\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                 Name,\n      const IUnknown*               pUnknown);\n    \n    HRESULT STDMETHODCALLTYPE GetParent(\n            REFIID                  riid,\n            void**                  ppParent);\n    \n    HRESULT STDMETHODCALLTYPE GetDevice(\n            REFIID                  riid,\n            void**                  ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetEvictionPriority(\n            UINT*                   pEvictionPriority);\n\n    HRESULT STDMETHODCALLTYPE GetSharedHandle(\n            HANDLE*                 pSharedHandle);\n\n    HRESULT STDMETHODCALLTYPE GetUsage(\n            DXGI_USAGE*             pUsage);\n\n    HRESULT STDMETHODCALLTYPE SetEvictionPriority(\n            UINT                    EvictionPriority);\n\n    HRESULT STDMETHODCALLTYPE CreateSharedHandle(\n      const SECURITY_ATTRIBUTES*    pAttributes,\n            DWORD                   dwAccess,\n            LPCWSTR                 lpName,\n            HANDLE*                 pHandle);\n\n    HRESULT STDMETHODCALLTYPE CreateSubresourceSurface(\n            UINT                    index,\n            IDXGISurface2**         ppSurface);\n\n    HRESULT GetKeyedMutex(void **ppvObject);\n\n  private:\n\n    ID3D11Resource* m_resource;\n    D3D11DXGIKeyedMutex m_keyedMutex;\n\n  };\n\n\n  /**\n   * \\brief Queries D3D11on12 resource info\n   *\n   * \\param [in] pResource The resource to query\n   * \\param [out] p11on12Info 11on12 info\n   * \\returns \\c S_OK on success, or \\c E_INVALIDARG\n   */\n  HRESULT GetResource11on12Info(\n          ID3D11Resource*             pResource,\n          D3D11_ON_12_RESOURCE_INFO*  p11on12Info);\n\n  /**\n   * \\brief Queries common resource description\n   * \n   * \\param [in] pResource The resource to query\n   * \\param [out] pDesc Resource description\n   * \\returns \\c S_OK on success, or \\c E_INVALIDARG\n   */\n  HRESULT GetCommonResourceDesc(\n          ID3D11Resource*             pResource,\n          D3D11_COMMON_RESOURCE_DESC* pDesc);\n\n  /**\n   * \\brief Checks whether a format can be used to view a resource\n   * \n   * Depending on whether the resource is a buffer or a\n   * texture, certain restrictions apply on which formats\n   * can be used to view the resource.\n   * \\param [in] pResource The resource to check\n   * \\param [in] BindFlags Bind flags required for the view\n   * \\param [in] Format The desired view format\n   * \\param [in] Plane Plane slice for planar formats\n   * \\returns \\c true if the format is compatible\n   */\n  BOOL CheckResourceViewCompatibility(\n          ID3D11Resource*             pResource,\n          UINT                        BindFlags,\n          DXGI_FORMAT                 Format,\n          UINT                        Plane);\n\n  /**\n   * \\brief Queries paged resource from resource pointer\n   *\n   * \\param [in] resource The resource\n   * \\returns Paged resource object\n   */\n  Rc<DxvkPagedResource> GetPagedResource(\n          ID3D11Resource*             pResource);\n\n  /**\n   * \\brief Increments private reference count of a resource\n   * \n   * Helper method that figures out the exact type of\n   * the resource and calls its \\c AddRefPrivate method.\n   * \\param [in] pResource The resource to reference\n   * \\param [in] Type Resource type\n   * \\returns \\c S_OK, or \\c E_INVALIDARG for an invalid resource\n   */\n  HRESULT ResourceAddRefPrivate(\n          ID3D11Resource*             pResource,\n          D3D11_RESOURCE_DIMENSION    Type);\n  \n  HRESULT ResourceAddRefPrivate(\n          ID3D11Resource*             pResource);\n\n  /**\n   * \\brief Decrements private reference count of a resource\n   * \n   * Helper method that figures out the exact type of\n   * the resource and calls its \\c ReleasePrivate method.\n   * \\param [in] pResource The resource to reference\n   * \\param [in] Type Resource type\n   * \\returns \\c S_OK, or \\c E_INVALIDARG for an invalid resource\n   */\n  HRESULT ResourceReleasePrivate(\n          ID3D11Resource*             pResource,\n          D3D11_RESOURCE_DIMENSION    Type);\n\n  HRESULT ResourceReleasePrivate(\n          ID3D11Resource*             pResource);\n\n  /**\n   * \\brief Typed private resource pointer\n   *\n   * Stores a resource and its type, in order to avoid\n   * unnecessary GetType calls. Also optionally stores\n   * a subresource index to avoid struct padding.\n   */\n  class D3D11ResourceRef {\n\n  public:\n\n    D3D11ResourceRef()\n    : m_type(D3D11_RESOURCE_DIMENSION_UNKNOWN),\n      m_subresource(0), m_resource(nullptr) { }\n\n    D3D11ResourceRef(ID3D11Resource* pResource)\n    : D3D11ResourceRef(pResource, 0) { }\n\n    D3D11ResourceRef(ID3D11Resource* pResource, UINT Subresource)\n    : m_type(D3D11_RESOURCE_DIMENSION_UNKNOWN),\n      m_subresource(Subresource), m_resource(pResource) {\n      if (m_resource) {\n        m_resource->GetType(&m_type);\n        ResourceAddRefPrivate(m_resource, m_type);\n      }\n    }\n\n    D3D11ResourceRef(ID3D11Resource* pResource, UINT Subresource, D3D11_RESOURCE_DIMENSION Type)\n    : m_type(Type), m_subresource(Subresource), m_resource(pResource) {\n      if (m_resource)\n        ResourceAddRefPrivate(m_resource, m_type);\n    }\n\n    D3D11ResourceRef(D3D11ResourceRef&& other)\n    : m_type(other.m_type), m_subresource(other.m_subresource), m_resource(other.m_resource) {\n      other.m_type = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n      other.m_subresource = 0;\n      other.m_resource = nullptr;\n    }\n\n    D3D11ResourceRef(const D3D11ResourceRef& other)\n    : m_type(other.m_type), m_subresource(other.m_subresource), m_resource(other.m_resource) {\n      if (m_resource)\n        ResourceAddRefPrivate(m_resource, m_type);\n    }\n\n    ~D3D11ResourceRef() {\n      if (m_resource)\n        ResourceReleasePrivate(m_resource, m_type);\n    }\n\n    D3D11ResourceRef& operator = (D3D11ResourceRef&& other) {\n      if (m_resource)\n        ResourceReleasePrivate(m_resource, m_type);\n\n      m_type = other.m_type;\n      m_subresource = other.m_subresource;\n      m_resource = other.m_resource;\n\n      other.m_type = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n      other.m_subresource = 0;\n      other.m_resource = nullptr;\n      return *this;\n    }\n\n    D3D11ResourceRef& operator = (const D3D11ResourceRef& other) {\n      if (other.m_resource)\n        ResourceAddRefPrivate(other.m_resource, other.m_type);\n\n      if (m_resource)\n        ResourceReleasePrivate(m_resource, m_type);\n\n      m_type = other.m_type;\n      m_subresource = other.m_subresource;\n      m_resource = other.m_resource;\n      return *this;\n    }\n\n    D3D11_RESOURCE_DIMENSION GetType() const {\n      return m_type;\n    }\n\n    UINT GetSubresource() const {\n      return m_subresource;\n    }\n\n    ID3D11Resource* Get() const {\n      return m_resource;\n    }\n\n  private:\n\n    D3D11_RESOURCE_DIMENSION  m_type;\n    UINT                      m_subresource;\n    ID3D11Resource*           m_resource;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_sampler.cpp",
    "content": "#include \"d3d11_device.h\"\n#include \"d3d11_sampler.h\"\n#include \"d3d11_util.h\"\n\nnamespace dxvk {\n  \n  D3D11SamplerState::D3D11SamplerState(\n          D3D11Device*        device,\n    const D3D11_SAMPLER_DESC& desc,\n          Container*          container)\n  : D3D11StateObject<ID3D11SamplerState, D3D11SamplerState>(device, container),\n    m_desc(desc), m_d3d10(this), m_destructionNotifier(this) {\n    DxvkSamplerKey info = { };\n\n    // While D3D11_FILTER is technically an enum, its value bits\n    // can be used to decode the filter properties more efficiently.\n    const uint32_t filterBits = uint32_t(desc.Filter);\n\n    VkFilter minFilter = (filterBits & 0x10) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;\n    VkFilter magFilter = (filterBits & 0x04) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;\n\n    info.setFilter(minFilter, magFilter,\n      (filterBits & 0x01) ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST);\n\n    // Enforce LOD bias specified in the device options\n    float lodBias = desc.MipLODBias;\n\n    if (minFilter == VK_FILTER_LINEAR && magFilter == VK_FILTER_LINEAR) {\n      lodBias += device->GetOptions()->samplerLodBias;\n\n      if (device->GetOptions()->clampNegativeLodBias)\n        lodBias = std::max(lodBias, 0.0f);\n    }\n\n    info.setLodRange(desc.MinLOD, desc.MaxLOD, lodBias);\n\n    // Enforce anisotropy specified in the device options\n    uint32_t anisotropy = (filterBits & 0x40) ? desc.MaxAnisotropy : 0u;\n    int32_t samplerAnisotropyOption = device->GetOptions()->samplerAnisotropy;\n\n    if (samplerAnisotropyOption >= 0 && minFilter == VK_FILTER_LINEAR)\n      anisotropy = samplerAnisotropyOption;\n\n    info.setAniso(anisotropy);\n\n    // Set up the remaining properties, which are\n    // stored directly in the sampler description\n    info.setAddressModes(\n      DecodeAddressMode(desc.AddressU),\n      DecodeAddressMode(desc.AddressV),\n      DecodeAddressMode(desc.AddressW));\n\n    info.setDepthCompare((filterBits & 0x180) == 0x80,\n      DecodeCompareOp(desc.ComparisonFunc));\n\n    info.setReduction(DecodeReductionMode(filterBits));\n\n    for (uint32_t i = 0; i < 4; i++)\n      info.borderColor.float32[i] = desc.BorderColor[i];\n\n    m_sampler = device->GetDXVKDevice()->createSampler(info);\n  }\n\n\n  D3D11SamplerState::~D3D11SamplerState() {\n    \n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SamplerState::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11SamplerState)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10SamplerState)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11SamplerState), riid)) {\n      Logger::warn(\"D3D11SamplerState::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11SamplerState::GetDesc(D3D11_SAMPLER_DESC* pDesc) {\n    *pDesc = m_desc;\n  }\n  \n  \n  HRESULT D3D11SamplerState::NormalizeDesc(D3D11_SAMPLER_DESC* pDesc) {\n    const uint32_t filterBits = uint32_t(pDesc->Filter);\n    \n    if (filterBits & 0xFFFFFE2A) {\n      Logger::err(str::format(\n        \"D3D11SamplerState: Unhandled filter: \", filterBits));\n      return E_INVALIDARG;\n    }\n    \n    if (pDesc->MaxAnisotropy > 16) {\n      return E_INVALIDARG;\n    } else if ((filterBits & 0x40) == 0 /* not anisotropic */) {\n      // Reset anisotropy if it is not used\n      pDesc->MaxAnisotropy = 0;\n    }\n    \n    if ((filterBits & 0x180) == 0x80 /* compare-to-depth */) {\n      if (!ValidateComparisonFunc(pDesc->ComparisonFunc))\n        return E_INVALIDARG;\n    } else {\n      // Reset compare func if it is not used\n      pDesc->ComparisonFunc = D3D11_COMPARISON_NEVER;\n    }\n    \n    if (!ValidateAddressMode(pDesc->AddressU)\n     || !ValidateAddressMode(pDesc->AddressV)\n     || !ValidateAddressMode(pDesc->AddressW))\n      return E_INVALIDARG;\n    \n    // Clear BorderColor to 0 if none of the address\n    // modes are D3D11_TEXTURE_ADDRESS_BORDER\n    if (pDesc->AddressU != D3D11_TEXTURE_ADDRESS_BORDER\n     && pDesc->AddressV != D3D11_TEXTURE_ADDRESS_BORDER\n     && pDesc->AddressW != D3D11_TEXTURE_ADDRESS_BORDER) {\n      for (int i = 0; i < 4; i++)\n        pDesc->BorderColor[i] = 0.0f;\n    }\n    \n    return S_OK;\n  }\n  \n  \n  bool D3D11SamplerState::ValidateAddressMode(D3D11_TEXTURE_ADDRESS_MODE Mode) {\n    return Mode >= D3D11_TEXTURE_ADDRESS_WRAP\n        && Mode <= D3D11_TEXTURE_ADDRESS_MIRROR_ONCE;\n  }\n  \n  \n  bool D3D11SamplerState::ValidateComparisonFunc(D3D11_COMPARISON_FUNC Comparison) {\n    return Comparison >= D3D11_COMPARISON_NEVER\n        && Comparison <= D3D11_COMPARISON_ALWAYS;\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_sampler.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../d3d10/d3d10_sampler.h\"\n\n#include \"d3d11_device_child.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  \n  class D3D11SamplerState : public D3D11StateObject<ID3D11SamplerState, D3D11SamplerState> {\n    using Container = D3D11StateObjectSet<D3D11SamplerState>;\n  public:\n    \n    using DescType = D3D11_SAMPLER_DESC;\n    \n    D3D11SamplerState(\n            D3D11Device*        device,\n      const D3D11_SAMPLER_DESC& desc,\n            Container*          container);\n    ~D3D11SamplerState();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_SAMPLER_DESC* pDesc) final;\n    \n    Rc<DxvkSampler> GetDXVKSampler() const {\n      return m_sampler;\n    }\n\n    D3D10SamplerState* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n\n    const D3D11_SAMPLER_DESC& Desc() const {\n      return m_desc;\n    }\n\n    static HRESULT NormalizeDesc(\n            D3D11_SAMPLER_DESC* pDesc);\n    \n  private:\n    \n    D3D11_SAMPLER_DESC m_desc;\n    Rc<DxvkSampler>    m_sampler;\n    D3D10SamplerState  m_d3d10;\n\n    std::atomic<uint32_t> m_refCount = { 0u };\n\n    D3DDestructionNotifier m_destructionNotifier;\n\n    static bool ValidateAddressMode(\n            D3D11_TEXTURE_ADDRESS_MODE  Mode);\n\n    static bool ValidateComparisonFunc(\n            D3D11_COMPARISON_FUNC       Comparison);\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_shader.cpp",
    "content": "#include <dxbc/dxbc_container.h>\n#include <dxbc/dxbc_interface.h>\n#include <dxbc/dxbc_parser.h>\n\n#include \"d3d11_device.h\"\n#include \"d3d11_shader.h\"\n\nnamespace dxvk {\n\n  class D3D11ShaderConverter : public DxvkIrShaderConverter {\n\n  public:\n\n    D3D11ShaderConverter(\n      const DxvkShaderHash&         ShaderKey,\n      const DxvkIrShaderCreateInfo& ModuleInfo,\n      const void*                   pShaderBytecode,\n            size_t                  BytecodeLength,\n            bool                    LowerIcb)\n    : m_key(ShaderKey), m_info(ModuleInfo), m_lowerIcb(LowerIcb) {\n      m_dxbc.resize(BytecodeLength);\n      std::memcpy(m_dxbc.data(), pShaderBytecode, BytecodeLength);\n    }\n\n    ~D3D11ShaderConverter() { }\n\n    void convertShader(\n            dxbc_spv::ir::Builder&    builder) {\n      auto debugName = m_key.toString();\n\n      dxbc_spv::dxbc::Converter::Options options = { };\n      options.name = debugName.c_str();\n      options.includeDebugNames = true;\n      options.boundCheckShaderIo = true;\n      options.lowerIcb = m_lowerIcb;\n      options.icbRegisterSpace = 0u;\n      options.icbRegisterIndex = D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;\n      options.classInstanceRegisterSpace = 0u;\n      options.classInstanceRegisterIndex = D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT + 1u;\n      options.limitTessFactor = true;\n\n      dxbc_spv::dxbc::Container container(m_dxbc.data(), m_dxbc.size());\n\n      dxbc_spv::dxbc::ShaderInfo shaderInfo =\n        dxbc_spv::dxbc::Parser(container.getCodeChunk()).getShaderInfo();\n\n      dxbc_spv::dxbc::Converter converter(std::move(container), options);\n\n      // Determine whether to create a regular shader or a pass-through GS\n      auto dstIsGs = m_key.stage() == VK_SHADER_STAGE_GEOMETRY_BIT;\n      auto srcIsGs = shaderInfo.getType() == dxbc_spv::dxbc::ShaderType::eGeometry;\n\n      if (dstIsGs && !srcIsGs) {\n        if (!converter.createPassthroughGs(builder))\n          throw DxvkError(str::format(\"Failed to create pass-through geometry shader: \", m_key.toString()));\n      } else {\n        if (!converter.convertShader(builder))\n          throw DxvkError(str::format(\"Failed to convert shader: \", m_key.toString()));\n      }\n    }\n\n    uint32_t determineResourceIndex(\n            dxbc_spv::ir::ShaderStage stage,\n            dxbc_spv::ir::ScalarType  type,\n            uint32_t                  regSpace,\n            uint32_t                  regIndex) const {\n      switch (type) {\n        case dxbc_spv::ir::ScalarType::eSampler:\n          return D3D11ShaderResourceMapping::computeSamplerBinding(stage, regIndex);\n        case dxbc_spv::ir::ScalarType::eCbv:\n          return D3D11ShaderResourceMapping::computeCbvBinding(stage, regIndex);\n        case dxbc_spv::ir::ScalarType::eSrv:\n          return D3D11ShaderResourceMapping::computeSrvBinding(stage, regIndex);\n        case dxbc_spv::ir::ScalarType::eUav:\n          return D3D11ShaderResourceMapping::computeUavBinding(stage, regIndex);\n        case dxbc_spv::ir::ScalarType::eUavCounter:\n          return D3D11ShaderResourceMapping::computeUavCounterBinding(stage, regIndex);\n        default:\n          return -1u;\n      }\n    }\n\n    void dumpSource(const std::string& path) const {\n      std::ofstream file(str::topath(str::format(path, \"/\", m_key.toString(), \".dxbc\").c_str()).c_str(), std::ios_base::trunc | std::ios_base::binary);\n      file.write(reinterpret_cast<const char*>(m_dxbc.data()), m_dxbc.size());\n    }\n\n    std::string getDebugName() const {\n      return m_key.toString();\n    }\n\n  private:\n\n    std::vector<uint8_t> m_dxbc;\n\n    DxvkShaderHash          m_key;\n    DxvkIrShaderCreateInfo  m_info;\n\n    bool                    m_lowerIcb = false;\n\n  };\n\n\n\n  \n  D3D11CommonShader:: D3D11CommonShader() { }\n  D3D11CommonShader::~D3D11CommonShader() { }\n  \n  \n  D3D11CommonShader::D3D11CommonShader(\n          D3D11Device*            pDevice,\n          D3D11ClassLinkage*      pLinkage,\n    const DxvkShaderHash&         ShaderKey,\n    const DxvkIrShaderCreateInfo& ModuleInfo,\n    const void*                   pShaderBytecode,\n          size_t                  BytecodeLength,\n    const D3D11ShaderIcbInfo&     Icb,\n    const D3D11BindingMask&       BindingMask)\n  : m_bindings(BindingMask) {\n    if (Logger::logLevel() <= LogLevel::Debug)\n      Logger::debug(str::format(\"Compiling shader \", ShaderKey.toString()));\n\n    if (pLinkage)\n      GatherInterefaceInfo(pLinkage, pShaderBytecode, BytecodeLength);\n\n    CreateIrShader(pDevice, ShaderKey, ModuleInfo, pShaderBytecode, BytecodeLength, Icb);\n    pDevice->GetDXVKDevice()->registerShader(m_shader);\n  }\n\n\n  void D3D11CommonShader::CreateIrShader(\n          D3D11Device*            pDevice,\n    const DxvkShaderHash&         ShaderKey,\n    const DxvkIrShaderCreateInfo& ModuleInfo,\n    const void*                   pShaderBytecode,\n          size_t                  BytecodeLength,\n    const D3D11ShaderIcbInfo&     Icb) {\n    constexpr size_t MaxEmbeddedIcbSize = 64u;\n\n    // Create icb if lowering is required\n    size_t icbSizeInBytes = Icb.size * sizeof(*Icb.data);\n\n    if (ModuleInfo.options.flags.test(DxvkShaderCompileFlag::LowerConstantArrays) && icbSizeInBytes > MaxEmbeddedIcbSize) {\n      DxvkBufferCreateInfo info = { };\n      info.size   = align(icbSizeInBytes, 256u);\n      info.usage  = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT\n                  | VK_BUFFER_USAGE_TRANSFER_SRC_BIT\n                  | VK_BUFFER_USAGE_TRANSFER_DST_BIT;\n      info.stages = VK_PIPELINE_STAGE_2_TRANSFER_BIT\n                  | util::pipelineStages(ShaderKey.stage());\n      info.access = VK_ACCESS_UNIFORM_READ_BIT\n                  | VK_ACCESS_TRANSFER_READ_BIT\n                  | VK_ACCESS_TRANSFER_WRITE_BIT;\n      info.debugName = \"Icb\";\n\n      m_buffer = pDevice->GetDXVKDevice()->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      pDevice->InitShaderIcb(this, icbSizeInBytes, Icb.data);\n    }\n\n    // Create actual shader converter\n    m_shader = pDevice->GetDXVKDevice()->createCachedShader(\n      ShaderKey.toString(), ModuleInfo, nullptr);\n\n    if (!m_shader) {\n      Rc<D3D11ShaderConverter> converter = new D3D11ShaderConverter(ShaderKey,\n        ModuleInfo, pShaderBytecode, BytecodeLength, bool(m_buffer));\n\n      m_shader = pDevice->GetDXVKDevice()->createCachedShader(\n        ShaderKey.toString(), ModuleInfo, std::move(converter));\n    }\n  }\n\n\n  void D3D11CommonShader::GatherInterefaceInfo(\n          D3D11ClassLinkage*      pLinkage,\n    const void*                   pShaderBytecode,\n          size_t                  BytecodeLength) {\n    dxbc_spv::dxbc::Container container(pShaderBytecode, BytecodeLength);\n    dxbc_spv::util::ByteReader ifaceChunk(container.getInterfaceChunk());\n\n    if (!ifaceChunk)\n      return;\n\n    dxbc_spv::dxbc::InterfaceChunk ifaceInfo(ifaceChunk);\n\n    if (!ifaceInfo)\n      return;\n\n    auto typeInfos = ifaceInfo.getClassTypes();\n    auto slotInfos = ifaceInfo.getInterfaceSlots();\n\n    for (auto i = typeInfos.first; i != typeInfos.second; i++) {\n      m_interfaces.AddType(i->id, i->name.c_str());\n      pLinkage->AddType(i->name.c_str(), i->cbSize, i->srvCount, i->samplerCount);\n    }\n\n    for (auto i = slotInfos.first; i != slotInfos.second; i++) {\n      for (const auto& e : i->entries)\n        m_interfaces.AddSlotInfo(i->index, i->count, e.typeId, e.tableId);\n    }\n\n    auto instances = ifaceInfo.getClassInstances();\n\n    for (auto i = instances.first; i != instances.second; i++) {\n      D3D11_CLASS_INSTANCE_DESC desc = { };\n      desc.ConstantBuffer = i->cbvIndex;\n      desc.BaseConstantBufferOffset = i->cbvOffset;\n      desc.BaseTexture = i->srvIndex & 0x7fu;\n      desc.BaseSampler = i->samplerIndex & 0xfu;\n\n      auto typeName = m_interfaces.GetTypeName(i->typeId);\n\n      if (typeName)\n        pLinkage->AddInstance(&desc, typeName, i->name.c_str());\n    }\n  }\n\n  \n  D3D11ShaderModuleSet:: D3D11ShaderModuleSet() { }\n  D3D11ShaderModuleSet::~D3D11ShaderModuleSet() { }\n  \n  \n  HRESULT D3D11ShaderModuleSet::GetShaderModule(\n          D3D11Device*            pDevice,\n          D3D11ClassLinkage*      pLinkage,\n    const DxvkShaderHash&         ShaderKey,\n    const DxvkIrShaderCreateInfo& ModuleInfo,\n    const void*                   pShaderBytecode,\n          size_t                  BytecodeLength,\n    const D3D11ShaderIcbInfo&     Icb,\n    const D3D11BindingMask&       BindingMask,\n          D3D11CommonShader*      pShader) {\n    // Use the shader's unique key for the lookup\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      \n      auto entry = m_modules.find(ShaderKey);\n      if (entry != m_modules.end()) {\n        *pShader = entry->second;\n        return S_OK;\n      }\n    }\n    \n    // This shader has not been compiled yet, so we have to create a\n    // new module. This takes a while, so we won't lock the structure.\n    D3D11CommonShader module;\n    \n    try {\n      module = D3D11CommonShader(pDevice, pLinkage, ShaderKey,\n        ModuleInfo, pShaderBytecode, BytecodeLength, Icb, BindingMask);\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_INVALIDARG;\n    }\n    \n    // Insert the new module into the lookup table. If another thread\n    // has compiled the same shader in the meantime, we should return\n    // that object instead and discard the newly created module.\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      \n      auto status = m_modules.insert({ ShaderKey, module });\n\n      if (!status.second) {\n        *pShader = status.first->second;\n        return S_OK;\n      }\n    }\n    \n    *pShader = std::move(module);\n    return S_OK;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_shader.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <unordered_map>\n\n#include \"../dxvk/dxvk_device.h\"\n#include \"../dxvk/dxvk_shader.h\"\n#include \"../dxvk/dxvk_shader_key.h\"\n#include \"../dxvk/dxvk_shader_ir.h\"\n\n#include \"../d3d10/d3d10_shader.h\"\n\n#include \"../util/sha1/sha1_util.h\"\n\n#include \"../util/util_env.h\"\n\n#include \"d3d11_class_linkage.h\"\n#include \"d3d11_device_child.h\"\n#include \"d3d11_interfaces.h\"\n#include \"d3d11_util.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n\n  constexpr uint32_t D3D11ShaderTypeCount = 6u;\n\n  using D3D11ShaderType = dxbc_spv::dxbc::ShaderType;\n  using D3D11ShaderTypeFlags = Flags<dxbc_spv::dxbc::ShaderType>;\n\n  /**\n   * \\brief Translates D3D11 shader stage to corresponding Vulkan stage\n   *\n   * \\param [in] ProgramType DXBC program type\n   * \\returns Corresponding Vulkan shader stage\n   */\n  constexpr VkShaderStageFlagBits GetShaderStage(D3D11ShaderType ProgramType) {\n    constexpr uint64_t lut\n      = (uint64_t(VK_SHADER_STAGE_VERTEX_BIT)                   << (8u * uint32_t(D3D11ShaderType::eVertex)))\n      | (uint64_t(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)     << (8u * uint32_t(D3D11ShaderType::eHull)))\n      | (uint64_t(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)  << (8u * uint32_t(D3D11ShaderType::eDomain)))\n      | (uint64_t(VK_SHADER_STAGE_GEOMETRY_BIT)                 << (8u * uint32_t(D3D11ShaderType::eGeometry)))\n      | (uint64_t(VK_SHADER_STAGE_FRAGMENT_BIT)                 << (8u * uint32_t(D3D11ShaderType::ePixel)))\n      | (uint64_t(VK_SHADER_STAGE_COMPUTE_BIT)                  << (8u * uint32_t(D3D11ShaderType::eCompute)));\n\n    return VkShaderStageFlagBits((lut >> (8u * uint32_t(ProgramType))) & 0xff);\n  }\n\n\n  /**\n   * \\brief Shader resource mapping\n   *\n   * Helper class to compute backend resource\n   * indices for D3D11 binding slots.\n   */\n  struct D3D11ShaderResourceMapping {\n    static constexpr uint32_t StageCount        = 6u;\n    static constexpr uint32_t CbvPerStage       = 16u;\n    static constexpr uint32_t SamplersPerStage  = 16u;\n    static constexpr uint32_t SrvPerStage       = 128u;\n    static constexpr uint32_t SrvTotal          = SrvPerStage * StageCount;\n    static constexpr uint32_t UavPerPipeline    = 64u;\n    static constexpr uint32_t UavTotal          = UavPerPipeline * 4u;\n    static constexpr uint32_t UavIndexGraphics  = SrvTotal;\n    static constexpr uint32_t UavIndexCompute   = UavIndexGraphics + UavPerPipeline * 2u;\n\n    template<typename T>\n    static uint32_t computeCbvBinding(T stage, uint32_t index) {\n      return computeStageIndex(stage) * CbvPerStage + index;\n    }\n\n    template<typename T>\n    static uint32_t computeSamplerBinding(T stage, uint32_t index) {\n      return computeStageIndex(stage) * SamplersPerStage + index;\n    }\n\n    template<typename T>\n    static uint32_t computeSrvBinding(T stage, uint32_t index) {\n      return computeStageIndex(stage) * SrvPerStage + index;\n    }\n\n    template<typename T>\n    static uint32_t computeUavBinding(T stage, uint32_t index) {\n      return (computeStageIndex(stage) == computeStageIndex(dxbc_spv::ir::ShaderStage::eCompute) ? UavIndexCompute : UavIndexGraphics) + index;\n    }\n\n    template<typename T>\n    static uint32_t computeUavCounterBinding(T stage, uint32_t index) {\n      return computeUavBinding(stage, index) + UavPerPipeline;\n    }\n\n    static constexpr uint32_t computeStageIndex(dxbc_spv::ir::ShaderStage stage) {\n      switch (stage) {\n        case dxbc_spv::ir::ShaderStage::ePixel:     return 0u;\n        case dxbc_spv::ir::ShaderStage::eVertex:    return 1u;\n        case dxbc_spv::ir::ShaderStage::eGeometry:  return 2u;\n        case dxbc_spv::ir::ShaderStage::eHull:      return 3u;\n        case dxbc_spv::ir::ShaderStage::eDomain:    return 4u;\n        case dxbc_spv::ir::ShaderStage::eCompute:   return 5u;\n        default:                                    return -1u;\n      }\n    }\n\n    static constexpr uint32_t computeStageIndex(D3D11ShaderType stage) {\n      return uint32_t(stage);\n    }\n\n  };\n\n\n  /**\n   * \\brief Immediate constant buffer info\n   */\n  struct D3D11ShaderIcbInfo {\n    /// Size in dwords\n    size_t size = 0u;\n    /// Constant data\n    const uint32_t* data = nullptr;\n  };\n\n\n  /**\n   * \\brief Common shader object\n   * \n   * Stores the compiled SPIR-V shader and the SHA-1\n   * hash of the original DXBC shader, which can be\n   * used to identify the shader.\n   */\n  class D3D11CommonShader {\n    \n  public:\n    \n    D3D11CommonShader();\n    D3D11CommonShader(\n            D3D11Device*            pDevice,\n            D3D11ClassLinkage*      pLinkage,\n      const DxvkShaderHash&         ShaderKey,\n      const DxvkIrShaderCreateInfo& ModuleInfo,\n      const void*                   pShaderBytecode,\n            size_t                  BytecodeLength,\n      const D3D11ShaderIcbInfo&     Icb,\n      const D3D11BindingMask&       BindingMask);\n    ~D3D11CommonShader();\n\n    Rc<DxvkShader> GetShader() const {\n      return m_shader;\n    }\n\n    DxvkBufferSlice GetIcb() const {\n      return m_buffer != nullptr\n        ? DxvkBufferSlice(m_buffer)\n        : DxvkBufferSlice();\n    }\n    \n    std::string GetName() const {\n      return m_shader->debugName();\n    }\n\n    D3D11BindingMask GetBindingMask() const {\n      return m_bindings;\n    }\n\n    D3D11InstanceData GetClassInstanceData(\n            uint32_t                Slot,\n            D3D11ClassInstance*     pClassInstance) const {\n      return m_interfaces.EncodeInstanceData(Slot, pClassInstance);\n    }\n\n  private:\n\n    Rc<DxvkShader> m_shader;\n    Rc<DxvkBuffer> m_buffer;\n\n    D3D11BindingMask    m_bindings = { };\n    D3D11InterfaceInfo  m_interfaces = { };\n\n    void CreateIrShader(\n            D3D11Device*            pDevice,\n      const DxvkShaderHash&         ShaderKey,\n      const DxvkIrShaderCreateInfo& ModuleInfo,\n      const void*                   pShaderBytecode,\n            size_t                  BytecodeLength,\n      const D3D11ShaderIcbInfo&     Icb);\n\n    void GatherInterefaceInfo(\n            D3D11ClassLinkage*      pLinkage,\n      const void*                   pShaderBytecode,\n            size_t                  BytecodeLength);\n\n  };\n\n\n  /**\n   * \\brief Common shader interface\n   * \n   * Implements methods for all D3D11*Shader\n   * interfaces and stores the actual shader\n   * module object.\n   */\n  template<typename D3D11Interface, typename D3D10Interface>\n  class D3D11Shader : public D3D11DeviceChild<D3D11Interface> {\n    using D3D10ShaderClass = D3D10Shader<D3D10Interface, D3D11Interface>;\n  public:\n    \n    D3D11Shader(D3D11Device* device, const D3D11CommonShader& shader)\n    : D3D11DeviceChild<D3D11Interface>(device),\n      m_shader(shader), m_d3d10(this),\n      m_destructionNotifier(this) { }\n    \n    ~D3D11Shader() { }\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final {\n      *ppvObject = nullptr;\n      \n      if (riid == __uuidof(IUnknown)\n       || riid == __uuidof(ID3D11DeviceChild)\n       || riid == __uuidof(D3D11Interface)) {\n        *ppvObject = ref(this);\n        return S_OK;\n      }\n      \n      if (riid == __uuidof(IUnknown)\n       || riid == __uuidof(ID3D10DeviceChild)\n       || riid == __uuidof(D3D10Interface)) {\n        *ppvObject = ref(&m_d3d10);\n        return S_OK;\n      }\n\n      if (riid == __uuidof(ID3DDestructionNotifier)) {\n        *ppvObject = ref(&m_destructionNotifier);\n        return S_OK;\n      }\n\n      if (logQueryInterfaceError(__uuidof(D3D11Interface), riid)) {\n        Logger::warn(\"D3D11Shader::QueryInterface: Unknown interface query\");\n        Logger::warn(str::format(riid));\n      }\n\n      return E_NOINTERFACE;\n    }\n    \n    const D3D11CommonShader* GetCommonShader() const {\n      return &m_shader;\n    }\n\n    D3D10ShaderClass* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n\n  private:\n    \n    D3D11CommonShader m_shader;\n    D3D10ShaderClass  m_d3d10;\n\n    D3DDestructionNotifier m_destructionNotifier;\n    \n  };\n  \n  using D3D11VertexShader   = D3D11Shader<ID3D11VertexShader,   ID3D10VertexShader>;\n  using D3D11HullShader     = D3D11Shader<ID3D11HullShader,     ID3D10DeviceChild>;\n  using D3D11DomainShader   = D3D11Shader<ID3D11DomainShader,   ID3D10DeviceChild>;\n  using D3D11GeometryShader = D3D11Shader<ID3D11GeometryShader, ID3D10GeometryShader>;\n  using D3D11PixelShader    = D3D11Shader<ID3D11PixelShader,    ID3D10PixelShader>;\n  using D3D11ComputeShader  = D3D11Shader<ID3D11ComputeShader,  ID3D10DeviceChild>;\n  \n  \n  /**\n   * \\brief Shader module set\n   * \n   * Some applications may compile the same shader multiple\n   * times, so we should cache the resulting shader modules\n   * and reuse them rather than creating new ones. This\n   * class is thread-safe.\n   */\n  class D3D11ShaderModuleSet {\n    \n  public:\n    \n    D3D11ShaderModuleSet();\n    ~D3D11ShaderModuleSet();\n    \n    HRESULT GetShaderModule(\n            D3D11Device*            pDevice,\n            D3D11ClassLinkage*      pLinkage,\n      const DxvkShaderHash&         ShaderKey,\n      const DxvkIrShaderCreateInfo& ModuleInfo,\n      const void*                   pShaderBytecode,\n            size_t                  BytecodeLength,\n      const D3D11ShaderIcbInfo&     Icb,\n      const D3D11BindingMask&       BindingMask,\n            D3D11CommonShader*      pShader);\n    \n  private:\n    \n    dxvk::mutex m_mutex;\n    \n    std::unordered_map<\n      DxvkShaderHash,\n      D3D11CommonShader,\n      DxvkHash, DxvkEq> m_modules;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_state.cpp",
    "content": "#include \"d3d11_state.h\"\n\n#include \"../dxvk/dxvk_hash.h\"\n\nnamespace dxvk {\n  \n  size_t D3D11StateDescHash::operator () (\n    const D3D11_BLEND_DESC1& desc) const {\n    DxvkHashState hash;\n    hash.add(desc.AlphaToCoverageEnable);\n    hash.add(desc.IndependentBlendEnable);\n    \n    // Render targets 1 to 7 are ignored and may contain\n    // undefined data if independent blend is disabled\n    const uint32_t usedRenderTargets = desc.IndependentBlendEnable ? 8 : 1;\n    \n    for (uint32_t i = 0; i < usedRenderTargets; i++)\n      hash.add(this->operator () (desc.RenderTarget[i]));\n    \n    return hash;\n  }\n  \n  \n  size_t D3D11StateDescHash::operator () (\n    const D3D11_DEPTH_STENCILOP_DESC& desc) const {\n    DxvkHashState hash;\n    hash.add(desc.StencilFunc);\n    hash.add(desc.StencilDepthFailOp);\n    hash.add(desc.StencilPassOp);\n    hash.add(desc.StencilFailOp);\n    return hash;\n  }\n  \n  \n  size_t D3D11StateDescHash::operator () (\n    const D3D11_DEPTH_STENCIL_DESC& desc) const {\n    DxvkHashState hash;\n    hash.add(desc.DepthEnable);\n    hash.add(desc.DepthWriteMask);\n    hash.add(desc.DepthFunc);\n    hash.add(desc.StencilEnable);\n    hash.add(desc.StencilReadMask);\n    hash.add(desc.StencilWriteMask);\n    hash.add(this->operator () (desc.FrontFace));\n    hash.add(this->operator () (desc.BackFace));\n    return hash;\n  }\n  \n  \n  size_t D3D11StateDescHash::operator () (\n    const D3D11_RASTERIZER_DESC2& desc) const {\n    std::hash<float> fhash;\n\n    DxvkHashState hash;\n    hash.add(desc.FillMode);\n    hash.add(desc.CullMode);\n    hash.add(desc.FrontCounterClockwise);\n    hash.add(desc.DepthBias);\n    hash.add(fhash(desc.SlopeScaledDepthBias));\n    hash.add(fhash(desc.DepthBiasClamp));\n    hash.add(desc.DepthClipEnable);\n    hash.add(desc.ScissorEnable);\n    hash.add(desc.MultisampleEnable);\n    hash.add(desc.AntialiasedLineEnable);\n    hash.add(desc.ForcedSampleCount);\n    hash.add(desc.ConservativeRaster);\n    return hash;\n  }\n  \n  \n  size_t D3D11StateDescHash::operator () (\n    const D3D11_RENDER_TARGET_BLEND_DESC1& desc) const {\n    DxvkHashState hash;\n    hash.add(desc.BlendEnable);\n    hash.add(desc.LogicOpEnable);\n    hash.add(desc.SrcBlend);\n    hash.add(desc.DestBlend);\n    hash.add(desc.BlendOp);\n    hash.add(desc.SrcBlendAlpha);\n    hash.add(desc.DestBlendAlpha);\n    hash.add(desc.BlendOpAlpha);\n    hash.add(desc.LogicOp);\n    hash.add(desc.RenderTargetWriteMask);\n    return hash;\n  }\n  \n  \n  size_t D3D11StateDescHash::operator () (\n    const D3D11_SAMPLER_DESC& desc) const {\n    std::hash<float> fhash;\n    \n    DxvkHashState hash;\n    hash.add(desc.Filter);\n    hash.add(desc.AddressU);\n    hash.add(desc.AddressV);\n    hash.add(desc.AddressW);\n    hash.add(fhash(desc.MipLODBias));\n    hash.add(desc.MaxAnisotropy);\n    hash.add(desc.ComparisonFunc);\n    for (uint32_t i = 0; i < 4; i++)\n      hash.add(fhash(desc.BorderColor[i]));\n    hash.add(fhash(desc.MinLOD));\n    hash.add(fhash(desc.MaxLOD));\n    return hash;\n  }\n  \n  \n  bool D3D11StateDescEqual::operator () (\n    const D3D11_BLEND_DESC1& a,\n    const D3D11_BLEND_DESC1& b) const {\n    bool eq = a.AlphaToCoverageEnable  == b.AlphaToCoverageEnable\n           && a.IndependentBlendEnable == b.IndependentBlendEnable;\n    \n    // Render targets 1 to 7 are ignored and may contain\n    // undefined data if independent blend is disabled\n    const uint32_t usedRenderTargets = a.IndependentBlendEnable ? 8 : 1;\n    \n    for (uint32_t i = 0; eq && (i < usedRenderTargets); i++)\n      eq &= this->operator () (a.RenderTarget[i], b.RenderTarget[i]);\n    \n    return eq;\n  }\n  \n  \n  bool D3D11StateDescEqual::operator () (\n    const D3D11_DEPTH_STENCILOP_DESC& a,\n    const D3D11_DEPTH_STENCILOP_DESC& b) const {\n    return a.StencilFunc           == b.StencilFunc\n        && a.StencilDepthFailOp    == b.StencilDepthFailOp\n        && a.StencilPassOp         == b.StencilPassOp\n        && a.StencilFailOp         == b.StencilFailOp;\n  }\n  \n  \n  bool D3D11StateDescEqual::operator () (\n    const D3D11_DEPTH_STENCIL_DESC& a,\n    const D3D11_DEPTH_STENCIL_DESC& b) const {\n    return a.DepthEnable           == b.DepthEnable\n        && a.DepthWriteMask        == b.DepthWriteMask\n        && a.DepthFunc             == b.DepthFunc\n        && a.StencilEnable         == b.StencilEnable\n        && a.StencilReadMask       == b.StencilReadMask\n        && a.StencilWriteMask      == b.StencilWriteMask\n        && this->operator () (a.FrontFace, b.FrontFace)\n        && this->operator () (a.BackFace, b.BackFace);\n  }\n  \n  \n  bool D3D11StateDescEqual::operator () (\n    const D3D11_RASTERIZER_DESC2& a,\n    const D3D11_RASTERIZER_DESC2& b) const {\n    return a.FillMode              == b.FillMode\n        && a.CullMode              == b.CullMode\n        && a.FrontCounterClockwise == b.FrontCounterClockwise\n        && a.DepthBias             == b.DepthBias\n        && a.SlopeScaledDepthBias  == b.SlopeScaledDepthBias\n        && a.DepthBiasClamp        == b.DepthBiasClamp\n        && a.DepthClipEnable       == b.DepthClipEnable\n        && a.ScissorEnable         == b.ScissorEnable\n        && a.MultisampleEnable     == b.MultisampleEnable\n        && a.AntialiasedLineEnable == b.AntialiasedLineEnable\n        && a.ForcedSampleCount     == b.ForcedSampleCount\n        && a.ConservativeRaster    == b.ConservativeRaster;\n  }\n  \n  \n  bool D3D11StateDescEqual::operator () (\n    const D3D11_RENDER_TARGET_BLEND_DESC1& a,\n    const D3D11_RENDER_TARGET_BLEND_DESC1& b) const {\n    return a.BlendEnable           == b.BlendEnable\n        && a.LogicOpEnable         == b.LogicOpEnable\n        && a.SrcBlend              == b.SrcBlend\n        && a.DestBlend             == b.DestBlend\n        && a.BlendOp               == b.BlendOp\n        && a.SrcBlendAlpha         == b.SrcBlendAlpha\n        && a.DestBlendAlpha        == b.DestBlendAlpha\n        && a.BlendOpAlpha          == b.BlendOpAlpha\n        && a.LogicOp               == b.LogicOp\n        && a.RenderTargetWriteMask == b.RenderTargetWriteMask;\n  }\n  \n  \n  bool D3D11StateDescEqual::operator () (\n    const D3D11_SAMPLER_DESC& a,\n    const D3D11_SAMPLER_DESC& b) const {\n    return a.Filter         == b.Filter\n        && a.AddressU       == b.AddressU\n        && a.AddressV       == b.AddressV\n        && a.AddressW       == b.AddressW\n        && a.MipLODBias     == b.MipLODBias\n        && a.MaxAnisotropy  == b.MaxAnisotropy\n        && a.ComparisonFunc == b.ComparisonFunc\n        && a.BorderColor[0] == b.BorderColor[0]\n        && a.BorderColor[1] == b.BorderColor[1]\n        && a.BorderColor[2] == b.BorderColor[2]\n        && a.BorderColor[3] == b.BorderColor[3]\n        && a.MinLOD         == b.MinLOD\n        && a.MaxLOD         == b.MaxLOD;\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_state.h",
    "content": "#pragma once\n\n#include <unordered_map>\n\n#include \"d3d11_include.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  \n  struct D3D11StateDescHash {\n    size_t operator () (const D3D11_BLEND_DESC1& desc) const;\n    size_t operator () (const D3D11_DEPTH_STENCILOP_DESC& desc) const;\n    size_t operator () (const D3D11_DEPTH_STENCIL_DESC& desc) const;\n    size_t operator () (const D3D11_RASTERIZER_DESC2& desc) const;\n    size_t operator () (const D3D11_RENDER_TARGET_BLEND_DESC1& desc) const;\n    size_t operator () (const D3D11_SAMPLER_DESC& desc) const;\n  };\n  \n  \n  struct D3D11StateDescEqual {\n    bool operator () (const D3D11_BLEND_DESC1& a, const D3D11_BLEND_DESC1& b) const;\n    bool operator () (const D3D11_DEPTH_STENCILOP_DESC& a, const D3D11_DEPTH_STENCILOP_DESC& b) const;\n    bool operator () (const D3D11_DEPTH_STENCIL_DESC& a, const D3D11_DEPTH_STENCIL_DESC& b) const;\n    bool operator () (const D3D11_RASTERIZER_DESC2& a, const D3D11_RASTERIZER_DESC2& b) const;\n    bool operator () (const D3D11_RENDER_TARGET_BLEND_DESC1& a, const D3D11_RENDER_TARGET_BLEND_DESC1& b) const;\n    bool operator () (const D3D11_SAMPLER_DESC& a, const D3D11_SAMPLER_DESC& b) const;\n  };\n  \n  \n  /**\n   * \\brief Unique state object set\n   * \n   * When creating state objects, D3D11 first checks if\n   * an object with the same description already exists\n   * and returns it if that is the case. This class\n   * implements that behaviour.\n   */\n  template<typename T>\n  class D3D11StateObjectSet {\n    using DescType = typename T::DescType;\n  public:\n    \n    /**\n     * \\brief Retrieves a state object\n     * \n     * Returns an object with the same description or\n     * creates a new one if no such object exists.\n     * \\param [in] device The calling D3D11 device\n     * \\param [in] desc State object description\n     * \\returns Pointer to the state object\n     */\n    T* Create(D3D11Device* device, const DescType& desc) {\n      std::lock_guard<dxvk::mutex> lock(m_mutex);\n      \n      auto entry = m_objects.find(desc);\n      \n      if (entry != m_objects.end())\n        return ref(&entry->second);\n      \n      auto result = m_objects.emplace(\n        std::piecewise_construct,\n        std::tuple(desc),\n        std::tuple(device, desc, this));\n      return ref(&result.first->second);\n    }\n\n    /**\n     * \\brief Destroys a state object\n     *\n     * If the object is no longer in use, it will be\n     * removed from the look-ip table.\n     * \\param [in] object Pointer to object to destroy\n     */\n    void Destroy(T* object, uint32_t version) {\n      std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n      if (object->IsCurrent(version)) {\n        DescType desc = object->Desc();\n        m_objects.erase(desc);\n      }\n    }\n\n  private:\n    \n    dxvk::mutex                                m_mutex;\n    std::unordered_map<DescType, T,\n      D3D11StateDescHash, D3D11StateDescEqual> m_objects;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_state_object.cpp",
    "content": "#include \"d3d11_state_object.h\"\n\nnamespace dxvk {\n\n  D3D11DeviceContextState::D3D11DeviceContextState(\n          D3D11Device*         pDevice)\n  : D3D11DeviceChild<ID3DDeviceContextState>(pDevice),\n    m_destructionNotifier(this) {\n\n  }\n\n  \n  D3D11DeviceContextState::~D3D11DeviceContextState() {\n\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DeviceContextState::QueryInterface(\n          REFIID                riid,\n          void**                ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n    \n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3DDeviceContextState)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3DDeviceContextState), riid)) {\n      Logger::warn(\"D3D11DeviceContextState::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_state_object.h",
    "content": "#pragma once\n\n#include \"d3d11_device.h\"\n#include \"d3d11_context_state.h\"\n#include \"d3d11_device_child.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Device context state implementation\n   *\n   * This is an opaque interface in D3D11, and we only\n   * implement the state block-like functionality, not\n   * the methods to disable certain context and device\n   * interfaces based on the emulated device IID.\n   */\n  class D3D11DeviceContextState : public D3D11DeviceChild<ID3DDeviceContextState> {\n\n  public:\n\n    D3D11DeviceContextState(\n            D3D11Device*         pDevice);\n    \n    ~D3D11DeviceContextState();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject);\n    \n    void SetState(const D3D11ContextState& State) {\n      m_state = State;\n    }\n\n    void GetState(D3D11ContextState& State) const {\n      State = m_state;\n    }\n\n  private:\n\n    D3D11ContextState m_state;\n\n    D3DDestructionNotifier m_destructionNotifier;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_swapchain.cpp",
    "content": "#include \"d3d11_context_imm.h\"\n#include \"d3d11_device.h\"\n#include \"d3d11_swapchain.h\"\n\n#include \"../dxvk/dxvk_latency_builtin.h\"\n\n#include \"../util/util_win32_compat.h\"\n\nnamespace dxvk {\n\n  static uint16_t MapGammaControlPoint(float x) {\n    if (x < 0.0f) x = 0.0f;\n    if (x > 1.0f) x = 1.0f;\n    return uint16_t(65535.0f * x);\n  }\n\n  static VkColorSpaceKHR ConvertColorSpace(DXGI_COLOR_SPACE_TYPE colorspace) {\n    switch (colorspace) {\n      case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709:    return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n      case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020: return VK_COLOR_SPACE_HDR10_ST2084_EXT;\n      case DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709:    return VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;\n      default:\n        Logger::warn(str::format(\"DXGI: ConvertColorSpace: Unknown colorspace \", colorspace));\n        return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n    }\n  }\n\n  static VkXYColorEXT ConvertXYColor(const UINT16 (&dxgiColor)[2]) {\n    return VkXYColorEXT{ float(dxgiColor[0]) / 50000.0f, float(dxgiColor[1]) / 50000.0f };\n  }\n\n  static float ConvertMaxLuminance(UINT dxgiLuminance) {\n    return float(dxgiLuminance);\n  }\n\n  static float ConvertMinLuminance(UINT dxgiLuminance) {\n    return float(dxgiLuminance) * 0.0001f;\n  }\n\n  static float ConvertLevel(UINT16 dxgiLevel) {\n    return float(dxgiLevel);\n  }\n\n  static VkHdrMetadataEXT ConvertHDRMetadata(const DXGI_HDR_METADATA_HDR10& dxgiMetadata) {\n    VkHdrMetadataEXT vkMetadata = { VK_STRUCTURE_TYPE_HDR_METADATA_EXT };\n    vkMetadata.displayPrimaryRed         = ConvertXYColor(dxgiMetadata.RedPrimary);\n    vkMetadata.displayPrimaryGreen       = ConvertXYColor(dxgiMetadata.GreenPrimary);\n    vkMetadata.displayPrimaryBlue        = ConvertXYColor(dxgiMetadata.BluePrimary);\n    vkMetadata.whitePoint                = ConvertXYColor(dxgiMetadata.WhitePoint);\n    vkMetadata.maxLuminance              = ConvertMaxLuminance(dxgiMetadata.MaxMasteringLuminance);\n    vkMetadata.minLuminance              = ConvertMinLuminance(dxgiMetadata.MinMasteringLuminance);\n    vkMetadata.maxContentLightLevel      = ConvertLevel(dxgiMetadata.MaxContentLightLevel);\n    vkMetadata.maxFrameAverageLightLevel = ConvertLevel(dxgiMetadata.MaxFrameAverageLightLevel);\n    return vkMetadata;\n  }\n\n\n  D3D11SwapChain::D3D11SwapChain(\n          D3D11DXGIDevice*        pContainer,\n          D3D11Device*            pDevice,\n          IDXGIVkSurfaceFactory*  pSurfaceFactory,\n    const DXGI_SWAP_CHAIN_DESC1*  pDesc)\n  : m_dxgiDevice(pContainer),\n    m_parent(pDevice),\n    m_surfaceFactory(pSurfaceFactory),\n    m_desc(*pDesc),\n    m_device(pDevice->GetDXVKDevice()),\n    m_frameLatencyCap(pDevice->GetOptions()->maxFrameLatency) {\n    CreateFrameLatencyEvent();\n    CreatePresenter();\n    CreateBackBuffers();\n    CreateBlitter();\n  }\n\n\n  D3D11SwapChain::~D3D11SwapChain() {\n    // Avoids hanging when in this state, see comment\n    // in DxvkDevice::~DxvkDevice.\n    if (this_thread::isInModuleDetachment())\n      return;\n\n    m_presenter->destroyResources();\n    \n    DestroyFrameLatencyEvent();\n    DestroyLatencyTracker();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    InitReturnPtr(ppvObject);\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDXGIVkSwapChain)\n     || riid == __uuidof(IDXGIVkSwapChain1)\n     || riid == __uuidof(IDXGIVkSwapChain2)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDXGIVkSwapChain), riid)) {\n      Logger::warn(\"D3D11SwapChain::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetDesc(\n          DXGI_SWAP_CHAIN_DESC1*    pDesc) {\n    *pDesc = m_desc;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetAdapter(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_dxgiDevice->GetParent(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetDevice(\n          REFIID                    riid,\n          void**                    ppDevice) {\n    return m_dxgiDevice->QueryInterface(riid, ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::GetImage(\n          UINT                      BufferId,\n          REFIID                    riid,\n          void**                    ppBuffer) {\n    InitReturnPtr(ppBuffer);\n\n    if (BufferId >= m_backBuffers.size()) {\n      Logger::err(\"D3D11: GetImage: Invalid buffer ID\");\n      return DXGI_ERROR_UNSUPPORTED;\n    }\n\n    return m_backBuffers[BufferId]->QueryInterface(riid, ppBuffer);\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D11SwapChain::GetImageIndex() {\n    return 0;\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D11SwapChain::GetFrameLatency() {\n    return m_frameLatency;\n  }\n\n\n  HANDLE STDMETHODCALLTYPE D3D11SwapChain::GetFrameLatencyEvent() {\n    HANDLE result = nullptr;\n    HANDLE processHandle = GetCurrentProcess();\n\n    if (!DuplicateHandle(processHandle, m_frameLatencyEvent,\n        processHandle, &result, 0, FALSE, DUPLICATE_SAME_ACCESS)) {\n      Logger::err(\"DxgiSwapChain::GetFrameLatencyWaitableObject: DuplicateHandle failed\");\n      return nullptr;\n    }\n\n    return result;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::ChangeProperties(\n    const DXGI_SWAP_CHAIN_DESC1*    pDesc,\n    const UINT*                     pNodeMasks,\n          IUnknown* const*          ppPresentQueues) {\n    if (m_desc.Format != pDesc->Format)\n      m_presenter->setSurfaceFormat(GetSurfaceFormat(pDesc->Format));\n\n    if (m_desc.Width != pDesc->Width || m_desc.Height != pDesc->Height)\n      m_presenter->setSurfaceExtent({ m_desc.Width, m_desc.Height });\n\n    m_desc = *pDesc;\n    CreateBackBuffers();\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetPresentRegion(\n    const RECT*                     pRegion) {\n    // TODO implement\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetGammaControl(\n          UINT                      NumControlPoints,\n    const DXGI_RGB*                 pControlPoints) {\n    bool isIdentity = true;\n\n    if (NumControlPoints > 1) {\n      std::array<DxvkGammaCp, 1025> cp;\n\n      if (NumControlPoints > cp.size())\n        return E_INVALIDARG;\n      \n      for (uint32_t i = 0; i < NumControlPoints; i++) {\n        uint16_t identity = MapGammaControlPoint(float(i) / float(NumControlPoints - 1));\n\n        cp[i].r = MapGammaControlPoint(pControlPoints[i].Red);\n        cp[i].g = MapGammaControlPoint(pControlPoints[i].Green);\n        cp[i].b = MapGammaControlPoint(pControlPoints[i].Blue);\n        cp[i].a = 0;\n\n        isIdentity &= cp[i].r == identity\n                   && cp[i].g == identity\n                   && cp[i].b == identity;\n      }\n\n      if (!isIdentity)\n        m_blitter->setGammaRamp(NumControlPoints, cp.data());\n    }\n\n    if (isIdentity)\n      m_blitter->setGammaRamp(0, nullptr);\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetFrameLatency(\n          UINT                      MaxLatency) {\n    if (MaxLatency == 0 || MaxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS)\n      return DXGI_ERROR_INVALID_CALL;\n\n    if (m_frameLatencyEvent) {\n      // Windows DXGI does not seem to handle the case where the new maximum\n      // latency is less than the current value, and some games relying on\n      // this behaviour will hang if we attempt to decrement the semaphore.\n      // Thus, only increment the semaphore as necessary.\n      if (MaxLatency > m_frameLatency)\n        ReleaseSemaphore(m_frameLatencyEvent, MaxLatency - m_frameLatency, nullptr);\n    }\n\n    m_frameLatency = MaxLatency;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::Present(\n          UINT                      SyncInterval,\n          UINT                      PresentFlags,\n    const DXGI_PRESENT_PARAMETERS*  pPresentParameters) {\n    HRESULT hr = S_OK;\n\n    if (m_device->getDeviceStatus() != VK_SUCCESS)\n      hr = DXGI_ERROR_DEVICE_RESET;\n\n    if (PresentFlags & DXGI_PRESENT_TEST) {\n      if (hr != S_OK)\n        return hr;\n\n      VkResult status = m_presenter->checkSwapChainStatus();\n      return status == VK_SUCCESS ? S_OK : DXGI_STATUS_OCCLUDED;\n    }\n\n    if (hr != S_OK) {\n      SyncFrameLatency();\n      return hr;\n    }\n\n    try {\n      hr = PresentImage(SyncInterval);\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      hr = E_FAIL;\n    }\n\n    // Ensure to synchronize and release the frame latency semaphore\n    // even if presentation failed with STATUS_OCCLUDED, or otherwise\n    // applications using the semaphore may deadlock. This works because\n    // we do not increment the frame ID in those situations.\n    SyncFrameLatency();\n\n    // Ignore latency stuff if presentation failed\n    DxvkLatencyStats latencyStats = { };\n\n    if (hr == S_OK && m_latency) {\n      latencyStats = m_latency->getStatistics(m_frameId);\n      m_latency->sleepAndBeginFrame(m_frameId + 1, std::abs(m_targetFrameRate));\n    }\n\n    if (m_latencyHud)\n      m_latencyHud->accumulateStats(latencyStats);\n\n    return hr;\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D11SwapChain::CheckColorSpaceSupport(\n          DXGI_COLOR_SPACE_TYPE     ColorSpace) {\n    UINT supportFlags = 0;\n\n    VkColorSpaceKHR vkColorSpace = ConvertColorSpace(ColorSpace);\n\n    if (m_presenter->supportsColorSpace(vkColorSpace))\n      supportFlags |= DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT;\n\n    return supportFlags;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetColorSpace(\n          DXGI_COLOR_SPACE_TYPE     ColorSpace) {\n    VkColorSpaceKHR colorSpace = ConvertColorSpace(ColorSpace);\n\n    if (!m_presenter->supportsColorSpace(colorSpace))\n      return E_INVALIDARG;\n\n    m_colorSpace = colorSpace;\n\n    m_presenter->setSurfaceFormat(GetSurfaceFormat(m_desc.Format));\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11SwapChain::SetHDRMetaData(\n    const DXGI_VK_HDR_METADATA*     pMetaData) {\n    // For some reason this call always seems to succeed on Windows\n    if (pMetaData->Type == DXGI_HDR_METADATA_TYPE_HDR10)\n      m_presenter->setHdrMetadata(ConvertHDRMetadata(pMetaData->HDR10));\n\n    return S_OK;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11SwapChain::GetLastPresentCount(\n          UINT64*                   pLastPresentCount) {\n    *pLastPresentCount = UINT64(m_frameId - DXGI_MAX_SWAP_CHAIN_BUFFERS);\n  }\n\n\n  void STDMETHODCALLTYPE D3D11SwapChain::GetFrameStatistics(\n          DXGI_VK_FRAME_STATISTICS* pFrameStatistics) {\n    std::lock_guard<dxvk::mutex> lock(m_frameStatisticsLock);\n    *pFrameStatistics = m_frameStatistics;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11SwapChain::SetTargetFrameRate(\n          double                    FrameRate) {\n    m_targetFrameRate = FrameRate;\n\n    if (m_presenter != nullptr)\n      m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency());\n  }\n\n\n  Rc<DxvkImageView> D3D11SwapChain::GetBackBufferView() {\n    Rc<DxvkImage> image = GetCommonTexture(m_backBuffers[0].ptr())->GetImage();\n\n    DxvkImageViewKey key;\n    key.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    key.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    key.format = image->info().format;\n    key.aspects = VK_IMAGE_ASPECT_COLOR_BIT;\n    key.mipIndex = 0u;\n    key.mipCount = 1u;\n    key.layerIndex = 0u;\n    key.layerCount = 1u;\n\n    return image->createView(key);\n  }\n\n\n  HRESULT D3D11SwapChain::PresentImage(UINT SyncInterval) {\n    // Flush pending rendering commands before\n    auto immediateContext = m_parent->GetContext();\n    auto immediateContextLock = immediateContext->LockContext();\n\n    immediateContext->EndFrame(m_latency);\n    immediateContext->ExecuteFlush(GpuFlushType::ExplicitFlush, nullptr, true);\n\n    m_presenter->setSyncInterval(SyncInterval);\n\n    // Presentation semaphores and WSI swap chain image\n    if (m_latency)\n      m_latency->notifyCpuPresentBegin(m_frameId + 1u);\n\n    PresenterSync sync;\n    Rc<DxvkImage> backBuffer;\n\n    VkResult status = m_presenter->acquireNextImage(sync, backBuffer);\n\n    if (status != VK_SUCCESS && m_latency)\n      m_latency->discardTimings();\n\n    if (status < 0)\n      return E_FAIL;\n\n    if (status == VK_NOT_READY)\n      return DXGI_STATUS_OCCLUDED;\n\n    m_frameId += 1;\n\n    // Present from CS thread so that we don't\n    // have to synchronize with it first.\n    DxvkImageViewKey viewInfo = { };\n    viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n    viewInfo.usage      = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n    viewInfo.format     = backBuffer->info().format;\n    viewInfo.aspects    = VK_IMAGE_ASPECT_COLOR_BIT;\n    viewInfo.mipIndex   = 0u;\n    viewInfo.mipCount   = 1u;\n    viewInfo.layerIndex = 0u;\n    viewInfo.layerCount = 1u;\n\n    immediateContext->EmitCs([\n      cDevice         = m_device,\n      cBlitter        = m_blitter,\n      cBackBuffer     = backBuffer->createView(viewInfo),\n      cSwapImage      = GetBackBufferView(),\n      cSync           = sync,\n      cPresenter      = m_presenter,\n      cLatency        = m_latency,\n      cColorSpace     = m_colorSpace,\n      cFrameId        = m_frameId\n    ] (DxvkContext* ctx) {\n      // Update back buffer color space as necessary\n      if (cSwapImage->image()->info().colorSpace != cColorSpace) {\n        DxvkImageUsageInfo usage = { };\n        usage.colorSpace = cColorSpace;\n\n        ctx->ensureImageCompatibility(cSwapImage->image(), usage);\n      }\n\n      // Blit the D3D back buffer onto the actual Vulkan\n      // swap chain and render the HUD if we have one.\n      auto contextObjects = ctx->beginExternalRendering();\n\n      cBlitter->present(contextObjects,\n        cBackBuffer, VkRect2D(),\n        cSwapImage, VkRect2D());\n\n      // Submit current command list and present\n      ctx->synchronizeWsi(cSync);\n      ctx->flushCommandList(nullptr, nullptr);\n\n      cDevice->presentImage(cPresenter, cLatency, cFrameId, nullptr);\n    });\n\n    if (m_backBuffers.size() > 1u)\n      RotateBackBuffers(immediateContext);\n\n    immediateContext->FlushCsChunk();\n\n    if (m_latency) {\n      m_latency->notifyCpuPresentEnd(m_frameId);\n\n      if (m_latency->needsAutoMarkers()) {\n        immediateContext->EmitCs([\n          cLatency = m_latency,\n          cFrameId = m_frameId\n        ] (DxvkContext* ctx) {\n          ctx->beginLatencyTracking(cLatency, cFrameId + 1u);\n        });\n      }\n    }\n\n    return S_OK;\n  }\n\n\n  void D3D11SwapChain::RotateBackBuffers(D3D11ImmediateContext* ctx) {\n    small_vector<Rc<DxvkImage>, 4> images;\n\n    for (uint32_t i = 0; i < m_backBuffers.size(); i++)\n      images.push_back(GetCommonTexture(m_backBuffers[i].ptr())->GetImage());\n\n    ctx->EmitCs([\n      cImages = std::move(images)\n    ] (DxvkContext* ctx) {\n      auto allocation = cImages[0]->storage();\n\n      for (size_t i = 0u; i + 1 < cImages.size(); i++) {\n        ctx->invalidateImage(cImages[i], cImages[i + 1]->storage(),\n          cImages[i + 1]->info().layout);\n      }\n\n      ctx->invalidateImage(cImages[cImages.size() - 1u],\n        std::move(allocation), cImages[0]->info().layout);\n    });\n  }\n\n\n  void D3D11SwapChain::CreateFrameLatencyEvent() {\n    m_frameLatencySignal = new sync::CallbackFence(m_frameId);\n\n    if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)\n      m_frameLatencyEvent = CreateSemaphore(nullptr, m_frameLatency, DXGI_MAX_SWAP_CHAIN_BUFFERS, nullptr);\n  }\n\n\n  void D3D11SwapChain::CreatePresenter() {\n    PresenterDesc presenterDesc = { };\n    presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation;\n\n    m_presenter = new Presenter(m_device, m_frameLatencySignal, presenterDesc, [\n      cAdapter  = m_device->adapter(),\n      cFactory  = m_surfaceFactory\n    ] (VkSurfaceKHR* surface) {\n      return cFactory->CreateSurface(\n        cAdapter->vki()->instance(),\n        cAdapter->handle(), surface);\n    });\n\n    m_presenter->setSurfaceFormat(GetSurfaceFormat(m_desc.Format));\n    m_presenter->setSurfaceExtent({ m_desc.Width, m_desc.Height });\n    m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency());\n\n    m_latency = m_device->createLatencyTracker(m_presenter);\n\n    Com<D3D11ReflexDevice> reflex = GetReflexDevice();\n    reflex->RegisterLatencyTracker(m_latency);\n  }\n\n\n  void D3D11SwapChain::CreateBackBuffers() {\n    // Explicitly destroy current swap image before\n    // creating a new one to free up resources\n    m_backBuffers.clear();\n\n    bool sequential = m_desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL ||\n                      m_desc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;\n    uint32_t backBufferCount = sequential ? m_desc.BufferCount : 1u;\n\n    // Create new back buffer\n    D3D11_COMMON_TEXTURE_DESC desc;\n    desc.Width              = std::max(m_desc.Width,  1u);\n    desc.Height             = std::max(m_desc.Height, 1u);\n    desc.Depth              = 1;\n    desc.MipLevels          = 1;\n    desc.ArraySize          = 1;\n    desc.Format             = m_desc.Format;\n    desc.SampleDesc         = m_desc.SampleDesc;\n    desc.Usage              = D3D11_USAGE_DEFAULT;\n    desc.BindFlags          = 0;\n    desc.CPUAccessFlags     = 0;\n    desc.MiscFlags          = 0;\n    desc.TextureLayout      = D3D11_TEXTURE_LAYOUT_UNDEFINED;\n\n    if (m_desc.BufferUsage & DXGI_USAGE_RENDER_TARGET_OUTPUT)\n      desc.BindFlags |= D3D11_BIND_RENDER_TARGET;\n\n    if (m_desc.BufferUsage & DXGI_USAGE_SHADER_INPUT)\n      desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;\n\n    if (m_desc.BufferUsage & DXGI_USAGE_UNORDERED_ACCESS)\n      desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;\n    \n    if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE)\n      desc.MiscFlags |= D3D11_RESOURCE_MISC_GDI_COMPATIBLE;\n    \n    DXGI_USAGE dxgiUsage = DXGI_USAGE_BACK_BUFFER;\n\n    for (uint32_t i = 0; i < backBufferCount; i++) {\n      if (m_desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD\n       || m_desc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD)\n         dxgiUsage |= DXGI_USAGE_DISCARD_ON_PRESENT;\n\n      m_backBuffers.push_back(new D3D11Texture2D(\n        m_parent, this, &desc, dxgiUsage));\n\n      dxgiUsage |= DXGI_USAGE_READ_ONLY;\n    }\n\n    small_vector<Rc<DxvkImage>, 4> images;\n\n    for (uint32_t i = 0; i < backBufferCount; i++)\n      images.push_back(GetCommonTexture(m_backBuffers[i].ptr())->GetImage());\n\n    // Initialize images so that we can use them. Clearing\n    // to black prevents garbled output for the first frame.\n    m_parent->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [\n      cImages = std::move(images)\n    ] (DxvkContext* ctx) {\n      for (size_t i = 0; i < cImages.size(); i++) {\n        ctx->setDebugName(cImages[i], str::format(\"Back buffer \", i).c_str());\n        ctx->initImage(cImages[i], VK_IMAGE_LAYOUT_UNDEFINED);\n      }\n    });\n  }\n\n\n  void D3D11SwapChain::CreateBlitter() {\n    Rc<hud::Hud> hud = hud::Hud::createHud(m_device);\n\n    if (hud) {\n      hud->addItem<hud::HudClientApiItem>(\"api\", 1, GetApiName());\n\n      if (m_latency)\n        m_latencyHud = hud->addItem<hud::HudLatencyItem>(\"latency\", 4);\n    }\n\n    m_blitter = new DxvkSwapchainBlitter(m_device, std::move(hud));\n  }\n\n\n  void D3D11SwapChain::DestroyFrameLatencyEvent() {\n    CloseHandle(m_frameLatencyEvent);\n  }\n\n\n  void D3D11SwapChain::DestroyLatencyTracker() {\n    // Need to make sure the context stops using\n    // the tracker for submissions\n    m_parent->GetContext()->InjectCs(DxvkCsQueue::Ordered, [\n      cLatency = m_latency\n    ] (DxvkContext* ctx) {\n      ctx->endLatencyTracking(cLatency);\n    });\n\n    Com<D3D11ReflexDevice> reflex = GetReflexDevice();\n    reflex->UnregisterLatencyTracker(m_latency);\n  }\n\n\n  void D3D11SwapChain::SyncFrameLatency() {\n    // Wait for the sync event so that we respect the maximum frame latency\n    m_frameLatencySignal->wait(m_frameId - GetActualFrameLatency());\n\n    m_frameLatencySignal->setCallback(m_frameId, [this,\n      cFrameId           = m_frameId,\n      cFrameLatencyEvent = m_frameLatencyEvent\n    ] () {\n      if (cFrameLatencyEvent)\n        ReleaseSemaphore(cFrameLatencyEvent, 1, nullptr);\n\n      std::lock_guard<dxvk::mutex> lock(m_frameStatisticsLock);\n      m_frameStatistics.PresentCount = cFrameId - DXGI_MAX_SWAP_CHAIN_BUFFERS;\n      m_frameStatistics.PresentQPCTime = dxvk::high_resolution_clock::get_counter();\n    });\n  }\n\n\n  uint32_t D3D11SwapChain::GetActualFrameLatency() {\n    // DXGI does not seem to implicitly synchronize waitable swap chains,\n    // so in that case we should just respect the user config. For regular\n    // swap chains, pick the latency from the DXGI device.\n    uint32_t maxFrameLatency = DXGI_MAX_SWAP_CHAIN_BUFFERS;\n\n    if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))\n      m_dxgiDevice->GetMaximumFrameLatency(&maxFrameLatency);\n\n    if (m_frameLatencyCap)\n      maxFrameLatency = std::min(maxFrameLatency, m_frameLatencyCap);\n\n    maxFrameLatency = std::min(maxFrameLatency, m_desc.BufferCount);\n    return maxFrameLatency;\n  }\n\n\n  VkSurfaceFormatKHR D3D11SwapChain::GetSurfaceFormat(DXGI_FORMAT Format) {\n    switch (Format) {\n      default:\n        Logger::warn(str::format(\"D3D11SwapChain: Unexpected format: \", m_desc.Format));\n        [[fallthrough]];\n\n      case DXGI_FORMAT_R8G8B8A8_UNORM:\n      case DXGI_FORMAT_B8G8R8A8_UNORM:\n        return { VK_FORMAT_R8G8B8A8_UNORM, m_colorSpace };\n\n      case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:\n      case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:\n        return { VK_FORMAT_R8G8B8A8_SRGB, m_colorSpace };\n\n      case DXGI_FORMAT_R10G10B10A2_UNORM:\n        return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorSpace };\n\n      case DXGI_FORMAT_R16G16B16A16_FLOAT:\n        return { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorSpace };\n    }\n  }\n\n\n  Com<D3D11ReflexDevice> D3D11SwapChain::GetReflexDevice() {\n    Com<ID3DLowLatencyDevice> llDevice;\n    m_parent->QueryInterface(__uuidof(ID3DLowLatencyDevice), reinterpret_cast<void**>(&llDevice));\n\n    return static_cast<D3D11ReflexDevice*>(llDevice.ptr());\n  }\n\n\n  std::string D3D11SwapChain::GetApiName() const {\n    Com<IDXGIDXVKDevice> device;\n    m_parent->QueryInterface(__uuidof(IDXGIDXVKDevice), reinterpret_cast<void**>(&device));\n\n    uint32_t apiVersion = device->GetAPIVersion();\n    uint32_t featureLevel = m_parent->GetFeatureLevel();\n\n    uint32_t flHi = (featureLevel >> 12);\n    uint32_t flLo = (featureLevel >> 8) & 0x7;\n\n    bool is11On12 = m_parent->Is11on12Device();\n\n    return str::format(\"D3D\", apiVersion, (is11On12 ? \"On12\" : \"\"), \" FL\", flHi, \"_\", flLo);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_swapchain.h",
    "content": "#pragma once\n\n#include \"d3d11_texture.h\"\n\n#include \"../dxvk/hud/dxvk_hud.h\"\n\n#include \"../dxvk/dxvk_latency.h\"\n#include \"../dxvk/dxvk_swapchain_blitter.h\"\n\n#include \"../util/sync/sync_signal.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  class D3D11DXGIDevice;\n\n  class D3D11SwapChain : public ComObject<IDXGIVkSwapChain2> {\n    constexpr static uint32_t DefaultFrameLatency = 1;\n  public:\n\n    D3D11SwapChain(\n            D3D11DXGIDevice*          pContainer,\n            D3D11Device*              pDevice,\n            IDXGIVkSurfaceFactory*    pSurfaceFactory,\n      const DXGI_SWAP_CHAIN_DESC1*    pDesc);\n    \n    ~D3D11SwapChain();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetDesc(\n            DXGI_SWAP_CHAIN_DESC1*    pDesc);\n\n    HRESULT STDMETHODCALLTYPE GetAdapter(\n            REFIID                    riid,\n            void**                    ppvObject);\n    \n    HRESULT STDMETHODCALLTYPE GetDevice(\n            REFIID                    riid,\n            void**                    ppDevice);\n    \n    HRESULT STDMETHODCALLTYPE GetImage(\n            UINT                      BufferId,\n            REFIID                    riid,\n            void**                    ppBuffer);\n\n    UINT STDMETHODCALLTYPE GetImageIndex();\n\n    UINT STDMETHODCALLTYPE GetFrameLatency();\n\n    HANDLE STDMETHODCALLTYPE GetFrameLatencyEvent();\n\n    HRESULT STDMETHODCALLTYPE ChangeProperties(\n      const DXGI_SWAP_CHAIN_DESC1*    pDesc,\n      const UINT*                     pNodeMasks,\n            IUnknown* const*          ppPresentQueues);\n\n    HRESULT STDMETHODCALLTYPE SetPresentRegion(\n      const RECT*                     pRegion);\n\n    HRESULT STDMETHODCALLTYPE SetGammaControl(\n            UINT                      NumControlPoints,\n      const DXGI_RGB*                 pControlPoints);\n\n    HRESULT STDMETHODCALLTYPE SetFrameLatency(\n            UINT                      MaxLatency);\n\n    HRESULT STDMETHODCALLTYPE Present(\n            UINT                      SyncInterval,\n            UINT                      PresentFlags,\n      const DXGI_PRESENT_PARAMETERS*  pPresentParameters);\n\n    UINT STDMETHODCALLTYPE CheckColorSpaceSupport(\n            DXGI_COLOR_SPACE_TYPE     ColorSpace);\n\n    HRESULT STDMETHODCALLTYPE SetColorSpace(\n            DXGI_COLOR_SPACE_TYPE     ColorSpace);\n\n    HRESULT STDMETHODCALLTYPE SetHDRMetaData(\n      const DXGI_VK_HDR_METADATA*     pMetaData);\n\n    void STDMETHODCALLTYPE GetLastPresentCount(\n            UINT64*                   pLastPresentCount);\n\n    void STDMETHODCALLTYPE GetFrameStatistics(\n            DXGI_VK_FRAME_STATISTICS* pFrameStatistics);\n\n    void STDMETHODCALLTYPE SetTargetFrameRate(\n            double                    FrameRate);\n\n  private:\n\n    enum BindingIds : uint32_t {\n      Image = 0,\n      Gamma = 1,\n    };\n\n    Com<D3D11DXGIDevice, false> m_dxgiDevice;\n    \n    D3D11Device*              m_parent;\n    Com<IDXGIVkSurfaceFactory> m_surfaceFactory;\n\n    DXGI_SWAP_CHAIN_DESC1     m_desc;\n\n    Rc<DxvkDevice>            m_device;\n    Rc<Presenter>             m_presenter;\n\n    Rc<DxvkSwapchainBlitter>  m_blitter;\n    Rc<DxvkLatencyTracker>    m_latency;\n\n    small_vector<Com<D3D11Texture2D, false>, 4> m_backBuffers;\n\n    uint64_t                  m_frameId      = DXGI_MAX_SWAP_CHAIN_BUFFERS;\n    uint32_t                  m_frameLatency = DefaultFrameLatency;\n    uint32_t                  m_frameLatencyCap = 0;\n    HANDLE                    m_frameLatencyEvent = nullptr;\n    Rc<sync::CallbackFence>   m_frameLatencySignal;\n\n    VkColorSpaceKHR           m_colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n\n    double                    m_targetFrameRate = 0.0;\n\n    dxvk::mutex               m_frameStatisticsLock;\n    DXGI_VK_FRAME_STATISTICS  m_frameStatistics = { };\n\n    Rc<hud::HudLatencyItem>   m_latencyHud;\n\n    Rc<DxvkImageView> GetBackBufferView();\n\n    HRESULT PresentImage(UINT SyncInterval);\n\n    void RotateBackBuffers(D3D11ImmediateContext* ctx);\n\n    void CreateFrameLatencyEvent();\n\n    void CreatePresenter();\n\n    void CreateBackBuffers();\n\n    void CreateBlitter();\n\n    void DestroyFrameLatencyEvent();\n\n    void DestroyLatencyTracker();\n\n    void SyncFrameLatency();\n\n    uint32_t GetActualFrameLatency();\n\n    VkSurfaceFormatKHR GetSurfaceFormat(DXGI_FORMAT Format);\n\n    Com<D3D11ReflexDevice> GetReflexDevice();\n\n    std::string GetApiName() const;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d11/d3d11_texture.cpp",
    "content": "#include \"d3d11_device.h\"\n#include \"d3d11_context_imm.h\"\n#include \"d3d11_gdi.h\"\n#include \"d3d11_texture.h\"\n\n#include \"../util/util_shared_res.h\"\n#include \"../util/util_win32_compat.h\"\n\nnamespace dxvk {\n  \n  D3D11CommonTexture::D3D11CommonTexture(\n          ID3D11Resource*             pInterface,\n          D3D11Device*                pDevice,\n    const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n    const D3D11_ON_12_RESOURCE_INFO*  p11on12Info,\n          D3D11_RESOURCE_DIMENSION    Dimension,\n          DXGI_USAGE                  DxgiUsage,\n          VkImage                     vkImage,\n          HANDLE                      hSharedHandle)\n  : m_interface(pInterface), m_device(pDevice), m_dimension(Dimension), m_desc(*pDesc),\n    m_11on12(p11on12Info ? *p11on12Info : D3D11_ON_12_RESOURCE_INFO()), m_dxgiUsage(DxgiUsage) {\n    DXGI_VK_FORMAT_MODE   formatMode   = GetFormatMode();\n    DXGI_VK_FORMAT_INFO   formatInfo   = m_device->LookupFormat(m_desc.Format, formatMode);\n    DXGI_VK_FORMAT_FAMILY formatFamily = m_device->LookupFamily(m_desc.Format, formatMode);\n    DXGI_VK_FORMAT_INFO   formatPacked = m_device->LookupPackedFormat(m_desc.Format, formatMode);\n    m_packedFormat = formatPacked.Format;\n\n    DxvkImageCreateInfo imageInfo;\n    imageInfo.type            = GetVkImageType();\n    imageInfo.format          = formatInfo.Format;\n    imageInfo.flags           = 0;\n    imageInfo.sampleCount     = VK_SAMPLE_COUNT_1_BIT;\n    imageInfo.extent.width    = m_desc.Width;\n    imageInfo.extent.height   = m_desc.Height;\n    imageInfo.extent.depth    = m_desc.Depth;\n    imageInfo.numLayers       = m_desc.ArraySize;\n    imageInfo.mipLevels       = m_desc.MipLevels;\n    imageInfo.usage           = VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n                              | VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n    imageInfo.stages          = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    imageInfo.access          = VK_ACCESS_TRANSFER_READ_BIT\n                              | VK_ACCESS_TRANSFER_WRITE_BIT;\n    imageInfo.tiling          = VK_IMAGE_TILING_OPTIMAL;\n    imageInfo.layout          = VK_IMAGE_LAYOUT_GENERAL;\n    imageInfo.initialLayout   = VK_IMAGE_LAYOUT_UNDEFINED;\n    imageInfo.shared          = vkImage != VK_NULL_HANDLE;\n\n    // Normalise hSharedhandle to INVALID_HANDLE_VALUE to allow passing in nullptr\n    if (hSharedHandle == nullptr)\n      hSharedHandle = INVALID_HANDLE_VALUE;\n\n    const auto sharingFlags = D3D11_RESOURCE_MISC_SHARED|D3D11_RESOURCE_MISC_SHARED_NTHANDLE|D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;\n\n    if (m_desc.MiscFlags & sharingFlags) {\n      if (pDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0 ||\n          (m_desc.MiscFlags & (D3D11_RESOURCE_MISC_SHARED|D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX)) == (D3D11_RESOURCE_MISC_SHARED|D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) ||\n          (m_desc.MiscFlags & sharingFlags) == D3D11_RESOURCE_MISC_SHARED_NTHANDLE)\n        throw DxvkError(str::format(\"D3D11: Cannot create shared texture:\",\n          \"\\n  MiscFlags:  \", m_desc.MiscFlags,\n          \"\\n  FeatureLevel:  \", pDevice->GetFeatureLevel()));\n\n      imageInfo.shared = true;\n      imageInfo.sharing.mode = hSharedHandle == INVALID_HANDLE_VALUE ? DxvkSharedHandleMode::Export : DxvkSharedHandleMode::Import;\n      imageInfo.sharing.type = (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE)\n        ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT\n        : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;\n      imageInfo.sharing.handle = hSharedHandle;\n    }\n\n    if (!pDevice->GetOptions()->disableMsaa)\n      DecodeSampleCount(m_desc.SampleDesc.Count, &imageInfo.sampleCount);\n\n    if ((m_desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) && IsR32UavCompatibleFormat(m_desc.Format)) {\n      formatFamily.Add(formatInfo.Format);\n      formatFamily.Add(VK_FORMAT_R32_SFLOAT);\n      formatFamily.Add(VK_FORMAT_R32_UINT);\n      formatFamily.Add(VK_FORMAT_R32_SINT);\n    }\n\n    // The image must be marked as mutable if it can be reinterpreted\n    // by a view with a different format. Depth-stencil formats cannot\n    // be reinterpreted in Vulkan, so we'll ignore those.\n    auto formatProperties = lookupFormatInfo(formatInfo.Format);\n    \n    bool isMutable = formatFamily.FormatCount > 1;\n    bool isMultiPlane = (formatProperties->aspectMask & VK_IMAGE_ASPECT_PLANE_0_BIT) != 0;\n    bool isColorFormat = (formatProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;\n\n    if (isMutable && (isColorFormat || isMultiPlane)) {\n      imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;\n      imageInfo.viewFormatCount = formatFamily.FormatCount;\n      imageInfo.viewFormats     = formatFamily.Formats;\n    }\n\n    // Adjust image flags based on the corresponding D3D flags\n    if (m_desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) {\n      imageInfo.usage  |= VK_IMAGE_USAGE_SAMPLED_BIT;\n      imageInfo.stages |= pDevice->GetEnabledShaderStages();\n      imageInfo.access |= VK_ACCESS_SHADER_READ_BIT;\n    }\n    \n    if (m_desc.BindFlags & D3D11_BIND_RENDER_TARGET) {\n      imageInfo.usage  |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n      imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n      imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT\n                       |  VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n    }\n    \n    if (m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) {\n      imageInfo.usage  |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n      imageInfo.stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n                       |  VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n      imageInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT\n                       |  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n    }\n    \n    if (m_desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) {\n      imageInfo.usage  |= VK_IMAGE_USAGE_STORAGE_BIT;\n      imageInfo.stages |= pDevice->GetEnabledShaderStages();\n      imageInfo.access |= VK_ACCESS_SHADER_READ_BIT\n                       |  VK_ACCESS_SHADER_WRITE_BIT;\n\n      // UAVs are not supported for sRGB formats on most drivers,\n      // but we can still create linear views for the image\n      if (formatProperties->flags.test(DxvkFormatFlag::ColorSpaceSrgb))\n        imageInfo.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;\n    }\n\n    // Multi-plane formats need views to be created with color formats, and\n    // may not report all relevant usage flags as supported on their own.\n    // Also, enable sampled bit to enable use with video processor APIs.\n    if (isMultiPlane) {\n      imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;\n      imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT\n                      |  VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;\n    }\n    \n    // Access pattern for meta-resolve operations\n    if (imageInfo.sampleCount != VK_SAMPLE_COUNT_1_BIT && isColorFormat) {\n      imageInfo.usage  |= VK_IMAGE_USAGE_SAMPLED_BIT;\n      imageInfo.stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n      imageInfo.access |= VK_ACCESS_SHADER_READ_BIT;\n    }\n    \n    if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE)\n      imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;\n    \n    if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_TILED) {\n      imageInfo.flags |= VK_IMAGE_CREATE_SPARSE_BINDING_BIT\n                      |  VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT\n                      |  VK_IMAGE_CREATE_SPARSE_ALIASED_BIT;\n    }\n\n    if (Dimension == D3D11_RESOURCE_DIMENSION_TEXTURE3D &&\n        (m_desc.BindFlags & D3D11_BIND_RENDER_TARGET))\n      imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;\n\n    // Swap chain back buffers need to be shader readable\n    if (DxgiUsage & DXGI_USAGE_BACK_BUFFER) {\n      imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;\n      imageInfo.stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n      imageInfo.access |= VK_ACCESS_SHADER_READ_BIT;\n      imageInfo.shared = VK_TRUE;\n    }\n\n    // Some image formats (i.e. the R32G32B32 ones) are\n    // only supported with linear tiling on most GPUs\n    if (!CheckImageSupport(&imageInfo, VK_IMAGE_TILING_OPTIMAL))\n      imageInfo.tiling = VK_IMAGE_TILING_LINEAR;\n    \n    // Determine map mode based on our findings\n    VkMemoryPropertyFlags memoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n    std::tie(m_mapMode, memoryProperties) = DetermineMapMode(pDevice, &imageInfo);\n    \n    // If the image is mapped directly to host memory, we need\n    // to enable linear tiling, and DXVK needs to be aware that\n    // the image can be accessed by the host.\n    if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) {\n      imageInfo.tiling        = VK_IMAGE_TILING_LINEAR;\n      imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;\n\n      if (pDesc->Usage != D3D11_USAGE_DYNAMIC) {\n        imageInfo.stages |= VK_PIPELINE_STAGE_HOST_BIT;\n        imageInfo.access |= VK_ACCESS_HOST_READ_BIT;\n\n        if (pDesc->CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)\n          imageInfo.access |= VK_ACCESS_HOST_WRITE_BIT;\n      }\n    }\n    \n    // If necessary, create the mapped linear buffer\n    uint32_t subresourceCount = m_desc.ArraySize * m_desc.MipLevels;\n\n    if (m_mapMode != D3D11_COMMON_TEXTURE_MAP_MODE_NONE) {\n      m_mapInfo.resize(subresourceCount);\n\n      for (uint32_t i = 0; i < subresourceCount; i++) {\n        m_mapInfo[i].layout = DetermineSubresourceLayout(&imageInfo,\n          GetSubresourceFromIndex(formatProperties->aspectMask, i));\n      }\n    }\n\n    if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER\n     || m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING\n     || m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC) {\n      m_buffers.resize(subresourceCount);\n\n      if (m_mapMode != D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC) {\n        for (uint32_t i = 0; i < subresourceCount; i++)\n          CreateMappedBuffer(i);\n      }\n    }\n\n    // Skip image creation if possible\n    if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING)\n      return;\n\n    // We must keep LINEAR images in GENERAL layout, but we\n    // can choose a better layout for the image based on how\n    // it is going to be used by the game.\n    if (imageInfo.tiling == VK_IMAGE_TILING_OPTIMAL && !isMultiPlane && imageInfo.sharing.mode == DxvkSharedHandleMode::None)\n      imageInfo.layout = OptimizeLayout(imageInfo.usage);\n\n    // Check if we can actually create the image\n    if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) {\n      throw DxvkError(str::format(\n        \"D3D11: Cannot create texture:\",\n        \"\\n  Format:  \", m_desc.Format,\n        \"\\n  Extent:  \", m_desc.Width,\n                    \"x\", m_desc.Height,\n                    \"x\", m_desc.Depth,\n        \"\\n  Samples: \", m_desc.SampleDesc.Count,\n        \"\\n  Layers:  \", m_desc.ArraySize,\n        \"\\n  Levels:  \", m_desc.MipLevels,\n        \"\\n  Usage:   \", std::hex, m_desc.BindFlags,\n        \"\\n  Flags:   \", std::hex, m_desc.MiscFlags));\n    }\n\n    if (m_11on12.Resource != nullptr)\n      vkImage = VkImage(m_11on12.VulkanHandle);\n\n    if (!vkImage)\n      m_image = m_device->GetDXVKDevice()->createImage(imageInfo, memoryProperties);\n    else\n      m_image = m_device->GetDXVKDevice()->importImage(imageInfo, vkImage, memoryProperties);\n\n    if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT)\n      m_mapPtr = m_image->mapPtr(0);\n\n    if (imageInfo.sharing.mode == DxvkSharedHandleMode::Export) {\n      if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) {\n        try {\n          Rc<DxvkKeyedMutex> mutex = new DxvkKeyedMutex(m_device->GetDXVKDevice(), 0, !!(m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE));\n          m_image->setKeyedMutex(std::move(mutex));\n        } catch (const DxvkError& e) {\n          Logger::warn(\"D3D11CommonTexture: Failed to create keyed mutex\");\n        }\n      }\n\n      ExportImageInfo();\n    }\n  }\n  \n  \n  D3D11CommonTexture::~D3D11CommonTexture() {\n    \n  }\n  \n  \n  VkDeviceSize D3D11CommonTexture::ComputeMappedOffset(UINT Subresource, UINT Plane, VkOffset3D Offset) const {\n    auto packedFormatInfo = lookupFormatInfo(m_packedFormat);\n\n    VkImageAspectFlags aspectMask = packedFormatInfo->aspectMask;\n    VkDeviceSize elementSize = packedFormatInfo->elementSize;\n\n    if (packedFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n      auto plane = &packedFormatInfo->planes[Plane];\n      elementSize = plane->elementSize;\n      Offset.x /= plane->blockSize.width;\n      Offset.y /= plane->blockSize.height;\n      aspectMask = vk::getPlaneAspect(Plane);\n    }\n\n    auto layout = GetSubresourceLayout(aspectMask, Subresource);\n    auto blockOffset = util::computeBlockOffset(Offset, packedFormatInfo->blockSize);\n\n    return VkDeviceSize(blockOffset.z) * layout.DepthPitch\n         + VkDeviceSize(blockOffset.y) * layout.RowPitch\n         + VkDeviceSize(blockOffset.x) * elementSize\n         + VkDeviceSize(layout.Offset);\n  }\n\n\n  D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT D3D11CommonTexture::GetSubresourceLayout(\n          VkImageAspectFlags    AspectMask,\n          UINT                  Subresource) const {\n    // Color is mapped directly and depth-stencil are interleaved\n    // in packed formats, so just use the cached subresource layout\n    constexpr VkImageAspectFlags PlaneAspects = VK_IMAGE_ASPECT_PLANE_0_BIT\n      | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT;\n\n    if ((Subresource < m_mapInfo.size()) && !(AspectMask & PlaneAspects))\n      return m_mapInfo[Subresource].layout;\n\n    // Safe-guard against invalid subresource index\n    if (Subresource >= m_desc.ArraySize * m_desc.MipLevels)\n      return D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT();\n\n    // Image info is only needed for direct-mapped images\n    VkImageSubresource subresource = GetSubresourceFromIndex(AspectMask, Subresource);\n    return DetermineSubresourceLayout(nullptr, subresource);\n  }\n\n\n  DXGI_VK_FORMAT_MODE D3D11CommonTexture::GetFormatMode() const {\n    if (m_desc.BindFlags & D3D11_BIND_RENDER_TARGET)\n      return DXGI_VK_FORMAT_MODE_COLOR;\n    \n    if (m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL)\n      return DXGI_VK_FORMAT_MODE_DEPTH;\n    \n    return DXGI_VK_FORMAT_MODE_ANY;\n  }\n  \n  \n  uint32_t D3D11CommonTexture::GetPlaneCount() const {\n    return vk::getPlaneCount(m_image->formatInfo()->aspectMask);\n  }\n\n\n  bool D3D11CommonTexture::CheckViewCompatibility(UINT BindFlags, DXGI_FORMAT Format, UINT Plane) const {\n    const DxvkImageCreateInfo& imageInfo = m_image->info();\n\n    // Check whether the given bind flags are supported\n    if ((m_desc.BindFlags & BindFlags) != BindFlags)\n      return false;\n\n    // Check whether the view format is compatible\n    DXGI_VK_FORMAT_MODE formatMode = GetFormatMode();\n    DXGI_VK_FORMAT_INFO viewFormat = m_device->LookupFormat(Format,        formatMode);\n    DXGI_VK_FORMAT_INFO baseFormat = m_device->LookupFormat(m_desc.Format, formatMode);\n    \n    // Check whether the plane index is valid for the given format\n    uint32_t planeCount = GetPlaneCount();\n\n    if (Plane >= planeCount)\n      return false;\n\n    if (imageInfo.flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) {\n      // Check whether the given combination of image\n      // view type and view format is actually supported\n      VkFormatFeatureFlags2 features = GetImageFormatFeatures(BindFlags);\n      \n      if (!CheckFormatFeatureSupport(viewFormat.Format, features))\n        return false;\n\n      // Using the image format itself is supported for non-planar formats\n      if (viewFormat.Format == baseFormat.Format && planeCount == 1)\n        return true;\n      \n      // If there is a list of compatible formats, the view format must be\n      // included in that list. For planar formats, the list is laid out in\n      // such a way that the n-th format is supported for the n-th plane. \n      for (size_t i = Plane; i < imageInfo.viewFormatCount; i += planeCount) {\n        if (imageInfo.viewFormats[i] == viewFormat.Format) {\n          return true;\n        }\n      }\n\n      // Otherwise, all bit-compatible formats can be used.\n      if (imageInfo.viewFormatCount == 0 && planeCount == 1) {\n        auto baseFormatInfo = lookupFormatInfo(baseFormat.Format);\n        auto viewFormatInfo = lookupFormatInfo(viewFormat.Format);\n        \n        return baseFormatInfo->aspectMask  == viewFormatInfo->aspectMask\n            && baseFormatInfo->elementSize == viewFormatInfo->elementSize;\n      }\n\n      return false;\n    } else {\n      // For non-mutable images, the view format\n      // must be identical to the image format.\n      return viewFormat.Format == baseFormat.Format && planeCount == 1;\n    }\n  }\n\n\n  void D3D11CommonTexture::SetDebugName(const char* pName) {\n    if (m_image) {\n      m_device->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [\n        cImage  = m_image,\n        cName   = std::string(pName ? pName : \"\")\n      ] (DxvkContext* ctx) {\n        ctx->setDebugName(cImage, cName.c_str());\n      });\n    }\n\n    if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING) {\n      for (uint32_t i = 0; i < m_buffers.size(); i++) {\n        m_device->GetContext()->InjectCs(DxvkCsQueue::HighPriority, [\n          cBuffer = m_buffers[i].buffer,\n          cName   = std::string(pName ? pName : \"\")\n        ] (DxvkContext* ctx) {\n          ctx->setDebugName(cBuffer, cName.c_str());\n        });\n      }\n    }\n  }\n\n\n  HRESULT D3D11CommonTexture::NormalizeTextureProperties(D3D11_COMMON_TEXTURE_DESC* pDesc) {\n    if (pDesc->Width == 0 || pDesc->Height == 0 || pDesc->Depth == 0 || pDesc->ArraySize == 0)\n      return E_INVALIDARG;\n    \n    if (FAILED(DecodeSampleCount(pDesc->SampleDesc.Count, nullptr)))\n      return E_INVALIDARG;\n    \n    if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE)\n     && (pDesc->Usage == D3D11_USAGE_STAGING\n      || (pDesc->Format != DXGI_FORMAT_B8G8R8A8_TYPELESS\n       && pDesc->Format != DXGI_FORMAT_B8G8R8A8_UNORM\n       && pDesc->Format != DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)))\n      return E_INVALIDARG;\n\n    if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS)\n     && (pDesc->BindFlags & (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET))\n                         != (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET))\n      return E_INVALIDARG;\n\n    // TILE_POOL is invalid for textures\n    if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILE_POOL)\n      return E_INVALIDARG;\n\n    // Perform basic validation for tiled resources\n    if (pDesc->MiscFlags & D3D11_RESOURCE_MISC_TILED) {\n      UINT invalidFlags = D3D11_RESOURCE_MISC_SHARED\n                        | D3D11_RESOURCE_MISC_SHARED_NTHANDLE\n                        | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX\n                        | D3D11_RESOURCE_MISC_GDI_COMPATIBLE;\n\n      if ((pDesc->MiscFlags & invalidFlags)\n       || (pDesc->Usage != D3D11_USAGE_DEFAULT)\n       || (pDesc->CPUAccessFlags))\n        return E_INVALIDARG;\n    }\n\n    // Use the maximum possible mip level count if the supplied\n    // mip level count is either unspecified (0) or invalid\n    const uint32_t maxMipLevelCount = (pDesc->SampleDesc.Count <= 1)\n      ? util::computeMipLevelCount({ pDesc->Width, pDesc->Height, pDesc->Depth })\n      : 1u;\n    \n    if (pDesc->MipLevels == 0 || pDesc->MipLevels > maxMipLevelCount)\n      pDesc->MipLevels = maxMipLevelCount;\n    \n    // Row-major is only supported for textures with one single\n    // subresource and one sample and cannot have bind flags.\n    if (pDesc->TextureLayout == D3D11_TEXTURE_LAYOUT_ROW_MAJOR\n     && (pDesc->MipLevels != 1 || pDesc->SampleDesc.Count != 1 || pDesc->BindFlags))\n      return E_INVALIDARG;\n\n    // Standard swizzle is unsupported\n    if (pDesc->TextureLayout == D3D11_TEXTURE_LAYOUT_64K_STANDARD_SWIZZLE)\n      return E_INVALIDARG;\n\n    return S_OK;\n  }\n  \n  \n  HRESULT D3D11CommonTexture::GetDescFromD3D12(\n          ID3D12Resource*         pResource,\n    const D3D11_RESOURCE_FLAGS*   pResourceFlags,\n          D3D11_COMMON_TEXTURE_DESC* pTextureDesc) {\n    D3D12_RESOURCE_DESC desc12 = pResource->GetDesc();\n\n    pTextureDesc->Width = desc12.Width;\n    pTextureDesc->Height = desc12.Height;\n\n    if (desc12.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) {\n      pTextureDesc->Depth = desc12.DepthOrArraySize;\n      pTextureDesc->ArraySize = 1;\n    } else {\n      pTextureDesc->Depth = 1;\n      pTextureDesc->ArraySize = desc12.DepthOrArraySize;\n    }\n\n    pTextureDesc->MipLevels = desc12.MipLevels;\n    pTextureDesc->Format = desc12.Format;\n    pTextureDesc->SampleDesc = desc12.SampleDesc;\n    pTextureDesc->Usage = D3D11_USAGE_DEFAULT;\n    pTextureDesc->BindFlags = 0;\n    pTextureDesc->CPUAccessFlags = 0;\n    pTextureDesc->MiscFlags = 0;\n\n    if (!(desc12.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE))\n      pTextureDesc->BindFlags |= D3D11_BIND_SHADER_RESOURCE;\n\n    if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)\n      pTextureDesc->BindFlags |= D3D11_BIND_RENDER_TARGET;\n\n    if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)\n      pTextureDesc->BindFlags |= D3D11_BIND_DEPTH_STENCIL;\n\n    if (desc12.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)\n      pTextureDesc->BindFlags |= D3D11_BIND_UNORDERED_ACCESS;\n\n    if (pResourceFlags) {\n      pTextureDesc->BindFlags = pResourceFlags->BindFlags;\n      pTextureDesc->MiscFlags |= pResourceFlags->MiscFlags;\n      pTextureDesc->CPUAccessFlags = pResourceFlags->CPUAccessFlags;\n    }\n\n    return S_OK;\n  }\n\n\n  BOOL D3D11CommonTexture::CheckImageSupport(\n    const DxvkImageCreateInfo*  pImageInfo,\n          VkImageTiling         Tiling) const {\n    // D3D12 images always use optimal tiling\n    if (m_11on12.Resource != nullptr && Tiling != VK_IMAGE_TILING_OPTIMAL)\n      return FALSE;\n\n    DxvkFormatQuery formatQuery = { };\n    formatQuery.format = pImageInfo->format;\n    formatQuery.type = pImageInfo->type;\n    formatQuery.tiling = Tiling;\n    formatQuery.usage = pImageInfo->usage;\n    formatQuery.flags = pImageInfo->flags;\n\n    if (pImageInfo->flags & VK_IMAGE_CREATE_EXTENDED_USAGE_BIT)\n      formatQuery.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;\n\n    auto properties = m_device->GetDXVKDevice()->getFormatLimits(formatQuery);\n    \n    if (!properties)\n      return FALSE;\n\n    return (pImageInfo->extent.width  <= properties->maxExtent.width)\n        && (pImageInfo->extent.height <= properties->maxExtent.height)\n        && (pImageInfo->extent.depth  <= properties->maxExtent.depth)\n        && (pImageInfo->numLayers     <= properties->maxArrayLayers)\n        && (pImageInfo->mipLevels     <= properties->maxMipLevels)\n        && (pImageInfo->sampleCount    & properties->sampleCounts);\n  }\n\n\n  BOOL D3D11CommonTexture::CheckFormatFeatureSupport(\n          VkFormat              Format,\n          VkFormatFeatureFlags2 Features) const {\n    DxvkFormatFeatures support = m_device->GetDXVKDevice()->getFormatFeatures(Format);\n\n    return (support.linear  & Features) == Features\n        || (support.optimal & Features) == Features;\n  }\n\n  \n  std::pair<D3D11_COMMON_TEXTURE_MAP_MODE, VkMemoryPropertyFlags> D3D11CommonTexture::DetermineMapMode(\n    const D3D11Device*          device,\n    const DxvkImageCreateInfo*  pImageInfo) const {\n    // Don't map an image unless the application requests it\n    if (!m_desc.CPUAccessFlags)\n      return { D3D11_COMMON_TEXTURE_MAP_MODE_NONE, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };\n\n    // For default images, always use a persistent staging buffer. Readback\n    // may cause a GPU sync, but nobody seems to be using this feature anyway.\n    if (m_desc.Usage == D3D11_USAGE_DEFAULT)\n      return { D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };\n\n    // If the resource cannot be used in the actual rendering pipeline, we\n    // do not need to create an actual image and can instead implement copy\n    // functions as buffer-to-image and image-to-buffer copies.\n    if (m_desc.Usage == D3D11_USAGE_STAGING)\n      return { D3D11_COMMON_TEXTURE_MAP_MODE_STAGING, 0u };\n\n    // If the packed format and image format don't match, we need to use\n    // a staging buffer and perform format conversion when mapping. The\n    // same is true if the game is broken and requires tight packing.\n    if (m_packedFormat != pImageInfo->format || device->GetOptions()->disableDirectImageMapping)\n      return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };\n\n    // Multi-plane and depth-stencil images have a special memory layout\n    // in D3D11, so we can't expose those directly to the app\n    auto formatInfo = lookupFormatInfo(pImageInfo->format);\n\n    if (formatInfo->aspectMask != VK_IMAGE_ASPECT_COLOR_BIT)\n      return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };\n\n    // If we can't use linear tiling for this image, we have to use a buffer\n    if (!CheckImageSupport(pImageInfo, VK_IMAGE_TILING_LINEAR))\n      return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };\n\n    // Determine memory flags for the actual image if we use direct mapping.\n    // Depending on the concrete use case, we may fall back to different\n    // memory types.\n    VkMemoryPropertyFlags memoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n                                      | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;\n\n    bool useCached = (m_device->GetOptions()->cachedDynamicResources == ~0u)\n                  || (m_device->GetOptions()->cachedDynamicResources & m_desc.BindFlags)\n                  || (m_desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ);\n\n    if (m_desc.Usage == D3D11_USAGE_STAGING || useCached)\n      memoryFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;\n    else if (m_desc.BindFlags)\n      memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n    // If there are multiple subresources, go through a buffer because\n    // we can otherwise not really discard individual subresources.\n    if (m_desc.ArraySize > 1u || m_desc.MipLevels != 1u)\n      return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };\n\n    // If the image is essentially linear already, expose it directly since\n    // there won't be any tangible benefit to using optimal tiling anyway.\n    VkExtent3D blockCount = util::computeBlockCount(pImageInfo->extent, formatInfo->blockSize);\n\n    if (blockCount.height == 1u && blockCount.depth == 1u)\n      return { D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT, memoryFlags };\n\n    // If the image looks like a video, we can generally expect it to get\n    // updated and read once per frame. This is one of the most common use\n    // cases for a mapped image, expose it directly in order to avoid copies.\n    if (blockCount.depth == 1u && blockCount.height >= 160 && formatInfo->elementSize <= 4u) {\n      static const std::array<std::pair<uint32_t, uint32_t>, 3> videoApectRatios = {{\n        {  4, 3 },\n        { 16, 9 },\n        { 21, 9 },\n      }};\n\n      bool isVideoAspectRatio = false;\n\n      for (const auto& a : videoApectRatios) {\n        // Due to codec limitations, video dimensions are often rounded to\n        // a multiple of 8. Account for this when checking the size.\n        isVideoAspectRatio |= blockCount.width > (a.first * (blockCount.height - 8u)) / a.second\n                           && blockCount.width < (a.first * (blockCount.height + 8u)) / a.second;\n      }\n\n      if (isVideoAspectRatio) {\n        // Keep video images in system memory to not waste precious HVV space\n        return { D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT, memoryFlags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };\n      }\n    }\n\n    // If the image exceeds a certain size, map it directly because the overhead\n    // of potentially copying the whole thing every frame likely outweighs any\n    // benefit we might get from faster memory and tiling. This solves such an\n    // issue in Warhammer III, which discards a 48 MB texture every single frame.\n    constexpr VkDeviceSize MaxImageStagingBufferSize = 1ull << 20;\n\n    VkDeviceSize imageSize = util::flattenImageExtent(blockCount) * formatInfo->elementSize;\n\n    if (imageSize > MaxImageStagingBufferSize)\n      return { D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT, memoryFlags };\n\n    // For smaller images, use a staging buffer. There are some common use\n    // cases where the image will only get written once, e.g. SMAA look-up\n    // tables in some games, which will benefit from faster GPU access.\n    return { D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };\n  }\n\n\n  D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT D3D11CommonTexture::DetermineSubresourceLayout(\n    const DxvkImageCreateInfo*  pImageInfo,\n    const VkImageSubresource&   subresource) const {\n    auto formatInfo = lookupFormatInfo(m_packedFormat);\n\n    if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) {\n      VkSubresourceLayout vkLayout = m_device->GetDXVKDevice()->queryImageSubresourceLayout(*pImageInfo, subresource);\n\n      D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT result = { };\n      result.Offset = vkLayout.offset;\n      result.RowPitch = vkLayout.rowPitch;\n      result.DepthPitch = vkLayout.depthPitch;\n\n      // We will only ever use direct mapping for single-aspect images,\n      // so ignore any sort of multi-plane shenanigans on this path\n      auto mipExtent = MipLevelExtent(subresource.mipLevel);\n      auto blockCount = util::computeBlockCount(mipExtent, formatInfo->blockSize);\n\n      // If the image dimensions support it, try to look as close to a\n      // linear buffer as we can. Some games use the depth pitch as a\n      // subresource size and will crash if it includes any padding.\n      if (blockCount.depth == 1u) {\n        if (blockCount.height == 1u) {\n          result.RowPitch = formatInfo->elementSize * blockCount.width;\n          result.DepthPitch = result.RowPitch;\n        } else {\n          result.DepthPitch = vkLayout.rowPitch * blockCount.height;\n        }\n      }\n\n      result.Size = blockCount.depth * result.DepthPitch;\n      return result;\n    } else {\n      D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT result = { };\n\n      VkImageAspectFlags aspects = formatInfo->aspectMask;\n      VkExtent3D mipExtent = MipLevelExtent(subresource.mipLevel);\n\n      while (aspects) {\n        auto aspect = vk::getNextAspect(aspects);\n        auto extent = mipExtent;\n        auto elementSize = formatInfo->elementSize;\n\n        if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n          auto plane = &formatInfo->planes[vk::getPlaneIndex(aspect)];\n          extent.width  /= plane->blockSize.width;\n          extent.height /= plane->blockSize.height;\n          elementSize = plane->elementSize;\n        }\n\n        auto blockCount = util::computeBlockCount(extent, formatInfo->blockSize);\n\n        if (!result.RowPitch) {\n          result.RowPitch   = elementSize * blockCount.width;\n          result.DepthPitch = elementSize * blockCount.width * blockCount.height;\n        }\n\n        VkDeviceSize size = elementSize * blockCount.width * blockCount.height * blockCount.depth;\n\n        if (aspect & subresource.aspectMask)\n          result.Size += size;\n        else if (!result.Size)\n          result.Offset += size;\n      }\n\n      return result;\n    }\n  }\n  \n  \n  void D3D11CommonTexture::ExportImageInfo() {\n    struct d3dkmt_d3d11_desc desc = { };\n    desc.dxgi.size = sizeof(desc);\n    desc.dxgi.version = 4;\n    desc.dxgi.keyed_mutex = !!(m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX);\n    desc.dxgi.nt_shared = !!(m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE);\n    desc.dimension = m_dimension;\n\n    if (desc.dxgi.keyed_mutex) {\n      auto keyedMutex = m_image->getKeyedMutex();\n      desc.dxgi.mutex_handle = keyedMutex ? keyedMutex->kmtGlobal() : 0;\n\n      if (keyedMutex) {\n        auto syncObject = keyedMutex->getSyncObject();\n        desc.dxgi.sync_handle = syncObject ? syncObject->kmtGlobal() : 0;\n      }\n    }\n\n    switch (m_dimension) {\n      case D3D11_RESOURCE_DIMENSION_UNKNOWN: break;\n      case D3D11_RESOURCE_DIMENSION_BUFFER: break;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D:\n        desc.d3d11_1d.Width = m_desc.Width;\n        desc.d3d11_1d.MipLevels = m_desc.MipLevels;\n        desc.d3d11_1d.ArraySize = m_desc.ArraySize;\n        desc.d3d11_1d.Format = m_desc.Format;\n        desc.d3d11_1d.Usage = m_desc.Usage;\n        desc.d3d11_1d.BindFlags = m_desc.BindFlags;\n        desc.d3d11_1d.CPUAccessFlags = m_desc.CPUAccessFlags;\n        desc.d3d11_1d.MiscFlags = m_desc.MiscFlags;\n        break;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D:\n        desc.d3d11_2d.Width = m_desc.Width;\n        desc.d3d11_2d.Height = m_desc.Height;\n        desc.d3d11_2d.MipLevels = m_desc.MipLevels;\n        desc.d3d11_2d.ArraySize = m_desc.ArraySize;\n        desc.d3d11_2d.Format = m_desc.Format;\n        desc.d3d11_2d.SampleDesc = m_desc.SampleDesc;\n        desc.d3d11_2d.Usage = m_desc.Usage;\n        desc.d3d11_2d.BindFlags = m_desc.BindFlags;\n        desc.d3d11_2d.CPUAccessFlags = m_desc.CPUAccessFlags;\n        desc.d3d11_2d.MiscFlags = m_desc.MiscFlags;\n        break;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D:\n        desc.d3d11_3d.Width = m_desc.Width;\n        desc.d3d11_3d.Height = m_desc.Height;\n        desc.d3d11_3d.Depth = m_desc.Depth;\n        desc.d3d11_3d.MipLevels = m_desc.MipLevels;\n        desc.d3d11_3d.Format = m_desc.Format;\n        desc.d3d11_3d.Usage = m_desc.Usage;\n        desc.d3d11_3d.BindFlags = m_desc.BindFlags;\n        desc.d3d11_3d.CPUAccessFlags = m_desc.CPUAccessFlags;\n        desc.d3d11_3d.MiscFlags = m_desc.MiscFlags;\n        break;\n    }\n\n    D3DKMT_ESCAPE escape = { };\n    escape.Type = D3DKMT_ESCAPE_UPDATE_RESOURCE_WINE;\n    escape.pPrivateDriverData = &desc;\n    escape.PrivateDriverDataSize = sizeof(desc);\n    escape.hContext = m_image->storage()->kmtLocal();\n\n    if (!D3DKMTEscape(&escape))\n      return;\n\n    /* try the legacy Proton shared resource implementation */\n\n    HANDLE hSharedHandle;\n\n    if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE)\n      hSharedHandle = m_image->sharedHandle();\n    else\n      hSharedHandle = openKmtHandle( m_image->sharedHandle() );\n\n    DxvkSharedTextureMetadata metadata;\n\n    metadata.Width          = m_desc.Width;\n    metadata.Height         = m_desc.Height;\n    metadata.MipLevels      = m_desc.MipLevels;\n    metadata.ArraySize      = m_desc.ArraySize;\n    metadata.Format         = m_desc.Format;\n    metadata.SampleDesc     = m_desc.SampleDesc;\n    metadata.Usage          = m_desc.Usage;\n    metadata.BindFlags      = m_desc.BindFlags;\n    metadata.CPUAccessFlags = m_desc.CPUAccessFlags;\n    metadata.MiscFlags      = m_desc.MiscFlags;\n    metadata.TextureLayout  = m_desc.TextureLayout;\n\n    if (hSharedHandle == INVALID_HANDLE_VALUE || !setSharedMetadata(hSharedHandle, &metadata, sizeof(metadata))) {\n      Logger::warn(\"D3D11: Failed to write shared resource info for a texture\");\n    }\n\n    if (hSharedHandle != INVALID_HANDLE_VALUE)\n      CloseHandle(hSharedHandle);\n  }\n  \n  \n  BOOL D3D11CommonTexture::IsR32UavCompatibleFormat(\n          DXGI_FORMAT           Format) {\n    return Format == DXGI_FORMAT_R8G8B8A8_TYPELESS\n        || Format == DXGI_FORMAT_B8G8R8A8_TYPELESS\n        || Format == DXGI_FORMAT_B8G8R8X8_TYPELESS\n        || Format == DXGI_FORMAT_R10G10B10A2_TYPELESS\n        || Format == DXGI_FORMAT_R16G16_TYPELESS\n        || Format == DXGI_FORMAT_R32_TYPELESS;\n  }\n\n\n  void D3D11CommonTexture::CreateMappedBuffer(UINT Subresource) {\n    const DxvkFormatInfo* formatInfo = lookupFormatInfo(\n      m_device->LookupPackedFormat(m_desc.Format, GetFormatMode()).Format);\n\n    DxvkBufferCreateInfo info;\n    info.size   = GetSubresourceLayout(formatInfo->aspectMask, Subresource).Size;\n    info.usage  = VK_BUFFER_USAGE_TRANSFER_SRC_BIT\n                | VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT\n                | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n    info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT\n                | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n    info.access = VK_ACCESS_TRANSFER_READ_BIT\n                | VK_ACCESS_TRANSFER_WRITE_BIT\n                | VK_ACCESS_SHADER_READ_BIT\n                | VK_ACCESS_SHADER_WRITE_BIT;\n    info.debugName = \"Image buffer\";\n\n    // We may read mapped buffers even if it is\n    // marked as CPU write-only on the D3D11 side.\n    if (m_desc.Usage != D3D11_USAGE_DYNAMIC) {\n      info.stages |= VK_PIPELINE_STAGE_HOST_BIT;\n      info.access |= VK_ACCESS_HOST_READ_BIT;\n\n      if (m_desc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)\n        info.access |= VK_ACCESS_HOST_WRITE_BIT;\n    }\n\n    VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n                                  | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;\n    \n    bool useCached = m_device->GetOptions()->cachedDynamicResources == ~0u;\n\n    if (m_desc.Usage == D3D11_USAGE_STAGING || useCached)\n      memType |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;\n\n    auto& entry = m_buffers[Subresource];\n    entry.buffer = m_device->GetDXVKDevice()->createBuffer(info, memType);\n    entry.slice = entry.buffer->storage();\n  }\n\n\n  void D3D11CommonTexture::FreeMappedBuffer(\n          UINT                  Subresource) {\n    auto& entry = m_buffers[Subresource];\n    entry.buffer = nullptr;\n    entry.slice = nullptr;\n  }\n  \n  \n  VkImageType D3D11CommonTexture::GetImageTypeFromResourceDim(D3D11_RESOURCE_DIMENSION Dimension) {\n    switch (Dimension) {\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: return VK_IMAGE_TYPE_1D;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: return VK_IMAGE_TYPE_2D;\n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: return VK_IMAGE_TYPE_3D;\n      default: throw DxvkError(\"D3D11CommonTexture: Unhandled resource dimension\");\n    }\n  }\n  \n  \n  VkImageLayout D3D11CommonTexture::OptimizeLayout(VkImageUsageFlags Usage) {\n    const VkImageUsageFlags usageFlags = Usage;\n\n    // Filter out unnecessary flags. Transfer operations\n    // are handled by the backend in a transparent manner.\n    Usage &= VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT\n      | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n      | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n    // Storage images require GENERAL.\n    if (Usage & VK_IMAGE_USAGE_STORAGE_BIT)\n      return VK_IMAGE_LAYOUT_GENERAL;\n\n    // If the image is used only as an attachment, we never\n    // have to transform the image back to a different layout.\n    if (Usage == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)\n      return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n    \n    if (Usage == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)\n      return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\n    // Otherwise, pick a layout that can be used for reading.\n    return usageFlags & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT\n      ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL\n      : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n  }\n  \n  \n\n\n  D3D11DXGISurface::D3D11DXGISurface(\n          ID3D11Resource*     pResource)\n  : m_resource  (pResource),\n    m_gdiSurface(nullptr) {\n    auto texture = GetCommonTexture(pResource);\n    if (texture && texture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE)\n      m_gdiSurface = new D3D11GDISurface(m_resource, 0);\n  }\n\n  D3D11DXGISurface::D3D11DXGISurface(\n          ID3D11Resource*     pParentResource,\n          UINT                Subresource)\n  : m_isSubresourceSurface(true),\n    m_subresource         (Subresource),\n    m_resource            (pParentResource),\n    m_gdiSurface(nullptr) {\n    auto texture = GetCommonTexture(pParentResource);\n    if (texture && texture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE)\n      m_gdiSurface = new D3D11GDISurface(m_resource, m_subresource);\n  }\n\n  \n  D3D11DXGISurface::~D3D11DXGISurface() {\n    if (m_gdiSurface)\n      delete m_gdiSurface;\n  }\n\n  \n  ULONG STDMETHODCALLTYPE D3D11DXGISurface::AddRef() {\n    return m_resource->AddRef();\n  }\n\n  \n  ULONG STDMETHODCALLTYPE D3D11DXGISurface::Release() {\n    return m_resource->Release();\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n\n    InitReturnPtr(ppvObject);\n\n    // Only a subset of interfaces are available for subresource surfaces\n    if (m_isSubresourceSurface) {\n      if (riid == __uuidof(IUnknown)\n       || riid == __uuidof(IDXGIObject)\n       || riid == __uuidof(IDXGIDeviceSubObject)\n       || riid == __uuidof(IDXGISurface)\n       || riid == __uuidof(IDXGISurface1)\n       || riid == __uuidof(IDXGISurface2)) {\n        *ppvObject = ref(this);\n        return S_OK;\n      }\n\n      return E_NOINTERFACE;\n    }\n\n    return m_resource->QueryInterface(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetPrivateData(\n          REFGUID                 Name,\n          UINT*                   pDataSize,\n          void*                   pData) {\n    return m_resource->GetPrivateData(Name, pDataSize, pData);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::SetPrivateData(\n          REFGUID                 Name,\n          UINT                    DataSize,\n    const void*                   pData) {\n    return m_resource->SetPrivateData(Name, DataSize, pData);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::SetPrivateDataInterface(\n          REFGUID                 Name,\n    const IUnknown*               pUnknown) {\n    return m_resource->SetPrivateDataInterface(Name, pUnknown);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetParent(\n          REFIID                  riid,\n          void**                  ppParent) {\n    return GetDevice(riid, ppParent);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetDevice(\n          REFIID                  riid,\n          void**                  ppDevice) {\n    Com<ID3D11Device> device;\n    m_resource->GetDevice(&device);\n    return device->QueryInterface(riid, ppDevice);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetDesc(\n          DXGI_SURFACE_DESC*      pDesc) {\n    auto buffer  = GetCommonBuffer (m_resource);\n    auto texture = GetCommonTexture(m_resource);\n\n    if (!pDesc)\n      return DXGI_ERROR_INVALID_CALL;\n\n    if (m_isSubresourceSurface) {\n      if (texture) {\n        auto desc = texture->Desc();\n\n        pDesc->Width      = std::max(1u, desc->Width >> (m_subresource % desc->MipLevels));\n        pDesc->Height     = std::max(1u, desc->Height >> (m_subresource % desc->MipLevels));\n        pDesc->Format     = desc->Format;\n        pDesc->SampleDesc = desc->SampleDesc;\n        return S_OK;\n      } else if (buffer) {\n        auto desc = buffer->Desc();\n        pDesc->Width              = desc->ByteWidth;\n        pDesc->Height             = 1;\n        pDesc->Format             = DXGI_FORMAT_UNKNOWN;\n        pDesc->SampleDesc.Count   = 1;\n        pDesc->SampleDesc.Quality = 0;\n        return S_OK;\n      } else {\n        return DXGI_ERROR_INVALID_CALL;\n      }\n    } else if (texture) {\n      auto desc = texture->Desc();\n      pDesc->Width      = desc->Width;\n      pDesc->Height     = desc->Height;\n      pDesc->Format     = desc->Format;\n      pDesc->SampleDesc = desc->SampleDesc;\n      return S_OK;\n    } else {\n      return DXGI_ERROR_INVALID_CALL;\n    }\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::Map(\n            DXGI_MAPPED_RECT*       pLockedRect,\n            UINT                    MapFlags) {\n    Com<ID3D11Device>        device;\n    Com<ID3D11DeviceContext> context;\n\n    m_resource->GetDevice(&device);\n    device->GetImmediateContext(&context);\n\n    if (pLockedRect) {\n      pLockedRect->Pitch = 0;\n      pLockedRect->pBits = nullptr;\n    }\n\n    D3D11_MAP mapType;\n\n    if (MapFlags & DXGI_MAP_READ && MapFlags & DXGI_MAP_WRITE)\n      mapType = D3D11_MAP_READ_WRITE;\n    else if (MapFlags & DXGI_MAP_READ)\n      mapType = D3D11_MAP_READ;\n    else if (MapFlags & DXGI_MAP_WRITE && MapFlags & DXGI_MAP_DISCARD)\n      mapType = D3D11_MAP_WRITE_DISCARD;\n    else if (MapFlags & DXGI_MAP_WRITE)\n      mapType = D3D11_MAP_WRITE;\n    else\n      return DXGI_ERROR_INVALID_CALL;\n    \n    D3D11_MAPPED_SUBRESOURCE sr;\n    HRESULT hr = context->Map(m_resource, m_subresource,\n      mapType, 0, pLockedRect ? &sr : nullptr);\n\n    if (hr != S_OK)\n      return hr;\n\n    pLockedRect->Pitch = sr.RowPitch;\n    pLockedRect->pBits = reinterpret_cast<unsigned char*>(sr.pData);\n    return hr;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::Unmap() {\n    Com<ID3D11Device>        device;\n    Com<ID3D11DeviceContext> context;\n\n    m_resource->GetDevice(&device);\n    device->GetImmediateContext(&context);\n    \n    context->Unmap(m_resource, m_subresource);\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetDC(\n          BOOL                    Discard,\n          HDC*                    phdc) {\n    if (!m_gdiSurface)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    return m_gdiSurface->Acquire(Discard, phdc);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::ReleaseDC(\n          RECT*                   pDirtyRect) {\n    if (!m_gdiSurface)\n      return DXGI_ERROR_INVALID_CALL;\n\n    return m_gdiSurface->Release(pDirtyRect);\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetResource(\n          REFIID                  riid,\n          void**                  ppParentResource,\n          UINT*                   pSubresourceIndex) {\n    HRESULT hr;\n\n    if (!ppParentResource)\n      return E_POINTER;\n\n    InitReturnPtr(ppParentResource);\n    hr = m_resource->QueryInterface(riid, ppParentResource);\n    if (SUCCEEDED(hr))\n      *pSubresourceIndex = m_subresource;\n\n    return hr;\n  }\n  \n  \n  bool D3D11DXGISurface::isSurfaceCompatible() const {\n    auto texture = GetCommonTexture(m_resource);\n\n    if (!texture)\n      return false;\n\n    auto desc = texture->Desc();\n\n    return desc->ArraySize == 1\n        && desc->MipLevels == 1;\n  }\n\n\n\n\n  D3D11VkInteropSurface::D3D11VkInteropSurface(\n          ID3D11Resource*     pResource,\n          D3D11CommonTexture* pTexture)\n  : m_resource(pResource),\n    m_texture (pTexture) {\n      \n  }\n  \n  \n  D3D11VkInteropSurface::~D3D11VkInteropSurface() {\n    \n  }\n  \n  \n  ULONG STDMETHODCALLTYPE D3D11VkInteropSurface::AddRef() {\n    return m_resource->AddRef();\n  }\n  \n  \n  ULONG STDMETHODCALLTYPE D3D11VkInteropSurface::Release() {\n    return m_resource->Release();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11VkInteropSurface::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_resource->QueryInterface(riid, ppvObject);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11VkInteropSurface::GetDevice(\n          IDXGIVkInteropDevice**  ppDevice) {\n    Com<ID3D11Device> device;\n    m_resource->GetDevice(&device);\n    \n    return device->QueryInterface(\n      __uuidof(IDXGIVkInteropDevice),\n      reinterpret_cast<void**>(ppDevice));\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11VkInteropSurface::GetVulkanImageInfo(\n          VkImage*              pHandle,\n          VkImageLayout*        pLayout,\n          VkImageCreateInfo*    pInfo) {\n    const Rc<DxvkImage> image = m_texture->GetImage();\n\n    if (!m_locked.load(std::memory_order_acquire)) {\n      // Need to make sure that the image cannot be relocated. This may\n      // be entered by multiple threads, which is fine since the actual\n      // work is serialized into the CS thread and only the first call\n      // will actually modify any image state.\n      Com<ID3D11Device> device;\n      m_resource->GetDevice(&device);\n\n      static_cast<D3D11Device*>(device.ptr())->LockImage(image, 0u);\n\n      m_locked.store(true, std::memory_order_release);\n    }\n\n    const DxvkImageCreateInfo& info = image->info();\n\n    if (pHandle != nullptr)\n      *pHandle = image->handle();\n\n    if (pLayout != nullptr)\n      *pLayout = info.layout;\n\n    if (pInfo != nullptr) {\n      // We currently don't support any extended structures\n      if (pInfo->sType != VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO\n       || pInfo->pNext != nullptr)\n        return E_INVALIDARG;\n\n      pInfo->flags          = 0;\n      pInfo->imageType      = info.type;\n      pInfo->format         = info.format;\n      pInfo->extent         = info.extent;\n      pInfo->mipLevels      = info.mipLevels;\n      pInfo->arrayLayers    = info.numLayers;\n      pInfo->samples        = info.sampleCount;\n      pInfo->tiling         = info.tiling;\n      pInfo->usage          = info.usage;\n      pInfo->sharingMode    = VK_SHARING_MODE_EXCLUSIVE;\n      pInfo->queueFamilyIndexCount = 0;\n      pInfo->initialLayout  = VK_IMAGE_LAYOUT_UNDEFINED;\n    }\n    \n    return S_OK;\n  }\n  \n  \n  ///////////////////////////////////////////\n  //      D 3 D 1 1 T E X T U R E 1 D\n  D3D11Texture1D::D3D11Texture1D(\n          D3D11Device*                pDevice,\n    const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n    const D3D11_ON_12_RESOURCE_INFO*  p11on12Info)\n  : D3D11DeviceChild<ID3D11Texture1D>(pDevice),\n    m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE1D, 0, VK_NULL_HANDLE, nullptr),\n    m_interop (this, &m_texture),\n    m_surface (this),\n    m_resource(this, pDevice),\n    m_d3d10   (this),\n    m_destructionNotifier(this) {\n    \n  }\n  \n  \n  D3D11Texture1D::~D3D11Texture1D() {\n    \n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Texture1D::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11Resource)\n     || riid == __uuidof(ID3D11Texture1D)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10Resource)\n     || riid == __uuidof(ID3D10Texture1D)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n    \n    if (m_surface.isSurfaceCompatible()\n     && (riid == __uuidof(IDXGISurface)\n      || riid == __uuidof(IDXGISurface1)\n      || riid == __uuidof(IDXGISurface2))) {\n      *ppvObject = ref(&m_surface);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(IDXGIObject)\n     || riid == __uuidof(IDXGIDeviceSubObject)\n     || riid == __uuidof(IDXGIResource)\n     || riid == __uuidof(IDXGIResource1)) {\n       *ppvObject = ref(&m_resource);\n       return S_OK;\n    }\n\n    if (riid == __uuidof(IDXGIKeyedMutex))\n      return m_resource.GetKeyedMutex(ppvObject);\n\n    if (riid == __uuidof(IDXGIVkInteropSurface)) {\n      *ppvObject = ref(&m_interop);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D10Texture1D), riid)) {\n      Logger::warn(\"D3D11Texture1D::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture1D::GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) {\n    *pResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE1D;\n  }\n  \n  \n  UINT STDMETHODCALLTYPE D3D11Texture1D::GetEvictionPriority() {\n    return DXGI_RESOURCE_PRIORITY_NORMAL;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture1D::SetEvictionPriority(UINT EvictionPriority) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11Texture1D::SetEvictionPriority: Stub\");\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture1D::GetDesc(D3D11_TEXTURE1D_DESC *pDesc) {\n    pDesc->Width          = m_texture.Desc()->Width;\n    pDesc->MipLevels      = m_texture.Desc()->MipLevels;\n    pDesc->ArraySize      = m_texture.Desc()->ArraySize;\n    pDesc->Format         = m_texture.Desc()->Format;\n    pDesc->Usage          = m_texture.Desc()->Usage;\n    pDesc->BindFlags      = m_texture.Desc()->BindFlags;\n    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;\n    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11Texture1D::SetDebugName(const char* pName) {\n    m_texture.SetDebugName(pName);\n  }\n\n\n  ///////////////////////////////////////////\n  //      D 3 D 1 1 T E X T U R E 2 D\n  D3D11Texture2D::D3D11Texture2D(\n          D3D11Device*                pDevice,\n    const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n    const D3D11_ON_12_RESOURCE_INFO*  p11on12Info,\n          HANDLE                      hSharedHandle)\n  : D3D11DeviceChild<ID3D11Texture2D1>(pDevice),\n    m_texture   (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE2D, 0, VK_NULL_HANDLE, hSharedHandle),\n    m_interop   (this, &m_texture),\n    m_surface   (this),\n    m_resource  (this, pDevice),\n    m_d3d10     (this),\n    m_swapChain (nullptr),\n    m_destructionNotifier(this) {\n  }\n\n\n  D3D11Texture2D::D3D11Texture2D(\n          D3D11Device*                pDevice,\n    const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n          DXGI_USAGE                  DxgiUsage,\n          VkImage                     vkImage)\n  : D3D11DeviceChild<ID3D11Texture2D1>(pDevice),\n    m_texture   (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, vkImage, nullptr),\n    m_interop   (this, &m_texture),\n    m_surface   (this),\n    m_resource  (this, pDevice),\n    m_d3d10     (this),\n    m_swapChain (nullptr),\n    m_destructionNotifier(this) {\n    \n  }\n\n\n  D3D11Texture2D::D3D11Texture2D(\n          D3D11Device*                pDevice,\n          IUnknown*                   pSwapChain,\n    const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n          DXGI_USAGE                  DxgiUsage)\n  : D3D11DeviceChild<ID3D11Texture2D1>(pDevice),\n    m_texture   (this, pDevice, pDesc, nullptr, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, VK_NULL_HANDLE, nullptr),\n    m_interop   (this, &m_texture),\n    m_surface   (this),\n    m_resource  (this, pDevice),\n    m_d3d10     (this),\n    m_swapChain (pSwapChain),\n    m_destructionNotifier(this) {\n    \n  }\n  \n  \n  D3D11Texture2D::~D3D11Texture2D() {\n    \n  }\n  \n  \n  ULONG STDMETHODCALLTYPE D3D11Texture2D::AddRef() {\n    uint32_t refCount = D3D11DeviceChild<ID3D11Texture2D1>::AddRef();\n\n    if (unlikely(m_swapChain != nullptr)) {\n      if (refCount == 1)\n        m_swapChain->AddRef();\n    }\n\n    return refCount;\n  }\n  \n\n  ULONG STDMETHODCALLTYPE D3D11Texture2D::Release() {\n    IUnknown* swapChain = m_swapChain;\n    uint32_t refCount = D3D11DeviceChild<ID3D11Texture2D1>::Release();\n\n    if (unlikely(swapChain != nullptr)) {\n      if (refCount == 0)\n        swapChain->Release();\n    }\n\n    return refCount;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11Texture2D::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11Resource)\n     || riid == __uuidof(ID3D11Texture2D)\n     || riid == __uuidof(ID3D11Texture2D1)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10Resource)\n     || riid == __uuidof(ID3D10Texture2D)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n\n    if (m_surface.isSurfaceCompatible()\n     && (riid == __uuidof(IDXGISurface)\n      || riid == __uuidof(IDXGISurface1)\n      || riid == __uuidof(IDXGISurface2))) {\n      *ppvObject = ref(&m_surface);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(IDXGIObject)\n     || riid == __uuidof(IDXGIDeviceSubObject)\n     || riid == __uuidof(IDXGIResource)\n     || riid == __uuidof(IDXGIResource1)) {\n       *ppvObject = ref(&m_resource);\n       return S_OK;\n    }\n\n    if (riid == __uuidof(IDXGIKeyedMutex))\n      return m_resource.GetKeyedMutex(ppvObject);\n    \n    if (riid == __uuidof(IDXGIVkInteropSurface)) {\n      *ppvObject = ref(&m_interop);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D10Texture2D), riid)) {\n      Logger::warn(\"D3D11Texture2D::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture2D::GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) {\n    *pResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D;\n  }\n  \n  \n  UINT STDMETHODCALLTYPE D3D11Texture2D::GetEvictionPriority() {\n    return DXGI_RESOURCE_PRIORITY_NORMAL;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture2D::SetEvictionPriority(UINT EvictionPriority) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11Texture2D::SetEvictionPriority: Stub\");\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture2D::GetDesc(D3D11_TEXTURE2D_DESC* pDesc) {\n    pDesc->Width          = m_texture.Desc()->Width;\n    pDesc->Height         = m_texture.Desc()->Height;\n    pDesc->MipLevels      = m_texture.Desc()->MipLevels;\n    pDesc->ArraySize      = m_texture.Desc()->ArraySize;\n    pDesc->Format         = m_texture.Desc()->Format;\n    pDesc->SampleDesc     = m_texture.Desc()->SampleDesc;\n    pDesc->Usage          = m_texture.Desc()->Usage;\n    pDesc->BindFlags      = m_texture.Desc()->BindFlags;\n    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;\n    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture2D::GetDesc1(D3D11_TEXTURE2D_DESC1* pDesc) {\n    pDesc->Width          = m_texture.Desc()->Width;\n    pDesc->Height         = m_texture.Desc()->Height;\n    pDesc->MipLevels      = m_texture.Desc()->MipLevels;\n    pDesc->ArraySize      = m_texture.Desc()->ArraySize;\n    pDesc->Format         = m_texture.Desc()->Format;\n    pDesc->SampleDesc     = m_texture.Desc()->SampleDesc;\n    pDesc->Usage          = m_texture.Desc()->Usage;\n    pDesc->BindFlags      = m_texture.Desc()->BindFlags;\n    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;\n    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;\n    pDesc->TextureLayout  = m_texture.Desc()->TextureLayout;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture2D::SetDebugName(const char* pName) {\n    m_texture.SetDebugName(pName);\n  }\n\n\n  ///////////////////////////////////////////\n  //      D 3 D 1 1 T E X T U R E 3 D\n  D3D11Texture3D::D3D11Texture3D(\n          D3D11Device*                pDevice,\n    const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n    const D3D11_ON_12_RESOURCE_INFO*  p11on12Info)\n  : D3D11DeviceChild<ID3D11Texture3D1>(pDevice),\n    m_texture (this, pDevice, pDesc, p11on12Info, D3D11_RESOURCE_DIMENSION_TEXTURE3D, 0, VK_NULL_HANDLE, nullptr),\n    m_interop (this, &m_texture),\n    m_resource(this, pDevice),\n    m_d3d10   (this),\n    m_destructionNotifier(this) {\n    \n  }\n  \n  \n  D3D11Texture3D::~D3D11Texture3D() {\n    \n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11Texture3D::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11Resource)\n     || riid == __uuidof(ID3D11Texture3D)\n     || riid == __uuidof(ID3D11Texture3D1)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10Resource)\n     || riid == __uuidof(ID3D10Texture3D)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(IDXGIObject)\n     || riid == __uuidof(IDXGIDeviceSubObject)\n     || riid == __uuidof(IDXGIResource)\n     || riid == __uuidof(IDXGIResource1)) {\n       *ppvObject = ref(&m_resource);\n       return S_OK;\n    }\n\n    if (riid == __uuidof(IDXGIKeyedMutex))\n      return m_resource.GetKeyedMutex(ppvObject);\n\n    if (riid == __uuidof(IDXGIVkInteropSurface)) {\n      *ppvObject = ref(&m_interop);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n    \n    if (logQueryInterfaceError(__uuidof(ID3D10Texture3D), riid)) {\n      Logger::warn(\"D3D11Texture3D::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture3D::GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) {\n    *pResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE3D;\n  }\n  \n  \n  UINT STDMETHODCALLTYPE D3D11Texture3D::GetEvictionPriority() {\n    return DXGI_RESOURCE_PRIORITY_NORMAL;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture3D::SetEvictionPriority(UINT EvictionPriority) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11Texture3D::SetEvictionPriority: Stub\");\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture3D::GetDesc(D3D11_TEXTURE3D_DESC* pDesc) {\n    pDesc->Width          = m_texture.Desc()->Width;\n    pDesc->Height         = m_texture.Desc()->Height;\n    pDesc->Depth          = m_texture.Desc()->Depth;\n    pDesc->MipLevels      = m_texture.Desc()->MipLevels;\n    pDesc->Format         = m_texture.Desc()->Format;\n    pDesc->Usage          = m_texture.Desc()->Usage;\n    pDesc->BindFlags      = m_texture.Desc()->BindFlags;\n    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;\n    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture3D::GetDesc1(D3D11_TEXTURE3D_DESC1* pDesc) {\n    pDesc->Width          = m_texture.Desc()->Width;\n    pDesc->Height         = m_texture.Desc()->Height;\n    pDesc->Depth          = m_texture.Desc()->Depth;\n    pDesc->MipLevels      = m_texture.Desc()->MipLevels;\n    pDesc->Format         = m_texture.Desc()->Format;\n    pDesc->Usage          = m_texture.Desc()->Usage;\n    pDesc->BindFlags      = m_texture.Desc()->BindFlags;\n    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;\n    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11Texture3D::SetDebugName(const char* pName) {\n    m_texture.SetDebugName(pName);\n  }\n\n\n  D3D11CommonTexture* GetCommonTexture(ID3D11Resource* pResource) {\n    D3D11_RESOURCE_DIMENSION dimension = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&dimension);\n    \n    switch (dimension) {\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D:\n        return static_cast<D3D11Texture1D*>(pResource)->GetCommonTexture();\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D:\n        return static_cast<D3D11Texture2D*>(pResource)->GetCommonTexture();\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D:\n        return static_cast<D3D11Texture3D*>(pResource)->GetCommonTexture();\n      \n      default:\n        return nullptr;\n    }\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_texture.h",
    "content": "#pragma once\n\n#include \"../util/util_small_vector.h\"\n\n#include \"../dxvk/dxvk_cs.h\"\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../d3d10/d3d10_texture.h\"\n\n#include \"d3d11_device_child.h\"\n#include \"d3d11_interfaces.h\"\n#include \"d3d11_on_12.h\"\n#include \"d3d11_resource.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  class D3D11GDISurface;\n  \n  /**\n   * \\brief Image memory mapping mode\n   * \n   * Determines how exactly \\c Map will\n   * behave when mapping an image.\n   */\n  enum D3D11_COMMON_TEXTURE_MAP_MODE {\n    D3D11_COMMON_TEXTURE_MAP_MODE_NONE,     ///< Not mapped\n    D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER,   ///< Mapped through buffer\n    D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC,  ///< Mapped through temporary buffer\n    D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT,   ///< Directly mapped to host mem\n    D3D11_COMMON_TEXTURE_MAP_MODE_STAGING,  ///< Buffer only, no image\n  };\n  \n  \n  /**\n   * \\brief Common texture description\n   * \n   * Contains all members that can be\n   * defined for 1D, 2D and 3D textures.\n   */\n  struct D3D11_COMMON_TEXTURE_DESC {\n    UINT             Width;\n    UINT             Height;\n    UINT             Depth;\n    UINT             MipLevels;\n    UINT             ArraySize;\n    DXGI_FORMAT      Format;\n    DXGI_SAMPLE_DESC SampleDesc;\n    D3D11_USAGE      Usage;\n    UINT             BindFlags;\n    UINT             CPUAccessFlags;\n    UINT             MiscFlags;\n    D3D11_TEXTURE_LAYOUT TextureLayout;\n  };\n\n\n  /**\n   * \\brief Packed subresource layout\n   */\n  struct D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT {\n    UINT64          Offset;\n    UINT64          Size;\n    UINT            RowPitch;\n    UINT            DepthPitch;\n  };\n  \n  \n  /**\n   * \\brief Region\n   */\n  struct D3D11_COMMON_TEXTURE_REGION {\n    VkOffset3D      Offset;\n    VkExtent3D      Extent;\n  };\n  \n  \n  /**\n   * \\brief D3D11 common texture object\n   * \n   * This class implements common texture methods and\n   * aims to work around the issue that there are three\n   * different interfaces for basically the same thing.\n   */\n  class D3D11CommonTexture {\n    \n  public:\n\n    static constexpr uint32_t UnmappedSubresource = ~0u;\n\n    D3D11CommonTexture(\n            ID3D11Resource*             pInterface,\n            D3D11Device*                pDevice,\n      const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n      const D3D11_ON_12_RESOURCE_INFO*  p11on12Info,\n            D3D11_RESOURCE_DIMENSION    Dimension,\n            DXGI_USAGE                  DxgiUsage,\n            VkImage                     vkImage,\n            HANDLE                      hSharedHandle);\n    \n    ~D3D11CommonTexture();\n    \n    /**\n     * \\brief Retrieves resource interface\n     * \\returns Resource interface\n     */\n    ID3D11Resource* GetInterface() const {\n      return m_interface;\n    }\n\n    /**\n     * \\brief Texture properties\n     * \n     * The returned data can be used to fill in\n     * \\c D3D11_TEXTURE2D_DESC and similar structs.\n     * \\returns Pointer to texture description\n     */\n    const D3D11_COMMON_TEXTURE_DESC* Desc() const {\n      return &m_desc;\n    }\n\n    /**\n     * \\brief Retrieves D3D11 texture type\n     * \\returns D3D11 resource dimension\n     */\n    D3D11_RESOURCE_DIMENSION GetDimension() const {\n      return m_dimension;\n    }\n\n    /**\n     * \\brief Retrieves Vulkan image type\n     *\n     * Returns the image type based on the D3D11 resource\n     * dimension. Also works if there is no actual image.\n     * \\returns Vulkan image type\n     */\n    VkImageType GetVkImageType() const {\n      return GetImageTypeFromResourceDim(m_dimension);\n    }\n\n    /**\n     * \\brief Computes extent of a given mip level\n     *\n     * This also works for staging resources that have no image.\n     * \\param [in] Level Mip level to compute the size of\n     */\n    VkExtent3D MipLevelExtent(uint32_t Level) const {\n      return util::computeMipLevelExtent(\n        VkExtent3D { m_desc.Width, m_desc.Height, m_desc.Depth }, Level);\n    }\n\n    /**\n     * \\brief Special DXGI usage flags\n     *\n     * Flags that are set in addition to the bind\n     * flags. Zero for application-created textures.\n     * \\returns DXGI usage flags\n     */\n    DXGI_USAGE GetDxgiUsage() const {\n      return m_dxgiUsage;\n    }\n\n    /**\n     * \\brief Counts number of subresources\n     * \\returns Number of subresources\n     */\n    UINT CountSubresources() const {\n      return m_desc.ArraySize * m_desc.MipLevels;\n    }\n    \n    /**\n     * \\brief Map mode\n     * \\returns Map mode\n     */\n    D3D11_COMMON_TEXTURE_MAP_MODE GetMapMode() const {\n      return m_mapMode;\n    }\n\n    /**\n     * \\brief Checks whether this texture has an image\n     *\n     * Staging textures will not use an image, only mapped buffers.\n     * \\returns \\c true for non-staging textures.\n     */\n    bool HasImage() const {\n      return m_mapMode != D3D11_COMMON_TEXTURE_MAP_MODE_STAGING;\n    }\n\n    /**\n     * \\brief Checks whether this texture has persistent buffers\n     * \\returns \\c true for buffer-mapped textures or staging textures.\n     */\n    bool HasPersistentBuffers() const {\n      return m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER\n          || m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING;\n    }\n\n    /**\n     * \\brief Map type of a given subresource\n     * \n     * \\param [in] Subresource Subresource index\n     * \\returns Current map mode of that subresource\n     */\n    uint32_t GetMapType(UINT Subresource) const {\n      return Subresource < m_mapInfo.size()\n        ? m_mapInfo[Subresource].mapType\n        : UnmappedSubresource;\n    }\n\n    /**\n     * \\brief Sets map type for a given subresource\n     *\n     * Also ensures taht a staging buffer is created\n     * in case of dynamic mapping.\n     * \\param [in] Subresource The subresource\n     * \\param [in] MapType The map type\n     */\n    void NotifyMap(UINT Subresource, D3D11_MAP MapType) {\n      if (likely(Subresource < m_mapInfo.size())) {\n        m_mapInfo[Subresource].mapType = uint32_t(MapType);\n\n        if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC)\n          CreateMappedBuffer(Subresource);\n      }\n    }\n\n    /**\n     * \\brief Resets map info for a given subresource\n     *\n     * For dynamic mapping, this will also free the\n     * staging buffer.\n     * \\param [in] Subresource The subresource\n     */\n    void NotifyUnmap(UINT Subresource) {\n      if (likely(Subresource < m_mapInfo.size())) {\n        m_mapInfo[Subresource].mapType = UnmappedSubresource;\n\n        if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC)\n          FreeMappedBuffer(Subresource);\n\n        if (Subresource < m_buffers.size())\n          m_buffers[Subresource].dirtyRegions.clear();\n      }\n    }\n\n    /**\n     * \\brief The DXVK image\n     * \\returns The DXVK image\n     */\n    Rc<DxvkImage> GetImage() const {\n      return m_image;\n    }\n    \n    /**\n     * \\brief Mapped subresource buffer\n     * \n     * \\param [in] Subresource Subresource index\n     * \\returns Mapped subresource buffer\n     */\n    Rc<DxvkBuffer> GetMappedBuffer(UINT Subresource) const {\n      return Subresource < m_buffers.size()\n        ? m_buffers[Subresource].buffer\n        : Rc<DxvkBuffer>();\n    }\n\n    /**\n     * \\brief Discards mapped buffer slice for a given subresource\n     *\n     * \\param [in] Subresource Subresource to discard\n     * \\returns Newly allocated mapped buffer slice\n     */\n    Rc<DxvkResourceAllocation> DiscardSlice(UINT Subresource) {\n      if (Subresource < m_buffers.size()) {\n        Rc<DxvkResourceAllocation> slice = m_buffers[Subresource].buffer->allocateStorage();\n        m_buffers[Subresource].slice = slice;\n        return slice;\n      } else {\n        return nullptr;\n      }\n    }\n\n    /**\n     * \\brief Retrieves mapped buffer slice for a given subresource\n     *\n     * \\param [in] Subresource Subresource index to query\n     * \\returns Currently mapped buffer slice\n     */\n    Rc<DxvkResourceAllocation> GetMappedSlice(UINT Subresource) const {\n      return Subresource < m_buffers.size()\n        ? m_buffers[Subresource].slice\n        : nullptr;\n    }\n\n    /**\n     * \\brief Returns underlying packed Vulkan format\n     *\n     * This works even for staging resources that have no image.\n     * Note that for depth-stencil resources, the returned format\n     * may be different from the image format on some systems.\n     * \\returns Packed Vulkan format\n     */\n    VkFormat GetPackedFormat() const {\n      return m_packedFormat;\n    }\n    \n    /**\n     * \\brief Checks whether the resource is eligible for tracking\n     *\n     * Mapped resources with no bind flags can be tracked so that\n     * mapping them will not necessarily cause a CS thread sync.\n     * \\returns \\c true if tracking is supported for this resource\n     */\n    bool HasSequenceNumber() const {\n      return m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING;\n    }\n\n    /**\n     * \\brief Tracks sequence number for a given subresource\n     *\n     * Stores which CS chunk the resource was last used on.\n     * \\param [in] Subresource Subresource index\n     * \\param [in] Seq Sequence number\n     */\n    void TrackSequenceNumber(UINT Subresource, uint64_t Seq) {\n      if (Subresource < m_mapInfo.size())\n        m_mapInfo[Subresource].seq = Seq;\n    }\n\n    /**\n     * \\brief Queries sequence number for a given subresource\n     *\n     * Returns which CS chunk the resource was last used on.\n     * \\param [in] Subresource Subresource index\n     * \\returns Sequence number for the given subresource\n     */\n    uint64_t GetSequenceNumber(UINT Subresource) {\n      if (HasSequenceNumber()) {\n        return Subresource < m_buffers.size()\n          ? m_mapInfo[Subresource].seq\n          : 0ull;\n      } else {\n        return DxvkCsThread::SynchronizeAll;\n      }\n    }\n\n    /**\n     * \\brief Allocates new backing storage\n     * \\returns New backing storage for the image\n     */\n    Rc<DxvkResourceAllocation> AllocStorage() {\n      return m_image->allocateStorage();\n    }\n\n    /**\n     * \\brief Discards backing storage\n     *\n     * Also updates the mapped pointer if the image is mapped.\n     * \\returns New backing storage for the image\n     */\n    Rc<DxvkResourceAllocation> DiscardStorage() {\n      auto storage = m_image->allocateStorage();\n      m_mapPtr = storage->mapPtr();\n      return storage;\n    }\n\n    /**\n     * \\brief Queries map pointer of the raw image\n     *\n     * If the image is mapped directly, the returned pointer will\n     * point directly to the image, otherwise it will point to a\n     * buffer that contains image data.\n     * \\param [in] Subresource Subresource index\n     * \\param [in] Offset Offset derived from the subresource layout\n     */\n    void* GetMapPtr(uint32_t Subresource, size_t Offset) const {\n      switch (m_mapMode) {\n        case D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT:\n          return reinterpret_cast<char*>(m_mapPtr) + Offset;\n\n        case D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER:\n        case D3D11_COMMON_TEXTURE_MAP_MODE_DYNAMIC:\n        case D3D11_COMMON_TEXTURE_MAP_MODE_STAGING:\n          return reinterpret_cast<char*>(m_buffers[Subresource].slice->mapPtr()) + Offset;\n\n        case D3D11_COMMON_TEXTURE_MAP_MODE_NONE:\n          return nullptr;\n      }\n\n      return nullptr;\n    }\n\n    /**\n     * \\brief Adds a dirty region\n     *\n     * This region will be updated on Unmap.\n     * \\param [in] Subresource Subresource index\n     * \\param [in] Offset Region offset\n     * \\param [in] Extent Region extent\n     */\n    void AddDirtyRegion(UINT Subresource, VkOffset3D Offset, VkExtent3D Extent) {\n      D3D11_COMMON_TEXTURE_REGION region;\n      region.Offset = Offset;\n      region.Extent = Extent;\n\n      if (Subresource < m_buffers.size())\n        m_buffers[Subresource].dirtyRegions.push_back(region);\n    }\n\n    /**\n     * \\brief Counts dirty regions\n     *\n     * \\param [in] Subresource Subresource index\n     * \\returns Dirty region count\n     */\n    UINT GetDirtyRegionCount(UINT Subresource) {\n      return (Subresource < m_buffers.size())\n        ? UINT(m_buffers[Subresource].dirtyRegions.size())\n        : UINT(0);\n    }\n\n    /**\n     * \\brief Queries a dirty regions\n     *\n     * \\param [in] Subresource Subresource index\n     * \\param [in] Region Region index\n     * \\returns Dirty region\n     */\n    D3D11_COMMON_TEXTURE_REGION GetDirtyRegion(UINT Subresource, UINT Region) {\n      return m_buffers[Subresource].dirtyRegions[Region];\n    }\n\n    /**\n     * \\brief Checks whether or not to track dirty regions\n     *\n     * If this returns true, then any functions that update the\n     * mapped staging buffer must also track dirty regions while\n     * the image is mapped. Otherwise, the entire image is dirty.\n     * \\returns \\c true if dirty regions must be tracked\n     */\n    bool NeedsDirtyRegionTracking() const {\n      // Only set this for images where Map can't return a pointer\n      return m_mapMode            == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER\n          && m_desc.Usage         == D3D11_USAGE_DEFAULT\n          && m_desc.TextureLayout == D3D11_TEXTURE_LAYOUT_UNDEFINED;\n    }\n\n    /**\n     * \\brief Computes pixel offset into mapped buffer\n     *\n     * \\param [in] Subresource Subresource index\n     * \\param [in] Subresource Plane index\n     * \\param [in] Offset Pixel coordinate to compute offset for\n     * \\returns Offset into mapped subresource buffer, in pixels\n     */\n    VkDeviceSize ComputeMappedOffset(UINT Subresource, UINT Plane, VkOffset3D Offset) const;\n    \n    /**\n     * \\brief Computes subresource from the subresource index\n     * \n     * Used by some functions that operate on only\n     * one subresource, such as \\c UpdateSubresource.\n     * \\param [in] Aspect The image aspect\n     * \\param [in] Subresource Subresource index\n     * \\returns The Vulkan image subresource\n     */\n    VkImageSubresource GetSubresourceFromIndex(\n            VkImageAspectFlags    Aspect,\n            UINT                  Subresource) const {\n      VkImageSubresource result;\n      result.aspectMask     = Aspect;\n      result.mipLevel       = Subresource % m_desc.MipLevels;\n      result.arrayLayer     = Subresource / m_desc.MipLevels;\n      return result;\n    }\n\n    /**\n     * \\brief Computes subresource layout for the given subresource\n     *\n     * \\param [in] AspectMask The image aspect\n     * \\param [in] Subresource Subresource index\n     * \\returns Memory layout of the mapped subresource\n     */\n    D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT GetSubresourceLayout(\n            VkImageAspectFlags    AspectMask,\n            UINT                  Subresource) const;\n\n    /**\n     * \\brief Format mode\n     * \n     * Determines whether the image is going to\n     * be used as a color image or a depth image.\n     * \\returns Format mode\n     */\n    DXGI_VK_FORMAT_MODE GetFormatMode() const;\n\n    /**\n     * \\brief Computes plane count\n     *\n     * For non-planar formats, the plane count will be 1.\n     * \\returns Image plane count\n     */\n    uint32_t GetPlaneCount() const;\n\n    /**\n     * \\brief Checks whether a view can be created for this textue\n     * \n     * View formats are only compatible if they are either identical\n     * or from the same family of typeless formats, where the resource\n     * format must be typeless and the view format must be typed. This\n     * will also check whether the required bind flags are supported.\n     * \\param [in] BindFlags Bind flags for the view\n     * \\param [in] Format The desired view format\n     * \\param [in] Plane Plane slice for planar formats\n     * \\returns \\c true if the format is compatible\n     */\n    bool CheckViewCompatibility(\n            UINT                BindFlags,\n            DXGI_FORMAT         Format,\n            UINT                Plane) const;\n    \n    /**\n     * \\brief Retrieves D3D11on12 resource info\n     * \\returns 11on12 resource info\n     */\n    D3D11_ON_12_RESOURCE_INFO Get11on12Info() const {\n      return m_11on12;\n    }\n\n    /**\n     * \\brief Sets debug name for texture\n     *\n     * Passes the given name to the backing image or buffer.\n     * \\param [in] name Debug name\n     */\n    void SetDebugName(const char* pName);\n\n    /**\n     * \\brief Normalizes and validates texture description\n     * \n     * Fills in undefined values and validates the texture\n     * parameters. Any error returned by this method should\n     * be forwarded to the application.\n     * \\param [in,out] pDesc Texture description\n     * \\returns \\c S_OK if the parameters are valid\n     */\n    static HRESULT NormalizeTextureProperties(\n            D3D11_COMMON_TEXTURE_DESC* pDesc);\n    \n    /**\n     * \\brief Initializes D3D11 texture description from D3D12\n     *\n     * \\param [in] pResource D3D12 resource\n     * \\param [in] pResourceFlags D3D11 flag overrides\n     * \\param [out] pTextureDesc D3D11 buffer description\n     * \\returns \\c S_OK if the parameters are valid\n     */\n    static HRESULT GetDescFromD3D12(\n            ID3D12Resource*         pResource,\n      const D3D11_RESOURCE_FLAGS*   pResourceFlags,\n            D3D11_COMMON_TEXTURE_DESC* pTextureDesc);\n\n  private:\n    \n    struct MappedBuffer {\n      Rc<DxvkBuffer>              buffer;\n      Rc<DxvkResourceAllocation>  slice;\n\n      std::vector<D3D11_COMMON_TEXTURE_REGION> dirtyRegions;\n    };\n\n    struct MappedInfo {\n      D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT layout = { };\n      uint32_t                    mapType = UnmappedSubresource;\n      uint64_t                    seq     = 0u;\n    };\n\n    ID3D11Resource*               m_interface;\n    D3D11Device*                  m_device;\n    D3D11_RESOURCE_DIMENSION      m_dimension;\n    D3D11_COMMON_TEXTURE_DESC     m_desc;\n    D3D11_ON_12_RESOURCE_INFO     m_11on12;\n    D3D11_COMMON_TEXTURE_MAP_MODE m_mapMode;\n    DXGI_USAGE                    m_dxgiUsage;\n    VkFormat                      m_packedFormat;\n    \n    Rc<DxvkImage>                 m_image;\n    small_vector<MappedBuffer, 6> m_buffers;\n    small_vector<MappedInfo, 6>   m_mapInfo;\n\n    void*                         m_mapPtr = nullptr;\n\n    void CreateMappedBuffer(\n            UINT                  Subresource);\n    \n    void FreeMappedBuffer(\n            UINT                  Subresource);\n    \n    BOOL CheckImageSupport(\n      const DxvkImageCreateInfo*  pImageInfo,\n            VkImageTiling         Tiling) const;\n    \n    BOOL CheckFormatFeatureSupport(\n            VkFormat              Format,\n            VkFormatFeatureFlags2 Features) const;\n    \n    std::pair<D3D11_COMMON_TEXTURE_MAP_MODE, VkMemoryPropertyFlags> DetermineMapMode(\n      const D3D11Device*          device,\n      const DxvkImageCreateInfo*  pImageInfo) const;\n\n    D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT DetermineSubresourceLayout(\n      const DxvkImageCreateInfo*  pImageInfo,\n      const VkImageSubresource&   subresource) const;\n\n    void ExportImageInfo();\n\n    static BOOL IsR32UavCompatibleFormat(\n            DXGI_FORMAT           Format);\n\n    static VkImageType GetImageTypeFromResourceDim(\n            D3D11_RESOURCE_DIMENSION  Dimension);\n    \n    static VkImageLayout OptimizeLayout(\n            VkImageUsageFlags         Usage);\n  };\n\n\n  /**\n   * \\brief IDXGISurface implementation for D3D11 textures\n   *\n   * Provides an implementation for 2D textures that\n   * have only one array layer and one mip level.\n   */\n  class D3D11DXGISurface : public IDXGISurface2 {\n\n  public:\n\n    D3D11DXGISurface(\n            ID3D11Resource*     pResource);\n\n    D3D11DXGISurface(\n            ID3D11Resource*     pParentResource,\n            UINT                Subresource);\n    \n    ~D3D11DXGISurface();\n    \n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                 Name,\n            UINT*                   pDataSize,\n            void*                   pData);\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                 Name,\n            UINT                    DataSize,\n      const void*                   pData);\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                 Name,\n      const IUnknown*               pUnknown);\n    \n    HRESULT STDMETHODCALLTYPE GetParent(\n            REFIID                  riid,\n            void**                  ppParent);\n    \n    HRESULT STDMETHODCALLTYPE GetDevice(\n            REFIID                  riid,\n            void**                  ppDevice);\n    \n    HRESULT STDMETHODCALLTYPE GetDesc(\n            DXGI_SURFACE_DESC*      pDesc);\n    \n    HRESULT STDMETHODCALLTYPE Map(\n            DXGI_MAPPED_RECT*       pLockedRect,\n            UINT                    MapFlags);\n    \n    HRESULT STDMETHODCALLTYPE Unmap();\n\n    HRESULT STDMETHODCALLTYPE GetDC(\n            BOOL                    Discard,\n            HDC*                    phdc);\n\n    HRESULT STDMETHODCALLTYPE ReleaseDC(\n            RECT*                   pDirtyRect);\n    \n    HRESULT STDMETHODCALLTYPE GetResource(\n            REFIID                  riid,\n            void**                  ppParentResource,\n            UINT*                   pSubresourceIndex);\n    \n    bool isSurfaceCompatible() const;\n\n  private:\n    \n    bool                m_isSubresourceSurface = false;\n    UINT                m_subresource = 0;\n    ID3D11Resource*     m_resource;\n    D3D11GDISurface*    m_gdiSurface;\n\n  };\n  \n  \n  /**\n   * \\brief Common texture interop class\n   * \n   * Provides access to the underlying Vulkan\n   * texture to external Vulkan libraries.\n   */\n  class D3D11VkInteropSurface : public IDXGIVkInteropSurface {\n    \n  public:\n    \n    D3D11VkInteropSurface(\n            ID3D11Resource*     pResource,\n            D3D11CommonTexture* pTexture);\n    \n    ~D3D11VkInteropSurface();\n    \n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n    \n    HRESULT STDMETHODCALLTYPE GetDevice(\n            IDXGIVkInteropDevice**  ppDevice);\n    \n    HRESULT STDMETHODCALLTYPE GetVulkanImageInfo(\n            VkImage*              pHandle,\n            VkImageLayout*        pLayout,\n            VkImageCreateInfo*    pInfo);\n    \n  private:\n    \n    ID3D11Resource*     m_resource;\n    D3D11CommonTexture* m_texture;\n\n    std::atomic<bool>   m_locked = { false };\n    \n  };\n  \n  \n  ///////////////////////////////////////////\n  //      D 3 D 1 1 T E X T U R E 1 D\n  class D3D11Texture1D : public D3D11DeviceChild<ID3D11Texture1D> {\n    \n  public:\n    \n    D3D11Texture1D(\n            D3D11Device*                pDevice,\n      const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n      const D3D11_ON_12_RESOURCE_INFO*  p11on12Info);\n    \n    ~D3D11Texture1D();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetType(\n            D3D11_RESOURCE_DIMENSION *pResourceDimension) final;\n    \n    UINT STDMETHODCALLTYPE GetEvictionPriority() final;\n    \n    void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final;\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_TEXTURE1D_DESC *pDesc) final;\n    \n    void STDMETHODCALLTYPE SetDebugName(const char* pName) final;\n\n    D3D11CommonTexture* GetCommonTexture() {\n      return &m_texture;\n    }\n\n    D3D10Texture1D* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n    \n  private:\n    \n    D3D11CommonTexture    m_texture;\n    D3D11VkInteropSurface m_interop;\n    D3D11DXGISurface      m_surface;\n    D3D11DXGIResource     m_resource;\n    D3D10Texture1D        m_d3d10;\n\n    D3DDestructionNotifier m_destructionNotifier;\n\n  };\n  \n  \n  ///////////////////////////////////////////\n  //      D 3 D 1 1 T E X T U R E 2 D\n  class D3D11Texture2D : public D3D11DeviceChild<ID3D11Texture2D1> {\n    \n  public:\n    \n    D3D11Texture2D(\n            D3D11Device*                pDevice,\n      const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n      const D3D11_ON_12_RESOURCE_INFO*  p11on12Info,\n            HANDLE                      hSharedHandle);\n\n    D3D11Texture2D(\n            D3D11Device*                pDevice,\n      const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n            DXGI_USAGE                  DxgiUsage,\n            VkImage                     vkImage);\n    \n    D3D11Texture2D(\n            D3D11Device*                pDevice,\n            IUnknown*                   pSwapChain,\n      const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n            DXGI_USAGE                  DxgiUsage);\n    \n    ~D3D11Texture2D();\n    \n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetType(\n            D3D11_RESOURCE_DIMENSION *pResourceDimension) final;\n    \n    UINT STDMETHODCALLTYPE GetEvictionPriority() final;\n    \n    void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final;\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_TEXTURE2D_DESC* pDesc) final;\n    \n    void STDMETHODCALLTYPE GetDesc1(\n            D3D11_TEXTURE2D_DESC1* pDesc) final;\n    \n    void STDMETHODCALLTYPE SetDebugName(const char* pName) final;\n\n    D3D11CommonTexture* GetCommonTexture() {\n      return &m_texture;\n    }\n    \n    D3D10Texture2D* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n\n  private:\n    \n    D3D11CommonTexture    m_texture;\n    D3D11VkInteropSurface m_interop;\n    D3D11DXGISurface      m_surface;\n    D3D11DXGIResource     m_resource;\n    D3D10Texture2D        m_d3d10;\n    IUnknown*             m_swapChain = nullptr;\n\n    D3DDestructionNotifier m_destructionNotifier;\n\n  };\n  \n  \n  ///////////////////////////////////////////\n  //      D 3 D 1 1 T E X T U R E 3 D\n  class D3D11Texture3D : public D3D11DeviceChild<ID3D11Texture3D1> {\n    \n  public:\n    \n    D3D11Texture3D(\n            D3D11Device*                pDevice,\n      const D3D11_COMMON_TEXTURE_DESC*  pDesc,\n      const D3D11_ON_12_RESOURCE_INFO*  p11on12Info);\n    \n    ~D3D11Texture3D();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void**  ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetType(\n            D3D11_RESOURCE_DIMENSION *pResourceDimension) final;\n    \n    UINT STDMETHODCALLTYPE GetEvictionPriority() final;\n    \n    void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final;\n    \n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_TEXTURE3D_DESC* pDesc) final;\n    \n    void STDMETHODCALLTYPE GetDesc1(\n            D3D11_TEXTURE3D_DESC1* pDesc) final;\n    \n    void STDMETHODCALLTYPE SetDebugName(const char* pName) final;\n\n    D3D11CommonTexture* GetCommonTexture() {\n      return &m_texture;\n    }\n    \n    D3D10Texture3D* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n\n  private:\n    \n    D3D11CommonTexture    m_texture;\n    D3D11VkInteropSurface m_interop;\n    D3D11DXGIResource     m_resource;\n    D3D10Texture3D        m_d3d10;\n\n    D3DDestructionNotifier m_destructionNotifier;\n    \n  };\n  \n  \n  /**\n   * \\brief Retrieves texture from resource pointer\n   * \n   * \\param [in] pResource The resource to query\n   * \\returns Pointer to texture info, or \\c nullptr\n   */\n  D3D11CommonTexture* GetCommonTexture(\n          ID3D11Resource*       pResource);\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_util.cpp",
    "content": "#include \"d3d11_util.h\"\n\nnamespace dxvk {\n  \n  HRESULT DecodeSampleCount(UINT Count, VkSampleCountFlagBits* pCount) {\n    VkSampleCountFlagBits flag;\n    \n    switch (Count) {\n      case  1: flag = VK_SAMPLE_COUNT_1_BIT;  break;\n      case  2: flag = VK_SAMPLE_COUNT_2_BIT;  break;\n      case  4: flag = VK_SAMPLE_COUNT_4_BIT;  break;\n      case  8: flag = VK_SAMPLE_COUNT_8_BIT;  break;\n      case 16: flag = VK_SAMPLE_COUNT_16_BIT; break;\n      default: return E_INVALIDARG;\n    }\n    \n    if (pCount != nullptr) {\n      *pCount = flag;\n      return S_OK;\n    } return S_FALSE;\n  }\n  \n  \n  VkSamplerAddressMode DecodeAddressMode(\n          D3D11_TEXTURE_ADDRESS_MODE  mode) {\n    switch (mode) {\n      case D3D11_TEXTURE_ADDRESS_WRAP:\n        return VK_SAMPLER_ADDRESS_MODE_REPEAT;\n        \n      case D3D11_TEXTURE_ADDRESS_MIRROR:\n        return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;\n      \n      case D3D11_TEXTURE_ADDRESS_CLAMP:\n        return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n        \n      case D3D11_TEXTURE_ADDRESS_BORDER:\n        return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;\n        \n      case D3D11_TEXTURE_ADDRESS_MIRROR_ONCE:\n        return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;\n      \n      default:\n        Logger::err(str::format(\"D3D11: Unsupported address mode: \", mode));\n        return VK_SAMPLER_ADDRESS_MODE_REPEAT;\n    }\n  }\n  \n  \n  VkCompareOp DecodeCompareOp(D3D11_COMPARISON_FUNC Mode) {\n    switch (Mode) {\n      case D3D11_COMPARISON_NEVER:          return VK_COMPARE_OP_NEVER;\n      case D3D11_COMPARISON_LESS:           return VK_COMPARE_OP_LESS;\n      case D3D11_COMPARISON_EQUAL:          return VK_COMPARE_OP_EQUAL;\n      case D3D11_COMPARISON_LESS_EQUAL:     return VK_COMPARE_OP_LESS_OR_EQUAL;\n      case D3D11_COMPARISON_GREATER:        return VK_COMPARE_OP_GREATER;\n      case D3D11_COMPARISON_NOT_EQUAL:      return VK_COMPARE_OP_NOT_EQUAL;\n      case D3D11_COMPARISON_GREATER_EQUAL:  return VK_COMPARE_OP_GREATER_OR_EQUAL;\n      case D3D11_COMPARISON_ALWAYS:         return VK_COMPARE_OP_ALWAYS;\n    }\n    \n    if (Mode != 0)  // prevent log spamming when apps use ZeroMemory\n      Logger::err(str::format(\"D3D11: Unsupported compare op: \", Mode));\n    return VK_COMPARE_OP_NEVER;\n  }\n  \n  \n  VkSamplerReductionMode DecodeReductionMode(\n          UINT                      Filter) {\n    switch (Filter & 0x180) {\n      default:\n        return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;\n      case 0x100:\n        return VK_SAMPLER_REDUCTION_MODE_MIN;\n      case 0x180:\n        return VK_SAMPLER_REDUCTION_MODE_MAX;\n    }\n  }\n\n\n  VkConservativeRasterizationModeEXT DecodeConservativeRasterizationMode(\n          D3D11_CONSERVATIVE_RASTERIZATION_MODE Mode) {\n    switch (Mode) {\n      case D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF:\n        return VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;\n      case D3D11_CONSERVATIVE_RASTERIZATION_MODE_ON:\n        return VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT;\n    }\n\n    Logger::err(str::format(\"D3D11: Unsupported conservative raster mode: \", Mode));\n    return VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;\n  }\n\n\n  VkFormatFeatureFlags2 GetBufferFormatFeatures(UINT BindFlags) {\n    VkFormatFeatureFlags2 features = 0;\n\n    if (BindFlags & D3D11_BIND_SHADER_RESOURCE)\n      features |= VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT;\n    if (BindFlags & D3D11_BIND_UNORDERED_ACCESS)\n      features |= VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT;\n    \n    return features;\n  }\n\n\n  VkFormatFeatureFlags2 GetImageFormatFeatures(UINT BindFlags) {\n    VkFormatFeatureFlags2 features = 0;\n\n    if (BindFlags & D3D11_BIND_DEPTH_STENCIL)\n      features |= VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT;\n    if (BindFlags & D3D11_BIND_RENDER_TARGET)\n      features |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT;\n    if (BindFlags & D3D11_BIND_SHADER_RESOURCE)\n      features |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT;\n    if (BindFlags & D3D11_BIND_UNORDERED_ACCESS)\n      features |= VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT;\n    \n    return features;\n  }\n\n\n  VkFormat GetPackedDepthStencilFormat(DXGI_FORMAT Format) {\n    switch (Format) {\n      case DXGI_FORMAT_R24G8_TYPELESS:\n      case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:\n      case DXGI_FORMAT_X24_TYPELESS_G8_UINT:\n      case DXGI_FORMAT_D24_UNORM_S8_UINT:\n        return VK_FORMAT_D24_UNORM_S8_UINT;\n      \n      case DXGI_FORMAT_R32G8X24_TYPELESS:\n      case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:\n      case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:\n      case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:\n        return VK_FORMAT_D32_SFLOAT_S8_UINT;\n      \n      default:\n        return VK_FORMAT_UNDEFINED;\n    }\n  }\n\n\n  BOOL IsMinMaxFilter(D3D11_FILTER Filter) {\n    return DecodeReductionMode(uint32_t(Filter)) != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;\n  }\n\n}"
  },
  {
    "path": "src/d3d11/d3d11_util.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"d3d11_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Shader binding mask\n   *\n   * Stores a bit masks of resource bindings\n   * that are accessed by any given shader.\n   */\n  struct D3D11BindingMask {\n    uint32_t cbvMask      = 0u;\n    uint32_t samplerMask  = 0u;\n    uint64_t uavMask      = 0u;\n    std::array<uint64_t, 2> srvMask = { };\n\n    void reset() {\n      cbvMask = 0u;\n      samplerMask = 0u;\n      uavMask = 0u;\n      srvMask = { };\n    }\n\n    bool empty() const {\n      uint64_t mask = (uint64_t(cbvMask) | uint64_t(samplerMask) << 32u)\n                    | (uavMask | srvMask[0] | srvMask[1]);\n      return !mask;\n    }\n\n    void setCbv(uint32_t index) {\n      cbvMask |= 1u << index;\n    }\n\n    void setSampler(uint32_t index) {\n      samplerMask |= 1u << index;\n    }\n\n    void setUav(uint32_t index) {\n      uavMask |= uint64_t(1u) << index;\n    }\n\n    void setSrv(uint32_t index) {\n      uint32_t mask = index / 64u;\n      uint32_t shift = index % 64u;\n\n      srvMask[mask] |= uint64_t(1u) << shift;\n    }\n\n    D3D11BindingMask operator & (const D3D11BindingMask& other) const {\n      D3D11BindingMask result = *this;\n      result.cbvMask &= other.cbvMask;\n      result.samplerMask &= other.samplerMask;\n      result.uavMask &= other.uavMask;\n      result.srvMask[0] &= other.srvMask[0];\n      result.srvMask[1] &= other.srvMask[1];\n      return result;\n    }\n  };\n\n\n  template<typename T>\n  UINT CompactSparseList(T* pData, UINT Mask) {\n    uint32_t count = 0;\n    \n    for (uint32_t id : bit::BitMask(Mask))\n      pData[count++] = pData[id];\n\n    return count;\n  }\n\n  HRESULT DecodeSampleCount(\n          UINT                      Count,\n          VkSampleCountFlagBits*    pCount);\n  \n  VkSamplerAddressMode DecodeAddressMode(\n          D3D11_TEXTURE_ADDRESS_MODE  mode);\n\n  VkCompareOp DecodeCompareOp(\n          D3D11_COMPARISON_FUNC     Mode);\n\n  VkSamplerReductionMode DecodeReductionMode(\n          UINT                      Filter);\n\n  VkConservativeRasterizationModeEXT DecodeConservativeRasterizationMode(\n          D3D11_CONSERVATIVE_RASTERIZATION_MODE Mode);\n\n  VkFormatFeatureFlags2 GetBufferFormatFeatures(\n          UINT                      BindFlags);\n\n  VkFormatFeatureFlags2 GetImageFormatFeatures(\n          UINT                      BindFlags);\n  \n  VkFormat GetPackedDepthStencilFormat(\n          DXGI_FORMAT               Format);\n\n  BOOL IsMinMaxFilter(D3D11_FILTER Filter);\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_video.cpp",
    "content": "#include <algorithm>\n\n#include \"d3d11_context_imm.h\"\n#include \"d3d11_video.h\"\n\n#include <d3d11_video_blit_frag.h>\n#include <d3d11_video_blit_vert.h>\n\n#include \"../dxvk/dxvk_shader_spirv.h\"\n\nnamespace dxvk {\n\n  D3D11VideoProcessorEnumerator::D3D11VideoProcessorEnumerator(\n          D3D11Device*            pDevice,\n    const D3D11_VIDEO_PROCESSOR_CONTENT_DESC& Desc)\n  : D3D11DeviceChild<ID3D11VideoProcessorEnumerator>(pDevice),\n    m_desc(Desc), m_destructionNotifier(this) {\n\n  }\n\n\n  D3D11VideoProcessorEnumerator::~D3D11VideoProcessorEnumerator() {\n\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11VideoProcessorEnumerator)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11VideoProcessorEnumerator), riid)) {\n      Logger::warn(\"D3D11VideoProcessorEnumerator::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorContentDesc(\n          D3D11_VIDEO_PROCESSOR_CONTENT_DESC* pContentDesc) {\n    *pContentDesc = m_desc;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::CheckVideoProcessorFormat(\n          DXGI_FORMAT             Format,\n          UINT*                   pFlags) {\n    Logger::warn(str::format(\"D3D11VideoProcessorEnumerator::CheckVideoProcessorFormat: stub, format \", Format));\n\n    if (!pFlags)\n      return E_INVALIDARG;\n\n    *pFlags = D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT | D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorCaps(\n          D3D11_VIDEO_PROCESSOR_CAPS* pCaps) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoProcessorEnumerator::GetVideoProcessorCaps: semi-stub\");\n\n    if (!pCaps)\n      return E_INVALIDARG;\n\n    *pCaps = {};\n    pCaps->RateConversionCapsCount = 1;\n    pCaps->MaxInputStreams = 52;\n    pCaps->MaxStreamStates = 52;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorRateConversionCaps(\n          UINT                    TypeIndex,\n          D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS* pCaps) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoProcessorEnumerator::GetVideoProcessorRateConversionCaps: semi-stub\");\n\n    if (!pCaps || TypeIndex)\n      return E_INVALIDARG;\n\n    *pCaps = {};\n    if (m_desc.InputFrameFormat == D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE) {\n      pCaps->ProcessorCaps = D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_FRAME_RATE_CONVERSION;\n    } else {\n      pCaps->ProcessorCaps = D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB;\n      pCaps->PastFrames = 1;\n      pCaps->FutureFrames = 1;\n    }\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorCustomRate(\n          UINT                    TypeIndex,\n          UINT                    CustomRateIndex,\n          D3D11_VIDEO_PROCESSOR_CUSTOM_RATE* pRate) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoProcessorEnumerator::GetVideoProcessorCustomRate: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorFilterRange(\n          D3D11_VIDEO_PROCESSOR_FILTER        Filter,\n          D3D11_VIDEO_PROCESSOR_FILTER_RANGE* pRange) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoProcessorEnumerator::GetVideoProcessorFilterRange: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n\n\n  D3D11VideoProcessor::D3D11VideoProcessor(\n          D3D11Device*                    pDevice,\n          D3D11VideoProcessorEnumerator*  pEnumerator,\n          UINT                            RateConversionIndex)\n  : D3D11DeviceChild<ID3D11VideoProcessor>(pDevice),\n    m_enumerator(pEnumerator), m_rateConversionIndex(RateConversionIndex),\n    m_destructionNotifier(this) {\n\n  }\n\n\n  D3D11VideoProcessor::~D3D11VideoProcessor() {\n\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessor::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11VideoProcessor)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11VideoProcessor), riid)) {\n      Logger::warn(\"D3D11VideoProcessor::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoProcessor::GetContentDesc(\n          D3D11_VIDEO_PROCESSOR_CONTENT_DESC *pDesc) {\n    m_enumerator->GetVideoProcessorContentDesc(pDesc);\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoProcessor::GetRateConversionCaps(\n          D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS *pCaps) {\n    m_enumerator->GetVideoProcessorRateConversionCaps(m_rateConversionIndex, pCaps);\n  }\n\n\n\n\n  D3D11VideoProcessorView::D3D11VideoProcessorView(\n          D3D11Device*            pDevice,\n          ID3D11Resource*         pResource,\n          DxvkImageViewKey        viewInfo)\n  : m_resource(pResource), m_image(GetCommonTexture(pResource)->GetImage()) {\n    D3D11_COMMON_RESOURCE_DESC resourceDesc = { };\n    GetCommonResourceDesc(pResource, &resourceDesc);\n\n    DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat(resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR);\n    DXGI_VK_FORMAT_FAMILY formatFamily = pDevice->LookupFamily(resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR);\n\n    VkImageAspectFlags aspectMask = lookupFormatInfo(formatInfo.Format)->aspectMask;\n\n    viewInfo.format = formatInfo.Format;\n    viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle);\n    viewInfo.aspects = aspectMask;\n\n    m_layers.aspectMask = aspectMask;\n    m_layers.baseArrayLayer = viewInfo.layerIndex;\n    m_layers.layerCount = viewInfo.layerCount;\n    m_layers.mipLevel = viewInfo.mipIndex;\n\n    // Create shadow image if we know that the base image is incompatible\n    // with the required usage flags and cannot be relocated.\n    if (m_image->info().shared && (m_image->info().usage & viewInfo.usage) != viewInfo.usage) {\n      DxvkImageCreateInfo imageInfo = { };\n      imageInfo.type = m_image->info().type;\n      imageInfo.format = viewInfo.format;\n      imageInfo.sampleCount = m_image->info().sampleCount;\n      imageInfo.extent = m_image->mipLevelExtent(viewInfo.mipIndex);\n      imageInfo.numLayers = viewInfo.layerCount;\n      imageInfo.mipLevels = viewInfo.mipCount;\n      imageInfo.usage = viewInfo.usage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n      imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;\n      imageInfo.access = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;\n      imageInfo.layout = VK_IMAGE_LAYOUT_GENERAL;\n      imageInfo.debugName = \"Video shadow image\";\n\n      if (viewInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {\n        imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;\n        imageInfo.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n      }\n\n      if (viewInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) {\n        imageInfo.stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n        imageInfo.access |= VK_ACCESS_SHADER_READ_BIT;\n\n        if (imageInfo.layout != VK_IMAGE_LAYOUT_GENERAL)\n          imageInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n      }\n\n      if (viewInfo.aspects != VK_IMAGE_ASPECT_COLOR_BIT) {\n        imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT\n                        |  VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;\n      }\n\n      m_shadow = pDevice->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      viewInfo.layerIndex = 0u;\n      viewInfo.mipIndex = 0u;\n    }\n\n    if (viewInfo.usage == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)\n      viewInfo.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\n    for (uint32_t i = 0; aspectMask && i < m_views.size(); i++) {\n      viewInfo.aspects = vk::getNextAspect(aspectMask);\n\n      if (viewInfo.aspects != VK_IMAGE_ASPECT_COLOR_BIT)\n        viewInfo.format = formatFamily.Formats[i];\n\n      m_views[i] = (m_shadow ? m_shadow : m_image)->createView(viewInfo);\n    }\n\n    m_isYCbCr = IsYCbCrFormat(resourceDesc.Format);\n  }\n\n\n  D3D11VideoProcessorView::~D3D11VideoProcessorView() {\n\n  }\n\n\n  bool D3D11VideoProcessorView::IsYCbCrFormat(DXGI_FORMAT Format) {\n    static const std::array<DXGI_FORMAT, 3> s_formats = {{\n      DXGI_FORMAT_NV12,\n      DXGI_FORMAT_YUY2,\n      DXGI_FORMAT_AYUV,\n    }};\n\n    return std::find(s_formats.begin(), s_formats.end(), Format) != s_formats.end();\n  }\n\n\n\n  D3D11VideoProcessorInputView::D3D11VideoProcessorInputView(\n          D3D11Device*            pDevice,\n          ID3D11Resource*         pResource,\n    const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC& Desc)\n  : D3D11DeviceChild<ID3D11VideoProcessorInputView>(pDevice),\n    m_common(pDevice, pResource, CreateViewInfo(Desc)),\n    m_desc(Desc), m_destructionNotifier(this) {\n\n  }\n\n\n  D3D11VideoProcessorInputView::~D3D11VideoProcessorInputView() {\n\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorInputView::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11View)\n     || riid == __uuidof(ID3D11VideoProcessorInputView)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11VideoProcessorInputView), riid)) {\n      Logger::warn(\"D3D11VideoProcessorInputView::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoProcessorInputView::GetResource(\n          ID3D11Resource**        ppResource) {\n    *ppResource = m_common.GetResource();\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoProcessorInputView::GetDesc(\n          D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* pDesc) {\n    *pDesc = m_desc;\n  }\n\n\n  DxvkImageViewKey D3D11VideoProcessorInputView::CreateViewInfo(\n    const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC& Desc) {\n    DxvkImageViewKey viewInfo = { };\n    viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n\n    switch (Desc.ViewDimension) {\n      case D3D11_VPIV_DIMENSION_TEXTURE2D:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n        viewInfo.mipIndex   = Desc.Texture2D.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = Desc.Texture2D.ArraySlice;\n        viewInfo.layerCount = 1;\n        break;\n\n      case D3D11_VPIV_DIMENSION_UNKNOWN:\n        throw DxvkError(\"Invalid view dimension\");\n    }\n\n    return viewInfo;\n  }\n\n\n  D3D11VideoProcessorOutputView::D3D11VideoProcessorOutputView(\n          D3D11Device*            pDevice,\n          ID3D11Resource*         pResource,\n    const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC& Desc)\n  : D3D11DeviceChild<ID3D11VideoProcessorOutputView>(pDevice),\n    m_common(pDevice, pResource, CreateViewInfo(Desc)),\n    m_desc(Desc), m_destructionNotifier(this) {\n\n  }\n\n\n  D3D11VideoProcessorOutputView::~D3D11VideoProcessorOutputView() {\n\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorOutputView::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11View)\n     || riid == __uuidof(ID3D11VideoProcessorOutputView)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11VideoProcessorOutputView), riid)) {\n      Logger::warn(\"D3D11VideoProcessorOutputView::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoProcessorOutputView::GetResource(\n          ID3D11Resource**        ppResource) {\n    *ppResource = m_common.GetResource();\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoProcessorOutputView::GetDesc(\n          D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* pDesc) {\n    *pDesc = m_desc;\n  }\n\n\n  DxvkImageViewKey D3D11VideoProcessorOutputView::CreateViewInfo(\n    const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC& Desc) {\n    DxvkImageViewKey viewInfo = { };\n    viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\n    switch (Desc.ViewDimension) {\n      case D3D11_VPOV_DIMENSION_TEXTURE2D:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n        viewInfo.mipIndex   = Desc.Texture2D.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = 0;\n        viewInfo.layerCount = 1;\n        break;\n\n      case D3D11_VPOV_DIMENSION_TEXTURE2DARRAY:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n        viewInfo.mipIndex   = Desc.Texture2DArray.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = Desc.Texture2DArray.FirstArraySlice;\n        viewInfo.layerCount = Desc.Texture2DArray.ArraySize;\n        break;\n\n      case D3D11_VPOV_DIMENSION_UNKNOWN:\n        throw DxvkError(\"Invalid view dimension\");\n    }\n\n    return viewInfo;\n  }\n\n\n\n\n  D3D11VideoContext::D3D11VideoContext(\n          D3D11ImmediateContext*  pContext,\n    const Rc<DxvkDevice>&         Device)\n  : m_ctx(pContext), m_device(Device) {\n\n  }\n\n\n  D3D11VideoContext::~D3D11VideoContext() {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11VideoContext::AddRef() {\n    return m_ctx->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D11VideoContext::Release() {\n    return m_ctx->Release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_ctx->QueryInterface(riid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::GetPrivateData(\n          REFGUID                 Name,\n          UINT*                   pDataSize,\n          void*                   pData) {\n    return m_ctx->GetPrivateData(Name, pDataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::SetPrivateData(\n          REFGUID                 Name,\n          UINT                    DataSize,\n    const void*                   pData)  {\n    return m_ctx->SetPrivateData(Name, DataSize, pData);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::SetPrivateDataInterface(\n          REFGUID                 Name,\n    const IUnknown*               pUnknown) {\n    return m_ctx->SetPrivateDataInterface(Name, pUnknown);\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::GetDevice(\n          ID3D11Device**          ppDevice) {\n    return m_ctx->GetDevice(ppDevice);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::GetDecoderBuffer(\n          ID3D11VideoDecoder*             pDecoder,\n          D3D11_VIDEO_DECODER_BUFFER_TYPE Type,\n          UINT*                           BufferSize,\n          void**                          ppBuffer) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoContext::GetDecoderBuffer: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::ReleaseDecoderBuffer(\n          ID3D11VideoDecoder*             pDecoder,\n          D3D11_VIDEO_DECODER_BUFFER_TYPE Type) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoContext::ReleaseDecoderBuffer: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::DecoderBeginFrame(\n          ID3D11VideoDecoder*             pDecoder,\n          ID3D11VideoDecoderOutputView*   pView,\n          UINT                            KeySize,\n    const void*                           pKey) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoContext::DecoderBeginFrame: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::DecoderEndFrame(\n          ID3D11VideoDecoder*             pDecoder) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoContext::DecoderEndFrame: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::SubmitDecoderBuffers(\n          ID3D11VideoDecoder*             pDecoder,\n          UINT                            BufferCount,\n    const D3D11_VIDEO_DECODER_BUFFER_DESC* pBufferDescs) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoContext::SubmitDecoderBuffers: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::DecoderExtension(\n          ID3D11VideoDecoder*             pDecoder,\n    const D3D11_VIDEO_DECODER_EXTENSION*  pExtension) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D11VideoContext::DecoderExtension: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputTargetRect(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          BOOL                            Enable,\n    const RECT*                           pRect) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetOutputTargetRect: Stub.\");\n\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();\n    state->outputTargetRectEnabled = Enable;\n\n    if (Enable)\n      state->outputTargetRect = *pRect;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputBackgroundColor(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          BOOL                            YCbCr,\n    const D3D11_VIDEO_COLOR*              pColor) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetOutputBackgroundColor: Stub\");\n\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();\n    state->outputBackgroundColorIsYCbCr = YCbCr;\n    state->outputBackgroundColor = *pColor;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputColorSpace(\n          ID3D11VideoProcessor*           pVideoProcessor,\n    const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();\n    state->outputColorSpace = *pColorSpace;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputAlphaFillMode(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE AlphaFillMode,\n          UINT                            StreamIndex) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetOutputAlphaFillMode: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputConstriction(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          BOOL                            Enable,\n          SIZE                            Size) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetOutputConstriction: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputStereoMode(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          BOOL                            Enable) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();\n    state->outputStereoModeEnabled = Enable;\n\n    if (Enable)\n      Logger::err(\"D3D11VideoContext: Stereo output not supported\");\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputExtension(\n          ID3D11VideoProcessor*           pVideoProcessor,\n    const GUID*                           pExtensionGuid,\n          UINT                            DataSize,\n          void*                           pData) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetOutputExtension: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamFrameFormat(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          D3D11_VIDEO_FRAME_FORMAT        Format) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    state->frameFormat = Format;\n\n    if (Format != D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE)\n      Logger::err(str::format(\"D3D11VideoContext: Unsupported frame format: \", Format));\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamColorSpace(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n    const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    state->colorSpace = *pColorSpace;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamOutputRate(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          D3D11_VIDEO_PROCESSOR_OUTPUT_RATE Rate,\n          BOOL                            Repeat,\n    const DXGI_RATIONAL*                  CustomRate) {\n    Logger::warn(str::format(\"D3D11VideoContext::VideoProcessorSetStreamOutputRate: Stub, Rate \", Rate));\n    if (CustomRate)\n      Logger::warn(str::format(\"CustomRate \", CustomRate->Numerator, \"/\", CustomRate->Denominator));\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamSourceRect(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL                            Enable,\n    const RECT*                           pRect) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetStreamSourceRect: Stub.\");\n\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    state->srcRectEnabled = Enable;\n\n    if (Enable)\n      state->srcRect = *pRect;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamDestRect(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL                            Enable,\n    const RECT*                           pRect) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    state->dstRectEnabled = Enable;\n\n    if (Enable)\n      state->dstRect = *pRect;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamAlpha(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL                            Enable,\n          FLOAT                           Alpha) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetStreamAlpha: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamPalette(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          UINT                            EntryCount,\n    const UINT*                           pEntries) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetStreamPalette: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamPixelAspectRatio(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL                            Enable,\n    const DXGI_RATIONAL*                  pSrcAspectRatio,\n    const DXGI_RATIONAL*                  pDstAspectRatio) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetStreamPixelAspectRatio: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamLumaKey(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL                            Enable,\n          FLOAT                           Lower,\n          FLOAT                           Upper) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetStreamLumaKey: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamStereoFormat(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL                            Enable,\n          D3D11_VIDEO_PROCESSOR_STEREO_FORMAT Format,\n          BOOL                            LeftViewFrame0,\n          BOOL                            BaseViewFrame0,\n          D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE FlipMode,\n          int                             MonoOffset) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetStreamStereoFormat: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamAutoProcessingMode(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL                            Enable) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    state->autoProcessingEnabled = Enable;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamFilter(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          D3D11_VIDEO_PROCESSOR_FILTER    Filter,\n          BOOL                            Enable,\n          int                             Level) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetStreamFilter: Stub\");\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamExtension(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n    const GUID*                           pExtensionGuid,\n          UINT                            DataSize,\n          void*                           pData) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorSetStreamExtension: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamRotation(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL                            Enable,\n          D3D11_VIDEO_PROCESSOR_ROTATION  Rotation) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    state->rotationEnabled = Enable;\n    state->rotation = Rotation;\n\n    if (Enable && Rotation != D3D11_VIDEO_PROCESSOR_ROTATION_IDENTITY)\n      Logger::err(str::format(\"D3D11VideoContext: Unsupported rotation: \", Rotation));\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputTargetRect(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          BOOL*                           pEnabled,\n          RECT*                           pRect) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();\n\n    if (pEnabled)\n      *pEnabled = state->outputTargetRectEnabled;\n\n    if (pRect)\n      *pRect = state->outputTargetRect;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputBackgroundColor(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          BOOL*                           pYCbCr,\n          D3D11_VIDEO_COLOR*              pColor) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();\n    \n    if (pYCbCr)\n      *pYCbCr = state->outputBackgroundColorIsYCbCr;\n\n    if (pColor)\n      *pColor = state->outputBackgroundColor;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputColorSpace(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();\n\n    if (pColorSpace)\n      *pColorSpace = state->outputColorSpace;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputAlphaFillMode(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE* pAlphaFillMode,\n          UINT*                           pStreamIndex) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetOutputAlphaFillMode: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputConstriction(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          BOOL*                           pEnabled,\n          SIZE*                           pSize) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetOutputConstriction: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputStereoMode(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          BOOL*                           pEnabled) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();\n\n    if (pEnabled)\n      *pEnabled = state->outputStereoModeEnabled;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputExtension(\n          ID3D11VideoProcessor*           pVideoProcessor,\n    const GUID*                           pExtensionGuid,\n          UINT                            DataSize,\n          void*                           pData) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetOutputExtension: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamFrameFormat(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          D3D11_VIDEO_FRAME_FORMAT*       pFormat) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    if (pFormat)\n      *pFormat = state->frameFormat;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamColorSpace(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    if (pColorSpace)\n      *pColorSpace = state->colorSpace;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamOutputRate(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          D3D11_VIDEO_PROCESSOR_OUTPUT_RATE* pRate,\n          BOOL*                           pRepeat,\n          DXGI_RATIONAL*                  pCustomRate) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetStreamOutputRate: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamSourceRect(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL*                           pEnabled,\n          RECT*                           pRect) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    if (pEnabled)\n      *pEnabled = state->srcRectEnabled;\n\n    if (pRect)\n      *pRect = state->srcRect;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamDestRect(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL*                           pEnabled,\n          RECT*                           pRect) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    if (pEnabled)\n      *pEnabled = state->dstRectEnabled;\n\n    if (pRect)\n      *pRect = state->dstRect;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamAlpha(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL*                           pEnabled,\n          FLOAT*                          pAlpha) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetStreamAlpha: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamPalette(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          UINT                            EntryCount,\n          UINT*                           pEntries) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetStreamPalette: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamPixelAspectRatio(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL*                           pEnabled,\n          DXGI_RATIONAL*                  pSrcAspectRatio,\n          DXGI_RATIONAL*                  pDstAspectRatio) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetStreamPixelAspectRatio: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamLumaKey(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL*                           pEnabled,\n          FLOAT*                          pLower,\n          FLOAT*                          pUpper) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetStreamLumaKey: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamStereoFormat(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL*                           pEnabled,\n          D3D11_VIDEO_PROCESSOR_STEREO_FORMAT* pFormat,\n          BOOL*                           pLeftViewFrame0,\n          BOOL*                           pBaseViewFrame0,\n          D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE* pFlipMode,\n          int*                            pMonoOffset) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetStreamStereoFormat: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamAutoProcessingMode(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL*                           pEnabled) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    *pEnabled = state->autoProcessingEnabled;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamFilter(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          D3D11_VIDEO_PROCESSOR_FILTER    Filter,\n          BOOL*                           pEnabled,\n          int*                            pLevel) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetStreamFilter: Stub\");\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamExtension(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n    const GUID*                           pExtensionGuid,\n          UINT                            DataSize,\n          void*                           pData) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      Logger::warn(\"D3D11VideoContext::VideoProcessorGetStreamExtension: Stub\");\n\n    return E_NOTIMPL;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamRotation(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          UINT                            StreamIndex,\n          BOOL*                           pEnable,\n          D3D11_VIDEO_PROCESSOR_ROTATION* pRotation) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);\n\n    if (!state)\n      return;\n\n    if (pEnable)\n      *pEnable = state->rotationEnabled;\n\n    if (pRotation)\n      *pRotation = state->rotation;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorBlt(\n          ID3D11VideoProcessor*           pVideoProcessor,\n          ID3D11VideoProcessorOutputView* pOutputView,\n          UINT                            FrameIdx,\n          UINT                            StreamCount,\n    const D3D11_VIDEO_PROCESSOR_STREAM*   pStreams) {\n    D3D10DeviceLock lock = m_ctx->LockContext();\n\n    m_ctx->EmitCs([] (DxvkContext* ctx) {\n      ctx->beginDebugLabel(vk::makeLabel(0x59eaff, \"Video blit\"));\n    });\n\n    auto videoProcessor = static_cast<D3D11VideoProcessor*>(pVideoProcessor);\n\n    auto& outputView = static_cast<D3D11VideoProcessorOutputView*>(pOutputView)->GetCommon();\n    auto views = outputView.GetViews();\n\n    bool hasStreamsEnabled = false;\n\n    m_dstIsYCbCr = outputView.IsYCbCr();\n\n    for (uint32_t vi = 0; vi < views.size(); vi++) {\n      if (!views[vi])\n        continue;\n\n      bool outputBound = false;\n\n      // Resetting and restoring all context state incurs\n      // a lot of overhead, so only do it as necessary\n      for (uint32_t i = 0; i < StreamCount; i++) {\n        auto streamState = videoProcessor->GetStreamState(i);\n\n        if (!pStreams[i].Enable || !streamState)\n          continue;\n\n        if (!hasStreamsEnabled) {\n          m_ctx->ResetDirtyTracking();\n          m_ctx->ResetCommandListState();\n\n          CopyBaseImageToShadow(outputView);\n\n          hasStreamsEnabled = true;\n        }\n\n        if (!outputBound) {\n          BindOutputView(views[vi], views[0]);\n          outputBound = true;\n        }\n\n        if (!views[1])\n          m_exportMode = ExportRGBA;\n        else if (!vi)\n          m_exportMode = ExportY;\n        else\n          m_exportMode = ExportCbCr;\n\n        BlitStream(streamState, &pStreams[i]);\n      }\n    }\n\n    if (hasStreamsEnabled) {\n      CopyShadowToBaseImage(outputView);\n\n      UnbindResources();\n\n      m_ctx->RestoreCommandListState();\n    }\n\n    m_ctx->EmitCs([] (DxvkContext* ctx) {\n      ctx->endDebugLabel();\n    });\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::NegotiateCryptoSessionKeyExchange(\n          ID3D11CryptoSession*            pSession,\n          UINT                            DataSize,\n          void*                           pData) {\n    Logger::warn(\"D3D11VideoContext::NegotiateCryptoSessionKeyExchange: Stub\");\n    return E_NOTIMPL;\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::EncryptionBlt(\n          ID3D11CryptoSession*            pSession,\n          ID3D11Texture2D*                pSrcSurface,\n          ID3D11Texture2D*                pDstSurface,\n          UINT                            IVSize,\n          void*                           pIV) {\n    Logger::warn(\"D3D11VideoContext::EncryptionBlt: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::DecryptionBlt(\n          ID3D11CryptoSession*            pSession,\n          ID3D11Texture2D*                pSrcSurface,\n          ID3D11Texture2D*                pDstSurface,\n          D3D11_ENCRYPTED_BLOCK_INFO*     pBlockInfo,\n          UINT                            KeySize,\n    const void*                           pKey,\n          UINT                            IVSize,\n          void*                           pIV) {\n    Logger::warn(\"D3D11VideoContext::DecryptionBlt: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::StartSessionKeyRefresh(\n          ID3D11CryptoSession*            pSession,\n          UINT                            RandomNumberSize,\n          void*                           pRandomNumber) {\n    Logger::warn(\"D3D11VideoContext::StartSessionKeyRefresh: Stub\");\n  }\n\n\n  void STDMETHODCALLTYPE D3D11VideoContext::FinishSessionKeyRefresh(\n          ID3D11CryptoSession*            pSession) {\n    Logger::warn(\"D3D11VideoContext::FinishSessionKeyRefresh: Stub\");\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::GetEncryptionBltKey(\n          ID3D11CryptoSession*            pSession,\n          UINT                            KeySize,\n          void*                           pKey) {\n    Logger::warn(\"D3D11VideoContext::GetEncryptionBltKey: Stub\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::NegotiateAuthenticatedChannelKeyExchange(\n          ID3D11AuthenticatedChannel*     pChannel,\n          UINT                            DataSize,\n          void*                           pData) {\n    Logger::warn(\"D3D11VideoContext::NegotiateAuthenticatedChannelKeyExchange: Stub\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::QueryAuthenticatedChannel(\n          ID3D11AuthenticatedChannel*     pChannel,\n          UINT                            InputSize,\n    const void*                           pInput,\n          UINT                            OutputSize,\n          void*                           pOutput) {\n    Logger::warn(\"D3D11VideoContext::QueryAuthenticatedChannel: Stub\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D11VideoContext::ConfigureAuthenticatedChannel(\n          ID3D11AuthenticatedChannel*     pChannel,\n          UINT                            InputSize,\n    const void*                           pInput,\n          D3D11_AUTHENTICATED_CONFIGURE_OUTPUT* pOutput) {\n    Logger::warn(\"D3D11VideoContext::ConfigureAuthenticatedChannel: Stub\");\n    return E_NOTIMPL;\n  }\n\n\n  void D3D11VideoContext::ApplyColorMatrix(float pDst[3][4], const float pSrc[3][4]) {\n    float result[3][4];\n\n    for (uint32_t i = 0; i < 3; i++) {\n      for (uint32_t j = 0; j < 4; j++) {\n        result[i][j] = pSrc[i][0] * pDst[0][j]\n                     + pSrc[i][1] * pDst[1][j]\n                     + pSrc[i][2] * pDst[2][j]\n                     + pSrc[i][3] * float(j == 3);\n      }\n    }\n\n    memcpy(pDst, &result[0][0], sizeof(result));\n  }\n\n\n  void D3D11VideoContext::ApplyYCbCrMatrix(float pColorMatrix[3][4], bool UseBt709) {\n    static const float pretransform[3][4] = {\n      { 0.0f, 1.0f, 0.0f,  0.0f },\n      { 0.0f, 0.0f, 1.0f, -0.5f },\n      { 1.0f, 0.0f, 0.0f, -0.5f },\n    };\n\n    static const float bt601[3][4] = {\n      { 1.0f,  0.000000f,  1.402000f, 0.0f },\n      { 1.0f, -0.344136f, -0.714136f, 0.0f },\n      { 1.0f,  1.772000f,  0.000000f, 0.0f },\n    };\n\n    static const float bt709[3][4] = {\n      { 1.0f,  0.000000f,  1.574800f, 0.0f },\n      { 1.0f, -0.187324f, -0.468124f, 0.0f },\n      { 1.0f,  1.855600f,  0.000000f, 0.0f },\n    };\n\n    ApplyColorMatrix(pColorMatrix, pretransform);\n    ApplyColorMatrix(pColorMatrix, UseBt709 ? bt709 : bt601);\n  }\n\n\n  void D3D11VideoContext::BindOutputView(\n          Rc<DxvkImageView>               View,\n          Rc<DxvkImageView>               FirstView) {\n    VkExtent3D viewExtent = View->mipLevelExtent(0);\n    m_dstExtent = { viewExtent.width, viewExtent.height };\n\n    VkExtent3D firstExtent = FirstView->mipLevelExtent(0);\n    m_dstSizeFact[0] = (float) viewExtent.width  / (float) firstExtent.width;\n    m_dstSizeFact[1] = (float) viewExtent.height / (float) firstExtent.height;\n\n    m_ctx->EmitCs([\n      cView   = std::move(View)\n    ] (DxvkContext* ctx) {\n      DxvkImageUsageInfo usage = { };\n      usage.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n      usage.stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n      usage.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\n      ctx->ensureImageCompatibility(cView->image(), usage);\n\n      DxvkRenderTargets rt;\n      rt.color[0].view = cView;\n\n      ctx->bindRenderTargets(std::move(rt), 0u);\n\n      DxvkInputAssemblyState iaState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false);\n      ctx->setInputAssemblyState(iaState);\n    });\n  }\n\n\n  void D3D11VideoContext::BlitStream(\n    const D3D11VideoProcessorStreamState* pStreamState,\n    const D3D11_VIDEO_PROCESSOR_STREAM*   pStream) {\n    CreateResources();\n\n    if (pStream->PastFrames || pStream->FutureFrames)\n      Logger::err(\"D3D11VideoContext: Ignoring non-zero PastFrames and FutureFrames\");\n\n    if (pStream->OutputIndex)\n      Logger::err(\"D3D11VideoContext: Ignoring non-zero OutputIndex\");\n\n    if (pStream->InputFrameOrField)\n      Logger::err(\"D3D11VideoContext: Ignoring non-zero InputFrameOrField\");\n\n    auto& view = static_cast<D3D11VideoProcessorInputView*>(pStream->pInputSurface)->GetCommon();\n\n    CopyBaseImageToShadow(view);\n\n    m_ctx->EmitCs([this,\n      cStreamState  = *pStreamState,\n      cImage        = view.GetImage(),\n      cViews        = view.GetViews(),\n      cSrcIsYCbCr   = view.IsYCbCr(),\n      cDstIsYCbCr   = m_dstIsYCbCr,\n      cDstExtent    = m_dstExtent,\n      cDstSizeFactX = m_dstSizeFact[0],\n      cDstSizeFactY = m_dstSizeFact[1],\n      cExportMode   = m_exportMode\n    ] (DxvkContext* ctx) {\n      DxvkImageUsageInfo usage = { };\n      usage.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n      usage.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n      usage.access = VK_ACCESS_SHADER_READ_BIT;\n\n      ctx->ensureImageCompatibility(cImage, usage);\n\n      VkViewport viewport;\n      viewport.x        = 0.0f;\n      viewport.y        = 0.0f;\n      viewport.width    = float(cDstExtent.width);\n      viewport.height   = float(cDstExtent.height);\n      viewport.minDepth = 0.0f;\n      viewport.maxDepth = 1.0f;\n\n      VkRect2D scissor;\n      scissor.offset = { 0, 0 };\n      scissor.extent = cDstExtent;\n\n      if (cStreamState.dstRectEnabled) {\n        viewport.x      = cDstSizeFactX * float(cStreamState.dstRect.left);\n        viewport.y      = cDstSizeFactY * float(cStreamState.dstRect.top);\n        viewport.width  = cDstSizeFactX * float(cStreamState.dstRect.right) - viewport.x;\n        viewport.height = cDstSizeFactY * float(cStreamState.dstRect.bottom) - viewport.y;\n      }\n\n      VkExtent3D viewExtent = cViews[0]->mipLevelExtent(0);\n\n      VkRect2D srcRect;\n      srcRect.offset = { 0, 0 };\n      srcRect.extent = { viewExtent.width, viewExtent.height };\n\n      if (cStreamState.srcRectEnabled) {\n        srcRect.offset.x      = cStreamState.srcRect.left;\n        srcRect.offset.y      = cStreamState.srcRect.top;\n        srcRect.extent.width  = cStreamState.srcRect.right - srcRect.offset.x;\n        srcRect.extent.height = cStreamState.srcRect.bottom - srcRect.offset.y;\n      }\n\n      UboData uboData = { };\n      uboData.colorMatrix[0][0] = 1.0f;\n      uboData.colorMatrix[1][1] = 1.0f;\n      uboData.colorMatrix[2][2] = 1.0f;\n      uboData.coordMatrix[0][0] = float(srcRect.extent.width) / float(viewExtent.width);\n      uboData.coordMatrix[1][1] = float(srcRect.extent.height) / float(viewExtent.height);\n      uboData.coordMatrix[2][0] = float(srcRect.offset.x) / float(viewExtent.width);\n      uboData.coordMatrix[2][1] = float(srcRect.offset.y) / float(viewExtent.height);\n      uboData.srcRect = srcRect;\n      uboData.yMin = 0.0f;\n      uboData.yMax = 1.0f;\n      uboData.isPlanar = cViews[1] != nullptr;\n      uboData.exportMode = cExportMode;\n\n      if (cSrcIsYCbCr && !cDstIsYCbCr)\n        ApplyYCbCrMatrix(uboData.colorMatrix, cStreamState.colorSpace.YCbCr_Matrix);\n\n      if (cStreamState.colorSpace.Nominal_Range) {\n        uboData.yMin = 0.0627451f;\n        uboData.yMax = 0.9215686f;\n      }\n\n      Rc<DxvkResourceAllocation> uboSlice = m_ubo->allocateStorage();\n      memcpy(uboSlice->mapPtr(), &uboData, sizeof(uboData));\n\n      DxvkViewport vp = { viewport, scissor };\n\n      ctx->invalidateBuffer(m_ubo, std::move(uboSlice));\n      ctx->setViewports(1, &vp);\n\n      ctx->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_vs));\n      ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fs));\n\n      ctx->bindUniformBuffer(VK_SHADER_STAGE_FRAGMENT_BIT, 0, DxvkBufferSlice(m_ubo));\n\n      for (uint32_t i = 0; i < cViews.size(); i++)\n        ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, Rc<DxvkImageView>(cViews[i]));\n\n      VkDrawIndirectCommand draw = { };\n      draw.vertexCount   = 3u;\n      draw.instanceCount = 1u;\n\n      ctx->draw(1, &draw);\n\n      for (uint32_t i = 0; i < cViews.size(); i++)\n        ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, nullptr);\n    });\n  }\n\n\n  void D3D11VideoContext::CopyBaseImageToShadow(\n    const D3D11VideoProcessorView&        View) {\n    auto shadow = View.GetShadow();\n\n    if (!shadow)\n      return;\n\n    VkImageSubresourceLayers imageLayers = View.GetImageSubresource();\n\n    VkImageSubresourceLayers shadowLayers = { };\n    shadowLayers.aspectMask = imageLayers.aspectMask;\n    shadowLayers.layerCount = imageLayers.layerCount;\n\n    m_ctx->SyncImage(shadow, shadowLayers, View.GetImage(), imageLayers);\n  }\n\n\n  void D3D11VideoContext::CopyShadowToBaseImage(\n    const D3D11VideoProcessorView&        View) {\n    auto shadow = View.GetShadow();\n\n    if (!shadow)\n      return;\n\n    VkImageSubresourceLayers imageLayers = View.GetImageSubresource();\n\n    VkImageSubresourceLayers shadowLayers = { };\n    shadowLayers.aspectMask = imageLayers.aspectMask;\n    shadowLayers.layerCount = imageLayers.layerCount;\n\n    m_ctx->SyncImage(View.GetImage(), imageLayers, shadow, shadowLayers);\n  }\n\n\n  void D3D11VideoContext::CreateUniformBuffer() {\n    DxvkBufferCreateInfo bufferInfo;\n    bufferInfo.size = sizeof(UboData);\n    bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;\n    bufferInfo.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n    bufferInfo.access = VK_ACCESS_UNIFORM_READ_BIT;\n    bufferInfo.debugName = \"Video blit parameters\";\n\n    m_ubo = m_device->createBuffer(bufferInfo, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n  }\n\n\n  void D3D11VideoContext::CreateShaders() {\n    const std::array<DxvkBindingInfo, 3> fsBindings = {{\n      { 0u, 0u, 0u, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_ACCESS_UNIFORM_READ_BIT, DxvkDescriptorFlag::UniformBuffer },\n      { 0u, 1u, 1u, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,  1u, VK_IMAGE_VIEW_TYPE_2D,       VK_ACCESS_SHADER_READ_BIT },\n      { 0u, 2u, 2u, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,  1u, VK_IMAGE_VIEW_TYPE_2D,       VK_ACCESS_SHADER_READ_BIT },\n    }};\n\n    DxvkSpirvShaderCreateInfo vsInfo = { };\n    m_vs = new DxvkSpirvShader(vsInfo, d3d11_video_blit_vert);\n\n    DxvkSpirvShaderCreateInfo fsInfo = { };\n    fsInfo.bindingCount = fsBindings.size();\n    fsInfo.bindings = fsBindings.data();\n    m_fs = new DxvkSpirvShader(fsInfo, d3d11_video_blit_frag);\n  }\n\n\n  void D3D11VideoContext::CreateResources() {\n    if (std::exchange(m_resourcesCreated, true))\n      return;\n\n    CreateUniformBuffer();\n    CreateShaders();\n  }\n\n\n  void D3D11VideoContext::UnbindResources() {\n    m_ctx->EmitCs([] (DxvkContext* ctx) {\n      ctx->bindRenderTargets(DxvkRenderTargets(), 0u);\n\n      ctx->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(nullptr);\n      ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(nullptr);\n\n      ctx->bindUniformBuffer(VK_SHADER_STAGE_FRAGMENT_BIT, 0, DxvkBufferSlice());\n    });\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_video.h",
    "content": "#pragma once\n\n#include \"d3d11_device.h\"\n\nnamespace dxvk {\n\n  static constexpr uint32_t D3D11_VK_VIDEO_STREAM_COUNT = 8;\n\n  class D3D11VideoProcessorEnumerator : public D3D11DeviceChild<ID3D11VideoProcessorEnumerator> {\n\n  public:\n\n    D3D11VideoProcessorEnumerator(\n            D3D11Device*            pDevice,\n      const D3D11_VIDEO_PROCESSOR_CONTENT_DESC& Desc);\n\n    ~D3D11VideoProcessorEnumerator();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetVideoProcessorContentDesc(\n            D3D11_VIDEO_PROCESSOR_CONTENT_DESC* pContentDesc);\n\n    HRESULT STDMETHODCALLTYPE CheckVideoProcessorFormat(\n            DXGI_FORMAT             Format,\n            UINT*                   pFlags);\n\n    HRESULT STDMETHODCALLTYPE GetVideoProcessorCaps(\n            D3D11_VIDEO_PROCESSOR_CAPS* pCaps);\n\n    HRESULT STDMETHODCALLTYPE GetVideoProcessorRateConversionCaps(\n            UINT                    TypeIndex,\n            D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS* pCaps);\n\n    HRESULT STDMETHODCALLTYPE GetVideoProcessorCustomRate(\n            UINT                    TypeIndex,\n            UINT                    CustomRateIndex,\n            D3D11_VIDEO_PROCESSOR_CUSTOM_RATE* pRate);\n\n    HRESULT STDMETHODCALLTYPE GetVideoProcessorFilterRange(\n            D3D11_VIDEO_PROCESSOR_FILTER        Filter,\n            D3D11_VIDEO_PROCESSOR_FILTER_RANGE* pRange);\n\n  private:\n\n    D3D11_VIDEO_PROCESSOR_CONTENT_DESC  m_desc;\n\n    D3DDestructionNotifier              m_destructionNotifier;\n\n  };\n\n\n  struct D3D11VideoProcessorStreamState {\n    BOOL autoProcessingEnabled  = TRUE;\n    BOOL dstRectEnabled         = FALSE;\n    BOOL srcRectEnabled         = FALSE;\n    BOOL rotationEnabled        = FALSE;\n    RECT dstRect                = RECT();\n    RECT srcRect                = RECT();\n    D3D11_VIDEO_FRAME_FORMAT frameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;\n    D3D11_VIDEO_PROCESSOR_ROTATION rotation = D3D11_VIDEO_PROCESSOR_ROTATION_IDENTITY;\n    D3D11_VIDEO_PROCESSOR_COLOR_SPACE colorSpace = D3D11_VIDEO_PROCESSOR_COLOR_SPACE();\n  };\n\n  struct D3D11VideoProcessorState {\n    BOOL outputStereoModeEnabled                       = FALSE;\n    BOOL outputBackgroundColorIsYCbCr                  = FALSE;\n    BOOL outputTargetRectEnabled                       = FALSE;\n    RECT outputTargetRect                              = RECT();\n    D3D11_VIDEO_COLOR outputBackgroundColor            = D3D11_VIDEO_COLOR();\n    D3D11_VIDEO_PROCESSOR_COLOR_SPACE outputColorSpace = D3D11_VIDEO_PROCESSOR_COLOR_SPACE();\n  };\n\n  class D3D11VideoProcessor : public D3D11DeviceChild<ID3D11VideoProcessor> {\n\n  public:\n\n    D3D11VideoProcessor(\n            D3D11Device*                    pDevice,\n            D3D11VideoProcessorEnumerator*  pEnumerator,\n            UINT                            RateConversionIndex);\n\n    ~D3D11VideoProcessor();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    void STDMETHODCALLTYPE GetContentDesc(\n            D3D11_VIDEO_PROCESSOR_CONTENT_DESC *pDesc);\n\n    void STDMETHODCALLTYPE GetRateConversionCaps(\n            D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS *pCaps);\n\n    D3D11VideoProcessorState* GetState() {\n      return &m_state;\n    }\n\n    D3D11VideoProcessorStreamState* GetStreamState(UINT StreamIndex) {\n      return StreamIndex < D3D11_VK_VIDEO_STREAM_COUNT\n        ? &m_streams[StreamIndex]\n        : nullptr;\n    }\n\n  private:\n\n    D3D11VideoProcessorEnumerator* m_enumerator;\n    uint32_t                       m_rateConversionIndex;\n    D3D11VideoProcessorState       m_state;\n    D3D11VideoProcessorStreamState m_streams[D3D11_VK_VIDEO_STREAM_COUNT];\n\n    D3DDestructionNotifier         m_destructionNotifier;\n\n  };\n\n\n  /**\n   * \\brief Common video processor view\n   */\n  class D3D11VideoProcessorView {\n\n  public:\n\n    D3D11VideoProcessorView(\n            D3D11Device*            pDevice,\n            ID3D11Resource*         pResource,\n            DxvkImageViewKey        viewInfo);\n\n    ~D3D11VideoProcessorView();\n\n    bool IsYCbCr() const {\n      return m_isYCbCr;\n    }\n\n    /**\n     * \\brief Queries base image\n     *\n     * Returns the base image for whihc\n     */\n    Rc<DxvkImage> GetImage() const {\n      return m_image;\n    }\n\n    /**\n     * \\brief Queries shadow image\n     *\n     * In some cases, a dedicated image is required in order to support\n     * the image usage flags. This must be kept in sync with the base\n     * image whenever the view is used for video input or output.\n     * \\returns Shadow image\n     */\n    Rc<DxvkImage> GetShadow() const {\n      return m_shadow;\n    }\n\n    /**\n     * \\brief Queries base image subresources\n     *\n     * Useful in case a shadow image is requried.\n     * \\returns Base image subresource layers\n     */\n    VkImageSubresourceLayers GetImageSubresource() const {\n      return m_layers;\n    }\n\n    /**\n     * \\brief Retrieves image views\n     *\n     * For planar formats, this will contain one view per plane.\n     * \\returns Array of views\n     */\n    std::array<Rc<DxvkImageView>, 2> GetViews() const {\n      return m_views;\n    }\n\n    /**\n     * \\brief Queries underlying D3D11 resource\n     * \\returns D3D11 resource pointer\n     */\n    ID3D11Resource* GetResource() {\n      return m_resource.ref();\n    }\n\n  private:\n\n    Com<ID3D11Resource>                   m_resource;\n\n    Rc<DxvkImage>                         m_image;\n    Rc<DxvkImage>                         m_shadow;\n\n    VkImageSubresourceLayers              m_layers = { };\n\n    std::array<Rc<DxvkImageView>, 2>      m_views;\n    bool                                  m_isYCbCr = false;\n\n    static bool IsYCbCrFormat(DXGI_FORMAT Format);\n\n  };\n\n\n\n  class D3D11VideoProcessorInputView : public D3D11DeviceChild<ID3D11VideoProcessorInputView> {\n\n  public:\n\n    D3D11VideoProcessorInputView(\n            D3D11Device*            pDevice,\n            ID3D11Resource*         pResource,\n      const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC& Desc);\n\n    ~D3D11VideoProcessorInputView();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    void STDMETHODCALLTYPE GetResource(\n            ID3D11Resource**        ppResource);\n\n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* pDesc);\n\n    const D3D11VideoProcessorView& GetCommon() const {\n      return m_common;\n    }\n\n  private:\n\n    D3D11VideoProcessorView               m_common;\n    D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC m_desc;\n\n    D3DDestructionNotifier                m_destructionNotifier;\n\n    static DxvkImageViewKey CreateViewInfo(const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC& Desc);\n  };\n\n\n\n  class D3D11VideoProcessorOutputView : public D3D11DeviceChild<ID3D11VideoProcessorOutputView> {\n\n  public:\n\n    D3D11VideoProcessorOutputView(\n            D3D11Device*            pDevice,\n            ID3D11Resource*         pResource,\n      const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC& Desc);\n\n    ~D3D11VideoProcessorOutputView();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    void STDMETHODCALLTYPE GetResource(\n            ID3D11Resource**        ppResource);\n\n    void STDMETHODCALLTYPE GetDesc(\n            D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* pDesc);\n\n    const D3D11VideoProcessorView& GetCommon() const {\n      return m_common;\n    }\n\n  private:\n\n    D3D11VideoProcessorView                 m_common;\n    D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC  m_desc;\n\n    D3DDestructionNotifier                  m_destructionNotifier;\n\n    static DxvkImageViewKey CreateViewInfo(const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC& Desc);\n  };\n\n\n\n  class D3D11VideoContext : public ID3D11VideoContext {\n\n  public:\n\n    D3D11VideoContext(\n            D3D11ImmediateContext*  pContext,\n      const Rc<DxvkDevice>&         Device);\n\n    ~D3D11VideoContext();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID                 Name,\n            UINT*                   pDataSize,\n            void*                   pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID                 Name,\n            UINT                    DataSize,\n      const void*                   pData);\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID                 Name,\n      const IUnknown*               pUnknown);\n\n    void STDMETHODCALLTYPE GetDevice(\n            ID3D11Device**          ppDevice);\n\n    HRESULT STDMETHODCALLTYPE GetDecoderBuffer(\n            ID3D11VideoDecoder*             pDecoder,\n            D3D11_VIDEO_DECODER_BUFFER_TYPE Type,\n            UINT*                           BufferSize,\n            void**                          ppBuffer);\n\n    HRESULT STDMETHODCALLTYPE ReleaseDecoderBuffer(\n            ID3D11VideoDecoder*             pDecoder,\n            D3D11_VIDEO_DECODER_BUFFER_TYPE Type);\n\n    HRESULT STDMETHODCALLTYPE DecoderBeginFrame(\n            ID3D11VideoDecoder*             pDecoder,\n            ID3D11VideoDecoderOutputView*   pView,\n            UINT                            KeySize,\n      const void*                           pKey);\n\n    HRESULT STDMETHODCALLTYPE DecoderEndFrame(\n            ID3D11VideoDecoder*             pDecoder);\n\n    HRESULT STDMETHODCALLTYPE SubmitDecoderBuffers(\n            ID3D11VideoDecoder*             pDecoder,\n            UINT                            BufferCount,\n      const D3D11_VIDEO_DECODER_BUFFER_DESC* pBufferDescs);\n\n    HRESULT STDMETHODCALLTYPE DecoderExtension(\n            ID3D11VideoDecoder*             pDecoder,\n      const D3D11_VIDEO_DECODER_EXTENSION*  pExtension);\n\n    void STDMETHODCALLTYPE VideoProcessorSetOutputTargetRect(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            BOOL                            Enable,\n      const RECT*                           pRect);\n\n    void STDMETHODCALLTYPE VideoProcessorSetOutputBackgroundColor(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            BOOL                            YCbCr,\n      const D3D11_VIDEO_COLOR*              pColor);\n\n    void STDMETHODCALLTYPE VideoProcessorSetOutputColorSpace(\n            ID3D11VideoProcessor*           pVideoProcessor,\n      const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace);\n\n    void STDMETHODCALLTYPE VideoProcessorSetOutputAlphaFillMode(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE AlphaFillMode,\n            UINT                            StreamIndex);\n\n    void STDMETHODCALLTYPE VideoProcessorSetOutputConstriction(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            BOOL                            Enable,\n            SIZE                            Size);\n\n    void STDMETHODCALLTYPE VideoProcessorSetOutputStereoMode(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            BOOL                            Enable);\n\n    HRESULT STDMETHODCALLTYPE VideoProcessorSetOutputExtension(\n            ID3D11VideoProcessor*           pVideoProcessor,\n      const GUID*                           pExtensionGuid,\n            UINT                            DataSize,\n            void*                           pData);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamFrameFormat(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            D3D11_VIDEO_FRAME_FORMAT        Format);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamColorSpace(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n      const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamOutputRate(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            D3D11_VIDEO_PROCESSOR_OUTPUT_RATE Rate,\n            BOOL                            Repeat,\n      const DXGI_RATIONAL*                  CustomRate);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamSourceRect(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL                            Enable,\n      const RECT*                           pRect);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamDestRect(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL                            Enable,\n      const RECT*                           pRect);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamAlpha(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL                            Enable,\n            FLOAT                           Alpha);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamPalette(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            UINT                            EntryCount,\n      const UINT*                           pEntries);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamPixelAspectRatio(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL                            Enable,\n      const DXGI_RATIONAL*                  pSrcAspectRatio,\n      const DXGI_RATIONAL*                  pDstAspectRatio);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamLumaKey(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL                            Enable,\n            FLOAT                           Lower,\n            FLOAT                           Upper);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamStereoFormat(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL                            Enable,\n            D3D11_VIDEO_PROCESSOR_STEREO_FORMAT Format,\n            BOOL                            LeftViewFrame0,\n            BOOL                            BaseViewFrame0,\n            D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE FlipMode,\n            int                             MonoOffset);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamAutoProcessingMode(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL                            Enable);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamFilter(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            D3D11_VIDEO_PROCESSOR_FILTER    Filter,\n            BOOL                            Enable,\n            int                             Level);\n\n    HRESULT STDMETHODCALLTYPE VideoProcessorSetStreamExtension(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n      const GUID*                           pExtensionGuid,\n            UINT                            DataSize,\n            void*                           pData);\n\n    void STDMETHODCALLTYPE VideoProcessorSetStreamRotation(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL                            Enable,\n            D3D11_VIDEO_PROCESSOR_ROTATION  Rotation);\n\n    void STDMETHODCALLTYPE VideoProcessorGetOutputTargetRect(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            BOOL*                           pEnabled,\n            RECT*                           pRect);\n\n    void STDMETHODCALLTYPE VideoProcessorGetOutputBackgroundColor(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            BOOL*                           pYCbCr,\n            D3D11_VIDEO_COLOR*              pColor);\n\n    void STDMETHODCALLTYPE VideoProcessorGetOutputColorSpace(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace);\n\n    void STDMETHODCALLTYPE VideoProcessorGetOutputAlphaFillMode(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE* pAlphaFillMode,\n            UINT*                           pStreamIndex);\n\n    void STDMETHODCALLTYPE VideoProcessorGetOutputConstriction(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            BOOL*                           pEnabled,\n            SIZE*                           pSize);\n\n    void STDMETHODCALLTYPE VideoProcessorGetOutputStereoMode(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            BOOL*                           pEnabled);\n\n    HRESULT STDMETHODCALLTYPE VideoProcessorGetOutputExtension(\n            ID3D11VideoProcessor*           pVideoProcessor,\n      const GUID*                           pExtensionGuid,\n            UINT                            DataSize,\n            void*                           pData);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamFrameFormat(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            D3D11_VIDEO_FRAME_FORMAT*       pFormat);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamColorSpace(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamOutputRate(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            D3D11_VIDEO_PROCESSOR_OUTPUT_RATE* pRate,\n            BOOL*                           pRepeat,\n            DXGI_RATIONAL*                  pCustomRate);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamSourceRect(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL*                           pEnabled,\n            RECT*                           pRect);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamDestRect(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL*                           pEnabled,\n            RECT*                           pRect);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamAlpha(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL*                           pEnabled,\n            FLOAT*                          pAlpha);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamPalette(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            UINT                            EntryCount,\n            UINT*                           pEntries);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamPixelAspectRatio(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL*                           pEnabled,\n            DXGI_RATIONAL*                  pSrcAspectRatio,\n            DXGI_RATIONAL*                  pDstAspectRatio);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamLumaKey(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL*                           pEnabled,\n            FLOAT*                          pLower,\n            FLOAT*                          pUpper);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamStereoFormat(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL*                           pEnabled,\n            D3D11_VIDEO_PROCESSOR_STEREO_FORMAT* pFormat,\n            BOOL*                           pLeftViewFrame0,\n            BOOL*                           pBaseViewFrame0,\n            D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE* pFlipMode,\n            int*                            pMonoOffset);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamAutoProcessingMode(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL*                           pEnabled);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamFilter(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            D3D11_VIDEO_PROCESSOR_FILTER    Filter,\n            BOOL*                           pEnabled,\n            int*                            pLevel);\n\n    HRESULT STDMETHODCALLTYPE VideoProcessorGetStreamExtension(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n      const GUID*                           pExtensionGuid,\n            UINT                            DataSize,\n            void*                           pData);\n\n    void STDMETHODCALLTYPE VideoProcessorGetStreamRotation(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            UINT                            StreamIndex,\n            BOOL*                           pEnable,\n            D3D11_VIDEO_PROCESSOR_ROTATION* pRotation);\n\n    HRESULT STDMETHODCALLTYPE VideoProcessorBlt(\n            ID3D11VideoProcessor*           pVideoProcessor,\n            ID3D11VideoProcessorOutputView* pOutputView,\n            UINT                            FrameIdx,\n            UINT                            StreamCount,\n      const D3D11_VIDEO_PROCESSOR_STREAM*   pStreams);\n\n    HRESULT STDMETHODCALLTYPE NegotiateCryptoSessionKeyExchange(\n            ID3D11CryptoSession*            pSession,\n            UINT                            DataSize,\n            void*                           pData);\n\n    void STDMETHODCALLTYPE EncryptionBlt(\n            ID3D11CryptoSession*            pSession,\n            ID3D11Texture2D*                pSrcSurface,\n            ID3D11Texture2D*                pDstSurface,\n            UINT                            IVSize,\n            void*                           pIV);\n\n    void STDMETHODCALLTYPE DecryptionBlt(\n            ID3D11CryptoSession*            pSession,\n            ID3D11Texture2D*                pSrcSurface,\n            ID3D11Texture2D*                pDstSurface,\n            D3D11_ENCRYPTED_BLOCK_INFO*     pBlockInfo,\n            UINT                            KeySize,\n      const void*                           pKey,\n            UINT                            IVSize,\n            void*                           pIV);\n\n    void STDMETHODCALLTYPE StartSessionKeyRefresh(\n            ID3D11CryptoSession*            pSession,\n            UINT                            RandomNumberSize,\n            void*                           pRandomNumber);\n\n    void STDMETHODCALLTYPE FinishSessionKeyRefresh(\n            ID3D11CryptoSession*            pSession);\n\n    HRESULT STDMETHODCALLTYPE GetEncryptionBltKey(\n            ID3D11CryptoSession*            pSession,\n            UINT                            KeySize,\n            void*                           pKey);\n\n    HRESULT STDMETHODCALLTYPE NegotiateAuthenticatedChannelKeyExchange(\n            ID3D11AuthenticatedChannel*     pChannel,\n            UINT                            DataSize,\n            void*                           pData);\n\n    HRESULT STDMETHODCALLTYPE QueryAuthenticatedChannel(\n            ID3D11AuthenticatedChannel*     pChannel,\n            UINT                            InputSize,\n      const void*                           pInput,\n            UINT                            OutputSize,\n            void*                           pOutput);\n\n    HRESULT STDMETHODCALLTYPE ConfigureAuthenticatedChannel(\n            ID3D11AuthenticatedChannel*     pChannel,\n            UINT                            InputSize,\n      const void*                           pInput,\n            D3D11_AUTHENTICATED_CONFIGURE_OUTPUT* pOutput);\n\n  private:\n\n    enum ExportMode : uint32_t {\n      ExportRGBA = 0,\n      ExportY    = 1,\n      ExportCbCr = 2,\n    };\n\n    struct alignas(16) UboData {\n      float colorMatrix[3][4];\n      float coordMatrix[3][2];\n      VkRect2D srcRect;\n      float yMin, yMax;\n      VkBool32 isPlanar;\n      ExportMode exportMode;\n    };\n\n    D3D11ImmediateContext*  m_ctx;\n\n    Rc<DxvkDevice>          m_device;\n    Rc<DxvkShader>          m_vs;\n    Rc<DxvkShader>          m_fs;\n    Rc<DxvkBuffer>          m_ubo;\n\n    VkExtent2D m_dstExtent      = { 0u, 0u };\n    float      m_dstSizeFact[2] = { 1.0f, 1.0f };\n    bool       m_dstIsYCbCr     = false;\n    ExportMode m_exportMode     = ExportRGBA;\n\n    bool m_resourcesCreated = false;\n\n    void ApplyColorMatrix(float pDst[3][4], const float pSrc[3][4]);\n\n    void ApplyYCbCrMatrix(float pColorMatrix[3][4], bool UseBt709);\n\n    void BindOutputView(\n            Rc<DxvkImageView>               View,\n            Rc<DxvkImageView>               FirstView);\n\n    void BlitStream(\n      const D3D11VideoProcessorStreamState* pStreamState,\n      const D3D11_VIDEO_PROCESSOR_STREAM*   pStream);\n\n    void CopyBaseImageToShadow(\n      const D3D11VideoProcessorView&        View);\n\n    void CopyShadowToBaseImage(\n      const D3D11VideoProcessorView&        View);\n\n    void CreateUniformBuffer();\n\n    void CreateShaders();\n\n    void CreateResources();\n\n    void UnbindResources();\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_view.h",
    "content": "#pragma once\n\n#include \"d3d11_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Buffer view info\n   * \n   * Stores the byte range covered\n   * by a buffer view.\n   */\n  struct D3D11_VK_BUFFER_VIEW_INFO {\n    VkDeviceSize                Offset;\n    VkDeviceSize                Length;\n  };\n\n  /**\n   * \\brief Image view info\n   * \n   * Stores the subresource range\n   * covered by an image view.\n   */\n  struct D3D11_VK_IMAGE_VIEW_INFO {\n    VkImageAspectFlags          Aspects;\n    uint32_t                    MinLevel;\n    uint32_t                    MinLayer;\n    uint32_t                    NumLevels;\n    uint32_t                    NumLayers;\n  };\n\n  /**\n   * \\brief Common view info\n   *\n   * Stores a pointer to the resource as\n   * well as the type-specific range that\n   * is affected by the view.\n   */\n  struct D3D11_VK_VIEW_INFO {\n    ID3D11Resource*             pResource;\n    D3D11_RESOURCE_DIMENSION    Dimension;\n    UINT                        BindFlags;\n    union {\n      D3D11_VK_BUFFER_VIEW_INFO Buffer;\n      D3D11_VK_IMAGE_VIEW_INFO  Image;\n    };\n  };\n\n  /**\n   * \\brief Checks whether two views overlap\n   * \n   * Overlapping views may conflict in case\n   * one or both views are used for writing.\n   * \\param [in] a First view to check\n   * \\param [in] b Second view to check\n   * \\returns \\c true if the views overlap\n   */\n  inline bool CheckViewOverlap(const D3D11_VK_VIEW_INFO& a, const D3D11_VK_VIEW_INFO& b) {\n    if (likely(a.pResource != b.pResource))\n      return false;\n    \n    if (a.Dimension == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      // Just check whether the buffer ranges overlap\n      return (a.Buffer.Offset < b.Buffer.Offset + b.Buffer.Length)\n          && (a.Buffer.Offset + a.Buffer.Length > b.Buffer.Offset);\n    } else {\n      // Check whether the subresource ranges overlap\n      return (a.Image.Aspects & b.Image.Aspects)\n          && (a.Image.MinLevel < b.Image.MinLevel + b.Image.NumLevels)\n          && (a.Image.MinLayer < b.Image.MinLayer + b.Image.NumLayers)\n          && (a.Image.MinLevel + a.Image.NumLevels > b.Image.MinLevel)\n          && (a.Image.MinLayer + a.Image.NumLayers > b.Image.MinLayer);\n    }\n  }\n\n  template<typename T1, typename T2>\n  bool CheckViewOverlap(const T1* a, const T2* b) {\n    return a && b && CheckViewOverlap(a->GetViewInfo(), b->GetViewInfo());\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_view_dsv.cpp",
    "content": "#include \"d3d11_device.h\"\n#include \"d3d11_buffer.h\"\n#include \"d3d11_resource.h\"\n#include \"d3d11_texture.h\"\n#include \"d3d11_view_dsv.h\"\n\nnamespace dxvk {\n  \n  D3D11DepthStencilView::D3D11DepthStencilView(\n          D3D11Device*                      pDevice,\n          ID3D11Resource*                   pResource,\n    const D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc)\n  : D3D11DeviceChild<ID3D11DepthStencilView>(pDevice),\n    m_resource(pResource), m_desc(*pDesc), m_d3d10(this),\n    m_destructionNotifier(this) {\n    ResourceAddRefPrivate(m_resource);\n\n    D3D11_COMMON_RESOURCE_DESC resourceDesc;\n    GetCommonResourceDesc(pResource, &resourceDesc);\n\n    DxvkImageViewKey viewInfo;\n    viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_DEPTH).Format;\n    viewInfo.layout = GetViewLayout();\n    viewInfo.aspects = lookupFormatInfo(viewInfo.format)->aspectMask;\n    viewInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n    \n    switch (pDesc->ViewDimension) {\n      case D3D11_DSV_DIMENSION_TEXTURE1D:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_1D;\n        viewInfo.mipIndex   = pDesc->Texture1D.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = 0;\n        viewInfo.layerCount = 1;\n        break;\n        \n      case D3D11_DSV_DIMENSION_TEXTURE1DARRAY:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_1D_ARRAY;\n        viewInfo.mipIndex   = pDesc->Texture1DArray.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice;\n        viewInfo.layerCount = pDesc->Texture1DArray.ArraySize;\n        break;\n        \n      case D3D11_DSV_DIMENSION_TEXTURE2D:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n        viewInfo.mipIndex   = pDesc->Texture2D.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = 0;\n        viewInfo.layerCount = 1;\n        break;\n        \n      case D3D11_DSV_DIMENSION_TEXTURE2DARRAY:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n        viewInfo.mipIndex   = pDesc->Texture2DArray.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice;\n        viewInfo.layerCount = pDesc->Texture2DArray.ArraySize;\n        break;\n        \n      case D3D11_DSV_DIMENSION_TEXTURE2DMS:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n        viewInfo.mipIndex   = 0;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = 0;\n        viewInfo.layerCount = 1;\n        break;\n      \n      case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n        viewInfo.mipIndex   = 0;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = pDesc->Texture2DMSArray.FirstArraySlice;\n        viewInfo.layerCount = pDesc->Texture2DMSArray.ArraySize;\n        break;\n      \n      default:\n        throw DxvkError(\"D3D11: Invalid view dimension for DSV\");\n    }\n    \n    // Normalize view type so that we won't accidentally\n    // bind 2D array views and 2D views at the same time\n    if (viewInfo.layerCount == 1) {\n      if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY)\n        viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;\n      if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY)\n        viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    }\n\n    // Populate view info struct\n    m_info.pResource = pResource;\n    m_info.Dimension = resourceDesc.Dim;\n    m_info.BindFlags = resourceDesc.BindFlags;\n    m_info.Image.Aspects   = viewInfo.aspects;\n    m_info.Image.MinLevel  = viewInfo.mipIndex;\n    m_info.Image.MinLayer  = viewInfo.layerIndex;\n    m_info.Image.NumLevels = viewInfo.mipCount;\n    m_info.Image.NumLayers = viewInfo.layerCount;\n\n    if (m_desc.Flags & D3D11_DSV_READ_ONLY_DEPTH)\n      m_info.Image.Aspects &= ~VK_IMAGE_ASPECT_DEPTH_BIT;\n\n    if (m_desc.Flags & D3D11_DSV_READ_ONLY_STENCIL)\n      m_info.Image.Aspects &= ~VK_IMAGE_ASPECT_STENCIL_BIT;\n\n    // Create the underlying image view object\n    m_view = GetCommonTexture(pResource)->GetImage()->createView(viewInfo);\n  }\n  \n  \n  D3D11DepthStencilView::~D3D11DepthStencilView() {\n    m_destructionNotifier.Notify();\n\n    ResourceReleasePrivate(m_resource);\n    m_resource = nullptr;\n\n    m_view = nullptr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11DepthStencilView::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11View)\n     || riid == __uuidof(ID3D11DepthStencilView)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10View)\n     || riid == __uuidof(ID3D10DepthStencilView)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11DepthStencilView), riid)) {\n      Logger::warn(\"D3D11DepthStencilView::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11DepthStencilView::GetResource(ID3D11Resource** ppResource) {\n    *ppResource = ref(m_resource);\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11DepthStencilView::GetDesc(D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc) {\n    *pDesc = m_desc;\n  }\n  \n  \n  HRESULT D3D11DepthStencilView::GetDescFromResource(\n          ID3D11Resource*                   pResource,\n          D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc) {\n    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resourceDim);\n    pDesc->Flags = 0;\n    \n    switch (resourceDim) {\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        D3D11_TEXTURE1D_DESC resourceDesc;\n        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format = resourceDesc.Format;\n        \n        if (resourceDesc.ArraySize == 1) {\n          pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE1D;\n          pDesc->Texture1D.MipSlice = 0;\n        } else {\n          pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE1DARRAY;\n          pDesc->Texture1DArray.MipSlice        = 0;\n          pDesc->Texture1DArray.FirstArraySlice = 0;\n          pDesc->Texture1DArray.ArraySize       = resourceDesc.ArraySize;\n        }\n      } return S_OK;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        D3D11_TEXTURE2D_DESC resourceDesc;\n        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format = resourceDesc.Format;\n        \n        if (resourceDesc.SampleDesc.Count == 1) {\n          if (resourceDesc.ArraySize == 1) {\n            pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;\n            pDesc->Texture2D.MipSlice = 0;\n          } else {\n            pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;\n            pDesc->Texture2DArray.MipSlice        = 0;\n            pDesc->Texture2DArray.FirstArraySlice = 0;\n            pDesc->Texture2DArray.ArraySize       = resourceDesc.ArraySize;\n          }\n        } else {\n          if (resourceDesc.ArraySize == 1) {\n            pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;\n          } else {\n            pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY;\n            pDesc->Texture2DMSArray.FirstArraySlice = 0;\n            pDesc->Texture2DMSArray.ArraySize       = resourceDesc.ArraySize;\n          }\n        }\n      } return S_OK;\n        \n      default:\n        Logger::err(str::format(\n          \"D3D11: Unsupported dimension for depth stencil view: \",\n          resourceDim));\n        return E_INVALIDARG;\n    }\n  }\n  \n  \n  HRESULT D3D11DepthStencilView::NormalizeDesc(\n          ID3D11Resource*                   pResource,\n          D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc) {\n    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resourceDim);\n    \n    DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;\n    uint32_t numLayers = 0;\n    \n    switch (resourceDim) {\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        D3D11_TEXTURE1D_DESC resourceDesc;\n        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE1D\n         && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE1DARRAY) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture1D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        numLayers = resourceDesc.ArraySize;\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        D3D11_TEXTURE2D_DESC resourceDesc;\n        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2D\n         && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2DARRAY\n         && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2DMS\n         && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture2D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        numLayers = resourceDesc.ArraySize;\n      } break;\n      \n      default:\n        return E_INVALIDARG;\n    }\n    \n    if (pDesc->Format == DXGI_FORMAT_UNKNOWN)\n      pDesc->Format = format;\n    \n    switch (pDesc->ViewDimension) {\n      case D3D11_DSV_DIMENSION_TEXTURE1DARRAY:\n        if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice)\n          pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice;\n        break;\n      \n      case D3D11_DSV_DIMENSION_TEXTURE2DARRAY:\n        if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice)\n          pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice;\n        break;\n      \n      case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY:\n        if (pDesc->Texture2DMSArray.ArraySize > numLayers - pDesc->Texture2DMSArray.FirstArraySlice)\n          pDesc->Texture2DMSArray.ArraySize = numLayers - pDesc->Texture2DMSArray.FirstArraySlice;\n        break;\n      \n      default:\n        break;\n    }\n    \n    return S_OK;\n  }\n  \n\n  VkImageLayout D3D11DepthStencilView::GetViewLayout() const {\n    switch (m_desc.Flags & (D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL)) {\n      default:  // case 0\n        return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n      case D3D11_DSV_READ_ONLY_DEPTH:\n        return VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR;\n      case D3D11_DSV_READ_ONLY_STENCIL:\n        return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR;\n      case D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL:\n        return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;\n    }\n  }\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_view_dsv.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../d3d10/d3d10_view_dsv.h\"\n\n#include \"d3d11_device_child.h\"\n#include \"d3d11_view.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  \n  /**\n   * \\brief Depth-stencil view\n   * \n   * Unordered access views are special in that they can\n   * have counters, which can be used inside shaders to\n   * atomically append or consume structures.\n   */\n  class D3D11DepthStencilView : public D3D11DeviceChild<ID3D11DepthStencilView> {\n    \n  public:\n    \n    D3D11DepthStencilView(\n            D3D11Device*                      pDevice,\n            ID3D11Resource*                   pResource,\n      const D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc);\n    \n    ~D3D11DepthStencilView();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetResource(ID3D11Resource** ppResource) final;\n    \n    void STDMETHODCALLTYPE GetDesc(D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc) final;\n    \n    const D3D11_VK_VIEW_INFO& GetViewInfo() const {\n      return m_info;\n    }\n\n    D3D11_RESOURCE_DIMENSION GetResourceType() const {\n      D3D11_RESOURCE_DIMENSION type;\n      m_resource->GetType(&type);\n      return type;\n    }\n    \n    Rc<DxvkImageView> GetImageView() const {\n      return m_view;\n    }\n    \n    UINT GetSampleCount() const {\n      return UINT(m_view->image()->info().sampleCount);\n    }\n\n    VkImageAspectFlags GetWritableAspectMask() const {\n      VkImageAspectFlags mask = m_view->formatInfo()->aspectMask;\n      if (m_desc.Flags & D3D11_DSV_READ_ONLY_DEPTH)   mask &= ~VK_IMAGE_ASPECT_DEPTH_BIT;\n      if (m_desc.Flags & D3D11_DSV_READ_ONLY_STENCIL) mask &= ~VK_IMAGE_ASPECT_STENCIL_BIT;\n      return mask;\n    }\n\n    DXGI_FORMAT GetViewFormat() const {\n      return m_desc.Format;\n    }\n\n    D3D10DepthStencilView* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n    \n    static HRESULT GetDescFromResource(\n            ID3D11Resource*                   pResource,\n            D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc);\n    \n    static HRESULT NormalizeDesc(\n            ID3D11Resource*                   pResource,\n            D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc);\n    \n  private:\n    \n    ID3D11Resource*                   m_resource;\n    D3D11_DEPTH_STENCIL_VIEW_DESC     m_desc;\n    D3D11_VK_VIEW_INFO                m_info;\n    Rc<DxvkImageView>                 m_view;\n    D3D10DepthStencilView             m_d3d10;\n\n    D3DDestructionNotifier            m_destructionNotifier;\n\n    VkImageLayout GetViewLayout() const;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_view_rtv.cpp",
    "content": "#include \"d3d11_device.h\"\n#include \"d3d11_buffer.h\"\n#include \"d3d11_resource.h\"\n#include \"d3d11_texture.h\"\n#include \"d3d11_view_rtv.h\"\n\nnamespace dxvk {\n  \n  D3D11RenderTargetView::D3D11RenderTargetView(\n          D3D11Device*                      pDevice,\n          ID3D11Resource*                   pResource,\n    const D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc)\n  : D3D11DeviceChild<ID3D11RenderTargetView1>(pDevice),\n    m_resource(pResource), m_desc(*pDesc), m_d3d10(this),\n    m_destructionNotifier(this) {\n    ResourceAddRefPrivate(m_resource);\n\n    auto texture = GetCommonTexture(pResource);\n\n    D3D11_COMMON_RESOURCE_DESC resourceDesc;\n    GetCommonResourceDesc(pResource, &resourceDesc);\n\n    DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat(\n      pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR);\n\n    DxvkImageViewKey viewInfo;\n    viewInfo.format = formatInfo.Format;\n    viewInfo.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n    viewInfo.aspects = lookupFormatInfo(viewInfo.format)->aspectMask;\n    viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n    viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle);\n    \n    switch (pDesc->ViewDimension) {\n      case D3D11_RTV_DIMENSION_TEXTURE1D:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_1D;\n        viewInfo.mipIndex   = pDesc->Texture1D.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = 0;\n        viewInfo.layerCount = 1;\n        break;\n        \n      case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_1D_ARRAY;\n        viewInfo.mipIndex   = pDesc->Texture1DArray.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice;\n        viewInfo.layerCount = pDesc->Texture1DArray.ArraySize;\n        break;\n        \n      case D3D11_RTV_DIMENSION_TEXTURE2D:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n        viewInfo.mipIndex   = pDesc->Texture2D.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = 0;\n        viewInfo.layerCount = 1;\n        break;\n        \n      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n        viewInfo.mipIndex   = pDesc->Texture2DArray.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice;\n        viewInfo.layerCount = pDesc->Texture2DArray.ArraySize;\n        break;\n        \n      case D3D11_RTV_DIMENSION_TEXTURE2DMS:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n        viewInfo.mipIndex   = 0;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = 0;\n        viewInfo.layerCount = 1;\n        break;\n      \n      case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n        viewInfo.mipIndex   = 0;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = pDesc->Texture2DMSArray.FirstArraySlice;\n        viewInfo.layerCount = pDesc->Texture2DMSArray.ArraySize;\n        break;\n      \n      case D3D11_RTV_DIMENSION_TEXTURE3D:\n        viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n        viewInfo.mipIndex   = pDesc->Texture3D.MipSlice;\n        viewInfo.mipCount   = 1;\n        viewInfo.layerIndex = pDesc->Texture3D.FirstWSlice;\n        viewInfo.layerCount = pDesc->Texture3D.WSize;\n        break;\n      \n      default:\n        throw DxvkError(\"D3D11: Invalid view dimension for RTV\");\n    }\n    \n    if (texture->GetPlaneCount() > 1)\n      viewInfo.aspects = vk::getPlaneAspect(GetPlaneSlice(pDesc));\n\n    // Normalize view type so that we won't accidentally\n    // bind 2D array views and 2D views at the same time\n    if (viewInfo.layerCount == 1) {\n      if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY)\n        viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;\n      if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY)\n        viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    }\n\n    // Populate view info struct\n    m_info.pResource = pResource;\n    m_info.Dimension = resourceDesc.Dim;\n    m_info.BindFlags = resourceDesc.BindFlags;\n    m_info.Image.Aspects   = viewInfo.aspects;\n    m_info.Image.MinLevel  = viewInfo.mipIndex;\n    m_info.Image.MinLayer  = viewInfo.layerIndex;\n    m_info.Image.NumLevels = viewInfo.mipCount;\n    m_info.Image.NumLayers = viewInfo.layerCount;\n    \n    // Create the underlying image view object\n    m_view = texture->GetImage()->createView(viewInfo);\n  }\n  \n  \n  D3D11RenderTargetView::~D3D11RenderTargetView() {\n    m_destructionNotifier.Notify();\n\n    ResourceReleasePrivate(m_resource);\n    m_resource = nullptr;\n\n    m_view = nullptr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11RenderTargetView::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11View)\n     || riid == __uuidof(ID3D11RenderTargetView)\n     || riid == __uuidof(ID3D11RenderTargetView1)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10View)\n     || riid == __uuidof(ID3D10RenderTargetView)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11RenderTargetView), riid)) {\n      Logger::warn(\"D3D11RenderTargetView::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11RenderTargetView::GetResource(ID3D11Resource** ppResource) {\n    *ppResource = ref(m_resource);\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11RenderTargetView::GetDesc(D3D11_RENDER_TARGET_VIEW_DESC* pDesc) {\n    pDesc->Format            = m_desc.Format;\n    pDesc->ViewDimension     = m_desc.ViewDimension;\n\n    switch (m_desc.ViewDimension) {\n      case D3D11_RTV_DIMENSION_UNKNOWN:\n        break;\n\n      case D3D11_RTV_DIMENSION_BUFFER:\n        pDesc->Buffer = m_desc.Buffer;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE1D:\n        pDesc->Texture1D = m_desc.Texture1D;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:\n        pDesc->Texture1DArray = m_desc.Texture1DArray;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE2D:\n        pDesc->Texture2D.MipSlice = m_desc.Texture2D.MipSlice;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:\n        pDesc->Texture2DArray.MipSlice        = m_desc.Texture2DArray.MipSlice;\n        pDesc->Texture2DArray.FirstArraySlice = m_desc.Texture2DArray.FirstArraySlice;\n        pDesc->Texture2DArray.ArraySize       = m_desc.Texture2DArray.ArraySize;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE2DMS:\n        pDesc->Texture2DMS = m_desc.Texture2DMS;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:\n        pDesc->Texture2DMSArray = m_desc.Texture2DMSArray;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE3D:\n        pDesc->Texture3D = m_desc.Texture3D;\n        break;\n    }\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11RenderTargetView::GetDesc1(D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) {\n    *pDesc = m_desc;\n  }\n  \n  \n  HRESULT D3D11RenderTargetView::GetDescFromResource(\n          ID3D11Resource*                   pResource,\n          D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc) {\n    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resourceDim);\n    \n    switch (resourceDim) {\n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        D3D11_TEXTURE1D_DESC resourceDesc;\n        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format = resourceDesc.Format;\n        \n        if (resourceDesc.ArraySize == 1) {\n          pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;\n          pDesc->Texture1D.MipSlice = 0;\n        } else {\n          pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1DARRAY;\n          pDesc->Texture1DArray.MipSlice        = 0;\n          pDesc->Texture1DArray.FirstArraySlice = 0;\n          pDesc->Texture1DArray.ArraySize       = resourceDesc.ArraySize;\n        }\n      } return S_OK;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        D3D11_TEXTURE2D_DESC resourceDesc;\n        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format = resourceDesc.Format;\n        \n        if (resourceDesc.SampleDesc.Count == 1) {\n          if (resourceDesc.ArraySize == 1) {\n            pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;\n            pDesc->Texture2D.MipSlice   = 0;\n            pDesc->Texture2D.PlaneSlice = 0;\n          } else {\n            pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;\n            pDesc->Texture2DArray.MipSlice        = 0;\n            pDesc->Texture2DArray.FirstArraySlice = 0;\n            pDesc->Texture2DArray.ArraySize       = resourceDesc.ArraySize;\n            pDesc->Texture2DArray.PlaneSlice      = 0;\n          }\n        } else {\n          if (resourceDesc.ArraySize == 1) {\n            pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;\n          } else {\n            pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;\n            pDesc->Texture2DMSArray.FirstArraySlice = 0;\n            pDesc->Texture2DMSArray.ArraySize       = resourceDesc.ArraySize;\n          }\n        }\n      } return S_OK;\n        \n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {\n        D3D11_TEXTURE3D_DESC resourceDesc;\n        static_cast<D3D11Texture3D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format         = resourceDesc.Format;\n        pDesc->ViewDimension  = D3D11_RTV_DIMENSION_TEXTURE3D;\n        pDesc->Texture3D.MipSlice    = 0;\n        pDesc->Texture3D.FirstWSlice = 0;\n        pDesc->Texture3D.WSize       = resourceDesc.Depth;\n      } return S_OK;\n      \n      default:\n        Logger::err(str::format(\n          \"D3D11: Unsupported dimension for render target view: \",\n          resourceDim));\n        return E_INVALIDARG;\n    }\n  }\n  \n  \n  D3D11_RENDER_TARGET_VIEW_DESC1 D3D11RenderTargetView::PromoteDesc(\n    const D3D11_RENDER_TARGET_VIEW_DESC*    pDesc,\n          UINT                              Plane) {\n    D3D11_RENDER_TARGET_VIEW_DESC1 dstDesc;\n    dstDesc.Format            = pDesc->Format;\n    dstDesc.ViewDimension     = pDesc->ViewDimension;\n\n    switch (pDesc->ViewDimension) {\n      case D3D11_RTV_DIMENSION_UNKNOWN:\n        break;\n\n      case D3D11_RTV_DIMENSION_BUFFER:\n        dstDesc.Buffer = pDesc->Buffer;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE1D:\n        dstDesc.Texture1D = pDesc->Texture1D;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:\n        dstDesc.Texture1DArray = pDesc->Texture1DArray;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE2D:\n        dstDesc.Texture2D.MipSlice   = pDesc->Texture2D.MipSlice;\n        dstDesc.Texture2D.PlaneSlice = Plane;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:\n        dstDesc.Texture2DArray.MipSlice        = pDesc->Texture2DArray.MipSlice;\n        dstDesc.Texture2DArray.FirstArraySlice = pDesc->Texture2DArray.FirstArraySlice;\n        dstDesc.Texture2DArray.ArraySize       = pDesc->Texture2DArray.ArraySize;\n        dstDesc.Texture2DArray.PlaneSlice      = Plane;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE2DMS:\n        dstDesc.Texture2DMS = pDesc->Texture2DMS;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:\n        dstDesc.Texture2DMSArray = pDesc->Texture2DMSArray;\n        break;\n\n      case D3D11_RTV_DIMENSION_TEXTURE3D:\n        dstDesc.Texture3D = pDesc->Texture3D;\n        break;\n    }\n\n    return dstDesc;\n  }\n\n\n  HRESULT D3D11RenderTargetView::NormalizeDesc(\n          ID3D11Resource*                   pResource,\n          D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc) {\n    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resourceDim);\n    \n    DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;\n    uint32_t numLayers = 0;\n    \n    switch (resourceDim) {\n      case D3D11_RESOURCE_DIMENSION_BUFFER: {\n        if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_BUFFER) {\n          Logger::err(\"D3D11: Incompatible view dimension for Buffer\");\n          return E_INVALIDARG;\n        }\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        D3D11_TEXTURE1D_DESC resourceDesc;\n        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE1D\n         && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE1DARRAY) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture1D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        numLayers = resourceDesc.ArraySize;\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        D3D11_TEXTURE2D_DESC resourceDesc;\n        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2D\n         && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2DARRAY\n         && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2DMS\n         && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture2D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        numLayers = resourceDesc.ArraySize;\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {\n        D3D11_TEXTURE3D_DESC resourceDesc;\n        static_cast<D3D11Texture3D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE3D) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture3D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        numLayers = std::max(resourceDesc.Depth >> pDesc->Texture3D.MipSlice, 1u);\n      } break;\n      \n      default:\n        return E_INVALIDARG;\n    }\n    \n    if (pDesc->Format == DXGI_FORMAT_UNKNOWN)\n      pDesc->Format = format;\n    \n    switch (pDesc->ViewDimension) {\n      case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:\n        if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice)\n          pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice;\n        break;\n      \n      case D3D11_RTV_DIMENSION_TEXTURE2D:\n        break;\n      \n      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:\n        if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice)\n          pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice;\n        break;\n      \n      case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:\n        if (pDesc->Texture2DMSArray.ArraySize > numLayers - pDesc->Texture2DMSArray.FirstArraySlice)\n          pDesc->Texture2DMSArray.ArraySize = numLayers - pDesc->Texture2DMSArray.FirstArraySlice;\n        break;\n      \n      case D3D11_RTV_DIMENSION_TEXTURE3D:\n        if (pDesc->Texture3D.WSize > numLayers - pDesc->Texture3D.FirstWSlice)\n          pDesc->Texture3D.WSize = numLayers - pDesc->Texture3D.FirstWSlice;\n        break;\n      \n      default:\n        break;\n    }\n    \n    return S_OK;\n  }\n  \n\n  UINT D3D11RenderTargetView::GetPlaneSlice(const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) {\n    switch (pDesc->ViewDimension) {\n      case D3D11_RTV_DIMENSION_TEXTURE2D:\n        return pDesc->Texture2D.PlaneSlice;\n      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:\n        return pDesc->Texture2DArray.PlaneSlice;\n      default:\n        return 0;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_view_rtv.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../d3d10/d3d10_view_rtv.h\"\n\n#include \"d3d11_device_child.h\"\n#include \"d3d11_view.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  \n  /**\n   * \\brief Render target view\n   */\n  class D3D11RenderTargetView : public D3D11DeviceChild<ID3D11RenderTargetView1> {\n    \n  public:\n    \n    D3D11RenderTargetView(\n            D3D11Device*                      pDevice,\n            ID3D11Resource*                   pResource,\n      const D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc);\n    \n    ~D3D11RenderTargetView();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetResource(ID3D11Resource** ppResource) final;\n    \n    void STDMETHODCALLTYPE GetDesc(D3D11_RENDER_TARGET_VIEW_DESC* pDesc) final;\n\n    void STDMETHODCALLTYPE GetDesc1(D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) final;\n\n    const D3D11_VK_VIEW_INFO& GetViewInfo() const {\n      return m_info;\n    }\n\n    BOOL HasBindFlag(UINT Flags) const {\n      return m_info.BindFlags & Flags;\n    }\n\n    D3D11_RESOURCE_DIMENSION GetResourceType() const {\n      D3D11_RESOURCE_DIMENSION type;\n      m_resource->GetType(&type);\n      return type;\n    }\n    \n    Rc<DxvkImageView> GetImageView() const {\n      return m_view;\n    }\n    \n    UINT GetSampleCount() const {\n      return UINT(m_view->image()->info().sampleCount);\n    }\n\n    D3D10RenderTargetView* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n\n    static HRESULT GetDescFromResource(\n            ID3D11Resource*                   pResource,\n            D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc);\n    \n    static D3D11_RENDER_TARGET_VIEW_DESC1 PromoteDesc(\n      const D3D11_RENDER_TARGET_VIEW_DESC*    pDesc,\n            UINT                              Plane);\n    \n    static HRESULT NormalizeDesc(\n            ID3D11Resource*                   pResource,\n            D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc);\n    \n    static UINT GetPlaneSlice(\n      const D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc);\n\n  private:\n    \n    ID3D11Resource*                   m_resource;\n    D3D11_RENDER_TARGET_VIEW_DESC1    m_desc;\n    D3D11_VK_VIEW_INFO                m_info;\n    Rc<DxvkImageView>                 m_view;\n    D3D10RenderTargetView             m_d3d10;\n\n    D3DDestructionNotifier            m_destructionNotifier;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_view_srv.cpp",
    "content": "#include \"d3d11_device.h\"\n#include \"d3d11_buffer.h\"\n#include \"d3d11_resource.h\"\n#include \"d3d11_texture.h\"\n#include \"d3d11_view_srv.h\"\n\nnamespace dxvk {\n  \n  D3D11ShaderResourceView::D3D11ShaderResourceView(\n          D3D11Device*                      pDevice,\n          ID3D11Resource*                   pResource,\n    const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc)\n  : D3D11DeviceChild<ID3D11ShaderResourceView1>(pDevice),\n    m_resource(pResource), m_desc(*pDesc), m_d3d10(this),\n    m_destructionNotifier(this) {\n    ResourceAddRefPrivate(m_resource);\n\n    D3D11_COMMON_RESOURCE_DESC resourceDesc;\n    GetCommonResourceDesc(pResource, &resourceDesc);\n\n    // Basic view resource info\n    m_info.pResource = pResource;\n    m_info.Dimension = resourceDesc.Dim;\n    m_info.BindFlags = resourceDesc.BindFlags;\n\n    if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      auto buffer = static_cast<D3D11Buffer*>(pResource);\n\n      // Move buffer description to a common struct to\n      // avoid having to handle the two cases separately\n      D3D11_BUFFEREX_SRV bufInfo;\n      \n      if (pDesc->ViewDimension == D3D11_SRV_DIMENSION_BUFFEREX) {\n        bufInfo.FirstElement = pDesc->BufferEx.FirstElement;\n        bufInfo.NumElements  = pDesc->BufferEx.NumElements;\n        bufInfo.Flags        = pDesc->BufferEx.Flags;\n      } else if (pDesc->ViewDimension == D3D11_SRV_DIMENSION_BUFFER) {\n        bufInfo.FirstElement = pDesc->Buffer.FirstElement;\n        bufInfo.NumElements  = pDesc->Buffer.NumElements;\n        bufInfo.Flags        = 0;\n      } else {\n        throw DxvkError(\"D3D11: Invalid view dimension for buffer SRV\");\n      }\n\n      // Fill in buffer view info\n      DxvkBufferViewKey viewInfo;\n      viewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n\n      if (bufInfo.Flags & D3D11_BUFFEREX_SRV_FLAG_RAW) {\n        // Raw buffer view. We'll represent this as a\n        // uniform texel buffer with UINT32 elements.\n        viewInfo.format = VK_FORMAT_R32_UINT;\n        viewInfo.offset = sizeof(uint32_t) * bufInfo.FirstElement;\n        viewInfo.size = sizeof(uint32_t) * bufInfo.NumElements;\n      } else if (pDesc->Format == DXGI_FORMAT_UNKNOWN) {\n        // Structured buffer view\n        viewInfo.format = VK_FORMAT_R32_UINT;\n        viewInfo.offset = buffer->Desc()->StructureByteStride * bufInfo.FirstElement;\n        viewInfo.size = buffer->Desc()->StructureByteStride * bufInfo.NumElements;\n      } else {\n        viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR).Format;\n        \n        const DxvkFormatInfo* formatInfo = lookupFormatInfo(viewInfo.format);\n        viewInfo.offset = formatInfo->elementSize * bufInfo.FirstElement;\n        viewInfo.size = formatInfo->elementSize * bufInfo.NumElements;\n      }\n\n      // Populate view info struct\n      m_info.Buffer.Offset = viewInfo.offset;\n      m_info.Buffer.Length = viewInfo.size;\n\n      // Create underlying buffer view object\n      m_bufferView = buffer->GetBuffer()->createView(viewInfo);\n    } else {\n      auto texture = GetCommonTexture(pResource);\n      auto formatInfo = pDevice->LookupFormat(pDesc->Format, texture->GetFormatMode());\n      \n      DxvkImageViewKey viewInfo;\n      viewInfo.format = formatInfo.Format;\n      viewInfo.aspects = formatInfo.Aspect;\n      viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n      viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle);\n\n      // Shaders expect the stencil value in the G component\n      if (viewInfo.aspects == VK_IMAGE_ASPECT_STENCIL_BIT) {\n        viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle({\n          VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R,\n          VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO });\n      }\n      \n      switch (pDesc->ViewDimension) {\n        case D3D11_SRV_DIMENSION_TEXTURE1D:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_1D;\n          viewInfo.mipIndex   = pDesc->Texture1D.MostDetailedMip;\n          viewInfo.mipCount   = pDesc->Texture1D.MipLevels;\n          viewInfo.layerIndex = 0;\n          viewInfo.layerCount = 1;\n          break;\n          \n        case D3D11_SRV_DIMENSION_TEXTURE1DARRAY:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_1D_ARRAY;\n          viewInfo.mipIndex   = pDesc->Texture1DArray.MostDetailedMip;\n          viewInfo.mipCount   = pDesc->Texture1DArray.MipLevels;\n          viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice;\n          viewInfo.layerCount = pDesc->Texture1DArray.ArraySize;\n          break;\n          \n        case D3D11_SRV_DIMENSION_TEXTURE2D:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n          viewInfo.mipIndex   = pDesc->Texture2D.MostDetailedMip;\n          viewInfo.mipCount   = pDesc->Texture2D.MipLevels;\n          viewInfo.layerIndex = 0;\n          viewInfo.layerCount = 1;\n          break;\n          \n        case D3D11_SRV_DIMENSION_TEXTURE2DARRAY:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n          viewInfo.mipIndex   = pDesc->Texture2DArray.MostDetailedMip;\n          viewInfo.mipCount   = pDesc->Texture2DArray.MipLevels;\n          viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice;\n          viewInfo.layerCount = pDesc->Texture2DArray.ArraySize;\n          break;\n          \n        case D3D11_SRV_DIMENSION_TEXTURE2DMS:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n          viewInfo.mipIndex   = 0;\n          viewInfo.mipCount   = 1;\n          viewInfo.layerIndex = 0;\n          viewInfo.layerCount = 1;\n          break;\n          \n        case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n          viewInfo.mipIndex   = 0;\n          viewInfo.mipCount   = 1;\n          viewInfo.layerIndex = pDesc->Texture2DMSArray.FirstArraySlice;\n          viewInfo.layerCount = pDesc->Texture2DMSArray.ArraySize;\n          break;\n          \n        case D3D11_SRV_DIMENSION_TEXTURE3D:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_3D;\n          viewInfo.mipIndex   = pDesc->Texture3D.MostDetailedMip;\n          viewInfo.mipCount   = pDesc->Texture3D.MipLevels;\n          viewInfo.layerIndex = 0;\n          viewInfo.layerCount = 1;\n          break;\n          \n        case D3D11_SRV_DIMENSION_TEXTURECUBE: {\n          const bool cubeArraysEnabled = pDevice->GetDXVKDevice()->features().core.features.imageCubeArray;\n          viewInfo.viewType   = cubeArraysEnabled ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE;\n          viewInfo.mipIndex   = pDesc->TextureCube.MostDetailedMip;\n          viewInfo.mipCount   = pDesc->TextureCube.MipLevels;\n          viewInfo.layerIndex = 0;\n          viewInfo.layerCount = 6;\n        } break;\n          \n        case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;\n          viewInfo.mipIndex   = pDesc->TextureCubeArray.MostDetailedMip;\n          viewInfo.mipCount   = pDesc->TextureCubeArray.MipLevels;\n          viewInfo.layerIndex = pDesc->TextureCubeArray.First2DArrayFace;\n          viewInfo.layerCount = pDesc->TextureCubeArray.NumCubes * 6;\n          break;\n          \n        default:\n          throw DxvkError(\"D3D11: Invalid view dimension for image SRV\");\n      }\n      \n      if (texture->GetPlaneCount() > 1)\n        viewInfo.aspects = vk::getPlaneAspect(GetPlaneSlice(pDesc));\n\n      // Populate view info struct\n      m_info.Image.Aspects   = viewInfo.aspects;\n      m_info.Image.MinLevel  = viewInfo.mipIndex;\n      m_info.Image.MinLayer  = viewInfo.layerIndex;\n      m_info.Image.NumLevels = viewInfo.mipCount;\n      m_info.Image.NumLayers = viewInfo.layerCount;\n\n      // Create the underlying image view object\n      m_imageView = texture->GetImage()->createView(viewInfo);\n    }\n  }\n  \n  \n  D3D11ShaderResourceView::~D3D11ShaderResourceView() {\n    m_destructionNotifier.Notify();\n\n    ResourceReleasePrivate(m_resource);\n    m_resource = nullptr;\n\n    m_imageView = nullptr;\n    m_bufferView = nullptr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11ShaderResourceView::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11View)\n     || riid == __uuidof(ID3D11ShaderResourceView)\n     || riid == __uuidof(ID3D11ShaderResourceView1)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n    \n    if (riid == __uuidof(ID3D10DeviceChild)\n     || riid == __uuidof(ID3D10View)\n     || riid == __uuidof(ID3D10ShaderResourceView)\n     || riid == __uuidof(ID3D10ShaderResourceView1)) {\n      *ppvObject = ref(&m_d3d10);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11ShaderResourceView), riid)) {\n      Logger::warn(\"D3D11ShaderResourceView::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11ShaderResourceView::GetResource(ID3D11Resource** ppResource) {\n    *ppResource = ref(m_resource);\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11ShaderResourceView::GetDesc(D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc) {\n    pDesc->Format            = m_desc.Format;\n    pDesc->ViewDimension     = m_desc.ViewDimension;\n\n    switch (m_desc.ViewDimension) {\n      case D3D11_SRV_DIMENSION_UNKNOWN:\n        break;\n\n      case D3D11_SRV_DIMENSION_BUFFER:\n        pDesc->Buffer = m_desc.Buffer;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE1D:\n        pDesc->Texture1D = m_desc.Texture1D;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE1DARRAY:\n        pDesc->Texture1DArray = m_desc.Texture1DArray;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE2D:\n        pDesc->Texture2D.MostDetailedMip = m_desc.Texture2D.MostDetailedMip;\n        pDesc->Texture2D.MipLevels       = m_desc.Texture2D.MipLevels;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE2DARRAY:\n        pDesc->Texture2DArray.MostDetailedMip = m_desc.Texture2DArray.MostDetailedMip;\n        pDesc->Texture2DArray.MipLevels       = m_desc.Texture2DArray.MipLevels;\n        pDesc->Texture2DArray.FirstArraySlice = m_desc.Texture2DArray.FirstArraySlice;\n        pDesc->Texture2DArray.ArraySize       = m_desc.Texture2DArray.ArraySize;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE2DMS:\n        pDesc->Texture2DMS = m_desc.Texture2DMS;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY:\n        pDesc->Texture2DMSArray = m_desc.Texture2DMSArray;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE3D:\n        pDesc->Texture3D = m_desc.Texture3D;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURECUBE:\n        pDesc->TextureCube = m_desc.TextureCube;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY:\n        pDesc->TextureCubeArray = m_desc.TextureCubeArray;\n        break;\n\n      case D3D11_SRV_DIMENSION_BUFFEREX:\n        pDesc->BufferEx = m_desc.BufferEx;\n        break;\n    }\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11ShaderResourceView::GetDesc1(D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) {\n    *pDesc = m_desc;\n  }\n  \n  \n  HRESULT D3D11ShaderResourceView::GetDescFromResource(\n          ID3D11Resource*                   pResource,\n          D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) {\n    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resourceDim);\n    \n    switch (resourceDim) {\n      case D3D11_RESOURCE_DIMENSION_BUFFER: {\n        D3D11_BUFFER_DESC bufferDesc;\n        static_cast<D3D11Buffer*>(pResource)->GetDesc(&bufferDesc);\n        \n        if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {\n          pDesc->Format              = DXGI_FORMAT_UNKNOWN;\n          pDesc->ViewDimension       = D3D11_SRV_DIMENSION_BUFFER;\n          pDesc->Buffer.FirstElement = 0;\n          pDesc->Buffer.NumElements  = bufferDesc.ByteWidth / bufferDesc.StructureByteStride;\n          return S_OK;\n        }\n      } return E_INVALIDARG;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        D3D11_TEXTURE1D_DESC resourceDesc;\n        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format = resourceDesc.Format;\n        \n        if (resourceDesc.ArraySize == 1) {\n          pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;\n          pDesc->Texture1D.MostDetailedMip = 0;\n          pDesc->Texture1D.MipLevels       = resourceDesc.MipLevels;\n        } else {\n          pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY;\n          pDesc->Texture1DArray.MostDetailedMip = 0;\n          pDesc->Texture1DArray.MipLevels       = resourceDesc.MipLevels;\n          pDesc->Texture1DArray.FirstArraySlice = 0;\n          pDesc->Texture1DArray.ArraySize       = resourceDesc.ArraySize;\n        }\n      } return S_OK;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        D3D11_TEXTURE2D_DESC resourceDesc;\n        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format = resourceDesc.Format;\n        \n        if (resourceDesc.SampleDesc.Count == 1) {\n          if (resourceDesc.ArraySize == 1) {\n            pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;\n            pDesc->Texture2D.MostDetailedMip = 0;\n            pDesc->Texture2D.MipLevels       = resourceDesc.MipLevels;\n            pDesc->Texture2D.PlaneSlice      = 0;\n          } else {\n            pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;\n            pDesc->Texture2DArray.MostDetailedMip = 0;\n            pDesc->Texture2DArray.MipLevels       = resourceDesc.MipLevels;\n            pDesc->Texture2DArray.FirstArraySlice = 0;\n            pDesc->Texture2DArray.ArraySize       = resourceDesc.ArraySize;\n            pDesc->Texture2DArray.PlaneSlice      = 0;\n          }\n        } else {\n          if (resourceDesc.ArraySize == 1) {\n            pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;\n          } else {\n            pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY;\n            pDesc->Texture2DMSArray.FirstArraySlice = 0;\n            pDesc->Texture2DMSArray.ArraySize       = resourceDesc.ArraySize;\n          }\n        }\n      } return S_OK;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {\n        D3D11_TEXTURE3D_DESC resourceDesc;\n        static_cast<D3D11Texture3D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format = resourceDesc.Format;\n        pDesc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;\n        pDesc->Texture3D.MostDetailedMip = 0;\n        pDesc->Texture3D.MipLevels       = resourceDesc.MipLevels;\n      } return S_OK;\n      \n      default:\n        Logger::err(str::format(\n          \"D3D11: Unsupported dimension for shader resource view: \",\n          resourceDim));\n        return E_INVALIDARG;\n    }\n  }\n  \n  \n  D3D11_SHADER_RESOURCE_VIEW_DESC1 D3D11ShaderResourceView::PromoteDesc(\n    const D3D11_SHADER_RESOURCE_VIEW_DESC*  pDesc,\n          UINT                              Plane) {\n    D3D11_SHADER_RESOURCE_VIEW_DESC1 dstDesc;\n    dstDesc.Format            = pDesc->Format;\n    dstDesc.ViewDimension     = pDesc->ViewDimension;\n\n    switch (pDesc->ViewDimension) {\n      case D3D11_SRV_DIMENSION_UNKNOWN:\n        break;\n\n      case D3D11_SRV_DIMENSION_BUFFER:\n        dstDesc.Buffer = pDesc->Buffer;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE1D:\n        dstDesc.Texture1D = pDesc->Texture1D;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE1DARRAY:\n        dstDesc.Texture1DArray = pDesc->Texture1DArray;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE2D:\n        dstDesc.Texture2D.MostDetailedMip = pDesc->Texture2D.MostDetailedMip;\n        dstDesc.Texture2D.MipLevels       = pDesc->Texture2D.MipLevels;\n        dstDesc.Texture2D.PlaneSlice      = Plane;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE2DARRAY:\n        dstDesc.Texture2DArray.MostDetailedMip = pDesc->Texture2DArray.MostDetailedMip;\n        dstDesc.Texture2DArray.MipLevels       = pDesc->Texture2DArray.MipLevels;\n        dstDesc.Texture2DArray.FirstArraySlice = pDesc->Texture2DArray.FirstArraySlice;\n        dstDesc.Texture2DArray.ArraySize       = pDesc->Texture2DArray.ArraySize;\n        dstDesc.Texture2DArray.PlaneSlice      = Plane;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE2DMS:\n        dstDesc.Texture2DMS = pDesc->Texture2DMS;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY:\n        dstDesc.Texture2DMSArray = pDesc->Texture2DMSArray;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE3D:\n        dstDesc.Texture3D = pDesc->Texture3D;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURECUBE:\n        dstDesc.TextureCube = pDesc->TextureCube;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY:\n        dstDesc.TextureCubeArray = pDesc->TextureCubeArray;\n        break;\n\n      case D3D11_SRV_DIMENSION_BUFFEREX:\n        dstDesc.BufferEx = pDesc->BufferEx;\n        break;\n    }\n\n    return dstDesc;\n  }\n\n\n  HRESULT D3D11ShaderResourceView::NormalizeDesc(\n          ID3D11Resource*                   pResource,\n          D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) {\n    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resourceDim);\n    \n    DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;\n    uint32_t mipLevels = 0;\n    uint32_t numLayers = 0;\n    \n    switch (resourceDim) {\n      case D3D11_RESOURCE_DIMENSION_BUFFER: {\n        if (pDesc->ViewDimension != D3D11_SRV_DIMENSION_BUFFER\n         && pDesc->ViewDimension != D3D11_SRV_DIMENSION_BUFFEREX) {\n          Logger::err(\"D3D11: Incompatible view dimension for Buffer\");\n          return E_INVALIDARG;\n        }\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        D3D11_TEXTURE1D_DESC resourceDesc;\n        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE1D\n         && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE1DARRAY) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture1D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        mipLevels = resourceDesc.MipLevels;\n        numLayers = resourceDesc.ArraySize;\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        D3D11_TEXTURE2D_DESC resourceDesc;\n        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE2D\n         && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE2DARRAY\n         && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE2DMS\n         && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY\n         && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURECUBE\n         && pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURECUBEARRAY) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture2D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        mipLevels = resourceDesc.MipLevels;\n        numLayers = resourceDesc.ArraySize;\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {\n        D3D11_TEXTURE3D_DESC resourceDesc;\n        static_cast<D3D11Texture3D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_SRV_DIMENSION_TEXTURE3D) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture3D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        mipLevels = resourceDesc.MipLevels;\n        numLayers = 1;\n      } break;\n      \n      default:\n        return E_INVALIDARG;\n    }\n    \n    if (pDesc->Format == DXGI_FORMAT_UNKNOWN)\n      pDesc->Format = format;\n    \n    switch (pDesc->ViewDimension) {\n      case D3D11_SRV_DIMENSION_BUFFER:\n        if (pDesc->Buffer.NumElements == 0)\n          return E_INVALIDARG;\n        break;\n\n      case D3D11_SRV_DIMENSION_BUFFEREX:\n        if (pDesc->BufferEx.NumElements == 0)\n          return E_INVALIDARG;\n        break;\n\n      case D3D11_SRV_DIMENSION_TEXTURE1D:\n        if (pDesc->Texture1D.MipLevels > mipLevels - pDesc->Texture1D.MostDetailedMip)\n          pDesc->Texture1D.MipLevels = mipLevels - pDesc->Texture1D.MostDetailedMip;\n        break;\n      \n      case D3D11_SRV_DIMENSION_TEXTURE1DARRAY:\n        if (pDesc->Texture1DArray.MipLevels > mipLevels - pDesc->Texture1DArray.MostDetailedMip)\n          pDesc->Texture1DArray.MipLevels = mipLevels - pDesc->Texture1DArray.MostDetailedMip;\n        if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice)\n          pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice;\n        break;\n      \n      case D3D11_SRV_DIMENSION_TEXTURE2D:\n        if (pDesc->Texture2D.MipLevels > mipLevels - pDesc->Texture2D.MostDetailedMip)\n          pDesc->Texture2D.MipLevels = mipLevels - pDesc->Texture2D.MostDetailedMip;\n        break;\n      \n      case D3D11_SRV_DIMENSION_TEXTURE2DARRAY:\n        if (pDesc->Texture2DArray.MipLevels > mipLevels - pDesc->Texture2DArray.MostDetailedMip)\n          pDesc->Texture2DArray.MipLevels = mipLevels - pDesc->Texture2DArray.MostDetailedMip;\n        if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice)\n          pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice;\n        break;\n      \n      case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY:\n        if (pDesc->Texture2DMSArray.ArraySize > numLayers - pDesc->Texture2DMSArray.FirstArraySlice)\n          pDesc->Texture2DMSArray.ArraySize = numLayers - pDesc->Texture2DMSArray.FirstArraySlice;\n        break;\n      \n      case D3D11_SRV_DIMENSION_TEXTURECUBE:\n        if (pDesc->TextureCube.MipLevels > mipLevels - pDesc->TextureCube.MostDetailedMip)\n          pDesc->TextureCube.MipLevels = mipLevels - pDesc->TextureCube.MostDetailedMip;\n        break;\n      \n      case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY:\n        if (pDesc->TextureCubeArray.MipLevels > mipLevels - pDesc->TextureCubeArray.MostDetailedMip)\n          pDesc->TextureCubeArray.MipLevels = mipLevels - pDesc->TextureCubeArray.MostDetailedMip;\n        if (pDesc->TextureCubeArray.NumCubes > (numLayers - pDesc->TextureCubeArray.First2DArrayFace) / 6)\n          pDesc->TextureCubeArray.NumCubes = (numLayers - pDesc->TextureCubeArray.First2DArrayFace) / 6;\n        break;\n      \n      case D3D11_SRV_DIMENSION_TEXTURE3D:\n        if (pDesc->Texture3D.MipLevels > mipLevels - pDesc->Texture3D.MostDetailedMip)\n          pDesc->Texture3D.MipLevels = mipLevels - pDesc->Texture3D.MostDetailedMip;\n        break;\n      \n      default:\n        break;\n    }\n    \n    return S_OK;\n  }\n  \n\n  UINT D3D11ShaderResourceView::GetPlaneSlice(const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) {\n    switch (pDesc->ViewDimension) {\n      case D3D11_SRV_DIMENSION_TEXTURE2D:\n        return pDesc->Texture2D.PlaneSlice;\n      case D3D11_SRV_DIMENSION_TEXTURE2DARRAY:\n        return pDesc->Texture2DArray.PlaneSlice;\n      default:\n        return 0;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d11/d3d11_view_srv.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../d3d10/d3d10_view_srv.h\"\n\n#include \"d3d11_device_child.h\"\n#include \"d3d11_view.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  \n  /**\n   * \\brief Shader resource view\n   */\n  class D3D11ShaderResourceView : public D3D11DeviceChild<ID3D11ShaderResourceView1> {\n    \n  public:\n    \n    D3D11ShaderResourceView(\n            D3D11Device*                      pDevice,\n            ID3D11Resource*                   pResource,\n      const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc);\n    \n    ~D3D11ShaderResourceView();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetResource(ID3D11Resource** ppResource) final;\n    \n    void STDMETHODCALLTYPE GetDesc(D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc) final;\n\n    void STDMETHODCALLTYPE GetDesc1(D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc) final;\n    \n    const D3D11_VK_VIEW_INFO& GetViewInfo() const {\n      return m_info;\n    }\n\n    BOOL TestHazards() const {\n      return m_info.BindFlags & (D3D11_BIND_RENDER_TARGET | D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_UNORDERED_ACCESS);\n    }\n\n    D3D11_RESOURCE_DIMENSION GetResourceType() const {\n      D3D11_RESOURCE_DIMENSION type;\n      m_resource->GetType(&type);\n      return type;\n    }\n\n    D3D11_COMMON_RESOURCE_DESC GetResourceDesc() const {\n      D3D11_COMMON_RESOURCE_DESC desc;\n      GetCommonResourceDesc(m_resource, &desc);\n      return desc;\n    }\n    \n    Rc<DxvkBufferView> GetBufferView() const {\n      return m_bufferView;\n    }\n    \n    Rc<DxvkImageView> GetImageView() const {\n      return m_imageView;\n    }\n\n    D3D10ShaderResourceView* GetD3D10Iface() {\n      return &m_d3d10;\n    }\n    \n    static HRESULT GetDescFromResource(\n            ID3D11Resource*                   pResource,\n            D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc);\n    \n    static D3D11_SHADER_RESOURCE_VIEW_DESC1 PromoteDesc(\n      const D3D11_SHADER_RESOURCE_VIEW_DESC*  pDesc,\n            UINT                              Plane);\n    \n    static HRESULT NormalizeDesc(\n            ID3D11Resource*                   pResource,\n            D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc);\n\n    static UINT GetPlaneSlice(\n      const D3D11_SHADER_RESOURCE_VIEW_DESC1* pDesc);\n\n  private:\n    \n    ID3D11Resource*                   m_resource;\n    D3D11_SHADER_RESOURCE_VIEW_DESC1  m_desc;\n    D3D11_VK_VIEW_INFO                m_info;\n    Rc<DxvkBufferView>                m_bufferView;\n    Rc<DxvkImageView>                 m_imageView;\n    D3D10ShaderResourceView           m_d3d10;\n\n    D3DDestructionNotifier            m_destructionNotifier;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_view_uav.cpp",
    "content": "#include \"d3d11_device.h\"\n#include \"d3d11_buffer.h\"\n#include \"d3d11_resource.h\"\n#include \"d3d11_texture.h\"\n#include \"d3d11_view_uav.h\"\n\nnamespace dxvk {\n  \n  D3D11UnorderedAccessView::D3D11UnorderedAccessView(\n          D3D11Device*                       pDevice,\n          ID3D11Resource*                    pResource,\n    const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc)\n  : D3D11DeviceChild<ID3D11UnorderedAccessView1>(pDevice),\n    m_resource(pResource), m_desc(*pDesc),\n    m_destructionNotifier(this) {\n    ResourceAddRefPrivate(m_resource);\n\n    D3D11_COMMON_RESOURCE_DESC resourceDesc;\n    GetCommonResourceDesc(pResource, &resourceDesc);\n    \n    // Basic view resource info\n    m_info.pResource = pResource;\n    m_info.Dimension = resourceDesc.Dim;\n    m_info.BindFlags = resourceDesc.BindFlags;\n\n    if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) {\n      auto buffer = static_cast<D3D11Buffer*>(pResource);\n      \n      DxvkBufferViewKey viewInfo;\n      viewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;\n      \n      if (pDesc->Buffer.Flags & D3D11_BUFFEREX_SRV_FLAG_RAW) {\n        viewInfo.format = VK_FORMAT_R32_UINT;\n        viewInfo.offset = sizeof(uint32_t) * pDesc->Buffer.FirstElement;\n        viewInfo.size = sizeof(uint32_t) * pDesc->Buffer.NumElements;\n      } else if (pDesc->Format == DXGI_FORMAT_UNKNOWN) {\n        viewInfo.format = VK_FORMAT_R32_UINT;\n        viewInfo.offset = buffer->Desc()->StructureByteStride * pDesc->Buffer.FirstElement;\n        viewInfo.size = buffer->Desc()->StructureByteStride * pDesc->Buffer.NumElements;\n      } else {\n        viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR).Format;\n        \n        const DxvkFormatInfo* formatInfo = lookupFormatInfo(viewInfo.format);\n        viewInfo.offset = formatInfo->elementSize * pDesc->Buffer.FirstElement;\n        viewInfo.size = formatInfo->elementSize * pDesc->Buffer.NumElements;\n      }\n      \n      if (pDesc->Buffer.Flags & (D3D11_BUFFER_UAV_FLAG_APPEND | D3D11_BUFFER_UAV_FLAG_COUNTER))\n        m_counterView = CreateCounterBufferView();\n      \n      // Populate view info struct\n      m_info.Buffer.Offset = viewInfo.offset;\n      m_info.Buffer.Length = viewInfo.size;\n\n      m_bufferView = buffer->GetBuffer()->createView(viewInfo);\n    } else {\n      auto texture = GetCommonTexture(pResource);\n      auto formatInfo = pDevice->LookupFormat(pDesc->Format, texture->GetFormatMode());\n      \n      DxvkImageViewKey viewInfo;\n      viewInfo.format = formatInfo.Format;\n      viewInfo.layout = VK_IMAGE_LAYOUT_GENERAL;\n      viewInfo.aspects = formatInfo.Aspect;\n      viewInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT;\n\n      if (!util::isIdentityMapping(formatInfo.Swizzle))\n        Logger::warn(str::format(\"UAV format \", pDesc->Format, \" has non-identity swizzle, but UAV swizzles are not supported\"));\n\n      switch (pDesc->ViewDimension) {\n        case D3D11_UAV_DIMENSION_TEXTURE1D:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_1D;\n          viewInfo.mipIndex   = pDesc->Texture1D.MipSlice;\n          viewInfo.mipCount   = 1;\n          viewInfo.layerIndex = 0;\n          viewInfo.layerCount = 1;\n          break;\n          \n        case D3D11_UAV_DIMENSION_TEXTURE1DARRAY:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_1D_ARRAY;\n          viewInfo.mipIndex   = pDesc->Texture1DArray.MipSlice;\n          viewInfo.mipCount   = 1;\n          viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice;\n          viewInfo.layerCount = pDesc->Texture1DArray.ArraySize;\n          break;\n          \n        case D3D11_UAV_DIMENSION_TEXTURE2D:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n          viewInfo.mipIndex   = pDesc->Texture2D.MipSlice;\n          viewInfo.mipCount   = 1;\n          viewInfo.layerIndex = 0;\n          viewInfo.layerCount = 1;\n          break;\n          \n        case D3D11_UAV_DIMENSION_TEXTURE2DARRAY:\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n          viewInfo.mipIndex   = pDesc->Texture2DArray.MipSlice;\n          viewInfo.mipCount   = 1;\n          viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice;\n          viewInfo.layerCount = pDesc->Texture2DArray.ArraySize;\n          break;\n          \n        case D3D11_UAV_DIMENSION_TEXTURE3D:\n          // FIXME we actually have to map this to a\n          // 2D array view in order to support W slices\n          viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_3D;\n          viewInfo.mipIndex   = pDesc->Texture3D.MipSlice;\n          viewInfo.mipCount   = 1;\n          viewInfo.layerIndex = 0;\n          viewInfo.layerCount = 1;\n          break;\n          \n        default:\n          throw DxvkError(\"D3D11: Invalid view dimension for image UAV\");\n      }\n\n      if (texture->GetPlaneCount() > 1)\n        viewInfo.aspects = vk::getPlaneAspect(GetPlaneSlice(pDesc));\n\n      // Populate view info struct\n      m_info.Image.Aspects   = viewInfo.aspects;\n      m_info.Image.MinLevel  = viewInfo.mipIndex;\n      m_info.Image.MinLayer  = viewInfo.layerIndex;\n      m_info.Image.NumLevels = viewInfo.mipCount;\n      m_info.Image.NumLayers = viewInfo.layerCount;\n\n      m_imageView = GetCommonTexture(pResource)->GetImage()->createView(viewInfo);\n    }\n  }\n  \n  \n  D3D11UnorderedAccessView::~D3D11UnorderedAccessView() {\n    m_destructionNotifier.Notify();\n\n    ResourceReleasePrivate(m_resource);\n    m_resource = nullptr;\n\n    m_bufferView = nullptr;\n    m_counterView = nullptr;\n    m_imageView = nullptr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE D3D11UnorderedAccessView::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(ID3D11DeviceChild)\n     || riid == __uuidof(ID3D11View)\n     || riid == __uuidof(ID3D11UnorderedAccessView)\n     || riid == __uuidof(ID3D11UnorderedAccessView1)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(ID3D11UnorderedAccessView), riid)) {\n      Logger::warn(\"D3D11UnorderedAccessView::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11UnorderedAccessView::GetResource(ID3D11Resource** ppResource) {\n    *ppResource = ref(m_resource);\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11UnorderedAccessView::GetDesc(D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc) {\n    pDesc->Format            = m_desc.Format;\n    pDesc->ViewDimension     = m_desc.ViewDimension;\n\n    switch (m_desc.ViewDimension) {\n      case D3D11_UAV_DIMENSION_UNKNOWN:\n        break;\n\n      case D3D11_UAV_DIMENSION_BUFFER:\n        pDesc->Buffer = m_desc.Buffer;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE1D:\n        pDesc->Texture1D = m_desc.Texture1D;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE1DARRAY:\n        pDesc->Texture1DArray = m_desc.Texture1DArray;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE2D:\n        pDesc->Texture2D.MipSlice = m_desc.Texture2D.MipSlice;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE2DARRAY:\n        pDesc->Texture2DArray.MipSlice        = m_desc.Texture2DArray.MipSlice;\n        pDesc->Texture2DArray.FirstArraySlice = m_desc.Texture2DArray.FirstArraySlice;\n        pDesc->Texture2DArray.ArraySize       = m_desc.Texture2DArray.ArraySize;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE3D:\n        pDesc->Texture3D = m_desc.Texture3D;\n        break;\n    }\n  }\n  \n  \n  void STDMETHODCALLTYPE D3D11UnorderedAccessView::GetDesc1(D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) {\n    *pDesc = m_desc;\n  }\n  \n  \n  HRESULT D3D11UnorderedAccessView::GetDescFromResource(\n          ID3D11Resource*                    pResource,\n          D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) {\n    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resourceDim);\n    \n    switch (resourceDim) {\n      case D3D11_RESOURCE_DIMENSION_BUFFER: {\n        D3D11_BUFFER_DESC bufferDesc;\n        static_cast<D3D11Buffer*>(pResource)->GetDesc(&bufferDesc);\n        \n        if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {\n          pDesc->Format              = DXGI_FORMAT_UNKNOWN;\n          pDesc->ViewDimension       = D3D11_UAV_DIMENSION_BUFFER;\n          pDesc->Buffer.FirstElement = 0;\n          pDesc->Buffer.NumElements  = bufferDesc.ByteWidth / bufferDesc.StructureByteStride;\n          pDesc->Buffer.Flags = 0;\n          return S_OK;\n        }\n      } return E_INVALIDARG;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        D3D11_TEXTURE1D_DESC resourceDesc;\n        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format = resourceDesc.Format;\n        \n        if (resourceDesc.ArraySize == 1) {\n          pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE1D;\n          pDesc->Texture1D.MipSlice = 0;\n        } else {\n          pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE1DARRAY;\n          pDesc->Texture1DArray.MipSlice        = 0;\n          pDesc->Texture1DArray.FirstArraySlice = 0;\n          pDesc->Texture1DArray.ArraySize       = resourceDesc.ArraySize;\n        }\n      } return S_OK;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        D3D11_TEXTURE2D_DESC resourceDesc;\n        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format = resourceDesc.Format;\n        \n        if (resourceDesc.ArraySize == 1) {\n          pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;\n          pDesc->Texture2D.MipSlice   = 0;\n          pDesc->Texture2D.PlaneSlice = 0;\n        } else {\n          pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;\n          pDesc->Texture2DArray.MipSlice        = 0;\n          pDesc->Texture2DArray.FirstArraySlice = 0;\n          pDesc->Texture2DArray.ArraySize       = resourceDesc.ArraySize;\n          pDesc->Texture2DArray.PlaneSlice      = 0;\n        }\n      } return S_OK;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {\n        D3D11_TEXTURE3D_DESC resourceDesc;\n        static_cast<D3D11Texture3D*>(pResource)->GetDesc(&resourceDesc);\n        \n        pDesc->Format        = resourceDesc.Format;\n        pDesc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;\n        pDesc->Texture3D.MipSlice = 0;\n        pDesc->Texture3D.WSize    = resourceDesc.Depth;\n      } return S_OK;\n      \n      default:\n        Logger::err(str::format(\n          \"D3D11: Unsupported dimension for unordered access view: \",\n          resourceDim));\n        return E_INVALIDARG;\n    }\n  }\n  \n  \n  D3D11_UNORDERED_ACCESS_VIEW_DESC1 D3D11UnorderedAccessView::PromoteDesc(\n    const D3D11_UNORDERED_ACCESS_VIEW_DESC*  pDesc,\n          UINT                               Plane) {\n    D3D11_UNORDERED_ACCESS_VIEW_DESC1 dstDesc;\n    dstDesc.Format            = pDesc->Format;\n    dstDesc.ViewDimension     = pDesc->ViewDimension;\n\n    switch (pDesc->ViewDimension) {\n      case D3D11_UAV_DIMENSION_UNKNOWN:\n        break;\n\n      case D3D11_UAV_DIMENSION_BUFFER:\n        dstDesc.Buffer = pDesc->Buffer;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE1D:\n        dstDesc.Texture1D = pDesc->Texture1D;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE1DARRAY:\n        dstDesc.Texture1DArray = pDesc->Texture1DArray;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE2D:\n        dstDesc.Texture2D.MipSlice   = pDesc->Texture2D.MipSlice;\n        dstDesc.Texture2D.PlaneSlice = Plane;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE2DARRAY:\n        dstDesc.Texture2DArray.MipSlice        = pDesc->Texture2DArray.MipSlice;\n        dstDesc.Texture2DArray.FirstArraySlice = pDesc->Texture2DArray.FirstArraySlice;\n        dstDesc.Texture2DArray.ArraySize       = pDesc->Texture2DArray.ArraySize;\n        dstDesc.Texture2DArray.PlaneSlice      = Plane;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE3D:\n        dstDesc.Texture3D = pDesc->Texture3D;\n        break;\n    }\n\n    return dstDesc;\n  }\n\n\n  HRESULT D3D11UnorderedAccessView::NormalizeDesc(\n          ID3D11Resource*                    pResource,\n          D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) {\n    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;\n    pResource->GetType(&resourceDim);\n    \n    DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;\n    uint32_t numLayers = 0;\n    \n    switch (resourceDim) {\n      case D3D11_RESOURCE_DIMENSION_BUFFER: {\n        if (pDesc->ViewDimension != D3D11_UAV_DIMENSION_BUFFER) {\n          Logger::err(\"D3D11: Incompatible view dimension for Buffer\");\n          return E_INVALIDARG;\n        }\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {\n        D3D11_TEXTURE1D_DESC resourceDesc;\n        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE1D\n         && pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE1DARRAY) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture1D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        numLayers = resourceDesc.ArraySize;\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {\n        D3D11_TEXTURE2D_DESC resourceDesc;\n        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE2D\n         && pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE2DARRAY) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture2D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        numLayers = resourceDesc.ArraySize;\n      } break;\n      \n      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {\n        D3D11_TEXTURE3D_DESC resourceDesc;\n        static_cast<D3D11Texture3D*>(pResource)->GetDesc(&resourceDesc);\n        \n        if (pDesc->ViewDimension != D3D11_UAV_DIMENSION_TEXTURE3D) {\n          Logger::err(\"D3D11: Incompatible view dimension for Texture3D\");\n          return E_INVALIDARG;\n        }\n        \n        format    = resourceDesc.Format;\n        numLayers = std::max(resourceDesc.Depth >> pDesc->Texture3D.MipSlice, 1u);\n      } break;\n      \n      default:\n        return E_INVALIDARG;\n    }\n    \n    if (pDesc->Format == DXGI_FORMAT_UNKNOWN)\n      pDesc->Format = format;\n    \n    switch (pDesc->ViewDimension) {\n      case D3D11_UAV_DIMENSION_BUFFER:\n        if (pDesc->Buffer.NumElements == 0)\n          return E_INVALIDARG;\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE1DARRAY:\n        if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice)\n          pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice;\n        break;\n      \n      case D3D11_UAV_DIMENSION_TEXTURE2D:\n        break;\n\n      case D3D11_UAV_DIMENSION_TEXTURE2DARRAY:\n        if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice)\n          pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice;\n        break;\n      \n      case D3D11_UAV_DIMENSION_TEXTURE3D:\n        if (pDesc->Texture3D.WSize > numLayers - pDesc->Texture3D.FirstWSlice)\n          pDesc->Texture3D.WSize = numLayers - pDesc->Texture3D.FirstWSlice;\n        break;\n      \n      default:\n        break;\n    }\n    \n    return S_OK;\n  }\n\n\n  UINT D3D11UnorderedAccessView::GetPlaneSlice(const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) {\n    switch (pDesc->ViewDimension) {\n      case D3D11_UAV_DIMENSION_TEXTURE2D:\n        return pDesc->Texture2D.PlaneSlice;\n      case D3D11_UAV_DIMENSION_TEXTURE2DARRAY:\n        return pDesc->Texture2DArray.PlaneSlice;\n      default:\n        return 0;\n    }\n  }\n\n\n  Rc<DxvkBufferView> D3D11UnorderedAccessView::CreateCounterBufferView() {\n    Rc<DxvkDevice> device = m_parent->GetDXVKDevice();\n\n    DxvkBufferCreateInfo info;\n    info.size   = sizeof(uint32_t);\n    info.usage  = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                | VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n    info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                | device->getShaderPipelineStages();\n    info.access = VK_ACCESS_TRANSFER_WRITE_BIT\n                | VK_ACCESS_TRANSFER_READ_BIT\n                | VK_ACCESS_SHADER_WRITE_BIT\n                | VK_ACCESS_SHADER_READ_BIT;\n    info.debugName = \"UAV counter\";\n\n    Rc<DxvkBuffer> buffer = device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n    DxvkBufferViewKey viewInfo;\n    viewInfo.format = VK_FORMAT_UNDEFINED;\n    viewInfo.offset = 0;\n    viewInfo.size = sizeof(uint32_t);\n    viewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;\n\n    return buffer->createView(viewInfo);\n  }\n  \n}\n"
  },
  {
    "path": "src/d3d11/d3d11_view_uav.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"d3d11_device_child.h\"\n#include \"d3d11_view.h\"\n\nnamespace dxvk {\n  \n  class D3D11Device;\n  \n  /**\n   * \\brief Unordered access view\n   * \n   * Unordered access views are special in that they can\n   * have counters, which can be used inside shaders to\n   * atomically append or consume structures.\n   */\n  class D3D11UnorderedAccessView : public D3D11DeviceChild<ID3D11UnorderedAccessView1> {\n    \n  public:\n    \n    D3D11UnorderedAccessView(\n            D3D11Device*                       pDevice,\n            ID3D11Resource*                    pResource,\n      const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc);\n    \n    ~D3D11UnorderedAccessView();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final;\n    \n    void STDMETHODCALLTYPE GetResource(ID3D11Resource** ppResource) final;\n    \n    void STDMETHODCALLTYPE GetDesc(D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc) final;\n\n    void STDMETHODCALLTYPE GetDesc1(D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc) final;\n    \n    const D3D11_VK_VIEW_INFO& GetViewInfo() const {\n      return m_info;\n    }\n\n    BOOL HasBindFlag(UINT Flags) const {\n      return m_info.BindFlags & Flags;\n    }\n\n    BOOL HasCounter() const {\n      return m_counterView != nullptr;\n    }\n\n    D3D11_RESOURCE_DIMENSION GetResourceType() const {\n      D3D11_RESOURCE_DIMENSION type;\n      m_resource->GetType(&type);\n      return type;\n    }\n    \n    Rc<DxvkBufferView> GetBufferView() const {\n      return m_bufferView;\n    }\n    \n    Rc<DxvkImageView> GetImageView() const {\n      return m_imageView;\n    }\n    \n    Rc<DxvkBufferView> GetCounterView() const {\n      return m_counterView;\n    }\n    \n    static HRESULT GetDescFromResource(\n            ID3D11Resource*                    pResource,\n            D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc);\n    \n    static D3D11_UNORDERED_ACCESS_VIEW_DESC1 PromoteDesc(\n      const D3D11_UNORDERED_ACCESS_VIEW_DESC*  pDesc,\n            UINT                               Plane);\n    \n    static HRESULT NormalizeDesc(\n            ID3D11Resource*                    pResource,\n            D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc);\n    \n    static UINT GetPlaneSlice(\n      const D3D11_UNORDERED_ACCESS_VIEW_DESC1* pDesc);\n\n  private:\n    \n    ID3D11Resource*                   m_resource;\n    D3D11_UNORDERED_ACCESS_VIEW_DESC1 m_desc;\n    D3D11_VK_VIEW_INFO                m_info;\n    Rc<DxvkBufferView>                m_bufferView;\n    Rc<DxvkImageView>                 m_imageView;\n    Rc<DxvkBufferView>                m_counterView;\n\n    D3DDestructionNotifier            m_destructionNotifier;\n\n    Rc<DxvkBufferView> CreateCounterBufferView();\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/d3d11/meson.build",
    "content": "d3d11_res = wrc_generator.process('version.rc')\n\ndxgi_common_src = [\n  '../dxgi/dxgi_format.cpp',\n]\n\nd3d10_src = [\n  '../d3d10/d3d10_blend.cpp',\n  '../d3d10/d3d10_buffer.cpp',\n  '../d3d10/d3d10_depth_stencil.cpp',\n  '../d3d10/d3d10_device.cpp',\n  '../d3d10/d3d10_input_layout.cpp',\n  '../d3d10/d3d10_multithread.cpp',\n  '../d3d10/d3d10_query.cpp',\n  '../d3d10/d3d10_rasterizer.cpp',\n  '../d3d10/d3d10_sampler.cpp',\n  '../d3d10/d3d10_texture.cpp',\n  '../d3d10/d3d10_util.cpp',\n  '../d3d10/d3d10_view_dsv.cpp',\n  '../d3d10/d3d10_view_rtv.cpp',\n  '../d3d10/d3d10_view_srv.cpp',\n]\n\nd3d11_src = [\n  'd3d11_annotation.cpp',\n  'd3d11_blend.cpp',\n  'd3d11_buffer.cpp',\n  'd3d11_class_linkage.cpp',\n  'd3d11_cmdlist.cpp',\n  'd3d11_context.cpp',\n  'd3d11_context_def.cpp',\n  'd3d11_context_ext.cpp',\n  'd3d11_context_imm.cpp',\n  'd3d11_cuda.cpp',\n  'd3d11_depth_stencil.cpp',\n  'd3d11_device.cpp',\n  'd3d11_enums.cpp',\n  'd3d11_features.cpp',\n  'd3d11_fence.cpp',\n  'd3d11_gdi.cpp',\n  'd3d11_initializer.cpp',\n  'd3d11_input_layout.cpp',\n  'd3d11_interop.cpp',\n  'd3d11_main.cpp',\n  'd3d11_on_12.cpp',\n  'd3d11_options.cpp',\n  'd3d11_query.cpp',\n  'd3d11_rasterizer.cpp',\n  'd3d11_resource.cpp',\n  'd3d11_sampler.cpp',\n  'd3d11_shader.cpp',\n  'd3d11_state.cpp',\n  'd3d11_state_object.cpp',\n  'd3d11_swapchain.cpp',\n  'd3d11_texture.cpp',\n  'd3d11_util.cpp',\n  'd3d11_video.cpp',\n  'd3d11_view_dsv.cpp',\n  'd3d11_view_rtv.cpp',\n  'd3d11_view_srv.cpp',\n  'd3d11_view_uav.cpp',\n]\n\nd3d11_shaders = files([\n  'shaders/d3d11_video_blit_frag.frag',\n  'shaders/d3d11_video_blit_vert.vert',\n])\n\nd3d11_ld_args      = []\nd3d11_link_depends = []\n\nif platform == 'windows'\n  d3d11_dxgi_deps = [ lib_gdi32, lib_dxgi ]\nelse\n  d3d11_ld_args      += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'd3d11.sym') ]\n  d3d11_link_depends += files('d3d11.sym')\n  d3d11_dxgi_deps = [ dxgi_dep ]\nendif\n\nd3d11_dll = shared_library(dxvk_name_prefix+'d3d11', dxgi_common_src + d3d11_src + d3d10_src,\n    glsl_generator.process(d3d11_shaders), d3d11_res,\n  dependencies        : d3d11_dxgi_deps + [ dxvk_dep, dxbc_spirv_dep ],\n  include_directories : dxvk_include_path,\n  install             : true,\n  vs_module_defs      : 'd3d11'+def_spec_ext,\n  link_args           : d3d11_ld_args,\n  link_depends        : [ d3d11_link_depends ],\n  kwargs              : dxvk_so_version,\n)\n\nd3d11_dep = declare_dependency(\n  link_with           : [ d3d11_dll ],\n  include_directories : [ dxvk_include_path ],\n)\n\nif platform != 'windows'\n  pkg.generate(d3d11_dll,\n    filebase: dxvk_pkg_prefix + 'd3d11',\n    subdirs:  'dxvk',\n  )\nendif\n"
  },
  {
    "path": "src/d3d11/shaders/d3d11_video_blit_frag.frag",
    "content": "#version 450\n\n#extension GL_EXT_samplerless_texture_functions : require\n\n#define EXPORT_RGBA (0u)\n#define EXPORT_Y (1u)\n#define EXPORT_CbCr (2u)\n\n// Can't use matrix types here since even a two-row\n// matrix will be padded to 16 bytes per column for\n// absolutely no reason\nlayout(std140, set = 0, binding = 0)\nuniform ubo_t {\n  vec4 color_matrix_r1;\n  vec4 color_matrix_r2;\n  vec4 color_matrix_r3;\n  vec2 coord_matrix_c1;\n  vec2 coord_matrix_c2;\n  vec2 coord_matrix_c3;\n  uvec2 src_offset;\n  uvec2 src_extent;\n  float y_min;\n  float y_max;\n  bool is_planar;\n  uint export_mode;\n};\n\nlayout(location = 0) in vec2 i_texcoord;\nlayout(location = 0) out vec4 o_color;\n\nlayout(set = 0, binding = 1) uniform texture2D s_inputY;\nlayout(set = 0, binding = 2) uniform texture2D s_inputCbCr;\n\nvoid main() {\n  // Transform input texture coordinates to\n  // account for rotation and source rectangle\n  mat3x2 coord_matrix = mat3x2(\n    coord_matrix_c1,\n    coord_matrix_c2,\n    coord_matrix_c3);\n\n  // Load color space transform\n  mat3x4 color_matrix = mat3x4(\n    color_matrix_r1,\n    color_matrix_r2,\n    color_matrix_r3);\n\n  // Compute actual pixel coordinates to sample. We filter\n  // manually in order to avoid bleeding from pixels outside\n  // the source rectangle.\n  vec2 abs_size_y = vec2(textureSize(s_inputY, 0));\n  vec2 abs_size_c = vec2(textureSize(s_inputCbCr, 0));\n\n  vec2 coord = coord_matrix * vec3(i_texcoord, 1.0f);\n  coord -= 0.5f / abs_size_y;\n\n  vec2 size_factor = abs_size_c / abs_size_y;\n\n  vec2 src_lo = vec2(src_offset);\n  vec2 src_hi = vec2(src_offset + src_extent - 1u);\n\n  vec2 abs_coord = coord * abs_size_y;\n  vec2 fract_coord = fract(clamp(abs_coord, src_lo, src_hi));\n\n  vec4 accum = vec4(0.0f, 0.0f, 0.0f, 0.0f);\n\n  for (int i = 0; i < 4; i++) {\n    ivec2 offset = ivec2(i & 1, i >> 1);\n\n    // Compute exact pixel coordinates for the current\n    // iteration and clamp it to the source rectangle.\n    vec2 fetch_coord = clamp(abs_coord + vec2(offset), src_lo, src_hi);\n\n    // Fetch actual pixel color in source color space\n    vec4 color;\n\n    if (is_planar) {\n      color.g  = texelFetch(s_inputY, ivec2(fetch_coord), 0).r;\n      color.rb = texelFetch(s_inputCbCr, ivec2(fetch_coord * size_factor), 0).gr;\n      color.g  = clamp((color.g - y_min) / (y_max - y_min), 0.0f, 1.0f);\n      color.a = 1.0f;\n    } else {\n      color = texelFetch(s_inputY, ivec2(fetch_coord), 0);\n    }\n\n    // Transform color space before accumulation\n    color.rgb = vec4(color.rgb, 1.0f) * color_matrix;\n\n    // Filter and accumulate final pixel color\n    vec2 factor = fract_coord;\n\n    if (offset.x == 0) factor.x = 1.0f - factor.x;\n    if (offset.y == 0) factor.y = 1.0f - factor.y;\n\n    accum += factor.x * factor.y * color;\n  }\n\n  if (export_mode == EXPORT_RGBA)\n    o_color = accum;\n  else if (export_mode == EXPORT_Y)\n    o_color = vec4(accum.g, 0.0, 0.0, 1.0);\n  else if (export_mode == EXPORT_CbCr)\n    o_color = vec4(accum.br, 0.0, 1.0);\n}\n"
  },
  {
    "path": "src/d3d11/shaders/d3d11_video_blit_vert.vert",
    "content": "#version 450\n\nlayout(location = 0) out vec2 o_texcoord;\n\nvoid main() {\n  vec2 coord = vec2(\n    float(gl_VertexIndex & 1) * 2.0f,\n    float(gl_VertexIndex & 2));\n\n  o_texcoord  = coord;\n  gl_Position = vec4(-1.0f + 2.0f * coord, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "src/d3d11/version.rc",
    "content": "#include <windows.h>\n\n// DLL version information.\nVS_VERSION_INFO    VERSIONINFO\nFILEVERSION        10,0,17763,1\nPRODUCTVERSION     10,0,17763,1\nFILEFLAGSMASK      VS_FFI_FILEFLAGSMASK\nFILEFLAGS          0\nFILEOS VOS_NT_WINDOWS32\nFILETYPE VFT_DLL\nFILESUBTYPE VFT2_UNKNOWN\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"080904b0\"\n    BEGIN\n      VALUE \"CompanyName\",      \"DXVK\"\n      VALUE \"FileDescription\",  \"Direct3D 11 Runtime\"\n      VALUE \"FileVersion\",      \"10.0.17763.1 (WinBuild.160101.0800)\"\n      VALUE \"InternalName\",     \"D3D11.dll\"\n      VALUE \"LegalCopyright\",   \"zlib/libpng license\"\n      VALUE \"OriginalFilename\", \"D3D11.dll\"\n      VALUE \"ProductName\",      \"DXVK\"\n      VALUE \"ProductVersion\",   \"10.0.17763.1\"\n    END\n  END\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0809, 1200\n  END\nEND\n\n"
  },
  {
    "path": "src/d3d8/d3d8.def",
    "content": "LIBRARY D3D8.DLL\nEXPORTS\n  ValidatePixelShader @ 2\n  ValidateVertexShader @ 3\n  DebugSetMute @ 4\n  Direct3DCreate8 @ 5\n"
  },
  {
    "path": "src/d3d8/d3d8.sym",
    "content": "{\n  global:\n    ValidatePixelShader;\n    ValidateVertexShader;\n    DebugSetMute;\n    Direct3DCreate8;\n\n  local:\n    *;\n};\n"
  },
  {
    "path": "src/d3d8/d3d8_batch.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n#include \"d3d8_buffer.h\"\n#include \"d3d8_format.h\"\n\n#include <vector>\n#include <cstdint>\n\nnamespace dxvk {\n\n  inline constexpr size_t            D3DPT_COUNT   = size_t(D3DPT_TRIANGLEFAN) + 1;\n  inline constexpr D3DPRIMITIVETYPE  D3DPT_INVALID = D3DPRIMITIVETYPE(0);\n\n  // Vertex buffer that can handle many tiny locks while\n  // still maintaing the lock ordering of direct-mapped buffers.\n  class D3D8BatchBuffer final : public D3D8VertexBuffer {\n\n  public:\n\n    D3D8BatchBuffer(\n        D3D8Device*                         pDevice,\n        D3DPOOL                             Pool,\n        DWORD                               Usage,\n        UINT                                Length,\n        DWORD                               FVF)\n      : D3D8VertexBuffer(pDevice, nullptr, Pool, Usage)\n      , m_data(Length)\n      , m_fvf(FVF) {\n    }\n\n    HRESULT STDMETHODCALLTYPE Lock(\n            UINT   OffsetToLock,\n            UINT   SizeToLock,\n            BYTE** ppbData,\n            DWORD  Flags) final {\n      *ppbData = m_data.data() + OffsetToLock;\n      return D3D_OK;\n    }\n\n    HRESULT STDMETHODCALLTYPE Unlock() final {\n      return D3D_OK;\n    }\n\n    HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) final {\n      if (unlikely(pDesc == nullptr))\n        return D3DERR_INVALIDCALL;\n\n      pDesc->Format = D3DFMT_VERTEXDATA;\n      pDesc->Type   = D3DRTYPE_VERTEXBUFFER;\n      pDesc->Usage  = m_usage;\n      pDesc->Pool   = m_pool;\n      pDesc->Size   = m_data.size();\n      pDesc->FVF    = m_fvf;\n\n      return D3D_OK;\n    }\n\n    void STDMETHODCALLTYPE PreLoad() final {\n    }\n\n    const void* GetPtr(UINT byteOffset = 0) const {\n      return m_data.data() + byteOffset;\n    }\n\n    UINT Size() const {\n      return m_data.size();\n    }\n\n  private:\n\n    std::vector<BYTE> m_data;\n    DWORD             m_fvf;\n\n  };\n\n\n  // Main handler for batching D3D8 draw calls.\n  class D3D8Batcher {\n\n    struct Batch {\n      D3DPRIMITIVETYPE PrimitiveType = D3DPT_INVALID;\n      std::vector<uint16_t> Indices;\n      UINT Offset = 0;\n      UINT MinVertex = std::numeric_limits<uint32_t>::max();\n      UINT MaxVertex = 0;\n      UINT PrimitiveCount = 0;\n      UINT DrawCallCount = 0;\n    };\n\n  public:\n\n    D3D8Batcher(D3D8Device* pDevice8, Com<d3d9::IDirect3DDevice9>&& pDevice9)\n      : m_device8(pDevice8)\n      , m_device(std::move(pDevice9)) {\n    }\n\n    inline D3D8BatchBuffer* CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool) {\n      return ref(new D3D8BatchBuffer(m_device8, Pool, Usage, Length, FVF));\n    }\n\n    inline void StateChange() {\n      if (likely(m_batches.empty()))\n        return;\n      for (auto& draw : m_batches) {\n\n        if (draw.PrimitiveType == D3DPT_INVALID)\n          continue;\n\n        for (auto& index : draw.Indices)\n          index -= draw.MinVertex;\n\n        m_device->DrawIndexedPrimitiveUP(\n          d3d9::D3DPRIMITIVETYPE(draw.PrimitiveType),\n          0,\n          draw.MaxVertex - draw.MinVertex,\n          draw.PrimitiveCount,\n          draw.Indices.data(),\n          d3d9::D3DFMT_INDEX16,\n          m_stream->GetPtr(draw.MinVertex * m_stride),\n          m_stride);\n\n        m_device->SetStreamSource(0, D3D8VertexBuffer::GetD3D9Nullable(m_stream), 0, m_stride);\n        m_device->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(m_indices));\n\n        draw.PrimitiveType = D3DPRIMITIVETYPE(0);\n        draw.Offset = 0;\n        draw.MinVertex = std::numeric_limits<uint32_t>::max();\n        draw.MaxVertex = 0;\n        draw.PrimitiveCount = 0;\n        draw.DrawCallCount = 0;\n      }\n    }\n\n    inline void EndFrame() {\n      // Nothing to be done.\n    }\n\n    inline HRESULT DrawPrimitive(\n            D3DPRIMITIVETYPE PrimitiveType,\n            UINT             StartVertex,\n            UINT             PrimitiveCount) {\n\n      // None of this linestrip or fan malarkey\n      D3DPRIMITIVETYPE batchedPrimType = PrimitiveType;\n      switch (PrimitiveType) {\n        case D3DPT_LINESTRIP:     batchedPrimType = D3DPT_LINELIST; break;\n        case D3DPT_TRIANGLEFAN:   batchedPrimType = D3DPT_TRIANGLELIST; break;\n        default: break;\n      }\n\n      Batch* batch = &m_batches[size_t(batchedPrimType)];\n      batch->PrimitiveType = batchedPrimType;\n\n      switch (PrimitiveType) {\n        case D3DPT_POINTLIST:\n          batch->Indices.resize(batch->Offset + PrimitiveCount);\n          for (uint32_t i = 0; i < PrimitiveCount; i++)\n            batch->Indices[batch->Offset++] = (StartVertex + i);\n          break;\n        case D3DPT_LINELIST:\n          batch->Indices.resize(batch->Offset + PrimitiveCount * 2);\n          for (uint32_t i = 0; i < PrimitiveCount; i++) {\n            batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 0);\n            batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 1);\n          }\n          break;\n        case D3DPT_LINESTRIP:\n          batch->Indices.resize(batch->Offset + PrimitiveCount * 2);\n          for (uint32_t i = 0; i < PrimitiveCount; i++) {\n            batch->Indices[batch->Offset++] = (StartVertex + i + 0);\n            batch->Indices[batch->Offset++] = (StartVertex + i + 1);\n          }\n          break;\n        case D3DPT_TRIANGLELIST:\n          batch->Indices.resize(batch->Offset + PrimitiveCount * 3);\n          for (uint32_t i = 0; i < PrimitiveCount; i++) {\n            batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 0);\n            batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 1);\n            batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 2);\n          }\n          break;\n        case D3DPT_TRIANGLESTRIP:\n          // Join with degenerate triangle\n          // 1 2 3, 3 4, 4 5 6\n          batch->Indices.resize(batch->Offset + PrimitiveCount + 2);\n          if (batch->Offset > 0) {\n            batch->Indices[batch->Offset + 1] = batch->Indices[batch->Offset-2];\n            batch->Indices[batch->Offset += 2] = StartVertex;\n          }\n          for (uint32_t i = 0; i < PrimitiveCount; i++) {\n            batch->Indices[batch->Offset++] = (StartVertex + i + 0);\n          }\n          break;\n        // 1 2 3 4 5 6 7 -> 1 2 3, 1 3 4, 1 4 5, 1 5 6, 1 6 7\n        case D3DPT_TRIANGLEFAN:\n          batch->Indices.resize(batch->Offset + PrimitiveCount * 3);\n          for (uint32_t i = 0; i < PrimitiveCount; i++) {\n            batch->Indices[batch->Offset++] = (StartVertex + 0);\n            batch->Indices[batch->Offset++] = (StartVertex + i + 1);\n            batch->Indices[batch->Offset++] = (StartVertex + i + 2);\n          }\n          break;\n        default:\n          return D3DERR_INVALIDCALL;\n      }\n      batch->MinVertex = std::min(batch->MinVertex, StartVertex);\n      if (!batch->Indices.empty())\n        batch->MaxVertex = std::max(batch->MaxVertex, UINT(batch->Indices.back() + 1));\n      batch->PrimitiveCount += PrimitiveCount;\n      batch->DrawCallCount++;\n      return D3D_OK;\n    }\n\n    inline void SetStream(UINT num, D3D8VertexBuffer* stream, UINT stride) {\n      if (unlikely(num != 0)) {\n        StateChange();\n        return;\n      }\n      if (unlikely(m_stream != stream || m_stride != stride)) {\n        StateChange();\n        m_stream = static_cast<D3D8BatchBuffer*>(stream);\n        m_stride = stride;\n      }\n    }\n\n    inline void SetIndices(D3D8IndexBuffer* indices, INT baseVertexIndex) {\n      if (m_indices != indices || m_baseVertexIndex != baseVertexIndex) {\n        StateChange();\n        m_indices = indices;\n        m_baseVertexIndex = baseVertexIndex;\n      }\n    }\n\n  private:\n\n    D3D8Device*                     m_device8 = nullptr;\n    Com<d3d9::IDirect3DDevice9>     m_device;\n\n    D3D8BatchBuffer*                m_stream = nullptr;\n    UINT                            m_stride = 0;\n    D3D8IndexBuffer*                m_indices = nullptr;\n    INT                             m_baseVertexIndex = 0;\n    std::array<Batch, D3DPT_COUNT>  m_batches;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_buffer.cpp",
    "content": "#include \"d3d8_buffer.h\"\n#include \"d3d8_device.h\"\n\nnamespace dxvk {\n\n  // D3D8VertexBuffer\n\n  D3D8VertexBuffer::D3D8VertexBuffer(\n          D3D8Device*                         pDevice,\n          Com<d3d9::IDirect3DVertexBuffer9>&& pBuffer,\n          D3DPOOL                             Pool,\n          DWORD                               Usage)\n    : D3D8VertexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {\n  }\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D8VertexBuffer::GetType() { return D3DRTYPE_VERTEXBUFFER; }\n\n  HRESULT STDMETHODCALLTYPE D3D8VertexBuffer::GetDesc(D3DVERTEXBUFFER_DESC* pDesc) {\n    return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DVERTEXBUFFER_DESC*>(pDesc));\n  }\n\n  // D3D8IndexBuffer\n\n  D3D8IndexBuffer::D3D8IndexBuffer(\n          D3D8Device*                        pDevice,\n          Com<d3d9::IDirect3DIndexBuffer9>&& pBuffer,\n          D3DPOOL                            Pool,\n          DWORD                              Usage)\n    : D3D8IndexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {\n  }\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D8IndexBuffer::GetType() { return D3DRTYPE_INDEXBUFFER; }\n\n  HRESULT STDMETHODCALLTYPE D3D8IndexBuffer::GetDesc(D3DINDEXBUFFER_DESC* pDesc) {\n    return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DINDEXBUFFER_DESC*>(pDesc));\n  }\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_buffer.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n#include \"d3d8_options.h\"\n#include \"d3d8_resource.h\"\n\nnamespace dxvk {\n\n  template <typename D3D9, typename D3D8>\n  class D3D8Buffer : public D3D8Resource<D3D9, D3D8> {\n\n  public:\n\n    D3D8Buffer(\n            D3D8Device*     pDevice,\n            Com<D3D9>&&     pBuffer,\n            D3DPOOL         Pool,\n            DWORD           Usage)\n      : D3D8Resource<D3D9, D3D8> (pDevice, Pool, std::move(pBuffer))\n      , m_usage                  (Usage) {\n      m_options = this->GetParent()->GetOptions();\n    }\n\n    HRESULT STDMETHODCALLTYPE Lock(\n            UINT   OffsetToLock,\n            UINT   SizeToLock,\n            BYTE** ppbData,\n            DWORD  Flags) {\n\n      if (m_options->forceLegacyDiscard &&\n          (Flags & D3DLOCK_DISCARD) &&\n         !((m_usage & D3DUSAGE_DYNAMIC) &&\n           (m_usage & D3DUSAGE_WRITEONLY)))\n          Flags &= ~D3DLOCK_DISCARD;\n\n      return this->GetD3D9()->Lock(\n        OffsetToLock,\n        SizeToLock,\n        reinterpret_cast<void**>(ppbData),\n        Flags);\n    }\n\n    HRESULT STDMETHODCALLTYPE Unlock() {\n      return this->GetD3D9()->Unlock();\n    }\n\n    void STDMETHODCALLTYPE PreLoad() {\n      this->GetD3D9()->PreLoad();\n    }\n\n  protected:\n\n    const D3D8Options* m_options = nullptr;\n    const DWORD        m_usage   = 0;\n\n  };\n\n\n  using D3D8VertexBufferBase = D3D8Buffer<d3d9::IDirect3DVertexBuffer9, IDirect3DVertexBuffer8>;\n  class D3D8VertexBuffer : public D3D8VertexBufferBase {\n\n  public:\n\n    D3D8VertexBuffer(\n            D3D8Device*                         pDevice,\n            Com<d3d9::IDirect3DVertexBuffer9>&& pBuffer,\n            D3DPOOL                             Pool,\n            DWORD                               Usage);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;\n\n    HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc);\n\n  };\n\n  using D3D8IndexBufferBase = D3D8Buffer<d3d9::IDirect3DIndexBuffer9, IDirect3DIndexBuffer8>;\n  class D3D8IndexBuffer final : public D3D8IndexBufferBase {\n\n  public:\n\n    D3D8IndexBuffer(\n            D3D8Device*                        pDevice,\n            Com<d3d9::IDirect3DIndexBuffer9>&& pBuffer,\n            D3DPOOL                            Pool,\n            DWORD                              Usage);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;\n\n    HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc) final;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_caps.h",
    "content": "#pragma once\n\nnamespace dxvk::d8caps {\n\n  constexpr uint32_t MAX_TEXTURE_STAGES = 8;\n  constexpr uint32_t MAX_STREAMS        = 16;\n\n  // ZBIAS can be an integer from 0 to 16 and needs to be remapped to float\n  constexpr float    ZBIAS_SCALE        = -1.0f / ((1u << 16) - 1); // Consider D16 precision\n  constexpr float    ZBIAS_SCALE_INV    = 1 / ZBIAS_SCALE;\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_device.cpp",
    "content": "#include \"d3d8_device.h\"\n#include \"d3d8_interface.h\"\n#include \"d3d8_shader.h\"\n\n#ifdef MSC_VER\n#pragma fenv_access (on)\n#endif\n\nnamespace dxvk {\n\n  D3D8Device::D3D8Device(\n        D3D8Interface*                pParent,\n        Com<d3d9::IDirect3DDevice9>&& pDevice,\n        D3DDEVTYPE                    DeviceType,\n        HWND                          hFocusWindow,\n        DWORD                         BehaviorFlags,\n        D3DPRESENT_PARAMETERS*        pParams)\n    : D3D8DeviceBase(std::move(pDevice))\n    , m_d3d8Options(pParent->GetOptions())\n    , m_parent(pParent)\n    , m_presentParams(*pParams)\n    , m_deviceType(DeviceType)\n    , m_window(hFocusWindow)\n    , m_behaviorFlags(BehaviorFlags)\n    , m_multithread(BehaviorFlags & D3DCREATE_MULTITHREADED) {\n    // Get the bridge interface to D3D9.\n    if (unlikely(FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast<void**>(&m_bridge))))) {\n      throw DxvkError(\"D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!\");\n    }\n\n    ResetState();\n    RecreateBackBuffersAndAutoDepthStencil();\n\n    if (m_d3d8Options.batching)\n      m_batcher = new D3D8Batcher(this, GetD3D9());\n  }\n\n  D3D8Device::~D3D8Device() {\n    if (m_batcher)\n      delete m_batcher;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize) {\n    Logger::debug(str::format(\"D3D8Device::GetInfo: \", DevInfoID));\n\n    if (unlikely(pDevInfoStruct == nullptr || DevInfoStructSize == 0))\n      return D3DERR_INVALIDCALL;\n\n    HRESULT res;\n    Com<d3d9::IDirect3DQuery9> pQuery;\n\n    switch (DevInfoID) {\n      // pre-D3D8 queries\n      case 0:\n      case D3DDEVINFOID_TEXTUREMANAGER:\n      case D3DDEVINFOID_D3DTEXTUREMANAGER:\n      case D3DDEVINFOID_TEXTURING:\n        return E_FAIL;\n\n      case D3DDEVINFOID_VCACHE:\n        // The query will return D3D_OK on Nvidia and D3DERR_NOTAVAILABLE on AMD/Intel\n        // in D3D9, however in the case of the latter we'll need to return a\n        // zeroed out query result and S_FALSE. This behavior has been observed both\n        // on modern native AMD drivers and D3D8-era native ATI drivers.\n        res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VCACHE, &pQuery);\n\n        struct D3DDEVINFO_VCACHE {\n          DWORD         Pattern;\n          DWORD         OptMethod;\n          DWORD         CacheSize;\n          DWORD         MagicNumber;\n        };\n\n        if (FAILED(res)) {\n          // The struct size needs to be at least equal or larger\n          if (DevInfoStructSize < sizeof(D3DDEVINFO_VCACHE))\n            return D3DERR_INVALIDCALL;\n\n          memset(pDevInfoStruct, 0, sizeof(D3DDEVINFO_VCACHE));\n          return S_FALSE;\n        }\n\n        break;\n\n      case D3DDEVINFOID_RESOURCEMANAGER: // Not yet implemented by D9VK.\n        res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_RESOURCEMANAGER, &pQuery);\n        break;\n\n      case D3DDEVINFOID_VERTEXSTATS: // Not yet implemented by D9VK.\n        res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VERTEXSTATS, &pQuery);\n        break;\n\n      default:\n        Logger::warn(str::format(\"D3D8Device::GetInfo: Unsupported device info ID: \", DevInfoID));\n        return E_FAIL;\n    }\n\n    if (FAILED(res)) {\n      if (res == D3DERR_NOTAVAILABLE) // unsupported\n        return E_FAIL;\n      else // any unknown error\n        return S_FALSE;\n    }\n\n    if (pQuery != nullptr) {\n      // Immediately issue the query. D3D9 will begin it automatically before ending.\n      pQuery->Issue(D3DISSUE_END);\n      // TODO: Will immediately issuing the query actually yield meaingful results?\n      //\n      // Only relevant once RESOURCEMANAGER or VERTEXSTATS are implemented by D9VK,\n      // since VCACHE queries will immediately return data during this call.\n      res = pQuery->GetData(pDevInfoStruct, DevInfoStructSize, D3DGETDATA_FLUSH);\n    }\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::TestCooperativeLevel() {\n    // Equivalent of D3D11/DXGI present tests.\n    return GetD3D9()->TestCooperativeLevel();\n  }\n\n  UINT STDMETHODCALLTYPE D3D8Device::GetAvailableTextureMem() {\n    return GetD3D9()->GetAvailableTextureMem();\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::ResourceManagerDiscardBytes(DWORD bytes) {\n    return GetD3D9()->EvictManagedResources();\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetDirect3D(IDirect3D8** ppD3D8) {\n    if (unlikely(ppD3D8 == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *ppD3D8 = m_parent.ref();\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetDeviceCaps(D3DCAPS8* pCaps) {\n    d3d9::D3DCAPS9 caps9;\n    HRESULT res = GetD3D9()->GetDeviceCaps(&caps9);\n\n    if (likely(SUCCEEDED(res)))\n      ConvertCaps8(caps9, pCaps);\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetDisplayMode(D3DDISPLAYMODE* pMode) {\n    // swap chain 0\n    return GetD3D9()->GetDisplayMode(0, reinterpret_cast<d3d9::D3DDISPLAYMODE*>(pMode));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters) {\n    return GetD3D9()->GetCreationParameters(reinterpret_cast<d3d9::D3DDEVICE_CREATION_PARAMETERS*>(pParameters));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetCursorProperties(\n          UINT               XHotSpot,\n          UINT               YHotSpot,\n          IDirect3DSurface8* pCursorBitmap) {\n    D3D8Surface* surf = static_cast<D3D8Surface*>(pCursorBitmap);\n    return GetD3D9()->SetCursorProperties(XHotSpot, YHotSpot, D3D8Surface::GetD3D9Nullable(surf));\n  }\n\n  void STDMETHODCALLTYPE D3D8Device::SetCursorPosition(UINT XScreenSpace, UINT YScreenSpace, DWORD Flags) {\n    GetD3D9()->SetCursorPosition(XScreenSpace, YScreenSpace, Flags);\n  }\n\n  // Microsoft d3d8.h in the DirectX 9 SDK uses a different function signature...\n  void STDMETHODCALLTYPE D3D8Device::SetCursorPosition(int X, int Y, DWORD Flags) {\n    GetD3D9()->SetCursorPosition(X, Y, Flags);\n  }\n\n  BOOL STDMETHODCALLTYPE D3D8Device::ShowCursor(BOOL bShow) {\n    return GetD3D9()->ShowCursor(bShow);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateAdditionalSwapChain(\n      D3DPRESENT_PARAMETERS* pPresentationParameters,\n      IDirect3DSwapChain8** ppSwapChain) {\n    InitReturnPtr(ppSwapChain);\n\n    if (unlikely(pPresentationParameters == nullptr || ppSwapChain == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    Com<d3d9::IDirect3DSwapChain9> pSwapChain9;\n    d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);\n    HRESULT res = GetD3D9()->CreateAdditionalSwapChain(\n      &params,\n      &pSwapChain9\n    );\n\n    if (likely(SUCCEEDED(res)))\n      *ppSwapChain = ref(new D3D8SwapChain(this, pPresentationParameters, std::move(pSwapChain9)));\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) {\n    D3D8DeviceLock lock = LockDevice();\n\n    HRESULT res = m_parent->ValidatePresentationParameters(pPresentationParameters);\n\n    if (unlikely(FAILED(res)))\n      return res;\n\n    StateChange();\n\n    m_presentParams = *pPresentationParameters;\n    ResetState();\n\n    d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);\n    res = GetD3D9()->Reset(&params);\n\n    if (likely(SUCCEEDED(res)))\n      RecreateBackBuffersAndAutoDepthStencil();\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::Present(\n    const RECT* pSourceRect,\n    const RECT* pDestRect,\n          HWND hDestWindowOverride,\n    const RGNDATA* pDirtyRegion) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(ShouldBatch()))\n      m_batcher->EndFrame();\n\n    StateChange();\n    return GetD3D9()->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetBackBuffer(\n          UINT iBackBuffer,\n          D3DBACKBUFFER_TYPE Type,\n          IDirect3DSurface8** ppBackBuffer) {\n    D3D8DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppBackBuffer);\n\n    if (unlikely(ppBackBuffer == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (iBackBuffer >= m_backBuffers.size() || m_backBuffers[iBackBuffer] == nullptr) {\n      Com<d3d9::IDirect3DSurface9> pSurface9;\n      HRESULT res = GetD3D9()->GetBackBuffer(0, iBackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9);\n\n      if (likely(SUCCEEDED(res))) {\n        m_backBuffers[iBackBuffer] = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurface9));\n        *ppBackBuffer = m_backBuffers[iBackBuffer].ref();\n      }\n\n      return res;\n    }\n\n    *ppBackBuffer = m_backBuffers[iBackBuffer].ref();\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetRasterStatus(D3DRASTER_STATUS* pRasterStatus) {\n    return GetD3D9()->GetRasterStatus(0, reinterpret_cast<d3d9::D3DRASTER_STATUS*>(pRasterStatus));\n  }\n\n  void STDMETHODCALLTYPE D3D8Device::SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp) {\n    StateChange();\n    // For swap chain 0\n    GetD3D9()->SetGammaRamp(0, Flags, reinterpret_cast<const d3d9::D3DGAMMARAMP*>(pRamp));\n  }\n\n  void STDMETHODCALLTYPE D3D8Device::GetGammaRamp(D3DGAMMARAMP* pRamp) {\n    // For swap chain 0\n    GetD3D9()->GetGammaRamp(0, reinterpret_cast<d3d9::D3DGAMMARAMP*>(pRamp));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateTexture(\n          UINT                Width,\n          UINT                Height,\n          UINT                Levels,\n          DWORD               Usage,\n          D3DFORMAT           Format,\n          D3DPOOL             Pool,\n          IDirect3DTexture8** ppTexture) {\n    // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN\n    // before clearing the content of ppTexture.\n    if (unlikely(Format == D3DFMT_UNKNOWN))\n      return D3DERR_INVALIDCALL;\n\n    InitReturnPtr(ppTexture);\n\n    if (unlikely(ppTexture == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(isD3D9ExclusiveFormat(Format)))\n      return D3DERR_INVALIDCALL;\n\n    // Nvidia & Intel workaround for The Lord of the Rings: The Fellowship of the Ring\n    if (m_d3d8Options.placeP8InScratch && Format == D3DFMT_P8)\n      Pool = D3DPOOL_SCRATCH;\n\n    Com<d3d9::IDirect3DTexture9> pTex9;\n    HRESULT res = GetD3D9()->CreateTexture(\n      Width,\n      Height,\n      Levels,\n      Usage,\n      d3d9::D3DFORMAT(Format),\n      d3d9::D3DPOOL(Pool),\n      &pTex9,\n      NULL);\n\n    if (likely(SUCCEEDED(res)))\n      *ppTexture = ref(new D3D8Texture2D(this, Pool, std::move(pTex9)));\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateVolumeTexture(\n          UINT                      Width,\n          UINT                      Height,\n          UINT                      Depth,\n          UINT                      Levels,\n          DWORD                     Usage,\n          D3DFORMAT                 Format,\n          D3DPOOL                   Pool,\n          IDirect3DVolumeTexture8** ppVolumeTexture) {\n    // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN\n    // before clearing the content of ppVolumeTexture.\n    if (unlikely(Format == D3DFMT_UNKNOWN))\n      return D3DERR_INVALIDCALL;\n\n    InitReturnPtr(ppVolumeTexture);\n\n    if (unlikely(ppVolumeTexture == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(isD3D9ExclusiveFormat(Format)))\n      return D3DERR_INVALIDCALL;\n\n    Com<d3d9::IDirect3DVolumeTexture9> pVolume9;\n    HRESULT res = GetD3D9()->CreateVolumeTexture(\n      Width, Height, Depth, Levels,\n      Usage,\n      d3d9::D3DFORMAT(Format),\n      d3d9::D3DPOOL(Pool),\n      &pVolume9,\n      NULL);\n\n    if (likely(SUCCEEDED(res)))\n      *ppVolumeTexture = ref(new D3D8Texture3D(this, Pool, std::move(pVolume9)));\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateCubeTexture(\n        UINT                      EdgeLength,\n          UINT                    Levels,\n          DWORD                   Usage,\n          D3DFORMAT               Format,\n          D3DPOOL                 Pool,\n          IDirect3DCubeTexture8** ppCubeTexture) {\n    // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN\n    // before clearing the content of ppCubeTexture.\n    if (unlikely(Format == D3DFMT_UNKNOWN))\n      return D3DERR_INVALIDCALL;\n\n    InitReturnPtr(ppCubeTexture);\n\n    if (unlikely(ppCubeTexture == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(isD3D9ExclusiveFormat(Format)))\n      return D3DERR_INVALIDCALL;\n\n    Com<d3d9::IDirect3DCubeTexture9> pCube9;\n    HRESULT res = GetD3D9()->CreateCubeTexture(\n      EdgeLength,\n      Levels,\n      Usage,\n      d3d9::D3DFORMAT(Format),\n      d3d9::D3DPOOL(Pool),\n      &pCube9,\n      NULL);\n\n    if (likely(SUCCEEDED(res)))\n      *ppCubeTexture = ref(new D3D8TextureCube(this, Pool, std::move(pCube9)));\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateVertexBuffer(\n          UINT                     Length,\n          DWORD                    Usage,\n          DWORD                    FVF,\n          D3DPOOL                  Pool,\n          IDirect3DVertexBuffer8** ppVertexBuffer) {\n    InitReturnPtr(ppVertexBuffer);\n\n    if (unlikely(ppVertexBuffer == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ShouldBatch())) {\n      *ppVertexBuffer = m_batcher->CreateVertexBuffer(Length, Usage, FVF, Pool);\n      return D3D_OK;\n    }\n\n    Com<d3d9::IDirect3DVertexBuffer9> pVertexBuffer9;\n    HRESULT res = GetD3D9()->CreateVertexBuffer(Length, Usage, FVF, d3d9::D3DPOOL(Pool), &pVertexBuffer9, NULL);\n\n    if (likely(SUCCEEDED(res)))\n      *ppVertexBuffer = ref(new D3D8VertexBuffer(this, std::move(pVertexBuffer9), Pool, Usage));\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateIndexBuffer(\n          UINT                    Length,\n          DWORD                   Usage,\n          D3DFORMAT               Format,\n          D3DPOOL                 Pool,\n          IDirect3DIndexBuffer8** ppIndexBuffer) {\n    InitReturnPtr(ppIndexBuffer);\n\n    if (unlikely(ppIndexBuffer == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    Com<d3d9::IDirect3DIndexBuffer9> pIndexBuffer9;\n    HRESULT res = GetD3D9()->CreateIndexBuffer(Length, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pIndexBuffer9, NULL);\n\n    if (likely(SUCCEEDED(res)))\n      *ppIndexBuffer = ref(new D3D8IndexBuffer(this, std::move(pIndexBuffer9), Pool, Usage));\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateRenderTarget(\n          UINT                Width,\n          UINT                Height,\n          D3DFORMAT           Format,\n          D3DMULTISAMPLE_TYPE MultiSample,\n          BOOL                Lockable,\n          IDirect3DSurface8** ppSurface) {\n    // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN\n    // before clearing the content of ppSurface.\n    if (unlikely(Format == D3DFMT_UNKNOWN))\n      return D3DERR_INVALIDCALL;\n\n    InitReturnPtr(ppSurface);\n\n    if (unlikely(ppSurface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(isD3D9ExclusiveFormat(Format)))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(!isRenderTargetFormat(Format)))\n      return D3DERR_INVALIDCALL;\n\n    Com<d3d9::IDirect3DSurface9> pSurf9;\n    HRESULT res = GetD3D9()->CreateRenderTarget(\n      Width,\n      Height,\n      d3d9::D3DFORMAT(Format),\n      d3d9::D3DMULTISAMPLE_TYPE(MultiSample),\n      0,\n      Lockable,\n      &pSurf9,\n      NULL);\n\n    if (likely(SUCCEEDED(res)))\n      *ppSurface = ref(new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurf9)));\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateDepthStencilSurface(\n          UINT                Width,\n          UINT                Height,\n          D3DFORMAT           Format,\n          D3DMULTISAMPLE_TYPE MultiSample,\n          IDirect3DSurface8** ppSurface) {\n    // D3D8 returns D3DERR_INVALIDCALL for D3DFMT_UNKNOWN\n    // before clearing the content of ppSurface.\n    if (unlikely(Format == D3DFMT_UNKNOWN))\n      return D3DERR_INVALIDCALL;\n\n    InitReturnPtr(ppSurface);\n\n    if (unlikely(ppSurface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(isD3D9ExclusiveFormat(Format)))\n      return D3DERR_INVALIDCALL;\n\n    Com<d3d9::IDirect3DSurface9> pSurf9;\n    HRESULT res = GetD3D9()->CreateDepthStencilSurface(\n      Width,\n      Height,\n      d3d9::D3DFORMAT(Format),\n      d3d9::D3DMULTISAMPLE_TYPE(MultiSample),\n      0,\n      FALSE, // z-buffer discarding is not used in D3D8\n      &pSurf9,\n      NULL);\n\n    if (likely(SUCCEEDED(res)))\n      *ppSurface = ref(new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurf9)));\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateImageSurface(\n          UINT                Width,\n          UINT                Height,\n          D3DFORMAT           Format,\n          IDirect3DSurface8** ppSurface) {\n    // Only D3D8 CreateImageSurface clears the content of ppSurface\n    // before checking if Format is equal to D3DFMT_UNKNOWN.\n    InitReturnPtr(ppSurface);\n\n    if (unlikely(Format == D3DFMT_UNKNOWN))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ppSurface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // CreateImageSurface is generally guaranteed to succeed even with unsupported\n    // formats, however D3D9 exclusive formats fail on native D3D8.\n    if (unlikely(isD3D9ExclusiveFormat(Format)))\n      return D3DERR_INVALIDCALL;\n\n    const bool isSupportedSurfaceFormat = m_bridge->IsSupportedSurfaceFormat(d3d9::D3DFORMAT(Format));\n    const D3DPOOL pool = isSupportedSurfaceFormat ? D3DPOOL_SYSTEMMEM : D3DPOOL_SCRATCH;\n\n    Com<d3d9::IDirect3DSurface9> pSurf;\n    HRESULT res = GetD3D9()->CreateOffscreenPlainSurface(\n      Width,\n      Height,\n      d3d9::D3DFORMAT(Format),\n      d3d9::D3DPOOL(pool),\n      &pSurf,\n      NULL);\n\n    if (likely(SUCCEEDED(res)))\n      *ppSurface = ref(new D3D8Surface(this, pool, std::move(pSurf)));\n\n    return res;\n  }\n\n  // Copies texture rect in system mem using memcpy.\n  // Rects must be congruent, but need not be aligned.\n  HRESULT copyTextureBuffers(\n      D3D8Surface*                  src,\n      D3D8Surface*                  dst,\n      const d3d9::D3DSURFACE_DESC&  srcDesc,\n      const d3d9::D3DSURFACE_DESC&  dstDesc,\n      const RECT&                   srcRect,\n      const RECT&                   dstRect) {\n    HRESULT res = D3D_OK;\n    D3DLOCKED_RECT srcLocked, dstLocked;\n\n    const bool compressed = isDXTFormat(D3DFORMAT(srcDesc.Format));\n\n    res = src->LockRect(&srcLocked, &srcRect, D3DLOCK_READONLY);\n    if (unlikely(FAILED(res)))\n      return res;\n\n    res = dst->LockRect(&dstLocked, &dstRect, 0);\n    if (unlikely(FAILED(res))) {\n      src->UnlockRect();\n      return res;\n    }\n\n    auto rows = srcRect.bottom  - srcRect.top;\n    auto cols = srcRect.right   - srcRect.left;\n    auto bpp  = srcLocked.Pitch / srcDesc.Width;\n\n    if (!compressed\n     && srcRect.left    == 0\n     && srcRect.right   == LONG(srcDesc.Width)\n     && srcDesc.Width   == dstDesc.Width\n     && srcLocked.Pitch == dstLocked.Pitch) {\n\n      // If copying the entire texture into a congruent destination,\n      // we can do this in one continuous copy.\n      std::memcpy(dstLocked.pBits, srcLocked.pBits, srcLocked.Pitch * rows);\n\n    } else {\n      // Bytes per row of the rect\n      auto amplitude = cols * bpp;\n\n      // Handle DXT compressed textures.\n      if (compressed) {\n        // DXT blocks are always 4x4 pixels.\n        constexpr UINT blockWidth  = 4;\n        constexpr UINT blockHeight = 4;\n\n        // Compute rect dimensions in 4x4 blocks\n        UINT rectWidthBlocks  = cols / blockWidth;\n        UINT rectHeightBlocks = rows / blockHeight;\n\n        // Compute total texture width in blocks\n        // to derive block size in bytes using the pitch.\n        UINT texWidthBlocks = std::max(srcDesc.Width / blockWidth, 1u);\n        UINT bytesPerBlock  = srcLocked.Pitch / texWidthBlocks;\n\n        // Copy H/4 rows of W/4 blocks\n        amplitude = rectWidthBlocks * bytesPerBlock;\n        rows      = rectHeightBlocks;\n      }\n\n      // Copy one row at a time\n      size_t srcOffset = 0, dstOffset = 0;\n      for (auto i = 0; i < rows; i++) {\n        std::memcpy(\n          reinterpret_cast<uint8_t*>(dstLocked.pBits) + dstOffset,\n          reinterpret_cast<uint8_t*>(srcLocked.pBits) + srcOffset,\n          amplitude);\n        srcOffset += srcLocked.Pitch;\n        dstOffset += dstLocked.Pitch;\n      }\n    }\n\n    res = dst->UnlockRect();\n    if (unlikely(FAILED(res))) {\n      src->UnlockRect();\n      return res;\n    }\n\n    res = src->UnlockRect();\n\n    return res;\n  }\n\n  /**\n   * \\brief D3D8 CopyRects implementation\n   *\n   * \\details\n   * The following table shows the possible combinations of source\n   * and destination surface pools, and how we handle each of them.\n   *\n   *     ┌────────────┬───────────────────────────┬───────────────────────┬───────────────────────┬──────────────────────┐\n   *     │ Src/Dst    │ DEFAULT                   │ MANAGED               │ SYSTEMMEM             │ SCRATCH              │\n   *     ├────────────┼───────────────────────────┼───────────────────────┼───────────────────────┼──────────────────────┤\n   *     │ DEFAULT    │  StretchRect              │  GetRenderTargetData  │  GetRenderTargetData  │ GetRenderTargetData  │\n   *     │ MANAGED    │  UpdateTextureFromBuffer  │  memcpy               │  memcpy               │ memcpy               │\n   *     │ SYSTEMMEM  │  UpdateSurface            │  memcpy               │  memcpy               │ memcpy               │\n   *     │ SCRATCH    │  memcpy + UpdateSurface   │  memcpy               │  memcpy               │ memcpy               │\n   *     └────────────┴───────────────────────────┴───────────────────────┴───────────────────────┴──────────────────────┘\n   */\n  HRESULT STDMETHODCALLTYPE D3D8Device::CopyRects(\n          IDirect3DSurface8*  pSourceSurface,\n    const RECT*               pSourceRectsArray,\n          UINT                cRects,\n          IDirect3DSurface8*  pDestinationSurface,\n    const POINT*              pDestPointsArray) {\n    D3D8DeviceLock lock = LockDevice();\n\n    // The source and destination surfaces can not be identical.\n    if (unlikely(pSourceSurface == nullptr ||\n                 pDestinationSurface == nullptr ||\n                 pSourceSurface == pDestinationSurface)) {\n      return D3DERR_INVALIDCALL;\n    }\n\n    // TODO: No stretching or clipping of either source or destination rectangles.\n    // All src/dest rectangles must fit within the dest surface.\n\n    Com<D3D8Surface> src = static_cast<D3D8Surface*>(pSourceSurface);\n    Com<D3D8Surface> dst = static_cast<D3D8Surface*>(pDestinationSurface);\n\n    d3d9::D3DSURFACE_DESC srcDesc, dstDesc;\n    src->GetD3D9()->GetDesc(&srcDesc);\n    dst->GetD3D9()->GetDesc(&dstDesc);\n\n    // This method does not support format conversion.\n    if (unlikely(srcDesc.Format != dstDesc.Format))\n      return D3DERR_INVALIDCALL;\n\n    // This method cannot be applied to surfaces whose formats\n    // are classified as depth stencil formats.\n    if (unlikely(isDepthStencilFormat(D3DFORMAT(srcDesc.Format))))\n      return D3DERR_INVALIDCALL;\n\n    StateChange();\n\n    // If pSourceRectsArray is NULL, then the entire surface is copied\n    RECT rect;\n    POINT point = { 0, 0 };\n    if (pSourceRectsArray == NULL) {\n      cRects = 1;\n      rect.top    = rect.left = 0;\n      rect.right  = srcDesc.Width;\n      rect.bottom = srcDesc.Height;\n      pSourceRectsArray = &rect;\n\n      pDestPointsArray = &point;\n    }\n\n    for (uint32_t i = 0; i < cRects; i++) {\n\n      RECT srcRect, dstRect;\n      srcRect = pSourceRectsArray[i];\n\n      // True if the copy is asymmetric\n      bool asymmetric = false;\n      // True if the copy requires stretching (not technically supported)\n      bool stretch = false;\n\n      if (pDestPointsArray != NULL) {\n        dstRect.left    = pDestPointsArray[i].x;\n        dstRect.right   = dstRect.left + (srcRect.right - srcRect.left);\n        dstRect.top     = pDestPointsArray[i].y;\n        dstRect.bottom  = dstRect.top + (srcRect.bottom - srcRect.top);\n        asymmetric  = dstRect.left  != srcRect.left  || dstRect.top    != srcRect.top\n                   || dstRect.right != srcRect.right || dstRect.bottom != srcRect.bottom;\n\n        stretch     = (dstRect.right-dstRect.left) != (srcRect.right-srcRect.left)\n                   || (dstRect.bottom-dstRect.top) != (srcRect.bottom-srcRect.top);\n      } else {\n        dstRect     = srcRect;\n      }\n\n      POINT dstPt = { dstRect.left, dstRect.top };\n\n      auto unsupported = [&] {\n        Logger::err(str::format(\"D3D8Device::CopyRects: Unsupported case from src pool \", srcDesc.Pool, \" to dst pool \", dstDesc.Pool));\n        return D3DERR_INVALIDCALL;\n      };\n\n      auto logError = [&] (HRESULT res) {\n        if (FAILED(res)) {\n          // Only a debug message because some games mess up CopyRects every frame in a way\n          // that fails on native too but are perfectly fine with it.\n          Logger::debug(str::format(\"D3D8Device::CopyRects: Failed to copy from src pool \", srcDesc.Pool, \" to dst pool \", dstDesc.Pool));\n        }\n        return res;\n      };\n\n      switch (dstDesc.Pool) {\n\n        // Dest: DEFAULT\n        case d3d9::D3DPOOL_DEFAULT:\n          switch (srcDesc.Pool) {\n            case d3d9::D3DPOOL_DEFAULT: {\n              // DEFAULT -> DEFAULT: use StretchRect\n              return logError(GetD3D9()->StretchRect(\n                src->GetD3D9(),\n                &srcRect,\n                dst->GetD3D9(),\n                &dstRect,\n                d3d9::D3DTEXF_NONE\n              ));\n            }\n            case d3d9::D3DPOOL_MANAGED: {\n              // MANAGED -> DEFAULT: UpdateTextureFromBuffer\n              return logError(m_bridge->UpdateTextureFromBuffer(\n                src->GetD3D9(),\n                dst->GetD3D9(),\n                &srcRect,\n                &dstPt\n              ));\n            }\n            case d3d9::D3DPOOL_SYSTEMMEM: {\n              // SYSTEMMEM -> DEFAULT: use UpdateSurface\n              return logError(GetD3D9()->UpdateSurface(\n                src->GetD3D9(),\n                &srcRect,\n                dst->GetD3D9(),\n                &dstPt\n              ));\n            }\n            case d3d9::D3DPOOL_SCRATCH: {\n              // SCRATCH -> DEFAULT: memcpy to a SYSTEMMEM temporary buffer and use UpdateSurface\n\n              const bool isSupportedSurfaceFormat = m_bridge->IsSupportedSurfaceFormat(srcDesc.Format);\n              // UpdateSurface will not work on surface formats unsupported by D3DPOOL_DEFAULT\n              if (unlikely(!isSupportedSurfaceFormat))\n                return logError(D3DERR_INVALIDCALL);\n\n              Com<IDirect3DSurface8> pTempImageSurface;\n              // The temporary image surface is guaranteed to end up in SYSTEMMEM for supported formats\n              HRESULT res = CreateImageSurface(\n                srcDesc.Width,\n                srcDesc.Height,\n                D3DFORMAT(srcDesc.Format),\n                &pTempImageSurface\n              );\n\n              if (FAILED(res)) {\n                return logError(res);\n              }\n\n              Com<D3D8Surface> pBlitImage = static_cast<D3D8Surface*>(pTempImageSurface.ptr());\n              // Temporary image surface dimensions are identical, so we can reuse srcDesc/Rect\n              res = copyTextureBuffers(src.ptr(), pBlitImage.ptr(), srcDesc, srcDesc, srcRect, srcRect);\n\n              if (FAILED(res)) {\n                return logError(res);\n              }\n\n              return logError(GetD3D9()->UpdateSurface(\n                pBlitImage->GetD3D9(),\n                &srcRect,\n                dst->GetD3D9(),\n                &dstPt\n              ));\n            }\n            default: {\n              return unsupported();\n            }\n          } break;\n\n        // Dest: MANAGED\n        case d3d9::D3DPOOL_MANAGED:\n          switch (srcDesc.Pool) {\n            // TODO: Copy on GPU (handle MANAGED similarly to SYSTEMMEM for now)\n            case d3d9::D3DPOOL_DEFAULT: {\n              // Get temporary off-screen surface for stretching.\n              Com<d3d9::IDirect3DSurface9> pBlitImage = dst->GetBlitImage();\n\n              // Stretch the source RT to the temporary surface.\n              HRESULT res = GetD3D9()->StretchRect(\n                src->GetD3D9(),\n                &srcRect,\n                pBlitImage.ptr(),\n                &dstRect,\n                d3d9::D3DTEXF_NONE);\n\n              if (FAILED(res)) {\n                return logError(res);\n              }\n\n              // Now sync the rendertarget data into main memory.\n              return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()));\n            }\n            case d3d9::D3DPOOL_MANAGED:\n            case d3d9::D3DPOOL_SYSTEMMEM:\n            case d3d9::D3DPOOL_SCRATCH: {\n              // MANAGED/SYSMEM/SCRATCH -> MANAGED: LockRect / memcpy\n\n              if (stretch) {\n                return logError(D3DERR_INVALIDCALL);\n              }\n\n              return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect));\n            }\n            default: {\n              return unsupported();\n            }\n          } break;\n\n        // DEST: SYSTEMMEM\n        case d3d9::D3DPOOL_SYSTEMMEM: {\n\n          // RT (DEFAULT) -> SYSTEMMEM: Use GetRenderTargetData as fast path if possible\n          if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget == src.ptr())) {\n\n            // GetRenderTargetData works if the formats and sizes match\n            if (srcDesc.MultiSampleType == d3d9::D3DMULTISAMPLE_NONE\n                && srcDesc.Width  == dstDesc.Width\n                && srcDesc.Height == dstDesc.Height\n                && srcDesc.Format == dstDesc.Format\n                && !asymmetric) {\n              return logError(GetD3D9()->GetRenderTargetData(src->GetD3D9(), dst->GetD3D9()));\n            }\n          }\n\n          switch (srcDesc.Pool) {\n            case d3d9::D3DPOOL_DEFAULT: {\n              // Get temporary off-screen surface for stretching.\n              Com<d3d9::IDirect3DSurface9> pBlitImage = dst->GetBlitImage();\n\n              // Stretch the source RT to the temporary surface.\n              HRESULT res = GetD3D9()->StretchRect(\n                src->GetD3D9(),\n                &srcRect,\n                pBlitImage.ptr(),\n                &dstRect,\n                d3d9::D3DTEXF_NONE);\n\n              if (FAILED(res)) {\n                return logError(res);\n              }\n\n              // Now sync the rendertarget data into main memory.\n              return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()));\n            }\n            // MANAGED/SYSMEM/SCRATCH -> SYSMEM: LockRect / memcpy\n            case d3d9::D3DPOOL_MANAGED:\n            case d3d9::D3DPOOL_SYSTEMMEM:\n            case d3d9::D3DPOOL_SCRATCH: {\n              if (stretch) {\n                return logError(D3DERR_INVALIDCALL);\n              }\n\n              return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect));\n            }\n            default: {\n              return unsupported();\n            }\n          } break;\n        }\n\n        // DEST: SCRATCH\n        case d3d9::D3DPOOL_SCRATCH: {\n\n          // RT (DEFAULT) -> SCRATCH: Use GetRenderTargetData as fast path if possible\n          if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget == src.ptr())) {\n\n            // GetRenderTargetData works if the formats and sizes match\n            if (srcDesc.MultiSampleType == d3d9::D3DMULTISAMPLE_NONE\n                && srcDesc.Width  == dstDesc.Width\n                && srcDesc.Height == dstDesc.Height\n                && srcDesc.Format == dstDesc.Format\n                && !asymmetric) {\n              return logError(GetD3D9()->GetRenderTargetData(src->GetD3D9(), dst->GetD3D9()));\n            }\n          }\n\n          switch (srcDesc.Pool) {\n            case d3d9::D3DPOOL_DEFAULT: {\n              // Get temporary off-screen surface for stretching.\n              Com<d3d9::IDirect3DSurface9> pBlitImage = dst->GetBlitImage();\n\n              // Stretch the source RT to the temporary surface.\n              HRESULT res = GetD3D9()->StretchRect(\n                src->GetD3D9(),\n                &srcRect,\n                pBlitImage.ptr(),\n                &dstRect,\n                d3d9::D3DTEXF_NONE);\n\n              if (FAILED(res)) {\n                return logError(res);\n              }\n\n              // Now sync the rendertarget data into main memory.\n              return logError(GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()));\n            }\n            // MANAGED/SYSMEM/SCRATCH -> SCRATCH: LockRect / memcpy\n            case d3d9::D3DPOOL_MANAGED:\n            case d3d9::D3DPOOL_SYSTEMMEM:\n            case d3d9::D3DPOOL_SCRATCH: {\n              if (stretch) {\n                return logError(D3DERR_INVALIDCALL);\n              }\n\n              return logError(copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect));\n            }\n            default: {\n              return unsupported();\n            }\n          } break;\n        }\n        default: {\n          return unsupported();\n        }\n      }\n    }\n\n    return D3DERR_INVALIDCALL;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::UpdateTexture(\n          IDirect3DBaseTexture8* pSourceTexture,\n          IDirect3DBaseTexture8* pDestinationTexture) {\n    if (unlikely(pSourceTexture == nullptr || pDestinationTexture == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D8Texture2D* src = static_cast<D3D8Texture2D*>(pSourceTexture);\n    D3D8Texture2D* dst = static_cast<D3D8Texture2D*>(pDestinationTexture);\n\n    StateChange();\n    return GetD3D9()->UpdateTexture(D3D8Texture2D::GetD3D9Nullable(src), D3D8Texture2D::GetD3D9Nullable(dst));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetFrontBuffer(IDirect3DSurface8* pDestSurface) {\n    if (unlikely(pDestSurface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    Com<D3D8Surface> surf = static_cast<D3D8Surface*>(pDestSurface);\n\n    StateChange();\n    // This actually gets a copy of the front buffer and writes it to pDestSurface\n    return GetD3D9()->GetFrontBufferData(0, D3D8Surface::GetD3D9Nullable(surf));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil) {\n    D3D8DeviceLock lock = LockDevice();\n\n    HRESULT res;\n\n    if (pRenderTarget != nullptr) {\n      D3D8Surface* surf = static_cast<D3D8Surface*>(pRenderTarget);\n\n      // This will always be a state change and needs to be forwarded to\n      // D3D9, even when the same render target is set, as the viewport\n      // needs to be readjusted and reset.\n      StateChange();\n      res = GetD3D9()->SetRenderTarget(0, D3D8Surface::GetD3D9Nullable(surf));\n\n      if (unlikely(FAILED(res))) return res;\n\n      m_renderTarget = surf;\n    }\n\n    // SetDepthStencilSurface is a separate call\n    D3D8Surface* zStencil = static_cast<D3D8Surface*>(pNewZStencil);\n\n    // Depth stencil dimensions can not be lower than\n    // those of the currently set render target.\n    if (m_renderTarget != nullptr && zStencil != nullptr) {\n      D3DSURFACE_DESC rtDesc;\n      res = m_renderTarget->GetDesc(&rtDesc);\n\n      if (unlikely(FAILED(res))) return res;\n\n      D3DSURFACE_DESC dsDesc;\n      res = zStencil->GetDesc(&dsDesc);\n\n      if (unlikely(FAILED(res))) return res;\n\n      if (unlikely(dsDesc.Width  < rtDesc.Width\n                || dsDesc.Height < rtDesc.Height))\n        return D3DERR_INVALIDCALL;\n    }\n\n    StateChange();\n    res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil));\n\n    if (unlikely(FAILED(res))) return res;\n\n    m_depthStencil = zStencil;\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderTarget(IDirect3DSurface8** ppRenderTarget) {\n    D3D8DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppRenderTarget);\n\n    if (unlikely(ppRenderTarget == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(m_renderTarget == nullptr)) {\n      Com<d3d9::IDirect3DSurface9> pRT9;\n      HRESULT res = GetD3D9()->GetRenderTarget(0, &pRT9); // use RT index 0\n\n      if (likely(SUCCEEDED(res))) {\n        m_renderTarget = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pRT9));\n        *ppRenderTarget = m_renderTarget.ref();\n      }\n\n      return res;\n    }\n\n    *ppRenderTarget = m_renderTarget.ref();\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface) {\n    D3D8DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppZStencilSurface);\n\n    if (unlikely(ppZStencilSurface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(m_depthStencil == nullptr)) {\n      Com<d3d9::IDirect3DSurface9> pStencil9;\n      HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9);\n\n      if (likely(SUCCEEDED(res))) {\n        m_depthStencil = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pStencil9));\n        *ppZStencilSurface = m_depthStencil.ref();\n      }\n\n      return res;\n    }\n\n    *ppZStencilSurface = m_depthStencil.ref();\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::BeginScene() { return GetD3D9()->BeginScene(); }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::EndScene() { StateChange(); return GetD3D9()->EndScene(); }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::Clear(\n          DWORD    Count,\n    const D3DRECT* pRects,\n          DWORD    Flags,\n          D3DCOLOR Color,\n          float    Z,\n          DWORD    Stencil) {\n    StateChange();\n    return GetD3D9()->Clear(Count, pRects, Flags, Color, Z, Stencil);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) {\n    StateChange();\n    return GetD3D9()->SetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) {\n    return GetD3D9()->GetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix) {\n    StateChange();\n    return GetD3D9()->MultiplyTransform(d3d9::D3DTRANSFORMSTATETYPE(TransformState), pMatrix);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetViewport(const D3DVIEWPORT8* pViewport) {\n    D3D8DeviceLock lock = LockDevice();\n\n    // Outright crashes on native, but let's be\n    // somewhat more elegant about it.\n    if (unlikely(pViewport == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // We need a valid render target to validate the viewport\n    if (unlikely(m_renderTarget == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3DSURFACE_DESC rtDesc;\n    HRESULT res = m_renderTarget->GetDesc(&rtDesc);\n\n    // D3D8 will fail when setting a viewport that's outside of the\n    // current render target, although this apparently works in D3D9\n    if (likely(SUCCEEDED(res)) &&\n        unlikely(pViewport->X + pViewport->Width  > rtDesc.Width ||\n                 pViewport->Y + pViewport->Height > rtDesc.Height)) {\n      // On Linux/Wine and in windowed mode, we can get in situations\n      // where the actual render target dimensions are off by one\n      // pixel to what the game sets them to. Allow this corner case\n      // to skip the validation, in order to prevent issues.\n      const bool isOnePixelWider  = pViewport->X + pViewport->Width  == rtDesc.Width  + 1;\n      const bool isOnePixelTaller = pViewport->Y + pViewport->Height == rtDesc.Height + 1;\n\n      if (unlikely(m_presentParams.Windowed && (isOnePixelWider || isOnePixelTaller))) {\n        Logger::debug(\"D3D8Device::SetViewport: Viewport exceeds render target dimensions by one pixel\");\n      } else {\n        return D3DERR_INVALIDCALL;\n      }\n    }\n\n    StateChange();\n    return GetD3D9()->SetViewport(reinterpret_cast<const d3d9::D3DVIEWPORT9*>(pViewport));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetViewport(D3DVIEWPORT8* pViewport) {\n    D3D8DeviceLock lock = LockDevice();\n    return GetD3D9()->GetViewport(reinterpret_cast<d3d9::D3DVIEWPORT9*>(pViewport));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetMaterial(const D3DMATERIAL8* pMaterial) {\n    StateChange();\n    return GetD3D9()->SetMaterial(reinterpret_cast<const d3d9::D3DMATERIAL9*>(pMaterial));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetMaterial(D3DMATERIAL8* pMaterial) {\n    return GetD3D9()->GetMaterial(reinterpret_cast<d3d9::D3DMATERIAL9*>(pMaterial));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetLight(DWORD Index, const D3DLIGHT8* pLight) {\n    StateChange();\n    return GetD3D9()->SetLight(Index, reinterpret_cast<const d3d9::D3DLIGHT9*>(pLight));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetLight(DWORD Index, D3DLIGHT8* pLight) {\n    return GetD3D9()->GetLight(Index, reinterpret_cast<d3d9::D3DLIGHT9*>(pLight));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::LightEnable(DWORD Index, BOOL Enable) {\n    StateChange();\n    return GetD3D9()->LightEnable(Index, Enable);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetLightEnable(DWORD Index, BOOL* pEnable) {\n    return GetD3D9()->GetLightEnable(Index, pEnable);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetClipPlane(DWORD Index, const float* pPlane) {\n    StateChange();\n    return GetD3D9()->SetClipPlane(Index, pPlane);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetClipPlane(DWORD Index, float* pPlane) {\n    return GetD3D9()->GetClipPlane(Index, pPlane);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateStateBlock(\n          D3DSTATEBLOCKTYPE     Type,\n          DWORD*                pToken) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(pToken == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // Applications cannot create a state block while another is being recorded\n    if (unlikely(ShouldRecord()))\n      return D3DERR_INVALIDCALL;\n\n    D3D8StateBlockType stateBlockType = ConvertStateBlockType(Type);\n\n    if (unlikely(stateBlockType == D3D8StateBlockType::Unknown)) {\n      Logger::warn(str::format(\"D3D8Device::CreateStateBlock: Invalid state block type: \", Type));\n      return D3DERR_INVALIDCALL;\n    }\n\n    Com<d3d9::IDirect3DStateBlock9> pStateBlock9;\n    HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9);\n\n    if (likely(SUCCEEDED(res))) {\n      m_token++;\n      m_stateBlocks.emplace(std::piecewise_construct,\n                            std::forward_as_tuple(m_token),\n                            std::forward_as_tuple(this, stateBlockType, pStateBlock9.ptr()));\n      *pToken = m_token;\n    }\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CaptureStateBlock(DWORD Token) {\n    D3D8DeviceLock lock = LockDevice();\n\n    // Applications cannot capture a state block while another is being recorded\n    if (unlikely(ShouldRecord()))\n      return D3DERR_INVALIDCALL;\n\n    auto stateBlockIter = m_stateBlocks.find(Token);\n\n    if (unlikely(stateBlockIter == m_stateBlocks.end())) {\n      Logger::warn(str::format(\"D3D8Device::CaptureStateBlock: Invalid token: \", std::hex, Token));\n      return D3D_OK;\n    }\n\n    return stateBlockIter->second.Capture();\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::ApplyStateBlock(DWORD Token) {\n    D3D8DeviceLock lock = LockDevice();\n\n    // Applications cannot apply a state block while another is being recorded\n    if (unlikely(ShouldRecord()))\n      return D3DERR_INVALIDCALL;\n\n    StateChange();\n\n    auto stateBlockIter = m_stateBlocks.find(Token);\n\n    if (unlikely(stateBlockIter == m_stateBlocks.end())) {\n      Logger::warn(str::format(\"D3D8Device::ApplyStateBlock: Invalid token: \", std::hex, Token));\n      return D3D_OK;\n    }\n\n    return stateBlockIter->second.Apply();\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DeleteStateBlock(DWORD Token) {\n    D3D8DeviceLock lock = LockDevice();\n\n    // Applications cannot delete a state block while another is being recorded\n    if (unlikely(ShouldRecord()))\n      return D3DERR_INVALIDCALL;\n\n    auto stateBlockIter = m_stateBlocks.find(Token);\n\n    if (unlikely(stateBlockIter == m_stateBlocks.end())) {\n      Logger::warn(str::format(\"D3D8Device::DeleteStateBlock: Invalid token: \", std::hex, Token));\n      return D3D_OK;\n    }\n\n    m_stateBlocks.erase(stateBlockIter);\n\n    // native apparently does drop the token counter in\n    // situations where the token being removed is the\n    // last allocated token, which allows some reuse\n    if (m_token == Token)\n      m_token--;\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::BeginStateBlock() {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(m_recorder != nullptr))\n      return D3DERR_INVALIDCALL;\n\n    HRESULT res = GetD3D9()->BeginStateBlock();\n\n    if (likely(SUCCEEDED(res))) {\n      m_token++;\n      auto stateBlockIterPair = m_stateBlocks.emplace(std::piecewise_construct,\n                                                      std::forward_as_tuple(m_token),\n                                                      std::forward_as_tuple(this));\n      m_recorder = &stateBlockIterPair.first->second;\n      m_recorderToken = m_token;\n    }\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::EndStateBlock(DWORD* pToken) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(pToken == nullptr || m_recorder == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    Com<d3d9::IDirect3DStateBlock9> pStateBlock;\n    HRESULT res = GetD3D9()->EndStateBlock(&pStateBlock);\n\n    if (likely(SUCCEEDED(res))) {\n      m_recorder->SetD3D9(std::move(pStateBlock));\n\n      *pToken = m_recorderToken;\n\n      m_recorder = nullptr;\n      m_recorderToken = 0;\n    }\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetClipStatus(const D3DCLIPSTATUS8* pClipStatus) {\n    StateChange();\n    return GetD3D9()->SetClipStatus(reinterpret_cast<const d3d9::D3DCLIPSTATUS9*>(pClipStatus));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetClipStatus(D3DCLIPSTATUS8* pClipStatus) {\n    return GetD3D9()->GetClipStatus(reinterpret_cast<d3d9::D3DCLIPSTATUS9*>(pClipStatus));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture) {\n    D3D8DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppTexture);\n\n    if (unlikely(ppTexture == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *ppTexture = m_textures[Stage].ref();\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(Stage >= d8caps::MAX_TEXTURE_STAGES))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetTexture(Stage, pTexture);\n\n    D3D8Texture2D* tex = static_cast<D3D8Texture2D*>(pTexture);\n\n    // Splinter Cell: Force perspective divide when a shadow map is bound to slot 0\n    if (unlikely(m_d3d8Options.shadowPerspectiveDivide && Stage == 0)) {\n      if (tex) {\n        D3DSURFACE_DESC surf;\n        tex->GetLevelDesc(0, &surf);\n        if (isDepthStencilFormat(surf.Format)) {\n          // If we bound a depth texture to stage 0 then we need to set the projected flag for stage 0 and 1\n          // Stage 1 is a non-depth light cookie texture but still requires perspective divide to work\n          GetD3D9()->SetTextureStageState(0, d3d9::D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED);\n          GetD3D9()->SetTextureStageState(1, d3d9::D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED);\n          m_shadowPerspectiveDivide = true;\n        } else if (m_shadowPerspectiveDivide) {\n          // Non-depth texture bound. Game will reset the transform flags to 0 on its own\n          m_shadowPerspectiveDivide = false;\n        }\n      } else if (m_shadowPerspectiveDivide) {\n        // Texture unbound. Game will reset the transform flags to 0 on its own\n        m_shadowPerspectiveDivide = false;\n      }\n    }\n\n    if (unlikely(m_textures[Stage] == tex))\n      return D3D_OK;\n\n    StateChange();\n    HRESULT res = GetD3D9()->SetTexture(Stage, D3D8Texture2D::GetD3D9Nullable(tex));\n\n    if (likely(SUCCEEDED(res)))\n      m_textures[Stage] = tex;\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetTextureStageState(\n          DWORD                    Stage,\n          D3DTEXTURESTAGESTATETYPE Type,\n          DWORD*                   pValue) {\n    d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type);\n\n    if (stateType != -1u) {\n      // if the type has been remapped to a sampler state type:\n      return GetD3D9()->GetSamplerState(Stage, stateType, pValue);\n    } else {\n      return GetD3D9()->GetTextureStageState(Stage, d3d9::D3DTEXTURESTAGESTATETYPE(Type), pValue);\n    }\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetTextureStageState(\n          DWORD                    Stage,\n          D3DTEXTURESTAGESTATETYPE Type,\n          DWORD                    Value) {\n    d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type);\n\n    if (unlikely(m_d3d8Options.shadowPerspectiveDivide && Type == D3DTSS_TEXTURETRANSFORMFLAGS)) {\n      // Splinter Cell: Ignore requests to change texture transform flags\n      // to 0 while shadow mapping perspective divide mode is enabled\n      if (m_shadowPerspectiveDivide && (Stage == 0 || Stage == 1))\n        return D3D_OK;\n    }\n\n    StateChange();\n    if (stateType != -1u) {\n      // if the type has been remapped to a sampler state type:\n      return GetD3D9()->SetSamplerState(Stage, stateType, Value);\n    } else {\n      return GetD3D9()->SetTextureStageState(Stage, d3d9::D3DTEXTURESTAGESTATETYPE(Type), Value);\n    }\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::ValidateDevice(DWORD* pNumPasses) {\n    return GetD3D9()->ValidateDevice(pNumPasses);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries) {\n    StateChange();\n    return GetD3D9()->SetPaletteEntries(PaletteNumber, pEntries);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) {\n    return GetD3D9()->GetPaletteEntries(PaletteNumber, pEntries);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetCurrentTexturePalette(UINT PaletteNumber) {\n    StateChange();\n    return GetD3D9()->SetCurrentTexturePalette(PaletteNumber);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetCurrentTexturePalette(UINT* PaletteNumber) {\n    return GetD3D9()->GetCurrentTexturePalette(PaletteNumber);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DrawPrimitive(\n          D3DPRIMITIVETYPE PrimitiveType,\n          UINT             StartVertex,\n          UINT             PrimitiveCount) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(ShouldBatch()))\n      return m_batcher->DrawPrimitive(PrimitiveType, StartVertex, PrimitiveCount);\n    return GetD3D9()->DrawPrimitive(d3d9::D3DPRIMITIVETYPE(PrimitiveType), StartVertex, PrimitiveCount);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DrawIndexedPrimitive(\n          D3DPRIMITIVETYPE PrimitiveType,\n          UINT             MinVertexIndex,\n          UINT             NumVertices,\n          UINT             StartIndex,\n          UINT             PrimitiveCount) {\n    D3D8DeviceLock lock = LockDevice();\n\n    return GetD3D9()->DrawIndexedPrimitive(\n      d3d9::D3DPRIMITIVETYPE(PrimitiveType),\n      static_cast<INT>(std::min(m_baseVertexIndex, static_cast<UINT>(std::numeric_limits<int32_t>::max()))), // set by SetIndices\n      MinVertexIndex,\n      NumVertices,\n      StartIndex,\n      PrimitiveCount);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DrawPrimitiveUP(\n          D3DPRIMITIVETYPE PrimitiveType,\n          UINT             PrimitiveCount,\n    const void*            pVertexStreamZeroData,\n          UINT             VertexStreamZeroStride) {\n    D3D8DeviceLock lock = LockDevice();\n\n    StateChange();\n\n    // Stream 0 is set to null by this call\n    m_streams[0] = D3D8VBO {nullptr, 0};\n\n    return GetD3D9()->DrawPrimitiveUP(d3d9::D3DPRIMITIVETYPE(PrimitiveType), PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DrawIndexedPrimitiveUP(\n          D3DPRIMITIVETYPE PrimitiveType,\n          UINT             MinVertexIndex,\n          UINT             NumVertices,\n          UINT             PrimitiveCount,\n    const void*            pIndexData,\n          D3DFORMAT        IndexDataFormat,\n    const void*            pVertexStreamZeroData,\n          UINT             VertexStreamZeroStride) {\n    D3D8DeviceLock lock = LockDevice();\n\n    StateChange();\n\n    // Stream 0 and the index buffer are set to null by this call\n    m_streams[0] = D3D8VBO {nullptr, 0};\n    m_indices = nullptr;\n    m_baseVertexIndex = 0;\n\n    return GetD3D9()->DrawIndexedPrimitiveUP(\n      d3d9::D3DPRIMITIVETYPE(PrimitiveType),\n      MinVertexIndex,\n      NumVertices,\n      PrimitiveCount,\n      pIndexData,\n      d3d9::D3DFORMAT(IndexDataFormat),\n      pVertexStreamZeroData,\n      VertexStreamZeroStride);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::ProcessVertices(\n      UINT                         SrcStartIndex,\n      UINT                         DestIndex,\n      UINT                         VertexCount,\n      IDirect3DVertexBuffer8*      pDestBuffer,\n      DWORD                        Flags) {\n    D3D8VertexBuffer* buffer = static_cast<D3D8VertexBuffer*>(pDestBuffer);\n    return GetD3D9()->ProcessVertices(\n      SrcStartIndex,\n      DestIndex,\n      VertexCount,\n      D3D8VertexBuffer::GetD3D9Nullable(buffer),\n      nullptr,\n      Flags\n    );\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetVertexShaderConstant(\n          DWORD StartRegister,\n    const void* pConstantData,\n          DWORD ConstantCount) {\n    StateChange();\n    // ConstantCount is actually the same as Vector4fCount\n    return GetD3D9()->SetVertexShaderConstantF(StartRegister, reinterpret_cast<const float*>(pConstantData), ConstantCount);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) {\n    return GetD3D9()->GetVertexShaderConstantF(Register, reinterpret_cast<float*>(pConstantData), ConstantCount);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetStreamSource(\n          UINT                    StreamNumber,\n          IDirect3DVertexBuffer8* pStreamData,\n          UINT                    Stride) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(StreamNumber >= d8caps::MAX_STREAMS))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetStreamSource(StreamNumber, pStreamData, Stride);\n\n    D3D8VertexBuffer* buffer = static_cast<D3D8VertexBuffer*>(pStreamData);\n    HRESULT res = GetD3D9()->SetStreamSource(StreamNumber, D3D8VertexBuffer::GetD3D9Nullable(buffer), 0, Stride);\n\n    if (likely(SUCCEEDED(res))) {\n      if (unlikely(ShouldBatch()))\n        m_batcher->SetStream(StreamNumber, buffer, Stride);\n\n      m_streams[StreamNumber].buffer = buffer;\n      // The previous stride is preserved if pStreamData is NULL\n      if (likely(buffer != nullptr))\n        m_streams[StreamNumber].stride = Stride;\n    }\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetStreamSource(\n          UINT                     StreamNumber,\n          IDirect3DVertexBuffer8** ppStreamData,\n          UINT*                    pStride) {\n    D3D8DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppStreamData);\n\n    if (likely(pStride != nullptr))\n      *pStride = 0;\n\n    if (unlikely(ppStreamData == nullptr || pStride == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(StreamNumber >= d8caps::MAX_STREAMS))\n      return D3DERR_INVALIDCALL;\n\n    const D3D8VBO& vbo = m_streams[StreamNumber];\n\n    *ppStreamData = vbo.buffer.ref();\n    *pStride      = vbo.stride;\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetIndices(pIndexData, BaseVertexIndex);\n\n    if (unlikely(BaseVertexIndex > std::numeric_limits<int32_t>::max()))\n      Logger::warn(\"D3D8Device::SetIndices: BaseVertexIndex exceeds INT_MAX\");\n\n    D3D8IndexBuffer* buffer = static_cast<D3D8IndexBuffer*>(pIndexData);\n    HRESULT res = GetD3D9()->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(buffer));\n\n    if (likely(SUCCEEDED(res))) {\n      if (unlikely(ShouldBatch()))\n        m_batcher->SetIndices(buffer, BaseVertexIndex);\n\n      m_indices = buffer;\n      // used by DrawIndexedPrimitive\n      m_baseVertexIndex = BaseVertexIndex;\n    }\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetIndices(\n          IDirect3DIndexBuffer8** ppIndexData,\n          UINT* pBaseVertexIndex) {\n    D3D8DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppIndexData);\n\n    if (unlikely(ppIndexData == nullptr || pBaseVertexIndex == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *ppIndexData      = m_indices.ref();\n    *pBaseVertexIndex = m_baseVertexIndex;\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) {\n    return GetD3D9()->GetPixelShaderConstantF(Register, reinterpret_cast<float*>(pConstantData), ConstantCount);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShaderConstant(\n          DWORD StartRegister,\n    const void* pConstantData,\n          DWORD ConstantCount) {\n    StateChange();\n    // ConstantCount is actually the same as Vector4fCount\n    return GetD3D9()->SetPixelShaderConstantF(StartRegister, reinterpret_cast<const float*>(pConstantData), ConstantCount);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DrawRectPatch(\n          UINT               Handle,\n    const float*             pNumSegs,\n    const D3DRECTPATCH_INFO* pRectPatchInfo) {\n    return GetD3D9()->DrawRectPatch(Handle, pNumSegs, reinterpret_cast<const d3d9::D3DRECTPATCH_INFO*>(pRectPatchInfo));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DrawTriPatch(\n          UINT              Handle,\n    const float*            pNumSegs,\n    const D3DTRIPATCH_INFO* pTriPatchInfo) {\n    return GetD3D9()->DrawTriPatch(Handle, pNumSegs, reinterpret_cast<const d3d9::D3DTRIPATCH_INFO*>(pTriPatchInfo));\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DeletePatch(UINT Handle) {\n    return GetD3D9()->DeletePatch(Handle);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) {\n    D3D8DeviceLock lock = LockDevice();\n\n    d3d9::D3DRENDERSTATETYPE State9 = d3d9::D3DRENDERSTATETYPE(State);\n\n    switch (State) {\n      // Most render states translate 1:1 to D3D9\n      default:\n        break;\n\n      // TODO: Implement D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT\n      // and advertise support with D3DPRASTERCAPS_PAT once that is done\n      case D3DRS_LINEPATTERN:\n        static bool s_linePatternErrorShown;\n\n        if (!std::exchange(s_linePatternErrorShown, true))\n          Logger::warn(\"D3D8Device::SetRenderState: Unimplemented render state D3DRS_LINEPATTERN\");\n\n        m_linePattern = bit::cast<D3DLINEPATTERN>(Value);\n        return D3D_OK;\n\n      // Not supported by D3D8, but its value is stored.\n      case D3DRS_ZVISIBLE:\n        m_zVisible = Value;\n        return D3D_OK;\n\n      // TODO: Implement D3DRS_ANTIALIASEDLINEENABLE in D9VK.\n      case D3DRS_EDGEANTIALIAS:\n        State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE;\n        break;\n\n      case D3DRS_ZBIAS:\n        State9 = d3d9::D3DRS_DEPTHBIAS;\n        Value  = bit::cast<DWORD>(static_cast<float>(Value) * d8caps::ZBIAS_SCALE);\n        break;\n\n      case D3DRS_SOFTWAREVERTEXPROCESSING:\n        // D3D9 can return D3DERR_INVALIDCALL, but we don't care.\n        if (!(m_behaviorFlags & D3DCREATE_MIXED_VERTEXPROCESSING))\n          return D3D_OK;\n\n        // This was a very easy footgun for D3D8 applications.\n        if (unlikely(ShouldRecord()))\n          return m_recorder->SetSoftwareVertexProcessing(Value);\n\n        return GetD3D9()->SetSoftwareVertexProcessing(Value);\n\n      // TODO: Implement D3DRS_PATCHSEGMENTS\n      case D3DRS_PATCHSEGMENTS:\n        static bool s_patchSegmentsErrorShown;\n\n        if (!std::exchange(s_patchSegmentsErrorShown, true))\n          Logger::warn(\"D3D8Device::SetRenderState: Unimplemented render state D3DRS_PATCHSEGMENTS\");\n\n        return GetD3D9()->SetNPatchMode(bit::cast<float>(Value));\n    }\n\n    // Skip GetRenderState() calls for state\n    // comparisons if the batcher isn't used.\n    if (unlikely(ShouldBatch())) {\n      DWORD value;\n      // Value at this point is converted for use with D3D9,\n      // so we need to compare it against D3D9 directly.\n      HRESULT res = GetD3D9()->GetRenderState(State9, &value);\n      if (likely(SUCCEEDED(res)) && value != Value)\n        StateChange();\n    }\n\n    // This call will never fail\n    return GetD3D9()->SetRenderState(State9, Value);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(pValue == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    d3d9::D3DRENDERSTATETYPE State9 = d3d9::D3DRENDERSTATETYPE(State);\n\n    switch (State) {\n      // Most render states translate 1:1 to D3D9\n      default:\n        break;\n\n      case D3DRS_LINEPATTERN:\n        *pValue = bit::cast<DWORD>(m_linePattern);\n        return D3D_OK;\n\n      // Not supported by D3D8, but its value is stored.\n      case D3DRS_ZVISIBLE:\n        *pValue = m_zVisible;\n        return D3D_OK;\n\n      case D3DRS_EDGEANTIALIAS:\n        State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE;\n        break;\n\n      case D3DRS_ZBIAS: {\n        DWORD bias  = 0;\n        HRESULT res = GetD3D9()->GetRenderState(d3d9::D3DRS_DEPTHBIAS, &bias);\n        *pValue     = static_cast<DWORD>(bit::cast<float>(bias) * d8caps::ZBIAS_SCALE_INV);\n        return res;\n      } break;\n\n      case D3DRS_SOFTWAREVERTEXPROCESSING:\n        *pValue = GetD3D9()->GetSoftwareVertexProcessing();\n        return D3D_OK;\n\n      case D3DRS_PATCHSEGMENTS:\n        const float patchSegments = GetD3D9()->GetNPatchMode();\n        *pValue = bit::cast<DWORD>(patchSegments);\n        return D3D_OK;\n    }\n\n    // This call will never fail\n    return GetD3D9()->GetRenderState(State9, pValue);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreateVertexShader(\n        const DWORD* pDeclaration,\n        const DWORD* pFunction,\n              DWORD* pHandle,\n              DWORD Usage ) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(pDeclaration == nullptr || pHandle == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D9VertexShaderCode translatedVS;\n    HRESULT res = TranslateVertexShader8(pDeclaration, pFunction, m_d3d8Options, translatedVS);\n    if (unlikely(FAILED(res)))\n      return res;\n\n    // Create vertex declaration\n    Com<d3d9::IDirect3DVertexDeclaration9> pVertexDecl;\n    res = GetD3D9()->CreateVertexDeclaration(translatedVS.declaration, &pVertexDecl);\n    if (unlikely(FAILED(res)))\n      return res;\n\n    Com<d3d9::IDirect3DVertexShader9> pVertexShader;\n    if (pFunction != nullptr) {\n      res = GetD3D9()->CreateVertexShader(translatedVS.function.data(), &pVertexShader);\n    } else {\n      // pFunction is NULL: fixed function pipeline\n      pVertexShader = nullptr;\n    }\n\n    if (likely(SUCCEEDED(res))) {\n      D3D8VertexShaderInfo& info = m_vertexShaders.emplace_back();\n\n      info.pVertexDecl = std::move(pVertexDecl);\n      info.pVertexShader = std::move(pVertexShader);\n\n      // Store D3D8 bytecodes in the shader info\n      for (uint32_t i = 0; pDeclaration[i] != D3DVSD_END(); i++)\n        info.declaration.push_back(pDeclaration[i]);\n      info.declaration.push_back(D3DVSD_END());\n\n      if (pFunction != nullptr) {\n        for (uint32_t i = 0; pFunction[i] != D3DVS_END(); i++)\n          info.function.push_back(pFunction[i]);\n        info.function.push_back(D3DVS_END());\n      }\n\n      // Set bit to indicate this is not an FVF\n      *pHandle = getShaderHandle(m_vertexShaders.size());\n    }\n\n    return res;\n  }\n\n  inline D3D8VertexShaderInfo* getVertexShaderInfo(D3D8Device* device, DWORD Handle) {\n\n    Handle = getShaderIndex(Handle);\n\n    if (unlikely(Handle >= device->m_vertexShaders.size())) {\n      Logger::debug(str::format(\"D3D8: Invalid vertex shader index \", std::hex, Handle));\n      return nullptr;\n    }\n\n    D3D8VertexShaderInfo& info = device->m_vertexShaders[Handle];\n\n    if (unlikely(info.pVertexDecl == nullptr && info.pVertexShader == nullptr)) {\n      Logger::debug(str::format(\"D3D8: Application provided deleted vertex shader \", std::hex, Handle));\n      return nullptr;\n    }\n\n    return &info;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetVertexShader(DWORD Handle) {\n    D3D8DeviceLock lock = LockDevice();\n\n    HRESULT res;\n\n    if (unlikely(ShouldRecord())) {\n      return m_recorder->SetVertexShader(Handle);\n    }\n\n    // Check for extra bit that indicates this is not an FVF\n    if (!isFVF(Handle)) {\n\n      D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle);\n\n      if (!info)\n        return D3DERR_INVALIDCALL;\n\n      StateChange();\n\n      GetD3D9()->SetVertexDeclaration(info->pVertexDecl.ptr());\n      res = GetD3D9()->SetVertexShader(info->pVertexShader.ptr());\n\n      if (likely(SUCCEEDED(res))) {\n        // Cache current shader\n        m_currentVertexShader = Handle;\n      }\n\n      return res;\n\n    } else if (m_currentVertexShader != Handle) {\n      StateChange();\n\n      //GetD3D9()->SetVertexDeclaration(nullptr);\n      GetD3D9()->SetVertexShader(nullptr);\n      res = GetD3D9()->SetFVF(Handle);\n\n      if (likely(SUCCEEDED(res))) {\n        // Cache current FVF\n        m_currentVertexShader = Handle;\n      }\n\n      return res;\n    }\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShader(DWORD* pHandle) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(pHandle == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // Return cached shader\n    *pHandle = m_currentVertexShader;\n\n    return D3D_OK;\n\n    /*\n    // Slow path. Use to debug cached shader validation. //\n\n    d3d9::IDirect3DVertexShader9* pVertexShader;\n    HRESULT res = GetD3D9()->GetVertexShader(&pVertexShader);\n\n    if (FAILED(res) || pVertexShader == nullptr) {\n      return GetD3D9()->GetFVF(pHandle);\n    }\n\n    for (DWORD i = 0; i < m_vertexShaders.size(); i++) {\n      D3D8VertexShaderInfo& info = m_vertexShaders[i];\n\n      if (info.pVertexShader == pVertexShader) {\n        *pHandle = getShaderHandle(i);\n        return res;\n      }\n    }\n\n    return res;\n    */\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DeleteVertexShader(DWORD Handle) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (!isFVF(Handle)) {\n      D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle);\n\n      if (!info)\n        return D3DERR_INVALIDCALL;\n\n      info->pVertexDecl = nullptr;\n      info->pVertexShader = nullptr;\n      info->declaration.clear();\n      info->function.clear();\n\n      if (m_currentVertexShader == Handle)\n        m_currentVertexShader = 0;\n    }\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData) {\n    D3D8DeviceLock lock = LockDevice();\n\n    D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle);\n\n    if (unlikely(!pInfo))\n      return D3DERR_INVALIDCALL;\n\n    UINT SizeOfData = *pSizeOfData;\n\n    // Get actual size\n    UINT ActualSize = pInfo->declaration.size() * sizeof(DWORD);\n\n    if (pData == nullptr) {\n      *pSizeOfData = ActualSize;\n      return D3D_OK;\n    }\n\n    // D3D8-specific behavior\n    if (SizeOfData < ActualSize) {\n      // D3DERR_MOREDATA should be returned according to the D3D8 documentation,\n      // along with a correction to the ActualSize, however tests have shown that\n      // D3DERR_INVALIDCALL is returned and no size correction is performed.\n      return D3DERR_INVALIDCALL;\n    }\n\n    memcpy(pData, pInfo->declaration.data(), ActualSize);\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) {\n    D3D8DeviceLock lock = LockDevice();\n\n    D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle);\n\n    if (unlikely(!pInfo))\n      return D3DERR_INVALIDCALL;\n\n    UINT SizeOfData = *pSizeOfData;\n\n    // Get actual size\n    UINT ActualSize = pInfo->function.size() * sizeof(DWORD);\n\n    if (pData == nullptr) {\n      *pSizeOfData = ActualSize;\n      return D3D_OK;\n    }\n\n    // D3D8-specific behavior\n    if (SizeOfData < ActualSize) {\n      // D3DERR_MOREDATA should be returned according to the D3D8 documentation,\n      // along with a correction to the ActualSize, however tests have shown that\n      // D3DERR_INVALIDCALL is returned and no size correction is performed.\n      return D3DERR_INVALIDCALL;\n    }\n\n    memcpy(pData, pInfo->function.data(), ActualSize);\n    return D3D_OK;\n\n  }\n\n  // Pixel Shaders //\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::CreatePixelShader(\n        const DWORD* pFunction,\n              DWORD* pHandle) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(pFunction == nullptr || pHandle == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    Com<d3d9::IDirect3DPixelShader9> pPixelShader;\n    HRESULT res = GetD3D9()->CreatePixelShader(pFunction, &pPixelShader);\n\n    if (likely(SUCCEEDED(res))) {\n      m_pixelShaders.push_back(std::move(pPixelShader));\n      // Still set the shader bit, to prevent conflicts with NULL.\n      *pHandle = getShaderHandle(m_pixelShaders.size());\n    }\n\n    return res;\n  }\n\n  inline d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle) {\n\n    Handle = getShaderIndex(Handle);\n\n    if (unlikely(Handle >= device->m_pixelShaders.size())) {\n      Logger::debug(str::format(\"D3D8: Invalid pixel shader index \", std::hex, Handle));\n      return nullptr;\n    }\n\n    d3d9::IDirect3DPixelShader9* pPixelShader = device->m_pixelShaders[Handle].ptr();\n\n    if (unlikely(pPixelShader == nullptr)) {\n      Logger::debug(str::format(\"D3D8: Application provided deleted pixel shader \", std::hex, Handle));\n      return nullptr;\n    }\n\n    return pPixelShader;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShader(DWORD Handle) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(ShouldRecord())) {\n      return m_recorder->SetPixelShader(Handle);\n    }\n\n    if (Handle == DWORD(NULL)) {\n      StateChange();\n      m_currentPixelShader = DWORD(NULL);\n      return GetD3D9()->SetPixelShader(nullptr);\n    }\n\n    d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle);\n\n    if (unlikely(!pPixelShader)) {\n      return D3DERR_INVALIDCALL;\n    }\n\n    StateChange();\n    HRESULT res = GetD3D9()->SetPixelShader(pPixelShader);\n\n    if (likely(SUCCEEDED(res))) {\n      // Cache current pixel shader\n      m_currentPixelShader = Handle;\n    }\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShader(DWORD* pHandle) {\n    D3D8DeviceLock lock = LockDevice();\n\n    if (unlikely(pHandle == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // Return cached shader\n    *pHandle = m_currentPixelShader;\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::DeletePixelShader(DWORD Handle) {\n    D3D8DeviceLock lock = LockDevice();\n\n    d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle);\n\n    if (unlikely(!pPixelShader)) {\n      return D3DERR_INVALIDCALL;\n    }\n\n    m_pixelShaders[getShaderIndex(Handle)] = nullptr;\n\n    if (m_currentPixelShader == Handle)\n      m_currentPixelShader = 0;\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) {\n    D3D8DeviceLock lock = LockDevice();\n\n    d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle);\n\n    if (unlikely(!pPixelShader))\n      return D3DERR_INVALIDCALL;\n\n    UINT SizeOfData = *pSizeOfData;\n\n    // Get actual size\n    UINT ActualSize = 0;\n    pPixelShader->GetFunction(nullptr, &ActualSize);\n\n    if (pData == nullptr) {\n      *pSizeOfData = ActualSize;\n      return D3D_OK;\n    }\n\n    // D3D8-specific behavior\n    if (SizeOfData < ActualSize) {\n      // D3DERR_MOREDATA should be returned according to the D3D8 documentation,\n      // along with a correction to the ActualSize, however tests have shown that\n      // D3DERR_INVALIDCALL is returned and no size correction is performed.\n      return D3DERR_INVALIDCALL;\n    }\n\n    return pPixelShader->GetFunction(pData, &SizeOfData);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_device.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n#include \"d3d8_multithread.h\"\n#include \"d3d8_texture.h\"\n#include \"d3d8_buffer.h\"\n#include \"d3d8_swapchain.h\"\n#include \"d3d8_state_block.h\"\n#include \"d3d8_util.h\"\n#include \"d3d8_caps.h\"\n#include \"d3d8_batch.h\"\n\n#include \"../d3d9/d3d9_bridge.h\"\n\n#include <array>\n#include <vector>\n#include <type_traits>\n#include <unordered_map>\n\nnamespace dxvk {\n\n  class D3D8Interface;\n\n  struct D3D8VertexShaderInfo;\n\n  using D3D8DeviceBase = D3D8WrappedObject<d3d9::IDirect3DDevice9, IDirect3DDevice8>;\n  class D3D8Device final : public D3D8DeviceBase {\n\n    friend class D3D8StateBlock;\n\n  public:\n\n    D3D8Device(\n            D3D8Interface*                pParent,\n            Com<d3d9::IDirect3DDevice9>&& pDevice,\n            D3DDEVTYPE                    DeviceType,\n            HWND                          hFocusWindow,\n            DWORD                         BehaviorFlags,\n            D3DPRESENT_PARAMETERS*        pParams);\n\n    ~D3D8Device();\n\n    HRESULT STDMETHODCALLTYPE TestCooperativeLevel();\n\n    UINT    STDMETHODCALLTYPE GetAvailableTextureMem();\n\n    HRESULT STDMETHODCALLTYPE ResourceManagerDiscardBytes(DWORD bytes);\n\n    HRESULT STDMETHODCALLTYPE GetDirect3D(IDirect3D8** ppD3D8);\n\n    HRESULT STDMETHODCALLTYPE GetDeviceCaps(D3DCAPS8* pCaps);\n\n    HRESULT STDMETHODCALLTYPE GetDisplayMode(D3DDISPLAYMODE* pMode);\n\n    HRESULT STDMETHODCALLTYPE GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters);\n\n    HRESULT STDMETHODCALLTYPE SetCursorProperties(\n      UINT               XHotSpot,\n      UINT               YHotSpot,\n      IDirect3DSurface8* pCursorBitmap);\n\n    void    STDMETHODCALLTYPE SetCursorPosition(UINT XScreenSpace, UINT YScreenSpace, DWORD Flags);\n\n    // Microsoft d3d8.h in the DirectX 9 SDK uses a different function signature...\n    void    STDMETHODCALLTYPE SetCursorPosition(int X, int Y, DWORD Flags);\n\n    BOOL    STDMETHODCALLTYPE ShowCursor(BOOL bShow);\n\n    HRESULT STDMETHODCALLTYPE CreateAdditionalSwapChain(\n        D3DPRESENT_PARAMETERS* pPresentationParameters,\n        IDirect3DSwapChain8** ppSwapChain);\n\n    HRESULT STDMETHODCALLTYPE Reset(D3DPRESENT_PARAMETERS* pPresentationParameters);\n\n    HRESULT STDMETHODCALLTYPE Present(\n      const RECT* pSourceRect,\n      const RECT* pDestRect,\n            HWND hDestWindowOverride,\n      const RGNDATA* pDirtyRegion);\n\n    HRESULT STDMETHODCALLTYPE GetBackBuffer(\n            UINT iBackBuffer,\n            D3DBACKBUFFER_TYPE Type,\n            IDirect3DSurface8** ppBackBuffer);\n\n    HRESULT STDMETHODCALLTYPE GetRasterStatus(D3DRASTER_STATUS* pRasterStatus);\n\n    void STDMETHODCALLTYPE SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp);\n\n    void STDMETHODCALLTYPE GetGammaRamp(D3DGAMMARAMP* pRamp);\n\n    HRESULT STDMETHODCALLTYPE CreateTexture(\n            UINT                Width,\n            UINT                Height,\n            UINT                Levels,\n            DWORD               Usage,\n            D3DFORMAT           Format,\n            D3DPOOL             Pool,\n            IDirect3DTexture8** ppTexture);\n\n    HRESULT STDMETHODCALLTYPE CreateVolumeTexture(\n            UINT                      Width,\n            UINT                      Height,\n            UINT                      Depth,\n            UINT                      Levels,\n            DWORD                     Usage,\n            D3DFORMAT                 Format,\n            D3DPOOL                   Pool,\n            IDirect3DVolumeTexture8** ppVolumeTexture);\n\n    HRESULT STDMETHODCALLTYPE CreateCubeTexture(\n          UINT                      EdgeLength,\n            UINT                    Levels,\n            DWORD                   Usage,\n            D3DFORMAT               Format,\n            D3DPOOL                 Pool,\n            IDirect3DCubeTexture8** ppCubeTexture);\n\n    HRESULT STDMETHODCALLTYPE CreateVertexBuffer(\n            UINT                     Length,\n            DWORD                    Usage,\n            DWORD                    FVF,\n            D3DPOOL                  Pool,\n            IDirect3DVertexBuffer8** ppVertexBuffer);\n\n    HRESULT STDMETHODCALLTYPE CreateIndexBuffer(\n            UINT                    Length,\n            DWORD                   Usage,\n            D3DFORMAT               Format,\n            D3DPOOL                 Pool,\n            IDirect3DIndexBuffer8** ppIndexBuffer);\n\n    HRESULT STDMETHODCALLTYPE CreateRenderTarget(\n            UINT                Width,\n            UINT                Height,\n            D3DFORMAT           Format,\n            D3DMULTISAMPLE_TYPE MultiSample,\n            BOOL                Lockable,\n            IDirect3DSurface8** ppSurface);\n\n    HRESULT STDMETHODCALLTYPE CreateDepthStencilSurface(\n            UINT                Width,\n            UINT                Height,\n            D3DFORMAT           Format,\n            D3DMULTISAMPLE_TYPE MultiSample,\n            IDirect3DSurface8** ppSurface);\n\n    HRESULT STDMETHODCALLTYPE CreateImageSurface(UINT Width, UINT Height, D3DFORMAT Format, IDirect3DSurface8** ppSurface);\n\n    HRESULT STDMETHODCALLTYPE CopyRects(\n            IDirect3DSurface8*  pSourceSurface,\n      const RECT*               pSourceRectsArray,\n            UINT                cRects,\n            IDirect3DSurface8*  pDestinationSurface,\n      const POINT*              pDestPointsArray);\n\n    HRESULT STDMETHODCALLTYPE UpdateTexture(\n            IDirect3DBaseTexture8* pSourceTexture,\n            IDirect3DBaseTexture8* pDestinationTexture);\n\n    HRESULT STDMETHODCALLTYPE GetFrontBuffer(IDirect3DSurface8* pDestSurface);\n\n    HRESULT STDMETHODCALLTYPE SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil);\n\n    HRESULT STDMETHODCALLTYPE GetRenderTarget(IDirect3DSurface8** ppRenderTarget);\n\n    HRESULT STDMETHODCALLTYPE GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface);\n\n    HRESULT STDMETHODCALLTYPE BeginScene();\n\n    HRESULT STDMETHODCALLTYPE EndScene();\n\n    HRESULT STDMETHODCALLTYPE Clear(\n            DWORD    Count,\n      const D3DRECT* pRects,\n            DWORD    Flags,\n            D3DCOLOR Color,\n            float    Z,\n            DWORD    Stencil);\n\n    HRESULT STDMETHODCALLTYPE SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix);\n\n    HRESULT STDMETHODCALLTYPE GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix);\n\n    HRESULT STDMETHODCALLTYPE MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix);\n\n    HRESULT STDMETHODCALLTYPE SetViewport(const D3DVIEWPORT8* pViewport);\n\n    HRESULT STDMETHODCALLTYPE GetViewport(D3DVIEWPORT8* pViewport);\n\n    HRESULT STDMETHODCALLTYPE SetMaterial(const D3DMATERIAL8* pMaterial);\n\n    HRESULT STDMETHODCALLTYPE GetMaterial(D3DMATERIAL8* pMaterial);\n\n    HRESULT STDMETHODCALLTYPE SetLight(DWORD Index, const D3DLIGHT8* pLight);\n\n    HRESULT STDMETHODCALLTYPE GetLight(DWORD Index, D3DLIGHT8* pLight);\n\n    HRESULT STDMETHODCALLTYPE LightEnable(DWORD Index, BOOL Enable);\n\n    HRESULT STDMETHODCALLTYPE GetLightEnable(DWORD Index, BOOL* pEnable);\n\n    HRESULT STDMETHODCALLTYPE SetClipPlane(DWORD Index, const float* pPlane);\n\n    HRESULT STDMETHODCALLTYPE GetClipPlane(DWORD Index, float* pPlane);\n\n    HRESULT STDMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);\n\n    HRESULT STDMETHODCALLTYPE GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue);\n\n    HRESULT STDMETHODCALLTYPE CreateStateBlock(\n            D3DSTATEBLOCKTYPE     Type,\n            DWORD*                pToken);\n\n    HRESULT STDMETHODCALLTYPE CaptureStateBlock(DWORD Token);\n\n    HRESULT STDMETHODCALLTYPE ApplyStateBlock(DWORD Token);\n\n    HRESULT STDMETHODCALLTYPE DeleteStateBlock(DWORD Token);\n\n    HRESULT STDMETHODCALLTYPE BeginStateBlock();\n\n    HRESULT STDMETHODCALLTYPE EndStateBlock(DWORD* pToken);\n\n    HRESULT STDMETHODCALLTYPE SetClipStatus(const D3DCLIPSTATUS8* pClipStatus);\n\n    HRESULT STDMETHODCALLTYPE GetClipStatus(D3DCLIPSTATUS8* pClipStatus);\n\n    HRESULT STDMETHODCALLTYPE GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture);\n\n    HRESULT STDMETHODCALLTYPE SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture);\n\n    HRESULT STDMETHODCALLTYPE GetTextureStageState(\n            DWORD                    Stage,\n            D3DTEXTURESTAGESTATETYPE Type,\n            DWORD*                   pValue);\n\n    HRESULT STDMETHODCALLTYPE SetTextureStageState(\n            DWORD                    Stage,\n            D3DTEXTURESTAGESTATETYPE Type,\n            DWORD                    Value);\n\n    HRESULT STDMETHODCALLTYPE ValidateDevice(DWORD* pNumPasses);\n\n    HRESULT STDMETHODCALLTYPE GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize);\n\n    HRESULT STDMETHODCALLTYPE SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries);\n\n    HRESULT STDMETHODCALLTYPE GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries);\n\n    HRESULT STDMETHODCALLTYPE SetCurrentTexturePalette(UINT PaletteNumber);\n\n    HRESULT STDMETHODCALLTYPE GetCurrentTexturePalette(UINT* PaletteNumber);\n\n    HRESULT STDMETHODCALLTYPE DrawPrimitive(\n            D3DPRIMITIVETYPE PrimitiveType,\n            UINT             StartVertex,\n            UINT             PrimitiveCount);\n\n    HRESULT STDMETHODCALLTYPE DrawIndexedPrimitive(\n            D3DPRIMITIVETYPE PrimitiveType,\n            UINT             MinVertexIndex,\n            UINT             NumVertices,\n            UINT             StartIndex,\n            UINT             PrimitiveCount);\n\n    HRESULT STDMETHODCALLTYPE DrawPrimitiveUP(\n            D3DPRIMITIVETYPE PrimitiveType,\n            UINT             PrimitiveCount,\n      const void*            pVertexStreamZeroData,\n            UINT             VertexStreamZeroStride);\n\n    HRESULT STDMETHODCALLTYPE DrawIndexedPrimitiveUP(\n            D3DPRIMITIVETYPE PrimitiveType,\n            UINT             MinVertexIndex,\n            UINT             NumVertices,\n            UINT             PrimitiveCount,\n      const void*            pIndexData,\n            D3DFORMAT        IndexDataFormat,\n      const void*            pVertexStreamZeroData,\n            UINT             VertexStreamZeroStride);\n\n    HRESULT STDMETHODCALLTYPE ProcessVertices(\n        UINT                         SrcStartIndex,\n        UINT                         DestIndex,\n        UINT                         VertexCount,\n        IDirect3DVertexBuffer8*      pDestBuffer,\n        DWORD                        Flags);\n\n    HRESULT STDMETHODCALLTYPE CreateVertexShader(\n      const DWORD*  pDeclaration,\n      const DWORD*  pFunction,\n            DWORD*  pHandle,\n            DWORD   Usage);\n\n    HRESULT STDMETHODCALLTYPE SetVertexShader(DWORD Handle);\n\n    HRESULT STDMETHODCALLTYPE GetVertexShader(DWORD* pHandle);\n\n    HRESULT STDMETHODCALLTYPE DeleteVertexShader(DWORD Handle);\n\n    HRESULT STDMETHODCALLTYPE SetVertexShaderConstant(\n            DWORD StartRegister,\n      const void* pConstantData,\n            DWORD ConstantCount);\n\n    HRESULT STDMETHODCALLTYPE GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount);\n\n    HRESULT STDMETHODCALLTYPE GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData);\n\n    HRESULT STDMETHODCALLTYPE GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData);\n\n    HRESULT STDMETHODCALLTYPE SetStreamSource(\n            UINT                    StreamNumber,\n            IDirect3DVertexBuffer8* pStreamData,\n            UINT                    Stride);\n\n    HRESULT STDMETHODCALLTYPE GetStreamSource(\n            UINT                     StreamNumber,\n            IDirect3DVertexBuffer8** ppStreamData,\n            UINT*                    pStride);\n\n    HRESULT STDMETHODCALLTYPE SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex);\n\n    HRESULT STDMETHODCALLTYPE GetIndices(\n            IDirect3DIndexBuffer8** ppIndexData,\n            UINT* pBaseVertexIndex);\n\n    HRESULT STDMETHODCALLTYPE CreatePixelShader(\n      const DWORD* pFunction,\n            DWORD* pHandle);\n\n    HRESULT STDMETHODCALLTYPE SetPixelShader(DWORD Handle);\n\n    HRESULT STDMETHODCALLTYPE GetPixelShader(DWORD* pHandle);\n\n    HRESULT STDMETHODCALLTYPE DeletePixelShader(THIS_ DWORD Handle);\n\n    HRESULT STDMETHODCALLTYPE GetPixelShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount);\n\n    HRESULT STDMETHODCALLTYPE SetPixelShaderConstant(\n            DWORD StartRegister,\n      const void* pConstantData,\n            DWORD ConstantCount);\n\n    HRESULT STDMETHODCALLTYPE GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData);\n\n    HRESULT STDMETHODCALLTYPE DrawRectPatch(\n            UINT               Handle,\n      const float*             pNumSegs,\n      const D3DRECTPATCH_INFO* pRectPatchInfo);\n\n    HRESULT STDMETHODCALLTYPE DrawTriPatch(\n            UINT              Handle,\n      const float*            pNumSegs,\n      const D3DTRIPATCH_INFO* pTriPatchInfo);\n\n    HRESULT STDMETHODCALLTYPE DeletePatch(UINT Handle);\n\n    const D3D8Options* GetOptions() const {\n      return &m_d3d8Options;\n    }\n\n    inline bool ShouldRecord() { return m_recorder != nullptr; }\n    inline bool ShouldBatch()  { return m_batcher  != nullptr; }\n\n    D3D8DeviceLock LockDevice() {\n      return m_multithread.AcquireLock();\n    }\n\n    /**\n     * Marks any state change in the device, so we can signal\n     * the batcher to emit draw calls. StateChange should be\n     * called immediately before changing any D3D9 state.\n     */\n    inline void StateChange() {\n      if (unlikely(ShouldBatch()))\n        m_batcher->StateChange();\n    }\n\n    inline void ResetState() {\n      // Mirrors how D3D9 handles the BackBufferCount\n      m_presentParams.BackBufferCount = std::max(m_presentParams.BackBufferCount, 1u);\n\n      // Reset D3D8 exclusive render states\n      m_linePattern = {};\n      m_zVisible    = 0;\n\n      // Purge cached objects\n      m_textures.fill(nullptr);\n      m_streams.fill(D3D8VBO());\n      m_indices = nullptr;\n      m_renderTarget = nullptr;\n      m_depthStencil = nullptr;\n\n      m_backBuffers.clear();\n      m_backBuffers.resize(m_presentParams.BackBufferCount);\n\n      m_autoDepthStencil = nullptr;\n\n      m_shadowPerspectiveDivide = false;\n    }\n\n    inline void RecreateBackBuffersAndAutoDepthStencil() {\n      for (UINT i = 0; i < m_presentParams.BackBufferCount; i++) {\n        Com<d3d9::IDirect3DSurface9> pSurface9;\n        GetD3D9()->GetBackBuffer(0, i, d3d9::D3DBACKBUFFER_TYPE_MONO, &pSurface9);\n        m_backBuffers[i] = new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pSurface9));\n      }\n\n      Com<d3d9::IDirect3DSurface9> pStencil9;\n      // This call will fail if the D3D9 device is created without\n      // the EnableAutoDepthStencil presentation parameter set to TRUE.\n      HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9);\n      m_autoDepthStencil = FAILED(res) ? nullptr : new D3D8Surface(this, D3DPOOL_DEFAULT, std::move(pStencil9));\n\n      m_renderTarget = m_backBuffers[0];\n      m_depthStencil = m_autoDepthStencil;\n    }\n\n    friend d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle);\n    friend D3D8VertexShaderInfo*        getVertexShaderInfo(D3D8Device* device, DWORD Handle);\n\n  private:\n\n    Com<IDxvkD3D8Bridge>  m_bridge;\n    const D3D8Options&    m_d3d8Options;\n\n    Com<D3D8Interface>    m_parent;\n\n    D3DPRESENT_PARAMETERS m_presentParams;\n    \n    // Value of D3DRS_LINEPATTERN\n    D3DLINEPATTERN        m_linePattern   = {};\n    // Value of D3DRS_ZVISIBLE (although the RS is not supported, its value is stored)\n    DWORD                 m_zVisible      = 0;\n\n    bool                  m_shadowPerspectiveDivide = false;\n\n    D3D8StateBlock*                            m_recorder = nullptr;\n    DWORD                                      m_recorderToken = 0;\n    DWORD                                      m_token    = 0;\n    std::unordered_map<DWORD, D3D8StateBlock>  m_stateBlocks;\n    D3D8Batcher*                               m_batcher  = nullptr;\n\n    struct D3D8VBO {\n      Com<D3D8VertexBuffer, false>   buffer = nullptr;\n      UINT                           stride = 0;\n    };\n\n    std::array<Com<D3D8Texture2D, false>, d8caps::MAX_TEXTURE_STAGES>  m_textures;\n    std::array<D3D8VBO, d8caps::MAX_STREAMS>                           m_streams;\n\n    Com<D3D8IndexBuffer, false>        m_indices;\n    UINT                               m_baseVertexIndex = 0;\n\n    std::vector<Com<D3D8Surface, false>> m_backBuffers;\n    Com<D3D8Surface, false>              m_autoDepthStencil;\n\n    Com<D3D8Surface, false>     m_renderTarget;\n    Com<D3D8Surface, false>     m_depthStencil;\n\n    std::vector<D3D8VertexShaderInfo>               m_vertexShaders;\n    std::vector<Com<d3d9::IDirect3DPixelShader9>>   m_pixelShaders;\n    DWORD                                           m_currentVertexShader  = 0; // can be FVF or vs index (marked by D3DFVF_RESERVED0)\n    DWORD                                           m_currentPixelShader   = 0;\n\n    D3DDEVTYPE            m_deviceType;\n    HWND                  m_window;\n\n    DWORD                 m_behaviorFlags;\n\n    D3D8Multithread       m_multithread;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_device_child.h",
    "content": "#pragma once\n\n// Common methods for device-tied objects.\n// - AddRef, Release from IUnknown\n// - GetDevice from various classes including IDirect3DResource8\n\n#include \"d3d8_include.h\"\n#include \"d3d8_wrapped_object.h\"\n\nnamespace dxvk {\n\n  class D3D8Device;\n\n  template <typename D3D9, typename D3D8>\n  class D3D8DeviceChild : public D3D8WrappedObject<D3D9, D3D8> {\n\n  public:\n\n    D3D8DeviceChild(D3D8Device* pDevice, Com<D3D9>&& Object)\n      : D3D8WrappedObject<D3D9, D3D8>(std::move(Object))\n      , m_parent( pDevice ) { }\n\n    ULONG STDMETHODCALLTYPE AddRef() {\n      uint32_t refCount = this->m_refCount++;\n      if (unlikely(!refCount)) {\n        this->AddRefPrivate();\n        GetDevice()->AddRef();\n      }\n\n      return refCount + 1;\n    }\n\n    ULONG STDMETHODCALLTYPE Release() {\n      uint32_t oldRefCount, refCount;\n\n      do {\n        oldRefCount = this->m_refCount.load(std::memory_order_acquire);\n\n        // clamp value to 0 to prevent underruns\n        if (unlikely(!oldRefCount))\n          return 0;\n\n        refCount = oldRefCount - 1;\n\n      } while (!this->m_refCount.compare_exchange_weak(oldRefCount,\n                                                       refCount,\n                                                       std::memory_order_release,\n                                                       std::memory_order_acquire));\n\n      if (unlikely(!refCount)) {\n        auto* pDevice = GetDevice();\n        this->ReleasePrivate();\n        pDevice->Release();\n      }\n\n      return refCount;\n    }\n\n    HRESULT STDMETHODCALLTYPE GetDevice(IDirect3DDevice8** ppDevice) {\n      InitReturnPtr(ppDevice);\n\n      if (ppDevice == nullptr)\n        return D3DERR_INVALIDCALL;\n\n      *ppDevice = ref(GetDevice());\n      return D3D_OK;\n    }\n\n    IDirect3DDevice8* GetDevice() {\n      return reinterpret_cast<IDirect3DDevice8*>(m_parent);\n    }\n\n    D3D8Device* GetParent() {\n      return m_parent;\n    }\n\n  protected:\n\n    D3D8Device* m_parent = nullptr;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_format.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n\nnamespace dxvk {\n\n  inline bool isDXTFormat(D3DFORMAT fmt) {\n    return fmt == D3DFMT_DXT1\n        || fmt == D3DFMT_DXT2\n        || fmt == D3DFMT_DXT3\n        || fmt == D3DFMT_DXT4\n        || fmt == D3DFMT_DXT5;\n  }\n\n  inline bool isDepthStencilFormat(D3DFORMAT fmt) {\n    return fmt == D3DFMT_D16_LOCKABLE\n        || fmt == D3DFMT_D16\n        || fmt == D3DFMT_D32\n        || fmt == D3DFMT_D15S1\n        || fmt == D3DFMT_D24X4S4\n        || fmt == D3DFMT_D24S8\n        || fmt == D3DFMT_D24X8;\n  }\n\n  // The d3d8 documentation states: Render target formats are restricted to\n  // D3DFMT_X1R5G5B5, D3DFMT_R5G6B5, D3DFMT_X8R8G8B8, and D3DFMT_A8R8G8B8.\n  // This limited RT format support is confirmed by age-accurate drivers.\n  inline bool isRenderTargetFormat(D3DFORMAT fmt) {\n    return fmt == D3DFMT_X1R5G5B5\n        || fmt == D3DFMT_R5G6B5\n        || fmt == D3DFMT_X8R8G8B8\n        || fmt == D3DFMT_A8R8G8B8\n        // NULL format support was later added to d3d9 with\n        // GeForce 6 series cards, and also advertised in d3d8.\n        || fmt == (D3DFORMAT) MAKEFOURCC('N', 'U', 'L', 'L');\n  }\n\n  // Some games will exhaustively query all formats in the 0-100 range,\n  // so filter out some known formats which are exclusive to d3d9\n  inline bool isD3D9ExclusiveFormat(D3DFORMAT fmt) {\n    const d3d9::D3DFORMAT d3d9Fmt = d3d9::D3DFORMAT(fmt);\n\n    return d3d9Fmt == d3d9::D3DFMT_A8B8G8R8            //32\n        || d3d9Fmt == d3d9::D3DFMT_X8B8G8R8            //33\n        || d3d9Fmt == d3d9::D3DFMT_A2R10G10B10         //35\n        || d3d9Fmt == d3d9::D3DFMT_A16B16G16R16        //36\n        || d3d9Fmt == d3d9::D3DFMT_L16                 //81\n        || d3d9Fmt == d3d9::D3DFMT_D32F_LOCKABLE       //82\n        || d3d9Fmt == d3d9::D3DFMT_D24FS8              //83\n        || d3d9Fmt == d3d9::D3DFMT_D32_LOCKABLE        //84\n        || d3d9Fmt == d3d9::D3DFMT_S8_LOCKABLE         //85\n        || d3d9Fmt == d3d9::D3DFMT_Q16W16V16U16        //110\n        || d3d9Fmt == d3d9::D3DFMT_R16F                //111\n        || d3d9Fmt == d3d9::D3DFMT_G16R16F             //112\n        || d3d9Fmt == d3d9::D3DFMT_A16B16G16R16F       //113\n        || d3d9Fmt == d3d9::D3DFMT_R32F                //114\n        || d3d9Fmt == d3d9::D3DFMT_G32R32F             //115\n        || d3d9Fmt == d3d9::D3DFMT_A32B32G32R32F       //116\n        || d3d9Fmt == d3d9::D3DFMT_CxV8U8              //117\n        || d3d9Fmt == d3d9::D3DFMT_A1                  //118\n        || d3d9Fmt == d3d9::D3DFMT_A2B10G10R10_XR_BIAS //119\n        || d3d9Fmt == (d3d9::D3DFORMAT) MAKEFOURCC('D', 'F', '1', '6')\n        || d3d9Fmt == (d3d9::D3DFORMAT) MAKEFOURCC('D', 'F', '2', '4')\n        || d3d9Fmt == (d3d9::D3DFORMAT) MAKEFOURCC('I', 'N', 'T', 'Z');\n  }\n\n  // Get bytes per pixel (or 4x4 block for DXT)\n  inline UINT getFormatStride(D3DFORMAT fmt) {\n    switch (fmt) {\n      default:\n      case D3DFMT_UNKNOWN:\n        return 0;\n      case D3DFMT_R3G3B2:\n      case D3DFMT_A8:\n      case D3DFMT_P8:\n      case D3DFMT_L8:\n      case D3DFMT_A4L4:\n        return 1;\n      case D3DFMT_R5G6B5:\n      case D3DFMT_X1R5G5B5:\n      case D3DFMT_A1R5G5B5:\n      case D3DFMT_A4R4G4B4:\n      case D3DFMT_A8R3G3B2:\n      case D3DFMT_X4R4G4B4:\n      case D3DFMT_A8P8:\n      case D3DFMT_A8L8:\n      case D3DFMT_V8U8:\n      case D3DFMT_L6V5U5:\n      case D3DFMT_D16_LOCKABLE:\n      case D3DFMT_D15S1:\n      case D3DFMT_D16:\n      case D3DFMT_UYVY:\n      case D3DFMT_YUY2:\n        return 2;\n      case D3DFMT_R8G8B8:\n        return 3;\n      case D3DFMT_A8R8G8B8:\n      case D3DFMT_X8R8G8B8:\n      case D3DFMT_A2B10G10R10:\n      case D3DFMT_G16R16:\n      case D3DFMT_X8L8V8U8:\n      case D3DFMT_Q8W8V8U8:\n      case D3DFMT_V16U16:\n      case D3DFMT_W11V11U10:\n      case D3DFMT_A2W10V10U10:\n      case D3DFMT_D32:\n      case D3DFMT_D24S8:\n      case D3DFMT_D24X8:\n      case D3DFMT_D24X4S4:\n        return 4;\n      case D3DFMT_DXT1:\n        return 8;\n      case D3DFMT_DXT2:\n      case D3DFMT_DXT3:\n      case D3DFMT_DXT4:\n      case D3DFMT_DXT5:\n        return 16;\n    }\n  }\n\n  inline UINT getSurfaceSize(D3DFORMAT Format, UINT Width, UINT Height) {\n    if (isDXTFormat(Format)) {\n      Width = ((Width + 3) >> 2);\n      Height = ((Height + 3) >> 2);\n    }\n    return Width * Height * getFormatStride(Format);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_include.h",
    "content": "#pragma once\n\n#ifndef _MSC_VER\n#ifdef _WIN32_WINNT\n#undef _WIN32_WINNT\n#endif\n#define _WIN32_WINNT 0x0A00\n#endif\n\n#include <stdint.h>\n#include <d3d8.h>\n\n// Declare __uuidof for D3D8 interfaces\n#ifdef __CRT_UUID_DECL\n__CRT_UUID_DECL(IDirect3D8,              0x1DD9E8DA, 0x1C77, 0x4D40, 0xB0, 0xCF, 0x98, 0xFE, 0xFD, 0xFF, 0x95, 0x12);\n__CRT_UUID_DECL(IDirect3DDevice8,        0x7385E5DF, 0x8FE8, 0x41D5, 0x86, 0xB6, 0xD7, 0xB4, 0x85, 0x47, 0xB6, 0xCF);\n__CRT_UUID_DECL(IDirect3DResource8,      0x1B36BB7B, 0x09B7, 0x410A, 0xB4, 0x45, 0x7D, 0x14, 0x30, 0xD7, 0xB3, 0x3F);\n__CRT_UUID_DECL(IDirect3DVertexBuffer8,  0x8AEEEAC7, 0x05F9, 0x44D4, 0xB5, 0x91, 0x00, 0x0B, 0x0D, 0xF1, 0xCB, 0x95);\n__CRT_UUID_DECL(IDirect3DVolume8,        0xBD7349F5, 0x14F1, 0x42E4, 0x9C, 0x79, 0x97, 0x23, 0x80, 0xDB, 0x40, 0xC0);\n__CRT_UUID_DECL(IDirect3DSwapChain8,     0x928C088B, 0x76B9, 0x4C6B, 0xA5, 0x36, 0xA5, 0x90, 0x85, 0x38, 0x76, 0xCD);\n__CRT_UUID_DECL(IDirect3DSurface8,       0xB96EEBCA, 0xB326, 0x4EA5, 0x88, 0x2F, 0x2F, 0xF5, 0xBA, 0xE0, 0x21, 0xDD);\n__CRT_UUID_DECL(IDirect3DIndexBuffer8,   0x0E689C9A, 0x053D, 0x44A0, 0x9D, 0x92, 0xDB, 0x0E, 0x3D, 0x75, 0x0F, 0x86);\n__CRT_UUID_DECL(IDirect3DBaseTexture8,   0xB4211CFA, 0x51B9, 0x4A9F, 0xAB, 0x78, 0xDB, 0x99, 0xB2, 0xBB, 0x67, 0x8E);\n__CRT_UUID_DECL(IDirect3DTexture8,       0xE4CDD575, 0x2866, 0x4F01, 0xB1, 0x2E, 0x7E, 0xEC, 0xE1, 0xEC, 0x93, 0x58);\n__CRT_UUID_DECL(IDirect3DCubeTexture8,   0x3EE5B968, 0x2ACA, 0x4C34, 0x8B, 0xB5, 0x7E, 0x0C, 0x3D, 0x19, 0xB7, 0x50);\n__CRT_UUID_DECL(IDirect3DVolumeTexture8, 0x4B8AAAFA, 0x140F, 0x42BA, 0x91, 0x31, 0x59, 0x7E, 0xAF, 0xAA, 0x2E, 0xAD);\n#elif defined(_MSC_VER)\ninterface DECLSPEC_UUID(\"1DD9E8DA-1C77-4D40-B0CF-98FEFDFF9512\") IDirect3D8;\ninterface DECLSPEC_UUID(\"7385E5DF-8FE8-41D5-86B6-D7B48547B6CF\") IDirect3DDevice8;\ninterface DECLSPEC_UUID(\"1B36BB7B-09B7-410A-B445-7D1430D7B33F\") IDirect3DResource8;\ninterface DECLSPEC_UUID(\"8AEEEAC7-05F9-44D4-B591-000B0DF1CB95\") IDirect3DVertexBuffer8;\ninterface DECLSPEC_UUID(\"BD7349F5-14F1-42E4-9C79-972380DB40C0\") IDirect3DVolume8;\ninterface DECLSPEC_UUID(\"928C088B-76B9-4C6B-A536-A590853876CD\") IDirect3DSwapChain8;\ninterface DECLSPEC_UUID(\"B96EEBCA-B326-4EA5-882F-2FF5BAE021DD\") IDirect3DSurface8;\ninterface DECLSPEC_UUID(\"0E689C9A-053D-44A0-9D92-DB0E3D750F86\") IDirect3DIndexBuffer8;\ninterface DECLSPEC_UUID(\"B4211CFA-51B9-4A9F-AB78-DB99B2BB678E\") IDirect3DBaseTexture8;\ninterface DECLSPEC_UUID(\"E4CDD575-2866-4F01-B12E-7EECE1EC9358\") IDirect3DTexture8;\ninterface DECLSPEC_UUID(\"3EE5B968-2ACA-4C34-8BB5-7E0C3D19B750\") IDirect3DCubeTexture8;\ninterface DECLSPEC_UUID(\"4B8AAAFA-140F-42BA-9131-597EAFAA2EAD\") IDirect3DVolumeTexture8;\n#endif\n\n// Undefine D3D8 macros\n#undef DIRECT3D_VERSION\n#undef D3D_SDK_VERSION\n\n#undef D3DCS_ALL            // parentheses added in D3D9\n#undef D3DFVF_POSITION_MASK // changed from 0x00E to 0x400E in D3D9\n#undef D3DFVF_RESERVED2     // reduced from 4 to 2 in DX9\n\n#undef D3DSP_REGNUM_MASK    // changed from 0x00000FFF to 0x000007FF in D3D9\n\n\n#if defined(__MINGW32__) || defined(__GNUC__)\n\n// Avoid redundant definitions (add D3D*_DEFINED macros here)\n#define D3DRECT_DEFINED\n#define D3DMATRIX_DEFINED\n\n// Temporarily undefine __CRT_UUID_DECL\n// to allow imports in the d3d9 namespace\n#pragma push_macro(\"__CRT_UUID_DECL\")\n#ifdef __CRT_UUID_DECL\n#undef __CRT_UUID_DECL\n#endif\n\n#endif // defined(__MINGW32__) || defined(__GNUC__)\n\n\n/**\n* \\brief Direct3D 9\n*\n* All D3D9 interfaces are included within\n* a namespace, so as not to collide with\n* D3D8 interfaces.\n*/\nnamespace d3d9 {\n#include <d3d9.h>\n}\n\n// Indicates d3d9:: namespace is in-use.\n#define DXVK_D3D9_NAMESPACE\n\n\n#if defined(__MINGW32__) || defined(__GNUC__)\n#pragma pop_macro(\"__CRT_UUID_DECL\")\n\n// Declare __uuidof for D3D9 interfaces, directly within the d3d9:: namespace\n#ifdef __CRT_UUID_DECL\n__CRT_UUID_DECL(d3d9::IDirect3D9,                  0x81BDCBCA, 0x64D4, 0x426D, 0xAE, 0x8D, 0xAD, 0x01, 0x47, 0xF4, 0x27, 0x5C);\n__CRT_UUID_DECL(d3d9::IDirect3DVolume9,            0x24F416E6, 0x1F67, 0x4AA7, 0xB8, 0x8E, 0xD3, 0x3F, 0x6F, 0x31, 0x28, 0xA1);\n__CRT_UUID_DECL(d3d9::IDirect3DSwapChain9,         0x794950F2, 0xADFC, 0x458A, 0x90, 0x5E, 0x10, 0xA1, 0x0B, 0x0B, 0x50, 0x3B);\n__CRT_UUID_DECL(d3d9::IDirect3DResource9,          0x05EEC05D, 0x8F7D, 0x4362, 0xB9, 0x99, 0xD1, 0xBA, 0xF3, 0x57, 0xC7, 0x04);\n__CRT_UUID_DECL(d3d9::IDirect3DSurface9,           0x0CFBAF3A, 0x9FF6, 0x429A, 0x99, 0xB3, 0xA2, 0x79, 0x6A, 0xF8, 0xB8, 0x9B);\n__CRT_UUID_DECL(d3d9::IDirect3DVertexBuffer9,      0xB64BB1B5, 0xFD70, 0x4DF6, 0xBF, 0x91, 0x19, 0xD0, 0xA1, 0x24, 0x55, 0xE3);\n__CRT_UUID_DECL(d3d9::IDirect3DIndexBuffer9,       0x7C9DD65E, 0xD3F7, 0x4529, 0xAC, 0xEE, 0x78, 0x58, 0x30, 0xAC, 0xDE, 0x35);\n__CRT_UUID_DECL(d3d9::IDirect3DBaseTexture9,       0x580CA87E, 0x1D3C, 0x4D54, 0x99, 0x1D, 0xB7, 0xD3, 0xE3, 0xC2, 0x98, 0xCE);\n__CRT_UUID_DECL(d3d9::IDirect3DCubeTexture9,       0xFFF32F81, 0xD953, 0x473A, 0x92, 0x23, 0x93, 0xD6, 0x52, 0xAB, 0xA9, 0x3F);\n__CRT_UUID_DECL(d3d9::IDirect3DTexture9,           0x85C31227, 0x3DE5, 0x4F00, 0x9B, 0x3A, 0xF1, 0x1A, 0xC3, 0x8C, 0x18, 0xB5);\n__CRT_UUID_DECL(d3d9::IDirect3DVolumeTexture9,     0x2518526C, 0xE789, 0x4111, 0xA7, 0xB9, 0x47, 0xEF, 0x32, 0x8D, 0x13, 0xE6);\n__CRT_UUID_DECL(d3d9::IDirect3DVertexDeclaration9, 0xDD13C59C, 0x36FA, 0x4098, 0xA8, 0xFB, 0xC7, 0xED, 0x39, 0xDC, 0x85, 0x46);\n__CRT_UUID_DECL(d3d9::IDirect3DVertexShader9,      0xEFC5557E, 0x6265, 0x4613, 0x8A, 0x94, 0x43, 0x85, 0x78, 0x89, 0xEB, 0x36);\n__CRT_UUID_DECL(d3d9::IDirect3DPixelShader9,       0x6D3BDBDC, 0x5B02, 0x4415, 0xB8, 0x52, 0xCE, 0x5E, 0x8B, 0xCC, 0xB2, 0x89);\n__CRT_UUID_DECL(d3d9::IDirect3DStateBlock9,        0xB07C4FE5, 0x310D, 0x4BA8, 0xA2, 0x3C, 0x4F, 0x0F, 0x20, 0x6F, 0x21, 0x8B);\n__CRT_UUID_DECL(d3d9::IDirect3DQuery9,             0xD9771460, 0xA695, 0x4F26, 0xBB, 0xD3, 0x27, 0xB8, 0x40, 0xB5, 0x41, 0xCC);\n__CRT_UUID_DECL(d3d9::IDirect3DDevice9,            0xD0223B96, 0xBF7A, 0x43FD, 0x92, 0xBD, 0xA4, 0x3B, 0x0D, 0x82, 0xB9, 0xEB);\n__CRT_UUID_DECL(d3d9::IDirect3D9Ex,                0x02177241, 0x69FC, 0x400C, 0x8F, 0xF1, 0x93, 0xA4, 0x4D, 0xF6, 0x86, 0x1D);\n__CRT_UUID_DECL(d3d9::IDirect3DSwapChain9Ex,       0x91886CAF, 0x1C3D, 0x4D2E, 0xA0, 0xAB, 0x3E, 0x4C, 0x7D, 0x8D, 0x33, 0x03);\n__CRT_UUID_DECL(d3d9::IDirect3DDevice9Ex,          0xB18B10CE, 0x2649, 0x405A, 0x87, 0x0F, 0x95, 0xF7, 0x77, 0xD4, 0x31, 0x3A);\n#endif\n\n#endif // defined(__MINGW32__) || defined(__GNUC__)\n\n\n//for some reason we need to specify __declspec(dllexport) for MinGW\n#if defined(__WINE__) || !defined(_WIN32)\n  #define DLLEXPORT __attribute__((visibility(\"default\")))\n#else\n  #define DLLEXPORT\n#endif\n\n\n#include \"../util/com/com_guid.h\"\n#include \"../util/com/com_object.h\"\n#include \"../util/com/com_pointer.h\"\n\n#include \"../util/log/log.h\"\n#include \"../util/log/log_debug.h\"\n\n#include \"../util/sync/sync_recursive.h\"\n\n#include \"../util/util_error.h\"\n#include \"../util/util_likely.h\"\n#include \"../util/util_string.h\"\n\n// Missed definitions in Wine/MinGW.\n\n#ifndef D3DPRESENT_BACK_BUFFERS_MAX_EX\n#define D3DPRESENT_BACK_BUFFERS_MAX_EX    30\n#endif\n\n#ifndef D3DSI_OPCODE_MASK\n#define D3DSI_OPCODE_MASK                 0x0000FFFF\n#endif\n\n#ifndef D3DSP_TEXTURETYPE_MASK\n#define D3DSP_TEXTURETYPE_MASK            0x78000000\n#endif\n\n#ifndef D3DUSAGE_AUTOGENMIPMAP\n#define D3DUSAGE_AUTOGENMIPMAP            0x00000400L\n#endif\n\n#ifndef D3DSP_DCL_USAGE_MASK\n#define D3DSP_DCL_USAGE_MASK              0x0000000f\n#endif\n\n#ifndef D3DSP_OPCODESPECIFICCONTROL_MASK\n#define D3DSP_OPCODESPECIFICCONTROL_MASK  0x00ff0000\n#endif\n\n#ifndef D3DSP_OPCODESPECIFICCONTROL_SHIFT\n#define D3DSP_OPCODESPECIFICCONTROL_SHIFT 16\n#endif\n\n#ifndef D3DCURSOR_IMMEDIATE_UPDATE\n#define D3DCURSOR_IMMEDIATE_UPDATE        0x00000001L\n#endif\n\n#ifndef D3DPRESENT_FORCEIMMEDIATE\n#define D3DPRESENT_FORCEIMMEDIATE         0x00000100L\n#endif\n\n// From d3dtypes.h\n\n#ifndef D3DDEVINFOID_TEXTUREMANAGER\n#define D3DDEVINFOID_TEXTUREMANAGER       1\n#endif\n\n#ifndef D3DDEVINFOID_D3DTEXTUREMANAGER\n#define D3DDEVINFOID_D3DTEXTUREMANAGER    2\n#endif\n\n#ifndef D3DDEVINFOID_TEXTURING\n#define D3DDEVINFOID_TEXTURING            3\n#endif\n\n// From d3dhal.h\n\n#ifndef D3DDEVINFOID_VCACHE\n#define D3DDEVINFOID_VCACHE               4\n#endif\n\n// MinGW headers are broken. Who'dve guessed?\n#ifndef _MSC_VER\n\n// Missing from d3d8types.h\n#ifndef D3DDEVINFOID_RESOURCEMANAGER\n#define D3DDEVINFOID_RESOURCEMANAGER      5\n#endif\n\n#ifndef D3DDEVINFOID_VERTEXSTATS\n#define D3DDEVINFOID_VERTEXSTATS          6 // Aka D3DDEVINFOID_D3DVERTEXSTATS\n#endif\n\n#ifndef D3DPRESENT_RATE_UNLIMITED\n#define D3DPRESENT_RATE_UNLIMITED         0x7FFFFFFF\n#endif\n\n#else // _MSC_VER\n\n// These are enum typedefs in the MinGW headers, but not defined by Microsoft\n#define D3DVSDT_TYPE                      DWORD\n#define D3DVSDE_REGISTER                  DWORD\n\n#endif\n"
  },
  {
    "path": "src/d3d8/d3d8_interface.cpp",
    "content": "#include \"d3d8_interface.h\"\n\n#include \"d3d8_device.h\"\n#include \"d3d8_texture.h\"\n\n#include <cstring>\n\nnamespace dxvk {\n\n  D3D8Interface::D3D8Interface()\n    : m_d3d9(d3d9::Direct3DCreate9(D3D_SDK_VERSION)) {\n    // Get the bridge interface to D3D9.\n    if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast<void**>(&m_bridge))))) {\n      throw DxvkError(\"D3D8Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!\");\n    }\n\n    m_bridge->EnableD3D8CompatibilityMode();\n\n    m_d3d8Options = D3D8Options(*m_bridge->GetConfig());\n\n    m_adapterCount = m_d3d9->GetAdapterCount();\n    m_adapterModeCounts.resize(m_adapterCount);\n    m_adapterModes.reserve(m_adapterCount);\n\n    for (UINT adapter = 0; adapter < m_adapterCount; adapter++) {\n      m_adapterModes.emplace_back();\n\n      // cache adapter modes and mode counts for each d3d9 format\n      for (d3d9::D3DFORMAT fmt : ADAPTER_FORMATS) {\n\n        const UINT modeCount = m_d3d9->GetAdapterModeCount(adapter, fmt);\n        for (UINT mode = 0; mode < modeCount; mode++) {\n\n          m_adapterModes[adapter].emplace_back();\n          m_d3d9->EnumAdapterModes(adapter, fmt, mode, &(m_adapterModes[adapter].back()));\n\n          // can't use modeCount as it's only for one fmt\n          m_adapterModeCounts[adapter]++;\n        }\n      }\n    }\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Interface::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3D8)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    Logger::warn(\"D3D8Interface::QueryInterface: Unknown interface query\");\n    Logger::warn(str::format(riid));\n    return E_NOINTERFACE;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Interface::GetAdapterIdentifier(\n          UINT Adapter,\n          DWORD Flags,\n          D3DADAPTER_IDENTIFIER8* pIdentifier) {\n    if (unlikely(pIdentifier == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // This flag now has the opposite effect.\n    // Either way, WHQLevel will be 1 with Direct3D9Ex\n    if (Flags & D3DENUM_NO_WHQL_LEVEL)\n      Flags &= ~D3DENUM_WHQL_LEVEL;\n    else\n      Flags |= D3DENUM_WHQL_LEVEL;\n\n    d3d9::D3DADAPTER_IDENTIFIER9 identifier9;\n    HRESULT res = m_d3d9->GetAdapterIdentifier(Adapter, Flags, &identifier9);\n\n    if (unlikely(FAILED(res)))\n      return res;\n\n    strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING);\n    strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING);\n\n    pIdentifier->DriverVersion    = identifier9.DriverVersion;\n    pIdentifier->VendorId         = identifier9.VendorId;\n    pIdentifier->DeviceId         = identifier9.DeviceId;\n    pIdentifier->SubSysId         = identifier9.SubSysId;\n    pIdentifier->Revision         = identifier9.Revision;\n    pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier;\n\n    pIdentifier->WHQLLevel = identifier9.WHQLLevel;\n\n    return D3D_OK;\n  }\n\n  HRESULT __stdcall D3D8Interface::EnumAdapterModes(\n          UINT Adapter,\n          UINT Mode,\n          D3DDISPLAYMODE* pMode) {\n    if (Adapter >= m_adapterCount || Mode >= m_adapterModeCounts[Adapter] || pMode == nullptr) {\n      return D3DERR_INVALIDCALL;\n    }\n\n    pMode->Width        = m_adapterModes[Adapter][Mode].Width;\n    pMode->Height       = m_adapterModes[Adapter][Mode].Height;\n    pMode->RefreshRate  = m_adapterModes[Adapter][Mode].RefreshRate;\n    pMode->Format       = D3DFORMAT(m_adapterModes[Adapter][Mode].Format);\n\n    return D3D_OK;\n  }\n\n  HRESULT __stdcall D3D8Interface::CreateDevice(\n        UINT Adapter,\n        D3DDEVTYPE DeviceType,\n        HWND hFocusWindow,\n        DWORD BehaviorFlags,\n        D3DPRESENT_PARAMETERS* pPresentationParameters,\n        IDirect3DDevice8** ppReturnedDeviceInterface) {\n    InitReturnPtr(ppReturnedDeviceInterface);\n\n    if (unlikely(ppReturnedDeviceInterface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    HRESULT res = ValidatePresentationParameters(pPresentationParameters);\n\n    if (unlikely(FAILED(res)))\n      return res;\n\n    Com<d3d9::IDirect3DDevice9> pDevice9;\n    d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);\n    res = m_d3d9->CreateDevice(\n      Adapter,\n      (d3d9::D3DDEVTYPE)DeviceType,\n      hFocusWindow,\n      BehaviorFlags,\n      &params,\n      &pDevice9\n    );\n\n    if (unlikely(FAILED(res)))\n      return res;\n\n    try {\n      *ppReturnedDeviceInterface = ref(new D3D8Device(\n        this, std::move(pDevice9),\n        DeviceType, hFocusWindow, BehaviorFlags,\n        pPresentationParameters\n      ));\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_NOTAVAILABLE;\n    }\n\n    return D3D_OK;\n  }\n\n  HRESULT D3D8Interface::ValidatePresentationParameters(\n        const D3DPRESENT_PARAMETERS* pPresentationParameters) {\n    if (unlikely(pPresentationParameters == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // D3DSWAPEFFECT_COPY can not be used with more than one back buffer.\n    // This is also technically true for D3DSWAPEFFECT_COPY_VSYNC, however\n    // RC Cars depends on it not being rejected.\n    if (unlikely(pPresentationParameters->SwapEffect == D3DSWAPEFFECT_COPY\n              && pPresentationParameters->BackBufferCount > 1))\n      return D3DERR_INVALIDCALL;\n\n    // In D3D8 nothing except D3DPRESENT_INTERVAL_DEFAULT can be used\n    // as a flag for windowed presentation.\n    if (unlikely(pPresentationParameters->Windowed\n              && pPresentationParameters->FullScreen_PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT))\n      return D3DERR_INVALIDCALL;\n\n    return D3D_OK;\n  }\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_interface.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n#include \"d3d8_util.h\"\n#include \"d3d8_options.h\"\n#include \"d3d8_format.h\"\n#include \"../d3d9/d3d9_bridge.h\"\n\nnamespace dxvk {\n\n  /**\n  * \\brief D3D8 interface implementation\n  *\n  * Implements the IDirect3DDevice8 interfaces\n  * which provides the way to get adapters and create other objects such as \\ref IDirect3DDevice8.\n  * similar to \\ref DxgiFactory but for D3D8.\n  */\n  class D3D8Interface final : public ComObjectClamp<IDirect3D8> {\n\n    // These must be valid render target formats, and as per the\n    // D3D8 documentation: \"Render target formats are restricted to\n    // D3DFMT_X1R5G5B5, D3DFMT_R5G6B5, D3DFMT_X8R8G8B8, and D3DFMT_A8R8G8B8.\"\n    //\n    // Additionally, the documentation states: \"Applications should not\n    // specify a DisplayFormat that contains an alpha channel.\"\n    //\n    // While D3DFMT_X1R5G5B5 is technically valid, no drivers list\n    // modes for it, therefore including it in caching queries is redundant.\n    static constexpr d3d9::D3DFORMAT ADAPTER_FORMATS[] = {\n      d3d9::D3DFMT_X8R8G8B8,\n      d3d9::D3DFMT_R5G6B5\n    };\n\n  public:\n    D3D8Interface();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction) {\n      return m_d3d9->RegisterSoftwareDevice(pInitializeFunction);\n    }\n\n    UINT STDMETHODCALLTYPE GetAdapterCount() {\n      return m_d3d9->GetAdapterCount();\n    }\n\n    HRESULT STDMETHODCALLTYPE GetAdapterIdentifier(\n      UINT                    Adapter,\n      DWORD                   Flags,\n      D3DADAPTER_IDENTIFIER8* pIdentifier);\n\n    UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter) {\n      return m_adapterModeCounts[Adapter];\n    }\n\n    HRESULT STDMETHODCALLTYPE EnumAdapterModes(\n      UINT            Adapter,\n      UINT            Mode,\n      D3DDISPLAYMODE* pMode);\n\n    HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) {\n      return m_d3d9->GetAdapterDisplayMode(Adapter, reinterpret_cast<d3d9::D3DDISPLAYMODE*>(pMode));\n    }\n\n    HRESULT STDMETHODCALLTYPE CheckDeviceType(\n        UINT        Adapter,\n        D3DDEVTYPE  DevType,\n        D3DFORMAT   AdapterFormat,\n        D3DFORMAT   BackBufferFormat,\n        BOOL        bWindowed) {\n      // Ignore the bWindowed parameter when querying D3D9. D3D8 does\n      // identical validations between windowed and fullscreen modes, adhering\n      // to the stricter fullscreen adapter and back buffer format validations.\n      return m_d3d9->CheckDeviceType(\n          Adapter,\n          (d3d9::D3DDEVTYPE)DevType,\n          (d3d9::D3DFORMAT)AdapterFormat,\n          (d3d9::D3DFORMAT)BackBufferFormat,\n          FALSE\n      );\n    }\n\n    HRESULT STDMETHODCALLTYPE CheckDeviceFormat(\n        UINT            Adapter,\n        D3DDEVTYPE      DeviceType,\n        D3DFORMAT       AdapterFormat,\n        DWORD           Usage,\n        D3DRESOURCETYPE RType,\n        D3DFORMAT       CheckFormat) {\n      if (unlikely(isD3D9ExclusiveFormat(CheckFormat)))\n        return D3DERR_NOTAVAILABLE;\n\n      if (unlikely((Usage & D3DUSAGE_RENDERTARGET) && !isRenderTargetFormat(CheckFormat)))\n        return D3DERR_NOTAVAILABLE;\n\n      return m_d3d9->CheckDeviceFormat(\n        Adapter,\n        (d3d9::D3DDEVTYPE)DeviceType,\n        (d3d9::D3DFORMAT)AdapterFormat,\n        Usage,\n        (d3d9::D3DRESOURCETYPE)RType,\n        (d3d9::D3DFORMAT)CheckFormat\n      );\n    }\n\n    HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType(\n            UINT                Adapter,\n            D3DDEVTYPE          DeviceType,\n            D3DFORMAT           SurfaceFormat,\n            BOOL                Windowed,\n            D3DMULTISAMPLE_TYPE MultiSampleType) {\n      DWORD* pQualityLevels = nullptr;\n      return m_d3d9->CheckDeviceMultiSampleType(\n        Adapter,\n        (d3d9::D3DDEVTYPE)DeviceType,\n        (d3d9::D3DFORMAT)SurfaceFormat,\n        Windowed,\n        (d3d9::D3DMULTISAMPLE_TYPE)MultiSampleType,\n        pQualityLevels\n      );\n    }\n\n    HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch(\n        UINT Adapter,\n        D3DDEVTYPE DeviceType,\n        D3DFORMAT AdapterFormat,\n        D3DFORMAT RenderTargetFormat,\n        D3DFORMAT DepthStencilFormat) {\n      if (unlikely(isD3D9ExclusiveFormat(RenderTargetFormat)\n                || isD3D9ExclusiveFormat(DepthStencilFormat)))\n        return D3DERR_NOTAVAILABLE;\n\n      if (unlikely(!isRenderTargetFormat(RenderTargetFormat)))\n        return D3DERR_NOTAVAILABLE;\n\n      return m_d3d9->CheckDepthStencilMatch(\n        Adapter,\n        (d3d9::D3DDEVTYPE)DeviceType,\n        (d3d9::D3DFORMAT)AdapterFormat,\n        (d3d9::D3DFORMAT)RenderTargetFormat,\n        (d3d9::D3DFORMAT)DepthStencilFormat\n      );\n    }\n\n    HRESULT STDMETHODCALLTYPE GetDeviceCaps(\n        UINT Adapter,\n        D3DDEVTYPE DeviceType,\n        D3DCAPS8* pCaps) {\n      if (unlikely(pCaps == nullptr))\n        return D3DERR_INVALIDCALL;\n\n      d3d9::D3DCAPS9 caps9;\n      HRESULT res = m_d3d9->GetDeviceCaps(Adapter, d3d9::D3DDEVTYPE(DeviceType), &caps9);\n\n      if (likely(SUCCEEDED(res)))\n        ConvertCaps8(caps9, pCaps);\n\n      return res;\n    }\n\n    HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter) {\n      return m_d3d9->GetAdapterMonitor(Adapter);\n    }\n\n    HRESULT STDMETHODCALLTYPE CreateDevice(\n        UINT Adapter,\n        D3DDEVTYPE DeviceType,\n        HWND hFocusWindow,\n        DWORD BehaviorFlags,\n        D3DPRESENT_PARAMETERS* pPresentationParameters,\n        IDirect3DDevice8** ppReturnedDeviceInterface);\n\n    HRESULT ValidatePresentationParameters(\n        const D3DPRESENT_PARAMETERS* pPresentationParameters);\n\n    const D3D8Options& GetOptions() const { return m_d3d8Options; }\n\n  private:\n\n    UINT                                            m_adapterCount = 0;\n    std::vector<UINT>                               m_adapterModeCounts;\n    std::vector<std::vector<d3d9::D3DDISPLAYMODE>>  m_adapterModes;\n\n    Com<d3d9::IDirect3D9>                           m_d3d9;\n    Com<IDxvkD3D8InterfaceBridge>                   m_bridge;\n    D3D8Options                                     m_d3d8Options;\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_main.cpp",
    "content": "#include \"d3d8_interface.h\"\n\nnamespace dxvk {\n\n  Logger Logger::s_instance(\"d3d8.log\");\n\n  HRESULT CreateD3D8(IDirect3D8** ppDirect3D8) {\n    if (!ppDirect3D8)\n      return D3DERR_INVALIDCALL;\n\n    try {\n      *ppDirect3D8 = ref(new D3D8Interface());\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      *ppDirect3D8 = nullptr;\n      return D3DERR_NOTAVAILABLE;\n    }\n\n    return D3D_OK;\n  }\n\n}\n\nextern \"C\" {\n\n  DLLEXPORT HRESULT __stdcall ValidatePixelShader(\n      const DWORD*     pPixelShader,\n      const D3DCAPS8*  pCaps,\n      BOOL             ErrorReturn,\n      char**           pErrorString) {\n    HRESULT res = S_OK;\n    std::string errorMessage = \"\";\n\n    // ValidatePixelShader returns immediately in case of a NULL pPixelShader\n    if (unlikely(pPixelShader == nullptr)) {\n      dxvk::Logger::warn(\"D3D8: ValidatePixelShader: Null pPixelShader\");\n      return E_FAIL;\n    } else {\n      const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pPixelShader[0]);\n      const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pPixelShader[0]);\n\n      if (unlikely(majorVersion != 1 || minorVersion > 4)) {\n        errorMessage = dxvk::str::format(\"D3D8: ValidatePixelShader: Unsupported PS version \",\n                                          majorVersion, \".\", minorVersion);\n        res = E_FAIL;\n      } else if (unlikely(pCaps && pPixelShader[0] > pCaps->PixelShaderVersion)) {\n        errorMessage = dxvk::str::format(\"D3D8: ValidatePixelShader: Caps: Unsupported PS version \",\n                                          majorVersion, \".\", minorVersion);\n        res = E_FAIL;\n      }\n    }\n\n    if (unlikely(res != S_OK)) {\n      dxvk::Logger::warn(errorMessage);\n\n      if (!ErrorReturn)\n        errorMessage = \"\";\n    }\n\n#ifdef _WIN32\n    if (pErrorString != nullptr) {\n      const size_t errorMessageSize = errorMessage.size() + 1;\n      // Wine tests call HeapFree() on the returned error string,\n      // so the expectation is for it to be allocated on the heap.\n      *pErrorString = static_cast<char*>(HeapAlloc(GetProcessHeap(), 0, errorMessageSize));\n      if (*pErrorString)\n        memcpy(*pErrorString, errorMessage.c_str(), errorMessageSize);\n    }\n#endif\n\n    return res;\n  }\n\n  DLLEXPORT HRESULT __stdcall ValidateVertexShader(\n      const DWORD*     pVertexShader,\n      const DWORD*     pVertexDecl,\n      const D3DCAPS8*  pCaps,\n      BOOL             ErrorReturn,\n      char**           pErrorString) {\n    HRESULT res = S_OK;\n    std::string errorMessage = \"\";\n\n    if (unlikely(pVertexShader == nullptr)) {\n      errorMessage = \"D3D8: ValidateVertexShader: Null pVertexShader\";\n      res = E_FAIL;\n    } else {\n      const uint32_t majorVersion = D3DSHADER_VERSION_MAJOR(pVertexShader[0]);\n      const uint32_t minorVersion = D3DSHADER_VERSION_MINOR(pVertexShader[0]);\n\n      if (unlikely(majorVersion != 1 || minorVersion > 1)) {\n        errorMessage = dxvk::str::format(\"D3D8: ValidateVertexShader: Unsupported VS version \",\n                                          majorVersion, \".\", minorVersion);\n        res = E_FAIL;\n      } else if (unlikely(pCaps && pVertexShader[0] > pCaps->VertexShaderVersion)) {\n        errorMessage = dxvk::str::format(\"D3D8: ValidateVertexShader: Caps: Unsupported VS version \",\n                                          majorVersion, \".\", minorVersion);\n        res = E_FAIL;\n      }\n    }\n\n    if (unlikely(res != S_OK)) {\n      dxvk::Logger::warn(errorMessage);\n\n      if (!ErrorReturn)\n        errorMessage = \"\";\n    }\n\n#ifdef _WIN32\n    if (pErrorString != nullptr) {\n      const size_t errorMessageSize = errorMessage.size() + 1;\n      // Wine tests call HeapFree() on the returned error string,\n      // so the expectation is for it to be allocated on the heap.\n      *pErrorString = static_cast<char*>(HeapAlloc(GetProcessHeap(), 0, errorMessageSize));\n      if (*pErrorString)\n        memcpy(*pErrorString, errorMessage.c_str(), errorMessageSize);\n    }\n#endif\n\n    return res;\n  }\n\n  DLLEXPORT void __stdcall DebugSetMute() {}\n\n  DLLEXPORT IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) {\n    IDirect3D8* pDirect3D = nullptr;\n    dxvk::CreateD3D8(&pDirect3D);\n\n    return pDirect3D;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_multithread.cpp",
    "content": "#include \"d3d8_device.h\"\n\nnamespace dxvk {\n\n  D3D8Multithread::D3D8Multithread(\n          BOOL                  Protected)\n    : m_protected( Protected ) { }\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_multithread.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Device lock\n   *\n   * Lightweight RAII wrapper that implements\n   * a subset of the functionality provided by\n   * \\c std::unique_lock, with the goal of being\n   * cheaper to construct and destroy.\n   */\n  class D3D8DeviceLock {\n\n  public:\n\n    D3D8DeviceLock()\n      : m_mutex(nullptr) { }\n\n    D3D8DeviceLock(sync::RecursiveSpinlock& mutex)\n      : m_mutex(&mutex) {\n      mutex.lock();\n    }\n\n    D3D8DeviceLock(D3D8DeviceLock&& other)\n      : m_mutex(other.m_mutex) {\n      other.m_mutex = nullptr;\n    }\n\n    D3D8DeviceLock& operator = (D3D8DeviceLock&& other) {\n      if (m_mutex)\n        m_mutex->unlock();\n\n      m_mutex = other.m_mutex;\n      other.m_mutex = nullptr;\n      return *this;\n    }\n\n    ~D3D8DeviceLock() {\n      if (m_mutex != nullptr)\n        m_mutex->unlock();\n    }\n\n  private:\n\n    sync::RecursiveSpinlock* m_mutex;\n\n  };\n\n\n  /**\n   * \\brief D3D8 context lock\n   */\n  class D3D8Multithread {\n\n  public:\n\n    D3D8Multithread(\n      BOOL                  Protected);\n\n    D3D8DeviceLock AcquireLock() {\n      return m_protected\n        ? D3D8DeviceLock(m_mutex)\n        : D3D8DeviceLock();\n    }\n\n  private:\n\n    BOOL            m_protected;\n\n    sync::RecursiveSpinlock m_mutex;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_options.cpp",
    "content": "#include \"d3d8_options.h\"\n\n#include \"../d3d9/d3d9_bridge.h\"\n#include \"../util/config/config.h\"\n#include \"../util/util_string.h\"\n\n#include <charconv>\n\nnamespace dxvk {\n\n  static inline uint32_t parseDword(std::string_view str) {\n    uint32_t value = std::numeric_limits<uint32_t>::max();\n    std::from_chars(str.data(), str.data() + str.size(), value);\n    return value;\n  }\n\n  void D3D8Options::parseVsDecl(const std::string& decl) {\n    if (decl.empty())\n      return;\n\n    if (decl.find_first_of(\"0123456789\") == std::string::npos) {\n      Logger::warn(str::format(\"D3D8: Invalid forceVsDecl value: \", decl));\n      Logger::warn(\"D3D8: Expected numbers.\");\n      return;\n    }\n\n    if (decl.find_first_of(\":,;\") == std::string::npos) {\n      Logger::warn(str::format(\"D3D8: Invalid forceVsDecl value: \", decl));\n      Logger::warn(\"D3D8: Expected a comma-separated list of colon-separated number pairs.\");\n      return;\n    }\n\n    std::vector<std::string_view> decls = str::split(decl, \":,;\");\n\n    if (decls.size() % 2 != 0) {\n      Logger::warn(str::format(\"D3D8: Invalid forceVsDecl value: \", decl));\n      Logger::warn(\"D3D8: Expected an even number of numbers.\");\n      return;\n    }\n\n    for (size_t i = 0; i < decls.size(); i += 2) {\n      uint32_t reg = parseDword(decls[i]);\n      uint32_t type = parseDword(decls[i+1]);\n      if (reg > D3DVSDE_NORMAL2) {\n        Logger::warn(str::format(\"D3D8: Invalid forceVsDecl register number: \", decls[i]));\n        return;\n      }\n      if (type > D3DVSDT_SHORT4) {\n        Logger::warn(str::format(\"D3D8: Invalid forceVsDecl type: \", decls[i+1]));\n        return;\n      }\n\n      forceVsDecl.emplace_back(D3DVSDE_REGISTER(reg), D3DVSDT_TYPE(type));\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_options.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n#include \"../d3d9/d3d9_bridge.h\"\n#include \"../util/config/config.h\"\n\nnamespace dxvk {\n\n  struct D3D8Options {\n\n    /// Override application vertex shader declarations.\n    std::vector<std::pair<D3DVSDE_REGISTER, D3DVSDT_TYPE>> forceVsDecl;\n\n    /// Enable/disable the drawcall batcher.\n    bool batching;\n\n    /// Place all P8 textures in D3DPOOL_SCRATCH.\n    bool placeP8InScratch;\n\n    /// Ignore D3DLOCK_DISCARD for everything except D3DUSAGE_DYNAMIC + D3DUSAGE_WRITEONLY buffers.\n    bool forceLegacyDiscard;\n\n    /// Force D3DTTFF_PROJECTED for the necessary stages when a depth texture is bound to slot 0.\n    bool shadowPerspectiveDivide;\n\n    D3D8Options() {}\n\n    D3D8Options(const Config& config) {\n      auto forceVsDeclStr     = config.getOption<std::string>(\"d3d8.forceVsDecl\",             \"\");\n      batching                = config.getOption<bool>       (\"d3d8.batching\",                false);\n      placeP8InScratch        = config.getOption<bool>       (\"d3d8.placeP8InScratch\",        false);\n      forceLegacyDiscard      = config.getOption<bool>       (\"d3d8.forceLegacyDiscard\",      false);\n      shadowPerspectiveDivide = config.getOption<bool>       (\"d3d8.shadowPerspectiveDivide\", false);\n\n      parseVsDecl(forceVsDeclStr);\n    }\n\n    void parseVsDecl(const std::string& decl);\n  };\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_resource.h",
    "content": "#pragma once\n\n/** Implements IDirect3DResource8\n*\n* - SetPrivateData, GetPrivateData, FreePrivateData\n* - SetPriority, GetPriority\n*\n* - Subclasses provide: PreLoad, GetType\n*/\n\n#include \"d3d8_device_child.h\"\n#include \"../util/com/com_private_data.h\"\n\nnamespace dxvk {\n\n  template <typename D3D9, typename D3D8>\n  class D3D8Resource : public D3D8DeviceChild<D3D9, D3D8> {\n\n  public:\n\n    D3D8Resource(D3D8Device* pDevice, D3DPOOL Pool, Com<D3D9>&& Object)\n      : D3D8DeviceChild<D3D9, D3D8>(pDevice, std::move(Object))\n      , m_pool                     ( Pool )\n      , m_priority                 ( 0 ) { }\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID     refguid,\n      const void*       pData,\n            DWORD       SizeOfData,\n            DWORD       Flags) final {\n      HRESULT hr;\n      if (Flags & D3DSPD_IUNKNOWN) {\n        if(unlikely(SizeOfData != sizeof(IUnknown*)))\n          return D3DERR_INVALIDCALL;\n        IUnknown* unknown =\n          const_cast<IUnknown*>(\n            reinterpret_cast<const IUnknown*>(pData));\n        hr = m_privateData.setInterface(\n          refguid, unknown);\n      }\n      else\n        hr = m_privateData.setData(\n          refguid, SizeOfData, pData);\n\n      if (unlikely(FAILED(hr)))\n        return D3DERR_INVALIDCALL;\n\n      return D3D_OK;\n    }\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID     refguid,\n            void*       pData,\n            DWORD*      pSizeOfData) final {\n      if (unlikely(pData == nullptr && pSizeOfData == nullptr))\n        return D3DERR_NOTFOUND;\n\n      HRESULT hr = m_privateData.getData(\n        refguid, reinterpret_cast<UINT*>(pSizeOfData), pData);\n\n      if (unlikely(FAILED(hr))) {\n        if(hr == DXGI_ERROR_MORE_DATA)\n          return D3DERR_MOREDATA;\n        else if (hr == DXGI_ERROR_NOT_FOUND)\n          return D3DERR_NOTFOUND;\n        else\n          return D3DERR_INVALIDCALL;\n      }\n\n      return D3D_OK;\n    }\n\n    HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final {\n      HRESULT hr = m_privateData.setData(refguid, 0, nullptr);\n\n      if (unlikely(FAILED(hr)))\n        return D3DERR_INVALIDCALL;\n\n      return D3D_OK;\n    }\n\n    DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) {\n      // Priority can only be set for D3DPOOL_MANAGED resources\n      if (likely(m_pool == D3DPOOL_MANAGED)) {\n        DWORD oldPriority = m_priority;\n        m_priority = PriorityNew;\n        return oldPriority;\n      }\n\n      return m_priority;\n    }\n\n    DWORD STDMETHODCALLTYPE GetPriority() {\n      return m_priority;\n    }\n\n    virtual IUnknown* GetInterface(REFIID riid) override try {\n      return D3D8DeviceChild<D3D9, D3D8>::GetInterface(riid);\n    } catch (const DxvkError& e) {\n      if (riid == __uuidof(IDirect3DResource8))\n        return this;\n\n      throw e;\n    }\n\n  protected:\n\n    const D3DPOOL        m_pool;\n          DWORD          m_priority;\n\n  private:\n\n    ComPrivateData m_privateData;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_shader.cpp",
    "content": "#include \"d3d8_shader.h\"\n\n#define VSD_SHIFT_MASK(token, field) ((token & field ## MASK) >> field ## SHIFT)\n#define VSD_ENCODE(token, field) ((token << field ## _SHIFT) & field ## _MASK)\n\n// Magic number from D3DVSD_SKIP(...)\n#define VSD_SKIP_FLAG 0x10000000\n\n// This bit is set on all parameter (non-instruction) tokens.\n#define VS_BIT_PARAM 0x80000000\n\nnamespace dxvk {\n\n  static constexpr uint32_t D3D8_NUM_VERTEX_INPUT_REGISTERS = 17;\n\n  /**\n   * Standard mapping of vertex input registers v0-v16 to D3D9 usages and usage indices\n   * (See D3DVSDE_REGISTER values in d3d8types.h or DirectX 8 docs for vertex shader input registers vN)\n   *\n   * \\cite https://learn.microsoft.com/en-us/windows/win32/direct3d9/mapping-between-a-directx-9-declaration-and-directx-8\n  */\n  static constexpr BYTE D3D8_VERTEX_INPUT_REGISTERS[D3D8_NUM_VERTEX_INPUT_REGISTERS][2] = {\n    {d3d9::D3DDECLUSAGE_POSITION, 0},      // dcl_position     v0\n    {d3d9::D3DDECLUSAGE_BLENDWEIGHT, 0},   // dcl_blendweight  v1\n    {d3d9::D3DDECLUSAGE_BLENDINDICES, 0},  // dcl_blendindices v2\n    {d3d9::D3DDECLUSAGE_NORMAL, 0},        // dcl_normal       v3\n    {d3d9::D3DDECLUSAGE_PSIZE, 0},         // dcl_psize        v4\n    {d3d9::D3DDECLUSAGE_COLOR, 0},         // dcl_color        v5 ; diffuse\n    {d3d9::D3DDECLUSAGE_COLOR, 1},         // dcl_color1       v6 ; specular\n    {d3d9::D3DDECLUSAGE_TEXCOORD, 0},      // dcl_texcoord0    v7\n    {d3d9::D3DDECLUSAGE_TEXCOORD, 1},      // dcl_texcoord1    v8\n    {d3d9::D3DDECLUSAGE_TEXCOORD, 2},      // dcl_texcoord2    v9\n    {d3d9::D3DDECLUSAGE_TEXCOORD, 3},      // dcl_texcoord3    v10\n    {d3d9::D3DDECLUSAGE_TEXCOORD, 4},      // dcl_texcoord4    v11\n    {d3d9::D3DDECLUSAGE_TEXCOORD, 5},      // dcl_texcoord5    v12\n    {d3d9::D3DDECLUSAGE_TEXCOORD, 6},      // dcl_texcoord6    v13\n    {d3d9::D3DDECLUSAGE_TEXCOORD, 7},      // dcl_texcoord7    v14\n    {d3d9::D3DDECLUSAGE_POSITION, 1},      // dcl_position1    v15 ; position 2\n    {d3d9::D3DDECLUSAGE_NORMAL, 1},        // dcl_normal1      v16 ; normal 2\n  };\n\n  /** Width in bytes of each d3d9::D3DDECLTYPE or d3d8 D3DVSDT_TYPE */\n  static constexpr BYTE D3D9_DECL_TYPE_SIZES[d3d9::MAXD3DDECLTYPE + 1] = {\n    4,  // FLOAT1\n    8,  // FLOAT2\n    12, // FLOAT3\n    16, // FLOAT4\n    4,  // D3DCOLOR\n\n    4,  // UBYTE4\n    4,  // SHORT2\n    8,  // SHORT4\n\n    // The following are for vs2.0+ //\n    4,  // UBYTE4N\n    4,  // SHORT2N\n    8,  // SHORT4N\n    4,  // USHORT2N\n    8,  // USHORT4N\n    6,  // UDEC3\n    6,  // DEC3N\n    8,  // FLOAT16_2\n    16, // FLOAT16_4\n\n    0   // UNUSED\n  };\n\n  /**\n   * Encodes a \\ref DxsoShaderInstruction\n   *\n   * \\param [in]  opcode  DxsoOpcode\n   * \\cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/instruction-token\n   */\n  constexpr DWORD encodeInstruction(d3d9::D3DSHADER_INSTRUCTION_OPCODE_TYPE opcode) {\n    DWORD token   = 0;\n    token        |= opcode & 0xFFFF;  // bits 0:15\n    return token;\n  }\n\n  /**\n   * Encodes a \\ref DxsoRegister\n   *\n   * \\param [in]  regType  DxsoRegisterType\n   * \\cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/destination-parameter-token\n   */\n  constexpr DWORD encodeDestRegister(d3d9::D3DSHADER_PARAM_REGISTER_TYPE type, UINT reg) {\n    DWORD token = 0;\n    token |= reg & 0x7FF;                  // bits 0:10   num\n    token |= ((type & 0x07) << 28);        // bits 28:30  type[0:2]\n    token |= ((type & 0x18) >>  3) << 11;  // bits 11:12  type[3:4]\n    // UINT addrMode : 1;                  // bit  13     hasRelative\n    token |= 0b1111 << 16;                 // bits 16:19  DxsoRegMask\n    // UINT resultModifier : 3;            // bits 20:23\n    // UINT resultShift : 3;               // bits 24:27\n    token |= 1u << 31;                     // bit  31     always 1\n    return token;\n  }\n\n  /**\n   * Encodes a \\ref DxsoDeclaration\n   *\n   * \\cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/dcl-instruction\n   */\n  constexpr DWORD encodeDeclaration(d3d9::D3DDECLUSAGE usage, DWORD index) {\n    DWORD token = 0;\n    token |= VSD_ENCODE(usage, D3DSP_DCL_USAGE);       // bits 0:4   DxsoUsage (TODO: missing MSB)\n    token |= VSD_ENCODE(index, D3DSP_DCL_USAGEINDEX);  // bits 16:19 usageIndex\n    token |= 1u << 31;                                 // bit 31     always 1\n    return token;\n  }\n\n  /**\n   * Validates and converts a D3D8 vertex shader\n   * + declaration to a D3D9 vertex shader + declaration.\n  */\n  HRESULT TranslateVertexShader8(\n      const DWORD*          pDeclaration,\n      const DWORD*          pFunction,\n      const D3D8Options&    options,\n      D3D9VertexShaderCode& pTranslatedVS) {\n    using d3d9::D3DDECLTYPE;\n    using d3d9::D3DDECLTYPE_UNUSED;\n\n    HRESULT res = D3D_OK;\n\n    std::vector<DWORD> defs; // Constant definitions\n\n    // shaderInputRegisters:\n    // set bit N to enable input register vN\n    DWORD shaderInputRegisters = 0;\n\n    d3d9::D3DVERTEXELEMENT9* vertexElements = pTranslatedVS.declaration;\n    uint32_t elementIdx = 0;\n\n    // These are used for pDeclaration and pFunction\n    uint32_t i = 0;\n    DWORD token;\n\n    std::stringstream dbg;\n    dbg << \"D3D8: Vertex Declaration Tokens:\\n\\t\";\n\n    WORD currentStream = 0;\n    WORD currentOffset = 0;\n\n    auto addVertexElement = [&] (D3DVSDE_REGISTER reg, D3DVSDT_TYPE type) {\n      vertexElements[elementIdx].Stream     = currentStream;\n      vertexElements[elementIdx].Offset     = currentOffset;\n      vertexElements[elementIdx].Method     = d3d9::D3DDECLMETHOD_DEFAULT;\n      vertexElements[elementIdx].Type       = D3DDECLTYPE(type); // (D3DVSDT_TYPE values map directly to D3DDECLTYPE)\n      vertexElements[elementIdx].Usage      = D3D8_VERTEX_INPUT_REGISTERS[reg][0];\n      vertexElements[elementIdx].UsageIndex = D3D8_VERTEX_INPUT_REGISTERS[reg][1];\n\n      // Increase stream offset\n      currentOffset += D3D9_DECL_TYPE_SIZES[type];\n\n      // Enable register vn\n      shaderInputRegisters |= 1 << reg;\n\n      // Finished with this element\n      elementIdx++;\n    };\n\n    // Remap d3d8 decl tokens to d3d9 vertex elements,\n    // and enable bits on shaderInputRegisters for each.\n    if (options.forceVsDecl.size() == 0) do {\n      token = pDeclaration[i++];\n\n      D3DVSD_TOKENTYPE tokenType = D3DVSD_TOKENTYPE(VSD_SHIFT_MASK(token, D3DVSD_TOKENTYPE));\n\n      switch (tokenType) {\n        case D3DVSD_TOKEN_NOP:\n          dbg << \"NOP\";\n          break;\n        case D3DVSD_TOKEN_STREAM: {\n          dbg << \"STREAM \";\n\n          // TODO: D3DVSD_STREAM_TESS\n          if (token & D3DVSD_STREAMTESSMASK) {\n            dbg << \"TESS\";\n          }\n\n          DWORD streamNum = VSD_SHIFT_MASK(token, D3DVSD_STREAMNUMBER);\n\n          currentStream = WORD(streamNum);\n          currentOffset = 0; // reset offset\n\n          dbg << \", num=\" << streamNum;\n          break;\n        }\n        case D3DVSD_TOKEN_STREAMDATA: {\n\n          dbg << \"STREAMDATA \";\n\n          // D3DVSD_SKIP\n          if (token & VSD_SKIP_FLAG) {\n            auto skipCount = VSD_SHIFT_MASK(token, D3DVSD_SKIPCOUNT);\n            dbg << \"SKIP \" << \" count=\" << skipCount;\n            currentOffset += WORD(skipCount) * sizeof(DWORD);\n            break;\n          }\n\n          // D3DVSD_REG\n          DWORD dataLoadType = VSD_SHIFT_MASK(token, D3DVSD_DATALOADTYPE);\n\n          if ( dataLoadType == 0 ) { // vertex\n            D3DVSDT_TYPE     type = D3DVSDT_TYPE(VSD_SHIFT_MASK(token, D3DVSD_DATATYPE));\n            D3DVSDE_REGISTER reg  = D3DVSDE_REGISTER(VSD_SHIFT_MASK(token, D3DVSD_VERTEXREG));\n\n            // FVF normals are expected to only have 3 components\n            if (unlikely(pFunction == nullptr && reg == D3DVSDE_NORMAL && type != D3DVSDT_FLOAT3)) {\n              Logger::err(\"D3D8Device::CreateVertexShader: Invalid FVF declaration: D3DVSDE_NORMAL must use D3DVSDT_FLOAT3\");\n              return D3DERR_INVALIDCALL;\n            }\n\n            addVertexElement(reg, type);\n\n            dbg << \"type=\" << type << \", register=\" << reg;\n          } else {\n            // TODO: When would this bit be 1?\n            dbg << \"D3DVSD_DATALOADTYPE \" << dataLoadType;\n          }\n          break;\n        }\n        case D3DVSD_TOKEN_TESSELLATOR:\n          dbg << \"TESSELLATOR \" << std::hex << token;\n          // TODO: D3DVSD_TOKEN_TESSELLATOR\n          break;\n        case D3DVSD_TOKEN_CONSTMEM: {\n          dbg << \"CONSTMEM \";\n          DWORD count     = VSD_SHIFT_MASK(token, D3DVSD_CONSTCOUNT);\n          DWORD regCount  = count * 4;\n          DWORD addr      = VSD_SHIFT_MASK(token, D3DVSD_CONSTADDRESS);\n          DWORD rs        = VSD_SHIFT_MASK(token, D3DVSD_CONSTRS);\n\n          dbg << \"count=\" << count << \", addr=\" << addr << \", rs=\" << rs;\n\n          // Add a DEF instruction for each constant\n          for (uint32_t j = 0; j < regCount; j += 4) {\n            defs.push_back(encodeInstruction(d3d9::D3DSIO_DEF));\n            defs.push_back(encodeDestRegister(d3d9::D3DSPR_CONST2, addr));\n            defs.push_back(pDeclaration[i+j]);\n            defs.push_back(pDeclaration[i+j+1]);\n            defs.push_back(pDeclaration[i+j+2]);\n            defs.push_back(pDeclaration[i+j+3]);\n            addr++;\n          }\n          i += regCount;\n          break;\n        }\n        case D3DVSD_TOKEN_EXT: {\n          dbg << \"EXT \" << std::hex << token << \" \";\n          DWORD extInfo = VSD_SHIFT_MASK(token, D3DVSD_EXTINFO);\n          DWORD extCount = VSD_SHIFT_MASK(token, D3DVSD_EXTCOUNT);\n          dbg << \"info=\" << extInfo << \", count=\" << extCount;\n          break;\n        }\n        case D3DVSD_TOKEN_END: {\n          vertexElements[elementIdx++] = D3DDECL_END();\n          dbg << \"END\";\n          break;\n        }\n        default:\n          dbg << \"UNKNOWN TYPE\";\n          break;\n      }\n      dbg << \"\\n\\t\";\n      //dbg << std::hex << token << \" \";\n    } while (token != D3DVSD_END());\n\n    Logger::debug(dbg.str());\n\n    // If forceVsDecl is set, use that decl instead.\n    if (options.forceVsDecl.size() > 0) {\n      for (auto [reg, type] : options.forceVsDecl) {\n        addVertexElement(reg, type);\n      }\n      vertexElements[elementIdx++] = D3DDECL_END();\n    }\n\n    if (pFunction != nullptr) {\n      std::vector<DWORD>& tokens = pTranslatedVS.function;\n\n      // Copy first token (version)\n      tokens.push_back(pFunction[0]);\n\n      DWORD vsMajor = D3DSHADER_VERSION_MAJOR(pFunction[0]);\n      DWORD vsMinor = D3DSHADER_VERSION_MINOR(pFunction[0]);\n      Logger::debug(str::format(\"VS version: \", vsMajor, \".\", vsMinor));\n\n      // Insert dcl instructions\n      for (UINT vn = 0; vn < D3D8_NUM_VERTEX_INPUT_REGISTERS; vn++) {\n\n        // If bit N is set then we need to dcl register vN\n        if ((shaderInputRegisters & (1 << vn)) != 0) {\n\n          Logger::debug(str::format(\"\\tShader Input Regsiter: v\", vn));\n\n          DWORD usage = D3D8_VERTEX_INPUT_REGISTERS[vn][0];\n          DWORD index = D3D8_VERTEX_INPUT_REGISTERS[vn][1];\n\n          tokens.push_back(encodeInstruction(d3d9::D3DSIO_DCL));                  // dcl opcode\n          tokens.push_back(encodeDeclaration(d3d9::D3DDECLUSAGE(usage), index));  // usage token\n          tokens.push_back(encodeDestRegister(d3d9::D3DSPR_INPUT, vn));           // dest register num\n        }\n      }\n\n      // Copy constant defs\n      for (DWORD def : defs) {\n        tokens.push_back(def);\n      }\n\n      // Copy shader tokens from input,\n      // skip first token (we already copied it)\n      i = 1;\n      do {\n        token = pFunction[i++];\n\n        DWORD opcode = token & D3DSI_OPCODE_MASK;\n\n        // Instructions\n        if ((token & VS_BIT_PARAM) == 0) {\n\n          // Swizzle fixup for opcodes that require explicit use of a replicate swizzle.\n          if (opcode == D3DSIO_RSQ  || opcode == D3DSIO_RCP\n           || opcode == D3DSIO_EXP  || opcode == D3DSIO_LOG\n           || opcode == D3DSIO_EXPP || opcode == D3DSIO_LOGP) {\n            tokens.push_back(token);                            // instr\n            tokens.push_back(token = pFunction[i++]);           // dest\n            token = pFunction[i++];                             // src0\n\n            // If no swizzling is done, then use the w-component.\n            // See d8vk#43 for more information as this may need to change in some cases.\n            if (((token & D3DVS_NOSWIZZLE) == D3DVS_NOSWIZZLE)) {\n              token &= ~D3DVS_SWIZZLE_MASK;\n              token |= (D3DVS_X_W | D3DVS_Y_W | D3DVS_Z_W | D3DVS_W_W);\n            }\n          }\n        }\n        tokens.push_back(token);\n      } while (token != D3DVS_END());\n    }\n\n    return res;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_shader.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n#include \"d3d8_options.h\"\n\nnamespace dxvk {\n\n  struct D3D9VertexShaderCode {\n    d3d9::D3DVERTEXELEMENT9 declaration[MAXD3DDECLLENGTH + 1];\n    std::vector<DWORD> function;\n  };\n\n  struct D3D8VertexShaderInfo {\n    Com<d3d9::IDirect3DVertexDeclaration9>  pVertexDecl;\n    Com<d3d9::IDirect3DVertexShader9>       pVertexShader;\n    std::vector<DWORD>                      declaration;\n    std::vector<DWORD>                      function;\n  };\n\n  HRESULT TranslateVertexShader8(\n    const DWORD*          pDeclaration,\n    const DWORD*          pFunction,\n    const D3D8Options&    overrides,\n    D3D9VertexShaderCode& pTranslatedVS);\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_state_block.cpp",
    "content": "#include \"d3d8_device.h\"\n#include \"d3d8_state_block.h\"\n\nnamespace dxvk {\n\n  D3D8StateBlock::D3D8StateBlock(\n          D3D8Device*                       pDevice,\n          D3D8StateBlockType                Type,\n          Com<d3d9::IDirect3DStateBlock9>&& pStateBlock)\n    : m_device(pDevice)\n    , m_stateBlock(std::move(pStateBlock)) {\n    if (Type == D3D8StateBlockType::All) {\n      m_captures.flags.set(D3D8CapturedStateFlag::Indices);\n      m_captures.flags.set(D3D8CapturedStateFlag::SWVP);\n\n      m_captures.flags.set(D3D8CapturedStateFlag::VertexBuffers);\n      m_captures.streams.setAll();\n\n      m_captures.flags.set(D3D8CapturedStateFlag::Textures);\n      m_captures.textures.setAll();\n    }\n\n    if (Type == D3D8StateBlockType::VertexState || Type == D3D8StateBlockType::All) {\n      // Lights, D3DTSS_TEXCOORDINDEX and D3DTSS_TEXTURETRANSFORMFLAGS,\n      // vertex shader, VS constants, and various render states.\n      m_captures.flags.set(D3D8CapturedStateFlag::VertexShader);\n    }\n\n    if (Type == D3D8StateBlockType::PixelState || Type == D3D8StateBlockType::All) {\n      // Pixel shader, PS constants, and various RS/TSS states.\n      m_captures.flags.set(D3D8CapturedStateFlag::PixelShader);\n    }\n\n    m_state.textures.fill(nullptr);\n    m_state.streams.fill(D3D8VBOP());\n\n    // Automatically capture state on creation via D3D8Device::CreateStateBlock.\n    if (Type != D3D8StateBlockType::None)\n      Capture();\n  }\n\n  // Construct a state block without a D3D9 object\n  D3D8StateBlock::D3D8StateBlock(D3D8Device* pDevice)\n    : D3D8StateBlock(pDevice, D3D8StateBlockType::None, nullptr) {\n  }\n\n  // Attach a D3D9 object to a state block that doesn't have one yet\n  void D3D8StateBlock::SetD3D9(Com<d3d9::IDirect3DStateBlock9>&& pStateBlock) {\n    if (likely(m_stateBlock == nullptr)) {\n      m_stateBlock = std::move(pStateBlock);\n    } else {\n      Logger::err(\"D3D8StateBlock::SetD3D9: m_stateBlock has already been initialized\");\n    }\n  }\n\n  HRESULT D3D8StateBlock::Capture() {\n    if (unlikely(m_stateBlock == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::Indices)) {\n      m_state.baseVertexIndex = m_device->m_baseVertexIndex;\n      m_state.indices = m_device->m_indices.ptr();\n    }\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::SWVP)) {\n      DWORD swvpState;\n      m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, &swvpState);\n      m_state.isSWVP = static_cast<bool>(swvpState);\n    }\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::VertexBuffers)) {\n      for (DWORD stream = 0; stream < m_state.streams.size(); stream++) {\n        if (m_captures.streams.get(stream)) {\n          m_state.streams[stream].buffer = m_device->m_streams[stream].buffer.ptr();\n          m_state.streams[stream].stride = m_device->m_streams[stream].stride;\n        }\n      }\n    }\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::Textures)) {\n      for (DWORD stage = 0; stage < m_state.textures.size(); stage++) {\n        if (m_captures.textures.get(stage))\n          m_state.textures[stage] = m_device->m_textures[stage].ptr();\n      }\n    }\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::VertexShader))\n      m_device->GetVertexShader(&m_state.vertexShaderHandle);\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::PixelShader))\n      m_device->GetPixelShader(&m_state.pixelShaderHandle);\n\n    return m_stateBlock->Capture();\n  }\n\n  HRESULT D3D8StateBlock::Apply() {\n    if (unlikely(m_stateBlock == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    HRESULT res = m_stateBlock->Apply();\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::Indices))\n      m_device->SetIndices(m_state.indices, m_state.baseVertexIndex);\n\n    // This was a very easy footgun for D3D8 applications.\n    if (m_captures.flags.test(D3D8CapturedStateFlag::SWVP))\n      m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, static_cast<DWORD>(m_state.isSWVP));\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::VertexBuffers)) {\n      for (DWORD stream = 0; stream < m_state.streams.size(); stream++) {\n        if (m_captures.streams.get(stream))\n          m_device->SetStreamSource(stream, m_state.streams[stream].buffer, m_state.streams[stream].stride);\n      }\n    }\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::Textures)) {\n      for (DWORD stage = 0; stage < m_state.textures.size(); stage++) {\n        if (m_captures.textures.get(stage))\n          m_device->SetTexture(stage, m_state.textures[stage]);\n      }\n    }\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::VertexShader))\n      m_device->SetVertexShader(m_state.vertexShaderHandle);\n\n    if (m_captures.flags.test(D3D8CapturedStateFlag::PixelShader))\n      m_device->SetPixelShader(m_state.pixelShaderHandle);\n\n    return res;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_state_block.h",
    "content": "#pragma once\n\n#include \"d3d8_caps.h\"\n#include \"d3d8_include.h\"\n#include \"d3d8_device.h\"\n#include \"d3d8_device_child.h\"\n\n#include \"../util/util_bit.h\"\n#include \"../util/util_flags.h\"\n\n#include <array>\n\nnamespace dxvk {\n\n  enum class D3D8CapturedStateFlag : uint8_t {\n    Indices,\n    SWVP,\n    VertexBuffers,\n    Textures,\n    VertexShader,\n    PixelShader\n  };\n\n  using D3D8CapturedStateFlags = Flags<D3D8CapturedStateFlag>;\n\n  struct D3D8StateCaptures {\n    D3D8CapturedStateFlags flags;\n\n    bit::bitset<d8caps::MAX_STREAMS>        streams;\n    bit::bitset<d8caps::MAX_TEXTURE_STAGES> textures;\n\n    D3D8StateCaptures() {\n      // Ensure all bits are initialized to false\n      streams.clearAll();\n      textures.clearAll();\n    }\n  };\n\n  struct D3D8VBOP {\n    IDirect3DVertexBuffer8* buffer = nullptr;\n    UINT                    stride = 0;\n  };\n\n  struct D3D8CapturableState {\n    std::array<D3D8VBOP, d8caps::MAX_STREAMS>                      streams;\n    std::array<IDirect3DBaseTexture8*, d8caps::MAX_TEXTURE_STAGES> textures;\n\n    IDirect3DIndexBuffer8* indices = nullptr;\n    UINT  baseVertexIndex    = 0;\n    DWORD vertexShaderHandle = 0;\n    DWORD pixelShaderHandle  = 0;\n\n    bool isSWVP = false; // D3DRS_SOFTWAREVERTEXPROCESSING\n  };\n\n  enum class D3D8StateBlockType : uint8_t {\n    None,\n    All,\n    PixelState,\n    VertexState,\n    Unknown\n  };\n\n  inline D3D8StateBlockType ConvertStateBlockType(D3DSTATEBLOCKTYPE type) {\n    switch (type) {\n      case D3DSBT_ALL:         return D3D8StateBlockType::All;\n      case D3DSBT_PIXELSTATE:  return D3D8StateBlockType::PixelState;\n      case D3DSBT_VERTEXSTATE: return D3D8StateBlockType::VertexState;\n      default:                 return D3D8StateBlockType::Unknown;\n    }\n  }\n\n  // Wrapper class for D3D9 state blocks. Captures D3D8-specific state.\n  class D3D8StateBlock  {\n\n  public:\n\n    D3D8StateBlock(\n            D3D8Device*                       pDevice,\n            D3D8StateBlockType                Type,\n            Com<d3d9::IDirect3DStateBlock9>&& pStateBlock);\n\n    D3D8StateBlock(D3D8Device* pDevice);\n\n    void SetD3D9(Com<d3d9::IDirect3DStateBlock9>&& pStateBlock);\n\n    HRESULT Capture();\n\n    HRESULT Apply();\n\n    inline HRESULT SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) {\n      m_state.indices = pIndexData;\n      m_state.baseVertexIndex = BaseVertexIndex;\n      m_captures.flags.set(D3D8CapturedStateFlag::Indices);\n      return D3D_OK;\n    }\n\n    inline HRESULT SetSoftwareVertexProcessing(bool value) {\n      m_state.isSWVP = value;\n      m_captures.flags.set(D3D8CapturedStateFlag::SWVP);\n      return D3D_OK;\n    }\n\n    inline HRESULT SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer8* pStreamData, UINT Stride) {\n      m_state.streams[StreamNumber].buffer = pStreamData;\n      // The previous stride is preserved if pStreamData is NULL\n      if (likely(pStreamData != nullptr))\n        m_state.streams[StreamNumber].stride = Stride;\n      m_captures.flags.set(D3D8CapturedStateFlag::VertexBuffers);\n      m_captures.streams.set(StreamNumber, true);\n      return D3D_OK;\n    }\n\n    inline HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) {\n      m_state.textures[Stage] = pTexture;\n      m_captures.flags.set(D3D8CapturedStateFlag::Textures);\n      m_captures.textures.set(Stage, true);\n      return D3D_OK;\n    }\n\n    inline HRESULT SetVertexShader(DWORD Handle) {\n      m_state.vertexShaderHandle = Handle;\n      m_captures.flags.set(D3D8CapturedStateFlag::VertexShader);\n      return D3D_OK;\n    }\n\n    inline HRESULT SetPixelShader(DWORD Handle) {\n      m_state.pixelShaderHandle = Handle;\n      m_captures.flags.set(D3D8CapturedStateFlag::PixelShader);\n      return D3D_OK;\n    }\n\n  private:\n\n    D3D8Device*                     m_device = nullptr;\n    Com<d3d9::IDirect3DStateBlock9> m_stateBlock;\n\n    D3D8CapturableState m_state;\n    D3D8StateCaptures   m_captures;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_subresource.h",
    "content": "#pragma once\n\n#include \"d3d8_resource.h\"\n\nnamespace dxvk {\n\n  // Base class for Surfaces and Volumes,\n  // which can be attached to Textures.\n\n  template <typename D3D9, typename D3D8>\n  class D3D8Subresource : public D3D8Resource<D3D9, D3D8> {\n\n    using Resource = D3D8Resource<D3D9, D3D8>;\n\n  public:\n\n    D3D8Subresource(\n            D3D8Device*             pDevice,\n      const D3DPOOL                 Pool,\n            Com<D3D9>&&             Object,\n            IDirect3DBaseTexture8*  pBaseTexture)\n    : Resource(pDevice, Pool, std::move(Object)),\n      m_container(pBaseTexture) {\n    }\n\n    // Refing subresources implicitly refs the container texture,\n    ULONG STDMETHODCALLTYPE AddRef() final {\n      if (m_container != nullptr)\n        return m_container->AddRef();\n\n      return Resource::AddRef();\n    }\n\n    // and releasing them implicitly releases the texture.\n    ULONG STDMETHODCALLTYPE Release() final {\n      if (m_container != nullptr)\n        return m_container->Release();\n\n      return Resource::Release();\n    }\n\n    // Clients can grab the container if they want\n    HRESULT STDMETHODCALLTYPE GetContainer(REFIID riid, void** ppContainer) final {\n      if (m_container != nullptr)\n        return m_container->QueryInterface(riid, ppContainer);\n\n      return this->GetDevice()->QueryInterface(riid, ppContainer);\n    }\n\n    inline IDirect3DBaseTexture8* GetBaseTexture() {\n      return m_container;\n    }\n\n  protected:\n\n    IDirect3DBaseTexture8* m_container = nullptr;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_surface.cpp",
    "content": "#include \"d3d8_surface.h\"\n#include \"d3d8_device.h\"\n\n#include \"d3d8_util.h\"\n\nnamespace dxvk {\n\n  D3D8Surface::D3D8Surface(\n          D3D8Device*                     pDevice,\n    const D3DPOOL                         Pool,\n          IDirect3DBaseTexture8*          pTexture,\n          Com<d3d9::IDirect3DSurface9>&&  pSurface)\n    : D3D8SurfaceBase (pDevice, Pool, std::move(pSurface), pTexture) {\n  }\n\n  // A surface does not need to be attached to a texture\n  D3D8Surface::D3D8Surface(\n          D3D8Device*                     pDevice,\n    const D3DPOOL                         Pool,\n          Com<d3d9::IDirect3DSurface9>&&  pSurface)\n    : D3D8Surface (pDevice, Pool, nullptr, std::move(pSurface)) {\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Surface::GetDesc(D3DSURFACE_DESC* pDesc) {\n    if (unlikely(pDesc == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    d3d9::D3DSURFACE_DESC desc;\n    HRESULT res = GetD3D9()->GetDesc(&desc);\n\n    if (likely(SUCCEEDED(res)))\n      ConvertSurfaceDesc8(&desc, pDesc);\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Surface::LockRect(\n      D3DLOCKED_RECT* pLockedRect,\n      CONST RECT*     pRect,\n      DWORD           Flags) {\n    return GetD3D9()->LockRect(reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect), pRect, Flags);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Surface::UnlockRect() {\n    return GetD3D9()->UnlockRect();\n  }\n\n  // TODO: Consider creating only one texture to\n  // encompass all surface levels of a texture.\n  Com<d3d9::IDirect3DSurface9> D3D8Surface::GetBlitImage() {\n    if (unlikely(m_blitImage == nullptr)) {\n      m_blitImage = CreateBlitImage();\n    }\n\n    return m_blitImage;\n  }\n\n  Com<d3d9::IDirect3DSurface9> D3D8Surface::CreateBlitImage() {\n    d3d9::D3DSURFACE_DESC desc;\n    GetD3D9()->GetDesc(&desc);\n\n    // NOTE: This adds a D3DPOOL_DEFAULT resource to the\n    // device, which counts as losable during device reset\n    Com<d3d9::IDirect3DSurface9> image;\n    HRESULT res = GetParent()->GetD3D9()->CreateRenderTarget(\n      desc.Width, desc.Height, desc.Format,\n      d3d9::D3DMULTISAMPLE_NONE, 0,\n      FALSE,\n      &image,\n      NULL);\n\n    if (FAILED(res))\n      throw DxvkError(\"D3D8: Failed to create blit image\");\n\n    return image;\n  }\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_surface.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n#include \"d3d8_subresource.h\"\n\nnamespace dxvk {\n\n  // Note: IDirect3DSurface8 does not actually inherit from IDirect3DResource8,\n  // however it does expose serveral of the methods typically found part of\n  // IDirect3DResource8, such as Set/Get/FreePrivateData, so model it as such.\n  using D3D8SurfaceBase = D3D8Subresource<d3d9::IDirect3DSurface9, IDirect3DSurface8>;\n  class D3D8Surface final : public D3D8SurfaceBase {\n\n  public:\n\n    D3D8Surface(\n            D3D8Device*                     pDevice,\n      const D3DPOOL                         Pool,\n            IDirect3DBaseTexture8*          pTexture,\n            Com<d3d9::IDirect3DSurface9>&&  pSurface);\n\n    D3D8Surface(\n            D3D8Device*                     pDevice,\n      const D3DPOOL                         Pool,\n            Com<d3d9::IDirect3DSurface9>&&  pSurface);\n\n    HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC* pDesc) final;\n\n    HRESULT STDMETHODCALLTYPE LockRect(\n        D3DLOCKED_RECT* pLockedRect,\n        CONST RECT*     pRect,\n        DWORD           Flags) final;\n\n    HRESULT STDMETHODCALLTYPE UnlockRect() final;\n\n    /**\n     * \\brief Allocate or reuse an image of the same size\n     * as this texture for performing blit into system mem.\n     */\n    Com<d3d9::IDirect3DSurface9> GetBlitImage();\n\n  private:\n\n    Com<d3d9::IDirect3DSurface9> CreateBlitImage();\n\n    Com<d3d9::IDirect3DSurface9> m_blitImage;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_swapchain.cpp",
    "content": "#include \"d3d8_swapchain.h\"\n\nnamespace dxvk {\n\n  D3D8SwapChain::D3D8SwapChain(\n          D3D8Device*                      pDevice,\n          D3DPRESENT_PARAMETERS*           pPresentationParameters,\n          Com<d3d9::IDirect3DSwapChain9>&& pSwapChain)\n    : D3D8SwapChainBase(pDevice, std::move(pSwapChain)) {\n    m_backBuffers.resize(pPresentationParameters->BackBufferCount);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8SwapChain::Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) {\n    return GetD3D9()->Present(src, dst, hWnd, dirtyRegion, 0);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8SwapChain::GetBackBuffer(\n      UINT                BackBuffer,\n      D3DBACKBUFFER_TYPE  Type,\n      IDirect3DSurface8** ppBackBuffer) {\n    if (unlikely(ppBackBuffer == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // Same logic as in D3D8Device::GetBackBuffer\n    if (BackBuffer >= m_backBuffers.size() || m_backBuffers[BackBuffer] == nullptr) {\n      Com<d3d9::IDirect3DSurface9> pSurface9;\n      HRESULT res = GetD3D9()->GetBackBuffer(BackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9);\n\n      if (likely(SUCCEEDED(res))) {\n        m_backBuffers[BackBuffer] = new D3D8Surface(GetParent(), D3DPOOL_DEFAULT, std::move(pSurface9));\n        *ppBackBuffer = m_backBuffers[BackBuffer].ref();\n      }\n\n      return res;\n    }\n\n    *ppBackBuffer = m_backBuffers[BackBuffer].ref();\n    return D3D_OK;\n  }\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_swapchain.h",
    "content": "#pragma once\n\n#include \"d3d8_device_child.h\"\n#include \"d3d8_surface.h\"\n\nnamespace dxvk {\n\n  using D3D8SwapChainBase = D3D8DeviceChild<d3d9::IDirect3DSwapChain9, IDirect3DSwapChain8>;\n  class D3D8SwapChain final : public D3D8SwapChainBase {\n\n  public:\n\n    D3D8SwapChain(\n            D3D8Device*                      pDevice,\n            D3DPRESENT_PARAMETERS*           pPresentationParameters,\n            Com<d3d9::IDirect3DSwapChain9>&& pSwapChain);\n\n    HRESULT STDMETHODCALLTYPE Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) final;\n\n    HRESULT STDMETHODCALLTYPE GetBackBuffer(\n        UINT                BackBuffer,\n        D3DBACKBUFFER_TYPE  Type,\n        IDirect3DSurface8** ppBackBuffer) final;\n\n  private:\n\n    std::vector<Com<D3D8Surface, false>> m_backBuffers;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_texture.cpp",
    "content": "#include \"d3d8_texture.h\"\n\n#include \"d3d8_util.h\"\n\nnamespace dxvk {\n\n  // D3D8Texture2D\n\n  D3D8Texture2D::D3D8Texture2D(\n          D3D8Device*                    pDevice,\n    const D3DPOOL                        Pool,\n          Com<d3d9::IDirect3DTexture9>&& pTexture)\n    : D3D8Texture2DBase(pDevice, Pool, std::move(pTexture), pTexture->GetLevelCount()) {\n  }\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D8Texture2D::GetType() { return D3DRTYPE_TEXTURE; }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture2D::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {\n    if (unlikely(pDesc == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    d3d9::D3DSURFACE_DESC surf;\n    HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf);\n\n    if (likely(SUCCEEDED(res)))\n      ConvertSurfaceDesc8(&surf, pDesc);\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture2D::GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel) {\n    return GetSubresource(Level, ppSurfaceLevel);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture2D::LockRect(\n      UINT            Level,\n      D3DLOCKED_RECT* pLockedRect,\n      CONST RECT*     pRect,\n      DWORD           Flags) {\n    return GetD3D9()->LockRect(Level, reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect), pRect, Flags);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture2D::UnlockRect(UINT Level) {\n    return GetD3D9()->UnlockRect(Level);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture2D::AddDirtyRect(CONST RECT* pDirtyRect) {\n    return GetD3D9()->AddDirtyRect(pDirtyRect);\n  }\n\n  // D3D8Texture3D\n\n  D3D8Texture3D::D3D8Texture3D(\n          D3D8Device*                           pDevice,\n    const D3DPOOL                               Pool,\n          Com<d3d9::IDirect3DVolumeTexture9>&&  pVolumeTexture)\n    : D3D8Texture3DBase(pDevice, Pool, std::move(pVolumeTexture), pVolumeTexture->GetLevelCount()) {}\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D8Texture3D::GetType() { return D3DRTYPE_VOLUMETEXTURE; }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture3D::GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) {\n    if (unlikely(pDesc == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    d3d9::D3DVOLUME_DESC vol;\n    HRESULT res = GetD3D9()->GetLevelDesc(Level, &vol);\n\n    if (likely(SUCCEEDED(res)))\n      ConvertVolumeDesc8(&vol, pDesc);\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture3D::GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel) {\n    return GetSubresource(Level, ppVolumeLevel);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture3D::LockBox(\n      UINT           Level,\n      D3DLOCKED_BOX* pLockedBox,\n      CONST D3DBOX*  pBox,\n      DWORD          Flags) {\n    return GetD3D9()->LockBox(\n      Level,\n      reinterpret_cast<d3d9::D3DLOCKED_BOX*>(pLockedBox),\n      reinterpret_cast<const d3d9::D3DBOX*>(pBox),\n      Flags\n    );\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture3D::UnlockBox(UINT Level) {\n    return GetD3D9()->UnlockBox(Level);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Texture3D::AddDirtyBox(CONST D3DBOX* pDirtyBox) {\n    return GetD3D9()->AddDirtyBox(reinterpret_cast<const d3d9::D3DBOX*>(pDirtyBox));\n  }\n\n  // D3D8TextureCube\n\n  D3D8TextureCube::D3D8TextureCube(\n          D3D8Device*                         pDevice,\n    const D3DPOOL                             Pool,\n          Com<d3d9::IDirect3DCubeTexture9>&&  pTexture)\n    : D3D8TextureCubeBase(pDevice, Pool, std::move(pTexture), pTexture->GetLevelCount() * CUBE_FACES) {\n  }\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D8TextureCube::GetType() { return D3DRTYPE_CUBETEXTURE; }\n\n  HRESULT STDMETHODCALLTYPE D3D8TextureCube::GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) {\n    if (unlikely(pDesc == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    d3d9::D3DSURFACE_DESC surf;\n    HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf);\n\n    if (likely(SUCCEEDED(res)))\n      ConvertSurfaceDesc8(&surf, pDesc);\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8TextureCube::GetCubeMapSurface(\n      D3DCUBEMAP_FACES    Face,\n      UINT                Level,\n      IDirect3DSurface8** ppSurfaceLevel) {\n    return GetSubresource((Level * CUBE_FACES) + Face, ppSurfaceLevel);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8TextureCube::LockRect(\n          D3DCUBEMAP_FACES Face,\n          UINT Level,\n          D3DLOCKED_RECT* pLockedRect,\n          const RECT* pRect,\n          DWORD Flags) {\n    return GetD3D9()->LockRect(\n      d3d9::D3DCUBEMAP_FACES(Face),\n      Level,\n      reinterpret_cast<d3d9::D3DLOCKED_RECT*>(pLockedRect),\n      pRect,\n      Flags);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8TextureCube::UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) {\n    return GetD3D9()->UnlockRect(d3d9::D3DCUBEMAP_FACES(Face), Level);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8TextureCube::AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect) {\n    return GetD3D9()->AddDirtyRect(d3d9::D3DCUBEMAP_FACES(Face), pDirtyRect);\n  }\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_texture.h",
    "content": "#pragma once\n\n#include \"d3d8_resource.h\"\n#include \"d3d8_surface.h\"\n#include \"d3d8_volume.h\"\n\n#include <vector>\n#include <new>\n\nnamespace dxvk {\n\n  template <typename SubresourceType, typename D3D9, typename D3D8>\n  class D3D8BaseTexture : public D3D8Resource<D3D9, D3D8> {\n\n  public:\n\n    constexpr static UINT CUBE_FACES = 6;\n\n    using SubresourceType8 = typename SubresourceType::D3D8;\n    using SubresourceType9 = typename SubresourceType::D3D9;\n\n    D3D8BaseTexture(\n            D3D8Device*                         pDevice,\n      const D3DPOOL                             Pool,\n            Com<D3D9>&&                         pBaseTexture,\n            UINT                                SubresourceCount)\n        : D3D8Resource<D3D9, D3D8> ( pDevice, Pool, std::move(pBaseTexture) ) {\n      m_subresources.resize(SubresourceCount, nullptr);\n    }\n\n    ~D3D8BaseTexture() {\n      for (size_t i = 0; i < m_subresources.size(); i++)\n        m_subresources[i] = nullptr;\n    }\n\n    virtual IUnknown* GetInterface(REFIID riid) final override try {\n      return D3D8Resource<D3D9, D3D8>::GetInterface(riid);\n    } catch (const DxvkError& e) {\n      if (riid == __uuidof(IDirect3DBaseTexture8))\n        return this;\n\n      throw e;\n    }\n\n    void STDMETHODCALLTYPE PreLoad() final {\n      this->GetD3D9()->PreLoad();\n    }\n\n    DWORD STDMETHODCALLTYPE SetLOD(DWORD LODNew) final {\n      return this->GetD3D9()->SetLOD(LODNew);\n    }\n\n    DWORD STDMETHODCALLTYPE GetLOD() final {\n      return this->GetD3D9()->GetLOD();\n    }\n\n    DWORD STDMETHODCALLTYPE GetLevelCount() final {\n      return this->GetD3D9()->GetLevelCount();\n    }\n\n  protected:\n\n    HRESULT STDMETHODCALLTYPE GetSubresource(UINT Index, SubresourceType8** ppSubresource) {\n      InitReturnPtr(ppSubresource);\n\n      if (unlikely(ppSubresource == nullptr))\n        return D3DERR_INVALIDCALL;\n\n      if (unlikely(Index >= m_subresources.size()))\n        return D3DERR_INVALIDCALL;\n\n      if (m_subresources[Index] == nullptr) {\n        try {\n          Com<SubresourceType9> subresource = LookupSubresource(Index);\n\n          // Cache the subresource\n          m_subresources[Index] = new SubresourceType(this->m_parent, this->m_pool, this, std::move(subresource));\n        } catch (const DxvkError& e) {\n          Logger::warn(e.message());\n          return D3DERR_INVALIDCALL;\n        }\n      }\n\n      *ppSubresource = m_subresources[Index].ref();\n      return D3D_OK;\n    }\n\n  private:\n\n    Com<SubresourceType9> LookupSubresource(UINT Index) {\n      Com<SubresourceType9> ptr = nullptr;\n      HRESULT res = D3DERR_INVALIDCALL;\n      if constexpr (std::is_same_v<D3D8, IDirect3DTexture8>) {\n        res = this->GetD3D9()->GetSurfaceLevel(Index, &ptr);\n      } else if constexpr (std::is_same_v<D3D8, IDirect3DVolumeTexture8>) {\n        res = this->GetD3D9()->GetVolumeLevel(Index, &ptr);\n      } else if constexpr (std::is_same_v<D3D8, IDirect3DCubeTexture8>) {\n        res = this->GetD3D9()->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACES(Index % CUBE_FACES), Index / CUBE_FACES, &ptr);\n      }\n\n      if (FAILED(res))\n        throw DxvkError(str::format(\"D3D8BaseTexture::GetSubresource: Failed to retrieve index \", Index));\n\n      return ptr;\n    }\n\n    std::vector<Com<SubresourceType, false>> m_subresources;\n\n  };\n\n  using D3D8Texture2DBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DTexture9, IDirect3DTexture8>;\n  class D3D8Texture2D final : public D3D8Texture2DBase {\n\n  public:\n\n    D3D8Texture2D(\n            D3D8Device*                    pDevice,\n      const D3DPOOL                        Pool,\n            Com<d3d9::IDirect3DTexture9>&& pTexture);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;\n\n    HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc);\n\n    HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel);\n\n    HRESULT STDMETHODCALLTYPE LockRect(\n        UINT            Level,\n        D3DLOCKED_RECT* pLockedRect,\n        CONST RECT*     pRect,\n        DWORD           Flags);\n\n    HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level);\n\n    HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect);\n\n  };\n\n  using D3D8Texture3DBase = D3D8BaseTexture<D3D8Volume, d3d9::IDirect3DVolumeTexture9, IDirect3DVolumeTexture8>;\n  class D3D8Texture3D final : public D3D8Texture3DBase {\n\n  public:\n\n    D3D8Texture3D(\n            D3D8Device*                           pDevice,\n      const D3DPOOL                               Pool,\n            Com<d3d9::IDirect3DVolumeTexture9>&&  pVolumeTexture);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;\n\n    HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc);\n\n    HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel);\n\n    HRESULT STDMETHODCALLTYPE LockBox(\n        UINT           Level,\n        D3DLOCKED_BOX* pLockedBox,\n        CONST D3DBOX*  pBox,\n        DWORD          Flags);\n\n    HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level);\n\n    HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox);\n\n  };\n\n  using D3D8TextureCubeBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DCubeTexture9, IDirect3DCubeTexture8>;\n  class D3D8TextureCube final : public D3D8TextureCubeBase {\n\n  public:\n\n    D3D8TextureCube(\n            D3D8Device*                         pDevice,\n      const D3DPOOL                             Pool,\n            Com<d3d9::IDirect3DCubeTexture9>&&  pTexture);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;\n\n    HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc);\n\n    HRESULT STDMETHODCALLTYPE GetCubeMapSurface(\n        D3DCUBEMAP_FACES    Face,\n        UINT                Level,\n        IDirect3DSurface8** ppSurfaceLevel);\n\n    HRESULT STDMETHODCALLTYPE LockRect(\n        D3DCUBEMAP_FACES Face,\n        UINT Level,\n        D3DLOCKED_RECT* pLockedRect,\n        const RECT* pRect,\n        DWORD Flags);\n\n    HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level);\n\n    HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect);\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_util.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n#include \"d3d8_format.h\"\n#include \"d3d8_options.h\"\n\n#include <utility>\n\nnamespace dxvk {\n\n  // (8<-9) D3DCAPSX: Writes to D3DCAPS8 from D3DCAPS9\n  inline void ConvertCaps8(const d3d9::D3DCAPS9& caps9, D3DCAPS8* pCaps8) {\n\n    // should be aligned\n    std::memcpy(pCaps8, &caps9, sizeof(D3DCAPS8));\n\n    // Max supported shader model is PS 1.4 and VS 1.1\n    pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 1);\n    // Late fixed-function capable hardware will advertise VS 1.1\n    // support, but will not advertise any support for PS\n    if (likely(caps9.PixelShaderVersion != D3DPS_VERSION(0, 0)))\n      pCaps8->PixelShaderVersion  = D3DPS_VERSION(1, 4);\n\n    // Remove D3D9-specific caps:\n\n    pCaps8->Caps2                 &= ~D3DCAPS2_CANAUTOGENMIPMAP;\n\n    pCaps8->Caps3                 &= ~D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION\n                                   & ~D3DCAPS3_COPY_TO_VIDMEM\n                                   & ~D3DCAPS3_COPY_TO_SYSTEMMEM;\n\n    pCaps8->PrimitiveMiscCaps     &= ~D3DPMISCCAPS_INDEPENDENTWRITEMASKS\n                                   & ~D3DPMISCCAPS_PERSTAGECONSTANT\n                                   & ~D3DPMISCCAPS_FOGANDSPECULARALPHA\n                                   & ~D3DPMISCCAPS_SEPARATEALPHABLEND\n                                   & ~D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS\n                                   & ~D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING\n                                   & ~D3DPMISCCAPS_FOGVERTEXCLAMPED\n                                   & ~D3DPMISCCAPS_POSTBLENDSRGBCONVERT;\n\n    pCaps8->RasterCaps            &= ~D3DPRASTERCAPS_SCISSORTEST\n                                   & ~D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS\n                                   & ~D3DPRASTERCAPS_DEPTHBIAS\n                                   & ~D3DPRASTERCAPS_MULTISAMPLE_TOGGLE;\n\n    pCaps8->SrcBlendCaps          &= ~D3DPBLENDCAPS_BLENDFACTOR;\n\n    pCaps8->DestBlendCaps         &= ~D3DPBLENDCAPS_BLENDFACTOR;\n\n    pCaps8->LineCaps              &= ~D3DLINECAPS_ANTIALIAS;\n\n    pCaps8->StencilCaps           &= ~D3DSTENCILCAPS_TWOSIDED;\n\n    pCaps8->VertexProcessingCaps  &= ~D3DVTXPCAPS_TEXGEN_SPHEREMAP;\n\n    // Add D3D8-specific caps:\n\n    // Removed in D3D9, since it can always render windowed\n    pCaps8->Caps2                     |= D3DCAPS2_CANRENDERWINDOWED\n    // A remnant from a bygone age of ddraw interop most likely\n    /*                                 | D3DCAPS2_NO2DDURING3DSCENE*/;\n\n    // Used in conjunction with D3DPRASTERCAPS_PAT, but generally unadvertised\n    /*pCaps8->PrimitiveMiscCaps       |= D3DPMISCCAPS_LINEPATTERNREP;*/\n\n    // Replaced by D3DPRASTERCAPS_DEPTHBIAS in D3D9\n    pCaps8->RasterCaps                |= D3DPRASTERCAPS_ZBIAS\n    // Advertised on Nvidia cards by modern drivers, but not on AMD or Intel\n    /*                                 | D3DPRASTERCAPS_ANTIALIASEDGES*/\n    // Advertised on Nvidia cards, but not on AMD or Intel\n    /*                                 | D3DPRASTERCAPS_STRETCHBLTMULTISAMPLE*/\n    // TODO: Implement D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT\n    /*                                 | D3DPRASTERCAPS_PAT*/;\n\n    // MAG only filter caps, generally unsupported\n    /*pCaps8->TextureFilterCaps       |= D3DPTFILTERCAPS_MAGFAFLATCUBIC*/\n    /*                                 | D3DPTFILTERCAPS_MAGFGAUSSIANCUBIC;*/\n    /*pCaps8->CubeTextureFilterCaps    = pCaps8->TextureFilterCaps;*/\n    /*pCaps8->VolumeTextureFilterCaps  = pCaps8->TextureFilterCaps;*/\n\n    // Not advertised on any modern hardware\n    /*pCaps8->VertexProcessingCaps    |= D3DVTXPCAPS_NO_VSDT_UBYTE4;*/\n  }\n\n  // (9<-8) D3DD3DPRESENT_PARAMETERS: Returns D3D9's params given an input for D3D8\n  inline d3d9::D3DPRESENT_PARAMETERS ConvertPresentParameters9(D3DPRESENT_PARAMETERS* pParams) {\n    // A 0 back buffer count needs to be corrected and made visible to the D3D8 application as well\n    pParams->BackBufferCount = std::max(pParams->BackBufferCount, 1u);\n\n    if (pParams->BackBufferFormat == D3DFMT_UNKNOWN)\n      pParams->BackBufferFormat = D3DFMT_X8R8G8B8;\n\n    d3d9::D3DPRESENT_PARAMETERS params;\n    params.BackBufferWidth = pParams->BackBufferWidth;\n    params.BackBufferHeight = pParams->BackBufferHeight;\n    params.BackBufferFormat = d3d9::D3DFORMAT(pParams->BackBufferFormat);\n    params.BackBufferCount = pParams->BackBufferCount;\n\n    params.MultiSampleType = d3d9::D3DMULTISAMPLE_TYPE(pParams->MultiSampleType);\n    // MultiSampleQuality is only used with D3DMULTISAMPLE_NONMASKABLE, which is not available in D3D8\n    params.MultiSampleQuality = 0;\n\n    // If an application passes multiple D3DPRESENT_INTERVAL flags, this will be\n    // validated appropriately by D3D9. Simply copy the values here.\n    UINT PresentationInterval = pParams->FullScreen_PresentationInterval;\n\n    if (pParams->Windowed) {\n      // D3D8: For windowed swap chain, the back buffer is copied to the window immediately.\n      PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;\n    }\n\n    D3DSWAPEFFECT SwapEffect = pParams->SwapEffect;\n\n    if (SwapEffect == D3DSWAPEFFECT_COPY_VSYNC) {\n      // D3DSWAPEFFECT_COPY_VSYNC has been removed from D3D9, use D3DSWAPEFFECT_COPY\n      SwapEffect = D3DSWAPEFFECT_COPY;\n\n      // D3D8: In windowed mode, D3DSWAPEFFECT_COPY_VSYNC enables VSYNC.\n      // In fullscreen, D3DPRESENT_INTERVAL_IMMEDIATE is meaningless.\n      if (pParams->Windowed || PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE) {\n        PresentationInterval = D3DPRESENT_INTERVAL_ONE;\n      }\n    }\n\n    params.SwapEffect = d3d9::D3DSWAPEFFECT(SwapEffect);\n    params.hDeviceWindow = pParams->hDeviceWindow;\n    params.Windowed = pParams->Windowed;\n    params.EnableAutoDepthStencil = pParams->EnableAutoDepthStencil;\n    params.AutoDepthStencilFormat = d3d9::D3DFORMAT(pParams->AutoDepthStencilFormat);\n    params.Flags = pParams->Flags;\n\n    // D3DPRESENT_RATE_UNLIMITED is unsupported, use D3DPRESENT_RATE_DEFAULT (or 0)\n    if (unlikely(pParams->FullScreen_RefreshRateInHz == D3DPRESENT_RATE_UNLIMITED)) {\n      params.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;\n    } else {\n      params.FullScreen_RefreshRateInHz = pParams->FullScreen_RefreshRateInHz;\n    }\n\n    // FullScreen_PresentationInterval -> PresentationInterval\n    params.PresentationInterval = PresentationInterval;\n\n    return params;\n  }\n\n  // (8<-9) Convert D3DSURFACE_DESC\n  inline void ConvertSurfaceDesc8(const d3d9::D3DSURFACE_DESC* pSurf9, D3DSURFACE_DESC* pSurf8) {\n    pSurf8->Format  = D3DFORMAT(pSurf9->Format);\n    pSurf8->Type    = D3DRESOURCETYPE(pSurf9->Type);\n    pSurf8->Usage   = pSurf9->Usage;\n    pSurf8->Pool    = D3DPOOL(pSurf9->Pool);\n    pSurf8->Size    = getSurfaceSize(pSurf8->Format, pSurf9->Width, pSurf9->Height);\n\n    pSurf8->MultiSampleType = D3DMULTISAMPLE_TYPE(pSurf9->MultiSampleType);\n    // DX8: No multisample quality\n    pSurf8->Width   = pSurf9->Width;\n    pSurf8->Height  = pSurf9->Height;\n  }\n\n  // (8<-9) Convert D3DVOLUME_DESC\n  inline void ConvertVolumeDesc8(const d3d9::D3DVOLUME_DESC* pVol9, D3DVOLUME_DESC* pVol8) {\n    pVol8->Format = D3DFORMAT(pVol9->Format);\n    pVol8->Type   = D3DRESOURCETYPE(pVol9->Type);\n    pVol8->Usage  = pVol9->Usage;\n    pVol8->Pool   = D3DPOOL(pVol9->Pool);\n    pVol8->Size   = getSurfaceSize(pVol8->Format, pVol9->Width, pVol9->Height) * pVol9->Depth;\n    pVol8->Width  = pVol9->Width;\n    pVol8->Height = pVol9->Height;\n    pVol8->Depth  = pVol9->Depth;\n  }\n\n  // If this D3DTEXTURESTAGESTATETYPE has been remapped to a d3d9::D3DSAMPLERSTATETYPE\n  // it will be returned, otherwise returns -1u\n  inline d3d9::D3DSAMPLERSTATETYPE GetSamplerStateType9(const D3DTEXTURESTAGESTATETYPE StageType) {\n    switch (StageType) {\n      // 13-21:\n      case D3DTSS_ADDRESSU:       return d3d9::D3DSAMP_ADDRESSU;\n      case D3DTSS_ADDRESSV:       return d3d9::D3DSAMP_ADDRESSV;\n      case D3DTSS_BORDERCOLOR:    return d3d9::D3DSAMP_BORDERCOLOR;\n      case D3DTSS_MAGFILTER:      return d3d9::D3DSAMP_MAGFILTER;\n      case D3DTSS_MINFILTER:      return d3d9::D3DSAMP_MINFILTER;\n      case D3DTSS_MIPFILTER:      return d3d9::D3DSAMP_MIPFILTER;\n      case D3DTSS_MIPMAPLODBIAS:  return d3d9::D3DSAMP_MIPMAPLODBIAS;\n      case D3DTSS_MAXMIPLEVEL:    return d3d9::D3DSAMP_MAXMIPLEVEL;\n      case D3DTSS_MAXANISOTROPY:  return d3d9::D3DSAMP_MAXANISOTROPY;\n      // 25:\n      case D3DTSS_ADDRESSW:       return d3d9::D3DSAMP_ADDRESSW;\n      default:                    return d3d9::D3DSAMPLERSTATETYPE(-1u);\n    }\n  }\n\n  inline DWORD isFVF(DWORD Handle) {\n    return (Handle & D3DFVF_RESERVED0) == 0;\n  }\n\n  inline DWORD getShaderHandle(DWORD Index) {\n    return (Index << 1) | D3DFVF_RESERVED0;\n  }\n\n  inline DWORD getShaderIndex(DWORD Handle) {\n    if ((Handle & D3DFVF_RESERVED0) != 0) {\n      return ((Handle & ~(D3DFVF_RESERVED0)) >> 1) - 1;\n    } else {\n      return Handle;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d8/d3d8_volume.cpp",
    "content": "#include \"d3d8_volume.h\"\n\n#include \"d3d8_util.h\"\n\nnamespace dxvk {\n\n  D3D8Volume::D3D8Volume(\n          D3D8Device*                   pDevice,\n    const D3DPOOL                       Pool,\n          IDirect3DVolumeTexture8*      pTexture,\n          Com<d3d9::IDirect3DVolume9>&& pVolume)\n    : D3D8VolumeBase(pDevice, Pool, std::move(pVolume), pTexture) {\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Volume::GetDesc(D3DVOLUME_DESC* pDesc) {\n    if (unlikely(pDesc == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    d3d9::D3DVOLUME_DESC desc;\n    HRESULT res = GetD3D9()->GetDesc(&desc);\n\n    if (likely(SUCCEEDED(res)))\n      ConvertVolumeDesc8(&desc, pDesc);\n\n    return res;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Volume::LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) {\n    return GetD3D9()->LockBox(\n      reinterpret_cast<d3d9::D3DLOCKED_BOX*>(pLockedBox),\n      reinterpret_cast<const d3d9::D3DBOX*>(pBox),\n      Flags\n    );\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D8Volume::UnlockBox() {\n    return GetD3D9()->UnlockBox();\n  }\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_volume.h",
    "content": "#pragma once\n\n#include \"d3d8_subresource.h\"\n\nnamespace dxvk {\n\n  using D3D8VolumeBase = D3D8Subresource<d3d9::IDirect3DVolume9, IDirect3DVolume8>;\n  class D3D8Volume final : public D3D8VolumeBase {\n\n  public:\n\n    D3D8Volume(\n            D3D8Device*                   pDevice,\n      const D3DPOOL                       Pool,\n            IDirect3DVolumeTexture8*      pTexture,\n            Com<d3d9::IDirect3DVolume9>&& pVolume);\n\n    HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC* pDesc);\n\n    HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final;\n\n    HRESULT STDMETHODCALLTYPE UnlockBox() final;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/d3d8_wrapped_object.h",
    "content": "#pragma once\n\n#include \"d3d8_include.h\"\n\nnamespace dxvk {\n\n  template <typename D3D9Type, typename D3D8Type>\n  class D3D8WrappedObject : public ComObjectClamp<D3D8Type> {\n\n  public:\n\n    using D3D9 = D3D9Type;\n    using D3D8 = D3D8Type;\n\n    D3D8WrappedObject(Com<D3D9>&& object)\n      : m_d3d9(std::move(object)) {\n    }\n\n    D3D9* GetD3D9() const {\n      return m_d3d9.ptr();\n    }\n\n    // For cases where the object may be null.\n    static D3D9* GetD3D9Nullable(D3D8WrappedObject* self) {\n      if (unlikely(self == NULL)) {\n        return NULL;\n      }\n      return self->m_d3d9.ptr();\n    }\n\n    template <typename T>\n    static D3D9* GetD3D9Nullable(Com<T>& self) {\n      return GetD3D9Nullable(self.ptr());\n    }\n\n    virtual IUnknown* GetInterface(REFIID riid) {\n      if (riid == __uuidof(IUnknown))\n        return this;\n      if (riid == __uuidof(D3D8))\n        return this;\n\n      throw DxvkError(\"D3D8WrappedObject::QueryInterface: Unknown interface query\");\n    }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final {\n      if (ppvObject == nullptr)\n        return E_POINTER;\n\n      *ppvObject = nullptr;\n\n      try {\n        *ppvObject = ref(this->GetInterface(riid));\n        return S_OK;\n      } catch (const DxvkError& e) {\n        Logger::warn(e.message());\n        Logger::warn(str::format(riid));\n        return E_NOINTERFACE;\n      }\n    }\n\n  private:\n\n    Com<D3D9> m_d3d9;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d8/meson.build",
    "content": "d3d8_res = wrc_generator.process('version.rc')\n\nd3d8_src = [\n  'd3d8_buffer.cpp',\n  'd3d8_device.cpp',\n  'd3d8_interface.cpp',\n  'd3d8_main.cpp',\n  'd3d8_multithread.cpp',\n  'd3d8_options.cpp',\n  'd3d8_shader.cpp',\n  'd3d8_state_block.cpp',\n  'd3d8_surface.cpp',\n  'd3d8_swapchain.cpp',\n  'd3d8_texture.cpp',\n  'd3d8_volume.cpp'\n]\n\nd3d8_ld_args      = []\nd3d8_link_depends = []\n\nif platform != 'windows'\n  lib_d3d9 = d3d9_dep\n  d3d8_ld_args      += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'd3d8.sym') ]\n  d3d8_link_depends += files('d3d8.sym')\nendif\n\nd3d8_dll = shared_library(dxvk_name_prefix+'d3d8', d3d8_src, d3d8_res,\n  dependencies        : [ lib_d3d9, util_dep, dxso_dep, dxvk_dep ],\n  include_directories : dxvk_include_path,\n  install             : true,\n  vs_module_defs      : 'd3d8'+def_spec_ext,\n  link_args           : d3d8_ld_args,\n  link_depends        : [ d3d8_link_depends ],\n  kwargs              : dxvk_so_version,\n)\n\nd3d8_dep = declare_dependency(\n  link_with           : [ d3d8_dll ],\n  include_directories : [ dxvk_include_path ],\n)\n\nif platform != 'windows'\n  pkg.generate(d3d8_dll,\n    filebase: dxvk_pkg_prefix + 'd3d8',\n    subdirs:  'dxvk',\n  )\nendif\n"
  },
  {
    "path": "src/d3d8/version.rc",
    "content": "#include <windows.h>\n\n// DLL version information.\nVS_VERSION_INFO    VERSIONINFO\nFILEVERSION        10,0,17763,1\nPRODUCTVERSION     10,0,17763,1\nFILEFLAGSMASK      VS_FFI_FILEFLAGSMASK\nFILEFLAGS          0\nFILEOS VOS_NT_WINDOWS32\nFILETYPE VFT_DLL\nFILESUBTYPE VFT2_UNKNOWN\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"080904b0\"\n    BEGIN\n      VALUE \"CompanyName\",      \"DXVK\"\n      VALUE \"FileDescription\",  \"Direct3D 8 Runtime\"\n      VALUE \"FileVersion\",      \"10.0.17763.1 (WinBuild.160101.0800)\"\n      VALUE \"InternalName\",     \"D3D8.dll\"\n      VALUE \"LegalCopyright\",   \"zlib/libpng license\"\n      VALUE \"OriginalFilename\", \"D3D8.dll\"\n      VALUE \"ProductName\",      \"DXVK\"\n      VALUE \"ProductVersion\",   \"10.0.17763.1\"\n    END\n  END\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0809, 1200\n  END\nEND\n"
  },
  {
    "path": "src/d3d9/d3d9.def",
    "content": "LIBRARY D3D9.DLL\nEXPORTS\n  Direct3DShaderValidatorCreate9 @24\n\n  PSGPError @25\n  PSGPSampleTexture @26\n\n  D3DPERF_BeginEvent @27\n  D3DPERF_EndEvent @28\n  D3DPERF_GetStatus @29\n  D3DPERF_QueryRepeatFrame @30\n  D3DPERF_SetMarker @31\n  D3DPERF_SetOptions @32\n  D3DPERF_SetRegion @33\n\n  DebugSetLevel @34\n  DebugSetMute @35\n\n  Direct3D9EnableMaximizedWindowedModeShim @36\n\n  Direct3DCreate9 @37\n  Direct3DCreate9Ex @38\n\n  DXVK_RegisterAnnotation @28257 NONAME\n  DXVK_UnRegisterAnnotation @28258 NONAME\n\n  Direct3D9ForceHybridEnumeration @16 NONAME PRIVATE\n  \n  Direct3DCreate9On12 @20\n  Direct3DCreate9On12Ex @21\n"
  },
  {
    "path": "src/d3d9/d3d9.sym",
    "content": "{\n  global:\n    Direct3DCreate9;\n    Direct3DCreate9Ex;\n    D3DPERF_BeginEvent;\n    D3DPERF_EndEvent;\n    D3DPERF_SetMarker;\n    D3DPERF_SetRegion;\n    D3DPERF_QueryRepeatFrame;\n    D3DPERF_SetOptions;\n    D3DPERF_GetStatus;\n    DebugSetMute;\n    DebugSetLevel;\n    PSGPError;\n    PSGPSampleTexture;\n    Direct3DShaderValidatorCreate9;\n    Direct3D9EnableMaximizedWindowedModeShim;\n    DXVK_RegisterAnnotation;\n    DXVK_UnRegisterAnnotation;\n    Direct3D9ForceHybridEnumeration;\n    Direct3DCreate9On12;\n    Direct3DCreate9On12Ex;\n\n  local:\n    *;\n};\n"
  },
  {
    "path": "src/d3d9/d3d9_adapter.cpp",
    "content": "#include \"d3d9_adapter.h\"\n\n#include \"d3d9_interface.h\"\n#include \"d3d9_monitor.h\"\n#include \"d3d9_caps.h\"\n#include \"d3d9_util.h\"\n\n#include \"../util/util_bit.h\"\n#include \"../util/util_luid.h\"\n#include \"../util/util_ratio.h\"\n#include \"../util/util_string.h\"\n\n#include <cfloat>\n\nnamespace dxvk {\n\n  const char* GetDriverDLL(DxvkGpuVendor vendor) {\n    switch (vendor) {\n      case DxvkGpuVendor::Nvidia: return \"nvd3dum.dll\";\n\n#if defined(__x86_64__) || defined(_M_X64)\n      default:\n      case DxvkGpuVendor::Amd:    return \"aticfx64.dll\";\n\n      case DxvkGpuVendor::Intel:  return \"igdumd64.dll\";\n#else\n      default:\n      case DxvkGpuVendor::Amd:    return \"aticfx32.dll\";\n\n      case DxvkGpuVendor::Intel:  return \"igdumd32.dll\";\n#endif\n    }\n  }\n\n\n  D3D9Adapter::D3D9Adapter(\n          D3D9InterfaceEx* pParent,\n    const D3D9ON12_ARGS*   p9On12Args,\n          Rc<DxvkAdapter>  Adapter,\n          UINT             Ordinal,\n          UINT             DisplayIndex)\n  : m_parent          (pParent),\n    m_adapter         (Adapter),\n    m_ordinal         (Ordinal),\n    m_displayIndex    (DisplayIndex),\n    m_modeCacheFormat (D3D9Format::Unknown) {\n    CacheIdentifierInfo();\n    // D3D9VkFormatTable needs to be constructed after we've cached the\n    // identifier info and determined the proper vendorID to be used.\n    m_d3d9Formats = std::make_unique<D3D9VkFormatTable>(this, Adapter, m_parent->GetOptions());\n\n    if (p9On12Args)\n      m_9On12Args = *p9On12Args;\n  }\n\n\n  template <size_t N>\n  static void copyToStringArray(char (&dst)[N], const char* src) {\n    dxvk::str::strlcpy(dst, src, N);\n  }\n\n\n  HRESULT D3D9Adapter::GetAdapterIdentifier(\n          DWORD                   Flags,\n          D3DADAPTER_IDENTIFIER9* pIdentifier) {\n    if (unlikely(pIdentifier == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    WCHAR wideDisplayName[32] = { };\n    if (!wsi::getDisplayName(wsi::getDefaultMonitor(), wideDisplayName)) {\n      Logger::err(\"D3D9Adapter::GetAdapterIdentifier: Failed to query monitor info\");\n      return D3DERR_INVALIDCALL;\n    }\n\n    std::string displayName = str::fromws(wideDisplayName);\n\n    copyToStringArray(pIdentifier->Description, m_deviceDesc.c_str());\n    copyToStringArray(pIdentifier->DeviceName,  displayName.c_str());    // The GDI device name. Not the actual device name.\n    copyToStringArray(pIdentifier->Driver,      m_deviceDriver.c_str()); // This is the driver's dll.\n\n    pIdentifier->DeviceIdentifier       = m_deviceGuid;\n    pIdentifier->DeviceId               = m_deviceId;\n    pIdentifier->VendorId               = m_vendorId;\n    pIdentifier->Revision               = 0;\n    pIdentifier->SubSysId               = 0;\n    pIdentifier->WHQLLevel              = m_parent->IsExtended() ? 1 : 0; // This doesn't check with the driver on Direct3D9Ex and is always 1.\n    pIdentifier->DriverVersion.QuadPart = INT64_MAX;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9Adapter::CheckDeviceType(\n          D3DDEVTYPE DevType,\n          D3D9Format AdapterFormat,\n          D3D9Format BackBufferFormat,\n          BOOL       bWindowed) {\n    if (!IsSupportedAdapterFormat(AdapterFormat))\n      return D3DERR_NOTAVAILABLE;\n\n    if (!IsSupportedBackBufferFormat(AdapterFormat, BackBufferFormat, bWindowed))\n      return D3DERR_NOTAVAILABLE;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9Adapter::CheckDeviceFormat(\n          D3DDEVTYPE      DeviceType,\n          D3D9Format      AdapterFormat,\n          DWORD           Usage,\n          D3DRESOURCETYPE RType,\n          D3D9Format      CheckFormat) {\n    if (unlikely(AdapterFormat == D3D9Format::Unknown))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(RType == D3DRTYPE_VERTEXBUFFER || RType == D3DRTYPE_INDEXBUFFER))\n      return D3DERR_INVALIDCALL;\n\n    if (!IsSupportedAdapterFormat(AdapterFormat))\n      return D3DERR_NOTAVAILABLE;\n\n    const bool isD3D8Compatible = m_parent->IsD3D8Compatible();\n    const bool isNvidia         = m_vendorId == uint32_t(DxvkGpuVendor::Nvidia);\n    const bool isAmd            = m_vendorId == uint32_t(DxvkGpuVendor::Amd);\n\n    const bool rt   = Usage & D3DUSAGE_RENDERTARGET;\n    const bool ds   = Usage & D3DUSAGE_DEPTHSTENCIL;\n\n    const bool surface       = RType == D3DRTYPE_SURFACE;\n    const bool texture       = RType == D3DRTYPE_TEXTURE;\n    const bool volumeTexture = RType == D3DRTYPE_VOLUMETEXTURE;\n\n    const bool twoDimensional = surface || texture;\n\n    const bool isDepthStencilFormat = IsDepthStencilFormat(CheckFormat);\n    const bool isLockableDepthStencilFormat = IsLockableDepthStencilFormat(CheckFormat);\n\n    if (ds && !isDepthStencilFormat)\n      return D3DERR_NOTAVAILABLE;\n\n    // Offscreen plain surfaces must only use lockable depth stencil formats\n    if (surface && !(rt || ds) && isDepthStencilFormat && !isLockableDepthStencilFormat)\n      return D3DERR_NOTAVAILABLE;\n\n    // Volume textures can not be used as render targets\n    if (rt && volumeTexture)\n      return D3DERR_NOTAVAILABLE;\n\n    if (unlikely(rt && CheckFormat == D3D9Format::A8 && m_parent->GetOptions().disableA8RT))\n      return D3DERR_NOTAVAILABLE;\n\n    // NULL RT format hack (supported across all\n    // vendors, and also advertised in D3D8)\n    if (unlikely(rt && CheckFormat == D3D9Format::NULL_FORMAT && twoDimensional))\n      return D3D_OK;\n\n    // AMD/Intel's driver hack for RESZ\n    // (also advertised in D3D8 by AMD drivers,\n    // not advertised at all by modern Intel drivers)\n    if (unlikely(rt && CheckFormat == D3D9Format::RESZ && surface))\n      return isAmd\n        ? D3D_OK\n        : D3DERR_NOTAVAILABLE;\n\n    // Nvidia/Intel's driver hack for ATOC\n    if (unlikely(CheckFormat == D3D9Format::ATOC && surface))\n      return (!isD3D8Compatible && !isAmd)\n        ? D3D_OK\n        : D3DERR_NOTAVAILABLE;\n\n    // Nvidia's driver hack for SSAA (supported on Nvidia\n    // drivers, ever since the GeForce 6 series)\n    if (unlikely(CheckFormat == D3D9Format::SSAA && surface)) {\n      if (!isD3D8Compatible && isNvidia)\n        Logger::warn(\"D3D9Adapter::CheckDeviceFormat: Transparency supersampling (SSAA) is unsupported\");\n      return D3DERR_NOTAVAILABLE;\n    }\n\n    // Nvidia specific depth bounds test hack\n    // (supported ever since the GeForce 6 series)\n    if (unlikely(CheckFormat == D3D9Format::NVDB && surface))\n      return (!isD3D8Compatible &&\n              m_adapter->features().core.features.depthBounds && isNvidia)\n        ? D3D_OK\n        : D3DERR_NOTAVAILABLE;\n\n    // AMD specific render to vertex buffer hack\n    // (not supported on modern AMD drivers)\n    if (unlikely(CheckFormat == D3D9Format::R2VB && surface)) {\n      if (!isD3D8Compatible && isAmd)\n        Logger::info(\"D3D9Adapter::CheckDeviceFormat: Render to vertex buffer (R2VB) is unsupported\");\n      return D3DERR_NOTAVAILABLE;\n    }\n\n    // AMD specific INST (geometry instancing)\n    // hack for SM2-only capable cards\n    if (unlikely(CheckFormat == D3D9Format::INST && surface))\n      return (!isD3D8Compatible && isAmd)\n        ? D3D_OK\n        : D3DERR_NOTAVAILABLE;\n\n    // AMD/Nvidia CENT(roid) hack (not advertised\n    // by either AMD or Nvidia drivers)\n    if (unlikely(CheckFormat == D3D9Format::CENT && surface))\n      return D3DERR_NOTAVAILABLE;\n\n    // I really don't want to support this...\n    if (unlikely(Usage & D3DUSAGE_DMAP)) {\n      Logger::warn(\"D3D9Adapter::CheckDeviceFormat: D3DUSAGE_DMAP is unsupported\");\n      return D3DERR_NOTAVAILABLE;\n    }\n\n    auto mapping = GetFormatMapping(CheckFormat);\n    if (mapping.FormatColor == VK_FORMAT_UNDEFINED)\n      return D3DERR_NOTAVAILABLE;\n\n    const bool srgb = (Usage & (D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE)) != 0;\n\n    if (mapping.FormatSrgb  == VK_FORMAT_UNDEFINED && srgb)\n      return D3DERR_NOTAVAILABLE;\n\n    if (RType == D3DRTYPE_CUBETEXTURE && mapping.Aspect != VK_IMAGE_ASPECT_COLOR_BIT)\n      return D3DERR_NOTAVAILABLE;\n\n    // Let's actually ask Vulkan now that we got some quirks out the way!\n    VkFormat format = mapping.FormatColor;\n    if (unlikely(mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED)) {\n      format = mapping.ConversionFormatInfo.FormatColor;\n    }\n    return CheckDeviceVkFormat(format, Usage, RType);\n  }\n\n\n  HRESULT D3D9Adapter::CheckDeviceMultiSampleType(\n        D3DDEVTYPE          DeviceType,\n        D3D9Format          SurfaceFormat,\n        BOOL                Windowed,\n        D3DMULTISAMPLE_TYPE MultiSampleType,\n        DWORD*              pQualityLevels) {\n    if (pQualityLevels != nullptr)\n      *pQualityLevels = 1;\n\n    if (unlikely(MultiSampleType > D3DMULTISAMPLE_16_SAMPLES))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(SurfaceFormat == D3D9Format::Unknown))\n      return D3DERR_INVALIDCALL;\n\n    auto dst = ConvertFormatUnfixed(SurfaceFormat);\n    // Wargame: European Escalation expects a D3DMULTISAMPLE_NONE\n    // NULL format check to succeed, otherwise it will crash\n    if (SurfaceFormat != D3D9Format::NULL_FORMAT && dst.FormatColor == VK_FORMAT_UNDEFINED)\n      return D3DERR_NOTAVAILABLE;\n\n    if (MultiSampleType != D3DMULTISAMPLE_NONE\n     && (SurfaceFormat == D3D9Format::D16_LOCKABLE\n      || SurfaceFormat == D3D9Format::D32F_LOCKABLE\n      || SurfaceFormat == D3D9Format::D32_LOCKABLE\n      || SurfaceFormat == D3D9Format::INTZ\n      || SurfaceFormat == D3D9Format::DXT1\n      || SurfaceFormat == D3D9Format::DXT2\n      || SurfaceFormat == D3D9Format::DXT3\n      || SurfaceFormat == D3D9Format::DXT4\n      || SurfaceFormat == D3D9Format::DXT5))\n      return D3DERR_NOTAVAILABLE;\n\n    uint32_t sampleCount = std::max<uint32_t>(MultiSampleType, 1u);\n\n    // Check if this is a power of two...\n    if (sampleCount & (sampleCount - 1))\n      return D3DERR_NOTAVAILABLE;\n\n    // Therefore...\n    VkSampleCountFlags sampleFlags = VkSampleCountFlags(sampleCount);\n\n    VkSampleCountFlags availableFlags;\n    if (dst.FormatColor == VK_FORMAT_UNDEFINED)\n      availableFlags = m_adapter->deviceProperties().core.properties.limits.framebufferDepthSampleCounts\n                     & m_adapter->deviceProperties().core.properties.limits.framebufferColorSampleCounts;\n    else if (IsDepthStencilFormat(SurfaceFormat))\n      availableFlags = m_adapter->deviceProperties().core.properties.limits.framebufferDepthSampleCounts;\n    else\n      availableFlags = m_adapter->deviceProperties().core.properties.limits.framebufferColorSampleCounts;\n\n    if (!(availableFlags & sampleFlags) && dst.FormatColor != VK_FORMAT_UNDEFINED) {\n      // Adreno 7XX GPUs cannot report general support for 8x MSAA because they do not support it for 128 bit formats.\n      // So take the format into consideration when checking whether the sample count is supported.\n\n      DxvkFormatQuery query = { };\n      query.format = dst.FormatColor;\n      query.type   = VK_IMAGE_TYPE_2D; // D3D9 only allows using MSAA with 2D textures\n      query.tiling = VK_IMAGE_TILING_OPTIMAL;\n      query.usage  = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;\n\n      if (!IsDepthStencilFormat(SurfaceFormat)) {\n        query.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\n        if (dst.FormatSrgb != VK_FORMAT_UNDEFINED)\n          query.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;\n      } else {\n        query.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n      }\n\n      if (dst.ConversionFormatInfo.FormatType != D3D9ConversionFormat_None)\n        query.usage |= VK_IMAGE_USAGE_STORAGE_BIT;\n\n      if (m_adapter->features().extAttachmentFeedbackLoopLayout.attachmentFeedbackLoopLayout)\n        query.usage |= VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;\n\n      auto limits = m_adapter->getFormatLimits(query);\n      if (!limits.has_value())\n        return D3DERR_NOTAVAILABLE;\n\n      availableFlags = limits->sampleCounts;\n    }\n\n    if (!(availableFlags & sampleFlags))\n      return D3DERR_NOTAVAILABLE;\n\n    if (pQualityLevels != nullptr) {\n      if (MultiSampleType == D3DMULTISAMPLE_NONMASKABLE)\n        *pQualityLevels = 32 - bit::lzcnt(availableFlags);\n      else\n        *pQualityLevels = 1;\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9Adapter::CheckDepthStencilMatch(\n          D3DDEVTYPE DeviceType,\n          D3D9Format AdapterFormat,\n          D3D9Format RenderTargetFormat,\n          D3D9Format DepthStencilFormat) {\n    if (!IsDepthStencilFormat(DepthStencilFormat))\n      return D3DERR_NOTAVAILABLE;\n\n    auto dsfMapping = GetFormatMapping(DepthStencilFormat);\n    if (dsfMapping.FormatColor == VK_FORMAT_UNDEFINED)\n      return D3DERR_NOTAVAILABLE;\n\n    if (RenderTargetFormat == dxvk::D3D9Format::NULL_FORMAT)\n      return D3D_OK;\n\n    auto rtfMapping = GetFormatMapping(RenderTargetFormat);\n    if (rtfMapping.FormatColor == VK_FORMAT_UNDEFINED)\n      return D3DERR_NOTAVAILABLE;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9Adapter::CheckDeviceFormatConversion(\n          D3DDEVTYPE DeviceType,\n          D3D9Format SourceFormat,\n          D3D9Format TargetFormat) {\n    bool sourceSupported = SourceFormat != D3D9Format::Unknown\n                        && (IsSupportedBackBufferFormat(SourceFormat)\n                        || (IsFourCCFormat(SourceFormat) && !IsVendorFormat(SourceFormat)));\n    bool targetSupported = TargetFormat == D3D9Format::X1R5G5B5\n                        || TargetFormat == D3D9Format::A1R5G5B5\n                        || TargetFormat == D3D9Format::R5G6B5\n                     // || TargetFormat == D3D9Format::R8G8B8 <-- We don't support R8G8B8\n                        || TargetFormat == D3D9Format::X8R8G8B8\n                        || TargetFormat == D3D9Format::A8R8G8B8\n                        || TargetFormat == D3D9Format::A2R10G10B10\n                        || TargetFormat == D3D9Format::A16B16G16R16\n                        || TargetFormat == D3D9Format::A2B10G10R10\n                        || TargetFormat == D3D9Format::A8B8G8R8\n                        || TargetFormat == D3D9Format::X8B8G8R8\n                        || TargetFormat == D3D9Format::A16B16G16R16F\n                        || TargetFormat == D3D9Format::A32B32G32R32F;\n\n    return (sourceSupported && targetSupported)\n      ? D3D_OK\n      : D3DERR_NOTAVAILABLE;\n  }\n\n\n  HRESULT D3D9Adapter::GetDeviceCaps(\n          D3DDEVTYPE DeviceType,\n          D3DCAPS9*  pCaps) {\n    using namespace dxvk::caps;\n\n    if (unlikely(pCaps == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(DeviceType == D3DDEVTYPE_SW)) {\n      if (m_parent->IsD3D8Compatible())\n        return D3DERR_INVALIDCALL;\n      else\n        return D3DERR_NOTAVAILABLE;\n    }\n\n    auto& options = m_parent->GetOptions();\n\n    const uint32_t maxShaderModel = m_parent->IsD3D8Compatible() ? std::min(1u, options.shaderModel) : options.shaderModel;\n    const auto& limits = m_adapter->deviceProperties().core.properties.limits;\n\n    // TODO: Actually care about what the adapter supports here.\n    // ^ For Intel and older cards most likely here.\n\n    // Device Type\n    pCaps->DeviceType               = DeviceType;\n    // Adapter Id\n    pCaps->AdapterOrdinal           = m_ordinal;\n    // Caps 1\n    pCaps->Caps                     = D3DCAPS_READ_SCANLINE;\n    // Caps 2\n    pCaps->Caps2                    = D3DCAPS2_FULLSCREENGAMMA\n                                 /* | D3DCAPS2_CANCALIBRATEGAMMA */\n                                 /* | D3DCAPS2_RESERVED */\n                                 /* | D3DCAPS2_CANMANAGERESOURCE */\n                                    | D3DCAPS2_DYNAMICTEXTURES\n                                    | D3DCAPS2_CANAUTOGENMIPMAP\n                                 /* | D3DCAPS2_CANSHARERESOURCE */;\n    // Caps 3\n    pCaps->Caps3                    = D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD\n                                    | D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION\n                                    | D3DCAPS3_COPY_TO_VIDMEM\n                                    | D3DCAPS3_COPY_TO_SYSTEMMEM\n                                 /* | D3DCAPS3_DXVAHD */\n                                 /* | D3DCAPS3_DXVAHD_LIMITED */;\n    // Presentation Intervals\n    pCaps->PresentationIntervals    = D3DPRESENT_INTERVAL_DEFAULT\n                                    | D3DPRESENT_INTERVAL_ONE\n                                    | D3DPRESENT_INTERVAL_TWO\n                                    | D3DPRESENT_INTERVAL_THREE\n                                    | D3DPRESENT_INTERVAL_FOUR\n                                    | D3DPRESENT_INTERVAL_IMMEDIATE;\n    // Cursor\n    pCaps->CursorCaps               = D3DCURSORCAPS_COLOR;\n    // Dev Caps\n    pCaps->DevCaps                  = D3DDEVCAPS_EXECUTESYSTEMMEMORY\n                                    | D3DDEVCAPS_EXECUTEVIDEOMEMORY\n                                    | D3DDEVCAPS_TLVERTEXSYSTEMMEMORY\n                                    | D3DDEVCAPS_TLVERTEXVIDEOMEMORY\n                                 /* | D3DDEVCAPS_TEXTURESYSTEMMEMORY */\n                                    | D3DDEVCAPS_TEXTUREVIDEOMEMORY\n                                    | D3DDEVCAPS_DRAWPRIMTLVERTEX\n                                    | D3DDEVCAPS_CANRENDERAFTERFLIP\n                                    | D3DDEVCAPS_TEXTURENONLOCALVIDMEM\n                                    | D3DDEVCAPS_DRAWPRIMITIVES2\n                                 /* | D3DDEVCAPS_SEPARATETEXTUREMEMORIES */\n                                    | D3DDEVCAPS_DRAWPRIMITIVES2EX\n                                    | D3DDEVCAPS_HWTRANSFORMANDLIGHT\n                                    | D3DDEVCAPS_CANBLTSYSTONONLOCAL\n                                    | D3DDEVCAPS_HWRASTERIZATION\n                                    | D3DDEVCAPS_PUREDEVICE\n                                 /* | D3DDEVCAPS_QUINTICRTPATCHES */\n                                 /* | D3DDEVCAPS_RTPATCHES */\n                                 /* | D3DDEVCAPS_RTPATCHHANDLEZERO */\n                                 /* | D3DDEVCAPS_NPATCHES */;\n    // Primitive Misc. Caps\n    pCaps->PrimitiveMiscCaps        = D3DPMISCCAPS_MASKZ\n                                    | D3DPMISCCAPS_CULLNONE\n                                    | D3DPMISCCAPS_CULLCW\n                                    | D3DPMISCCAPS_CULLCCW\n                                    | D3DPMISCCAPS_COLORWRITEENABLE\n                                    | D3DPMISCCAPS_CLIPPLANESCALEDPOINTS\n                                    | D3DPMISCCAPS_CLIPTLVERTS\n                                    | D3DPMISCCAPS_TSSARGTEMP\n                                    | D3DPMISCCAPS_BLENDOP\n                                 /* | D3DPMISCCAPS_NULLREFERENCE */\n                                    | D3DPMISCCAPS_INDEPENDENTWRITEMASKS\n                                    | D3DPMISCCAPS_PERSTAGECONSTANT\n                                    | D3DPMISCCAPS_FOGANDSPECULARALPHA\n                                    | D3DPMISCCAPS_SEPARATEALPHABLEND\n                                    | D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS\n                                    | D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING\n                                    | D3DPMISCCAPS_FOGVERTEXCLAMPED\n                                    | D3DPMISCCAPS_POSTBLENDSRGBCONVERT;\n    // Raster Caps\n    pCaps->RasterCaps               = D3DPRASTERCAPS_DITHER\n                                    | D3DPRASTERCAPS_ZTEST\n                                    | D3DPRASTERCAPS_FOGVERTEX\n                                    | D3DPRASTERCAPS_FOGTABLE\n                                    | D3DPRASTERCAPS_MIPMAPLODBIAS\n                                 /* | D3DPRASTERCAPS_ZBUFFERLESSHSR */\n                                    | D3DPRASTERCAPS_FOGRANGE\n                                    | D3DPRASTERCAPS_ANISOTROPY\n                                 /* | D3DPRASTERCAPS_WBUFFER */\n                                    | D3DPRASTERCAPS_WFOG\n                                    | D3DPRASTERCAPS_ZFOG\n                                    | D3DPRASTERCAPS_COLORPERSPECTIVE\n                                    | D3DPRASTERCAPS_SCISSORTEST\n                                    | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS\n                                    | D3DPRASTERCAPS_DEPTHBIAS\n                                    | D3DPRASTERCAPS_MULTISAMPLE_TOGGLE; // <-- TODO! (but difficult in Vk)\n    // Z Comparison Caps\n    pCaps->ZCmpCaps                 = D3DPCMPCAPS_NEVER\n                                    | D3DPCMPCAPS_LESS\n                                    | D3DPCMPCAPS_EQUAL\n                                    | D3DPCMPCAPS_LESSEQUAL\n                                    | D3DPCMPCAPS_GREATER\n                                    | D3DPCMPCAPS_NOTEQUAL\n                                    | D3DPCMPCAPS_GREATEREQUAL\n                                    | D3DPCMPCAPS_ALWAYS;\n    // Source Blend Caps\n    pCaps->SrcBlendCaps             = D3DPBLENDCAPS_ZERO\n                                    | D3DPBLENDCAPS_ONE\n                                    | D3DPBLENDCAPS_SRCCOLOR\n                                    | D3DPBLENDCAPS_INVSRCCOLOR\n                                    | D3DPBLENDCAPS_SRCALPHA\n                                    | D3DPBLENDCAPS_INVSRCALPHA\n                                    | D3DPBLENDCAPS_DESTALPHA\n                                    | D3DPBLENDCAPS_INVDESTALPHA\n                                    | D3DPBLENDCAPS_DESTCOLOR\n                                    | D3DPBLENDCAPS_INVDESTCOLOR\n                                    | D3DPBLENDCAPS_SRCALPHASAT\n                                    | D3DPBLENDCAPS_BOTHSRCALPHA\n                                    | D3DPBLENDCAPS_BOTHINVSRCALPHA\n                                    | D3DPBLENDCAPS_BLENDFACTOR;\n\n    // Only 9Ex devices advertise D3DPBLENDCAPS_SRCCOLOR2 and D3DPBLENDCAPS_INVSRCCOLOR2\n    if (m_parent->IsExtended())\n      pCaps->SrcBlendCaps          |= D3DPBLENDCAPS_SRCCOLOR2\n                                    | D3DPBLENDCAPS_INVSRCCOLOR2;\n\n    // Destination Blend Caps\n    pCaps->DestBlendCaps            = pCaps->SrcBlendCaps;\n    // Alpha Comparison Caps\n    pCaps->AlphaCmpCaps             = pCaps->ZCmpCaps;\n    // Shade Caps\n    pCaps->ShadeCaps                = D3DPSHADECAPS_COLORGOURAUDRGB\n                                    | D3DPSHADECAPS_SPECULARGOURAUDRGB\n                                    | D3DPSHADECAPS_ALPHAGOURAUDBLEND\n                                    | D3DPSHADECAPS_FOGGOURAUD;\n    // Texture Caps\n    pCaps->TextureCaps              = D3DPTEXTURECAPS_PERSPECTIVE\n                                 /* | D3DPTEXTURECAPS_POW2 */\n                                    | D3DPTEXTURECAPS_ALPHA\n                                 /* | D3DPTEXTURECAPS_SQUAREONLY */\n                                    | D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE\n                                    | D3DPTEXTURECAPS_ALPHAPALETTE\n                                 /* | D3DPTEXTURECAPS_NONPOW2CONDITIONAL */\n                                    | D3DPTEXTURECAPS_PROJECTED\n                                    | D3DPTEXTURECAPS_CUBEMAP\n                                    | D3DPTEXTURECAPS_VOLUMEMAP\n                                    | D3DPTEXTURECAPS_MIPMAP\n                                    | D3DPTEXTURECAPS_MIPVOLUMEMAP\n                                    | D3DPTEXTURECAPS_MIPCUBEMAP\n                                 /* | D3DPTEXTURECAPS_CUBEMAP_POW2 */\n                                 /* | D3DPTEXTURECAPS_VOLUMEMAP_POW2 */\n                                 /* | D3DPTEXTURECAPS_NOPROJECTEDBUMPENV */;\n    // Texture Filter Caps\n    pCaps->TextureFilterCaps        = D3DPTFILTERCAPS_MINFPOINT\n                                    | D3DPTFILTERCAPS_MINFLINEAR\n                                    | D3DPTFILTERCAPS_MINFANISOTROPIC\n                                 /* | D3DPTFILTERCAPS_MINFPYRAMIDALQUAD */\n                                 /* | D3DPTFILTERCAPS_MINFGAUSSIANQUAD */\n                                    | D3DPTFILTERCAPS_MIPFPOINT\n                                    | D3DPTFILTERCAPS_MIPFLINEAR\n                                 /* | D3DPTFILTERCAPS_CONVOLUTIONMONO */\n                                    | D3DPTFILTERCAPS_MAGFPOINT\n                                    | D3DPTFILTERCAPS_MAGFLINEAR\n                                    | D3DPTFILTERCAPS_MAGFANISOTROPIC\n                                 /* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */\n                                 /* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */;\n    // Cube Texture Filter Caps\n    pCaps->CubeTextureFilterCaps    = pCaps->TextureFilterCaps;\n    // Volume Texture Filter Caps\n    pCaps->VolumeTextureFilterCaps  = pCaps->TextureFilterCaps;\n    // Texture Address Caps\n    pCaps->TextureAddressCaps       = D3DPTADDRESSCAPS_WRAP\n                                    | D3DPTADDRESSCAPS_MIRROR\n                                    | D3DPTADDRESSCAPS_CLAMP\n                                    | D3DPTADDRESSCAPS_BORDER\n                                    | D3DPTADDRESSCAPS_INDEPENDENTUV\n                                    | D3DPTADDRESSCAPS_MIRRORONCE;\n    // Volume Texture Address Caps\n    pCaps->VolumeTextureAddressCaps = pCaps->TextureAddressCaps;\n    // Line Caps\n    pCaps->LineCaps                 = D3DLINECAPS_TEXTURE\n                                    | D3DLINECAPS_ZTEST\n                                    | D3DLINECAPS_BLEND\n                                    | D3DLINECAPS_ALPHACMP\n                                    | D3DLINECAPS_FOG\n                                    | D3DLINECAPS_ANTIALIAS; //<-- Lying about doing AA lines here, we don't *fully* support that.\n    // Max Texture Width\n    pCaps->MaxTextureWidth          = MaxTextureDimension;\n    // Max Texture Height\n    pCaps->MaxTextureHeight         = MaxTextureDimension;\n    // Max Volume Extent\n    pCaps->MaxVolumeExtent          = 8192;\n    // Max Texture Repeat\n    pCaps->MaxTextureRepeat         = 8192;\n    // Max Texture Aspect Ratio\n    pCaps->MaxTextureAspectRatio    = 8192;\n    // Max Anisotropy\n    pCaps->MaxAnisotropy            = 16;\n    // Max Vertex W\n    pCaps->MaxVertexW               = 1e10f;\n    // Guard Bands\n    pCaps->GuardBandLeft            = -32768.0f;\n    pCaps->GuardBandTop             = -32768.0f;\n    pCaps->GuardBandRight           =  32768.0f;\n    pCaps->GuardBandBottom          =  32768.0f;\n    // Extents Adjust\n    pCaps->ExtentsAdjust            = 0.0f;\n    // Stencil Caps\n    pCaps->StencilCaps              = D3DSTENCILCAPS_KEEP\n                                    | D3DSTENCILCAPS_ZERO\n                                    | D3DSTENCILCAPS_REPLACE\n                                    | D3DSTENCILCAPS_INCRSAT\n                                    | D3DSTENCILCAPS_DECRSAT\n                                    | D3DSTENCILCAPS_INVERT\n                                    | D3DSTENCILCAPS_INCR\n                                    | D3DSTENCILCAPS_DECR\n                                    | D3DSTENCILCAPS_TWOSIDED;\n    // FVF Caps\n    pCaps->FVFCaps                  = (MaxSimultaneousTextures & D3DFVFCAPS_TEXCOORDCOUNTMASK)\n                                 /* | D3DFVFCAPS_DONOTSTRIPELEMENTS */\n                                    | D3DFVFCAPS_PSIZE;\n    // Texture Op Caps\n    pCaps->TextureOpCaps            = D3DTEXOPCAPS_DISABLE\n                                    | D3DTEXOPCAPS_SELECTARG1\n                                    | D3DTEXOPCAPS_SELECTARG2\n                                    | D3DTEXOPCAPS_MODULATE\n                                    | D3DTEXOPCAPS_MODULATE2X\n                                    | D3DTEXOPCAPS_MODULATE4X\n                                    | D3DTEXOPCAPS_ADD\n                                    | D3DTEXOPCAPS_ADDSIGNED\n                                    | D3DTEXOPCAPS_ADDSIGNED2X\n                                    | D3DTEXOPCAPS_SUBTRACT\n                                    | D3DTEXOPCAPS_ADDSMOOTH\n                                    | D3DTEXOPCAPS_BLENDDIFFUSEALPHA\n                                    | D3DTEXOPCAPS_BLENDTEXTUREALPHA\n                                    | D3DTEXOPCAPS_BLENDFACTORALPHA\n                                    | D3DTEXOPCAPS_BLENDTEXTUREALPHAPM\n                                    | D3DTEXOPCAPS_BLENDCURRENTALPHA\n                                    | D3DTEXOPCAPS_PREMODULATE\n                                    | D3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR\n                                    | D3DTEXOPCAPS_MODULATECOLOR_ADDALPHA\n                                    | D3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR\n                                    | D3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA\n                                    | D3DTEXOPCAPS_BUMPENVMAP\n                                    | D3DTEXOPCAPS_BUMPENVMAPLUMINANCE\n                                    | D3DTEXOPCAPS_DOTPRODUCT3\n                                    | D3DTEXOPCAPS_MULTIPLYADD\n                                    | D3DTEXOPCAPS_LERP;\n    // Max Texture Blend Stages\n    pCaps->MaxTextureBlendStages    = MaxTextureBlendStages;\n    // Max Simultaneous Textures\n    pCaps->MaxSimultaneousTextures  = MaxSimultaneousTextures;\n    // Vertex Processing Caps\n    pCaps->VertexProcessingCaps      = D3DVTXPCAPS_TEXGEN\n                                     | D3DVTXPCAPS_MATERIALSOURCE7\n                                     | D3DVTXPCAPS_DIRECTIONALLIGHTS\n                                     | D3DVTXPCAPS_POSITIONALLIGHTS\n                                     | D3DVTXPCAPS_LOCALVIEWER\n                                     | D3DVTXPCAPS_TWEENING\n                                     | D3DVTXPCAPS_TEXGEN_SPHEREMAP\n                                  /* | D3DVTXPCAPS_NO_TEXGEN_NONLOCALVIEWER*/;\n    // Max Active Lights\n    pCaps->MaxActiveLights           = caps::MaxEnabledLights;\n    // Max User Clip Planes\n    pCaps->MaxUserClipPlanes         = MaxClipPlanes;\n    // Max Vertex Blend Matrices\n    pCaps->MaxVertexBlendMatrices    = 4;\n    // Max Vertex Blend Matrix Index\n    pCaps->MaxVertexBlendMatrixIndex = 0;\n    // Max Point Size\n    pCaps->MaxPointSize              = limits.pointSizeRange[1];\n    // Max Primitive Count\n    pCaps->MaxPrimitiveCount         = 0x00555555;\n    // Max Vertex Index\n    pCaps->MaxVertexIndex            = 0x00ffffff;\n    // Max Streams\n    pCaps->MaxStreams                = MaxStreams;\n    // Max Stream Stride\n    pCaps->MaxStreamStride           = 508; // bytes\n\n    // Late fixed-function capable cards, such as the GeForce 4 MX series,\n    // expose support for VS 1.1, while not advertising any PS support\n    const uint32_t majorVersionVS = maxShaderModel == 0 ? 1 : maxShaderModel;\n    const uint32_t majorVersionPS = maxShaderModel;\n    // Max supported SM1 is VS 1.1 and PS 1.4\n    const uint32_t minorVersionVS = majorVersionVS != 1 ? 0 : 1;\n    const uint32_t minorVersionPS = majorVersionPS != 1 ? 0 : 4;\n\n    // Shader Versions\n    pCaps->VertexShaderVersion = D3DVS_VERSION(majorVersionVS, minorVersionVS);\n    pCaps->PixelShaderVersion  = D3DPS_VERSION(majorVersionPS, minorVersionPS);\n\n    // Max Vertex Shader Const\n    pCaps->MaxVertexShaderConst       = MaxFloatConstantsVS;\n    // Max PS1 Value\n    pCaps->PixelShader1xMaxValue      = maxShaderModel > 0 ? std::numeric_limits<float>::max() : 0.0f;\n    // Dev Caps 2\n    pCaps->DevCaps2                   = D3DDEVCAPS2_STREAMOFFSET\n                                   /* | D3DDEVCAPS2_DMAPNPATCH */\n                                   /* | D3DDEVCAPS2_ADAPTIVETESSRTPATCH */\n                                   /* | D3DDEVCAPS2_ADAPTIVETESSNPATCH */\n                                      | D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES\n                                   /* | D3DDEVCAPS2_PRESAMPLEDDMAPNPATCH */\n                                      | D3DDEVCAPS2_VERTEXELEMENTSCANSHARESTREAMOFFSET;\n    // Max N Patch Tesselation Level\n    pCaps->MaxNpatchTessellationLevel = 0.0f;\n    // Reserved for... something\n    pCaps->Reserved5                  = 0;\n    // Master adapter for us is adapter 0, atm...\n    pCaps->MasterAdapterOrdinal       = 0;\n    // The group of adapters this one is in\n    pCaps->AdapterOrdinalInGroup      = 0;\n    // Number of adapters in current group\n    pCaps->NumberOfAdaptersInGroup    = 1;\n    // Decl Type Caps\n    pCaps->DeclTypes                  = D3DDTCAPS_UBYTE4\n                                      | D3DDTCAPS_UBYTE4N\n                                      | D3DDTCAPS_SHORT2N\n                                      | D3DDTCAPS_SHORT4N\n                                      | D3DDTCAPS_USHORT2N\n                                      | D3DDTCAPS_USHORT4N\n                                      | D3DDTCAPS_UDEC3\n                                      | D3DDTCAPS_DEC3N\n                                      | D3DDTCAPS_FLOAT16_2\n                                      | D3DDTCAPS_FLOAT16_4;\n    // Number of simultaneous RTs\n    pCaps->NumSimultaneousRTs         = MaxSimultaneousRenderTargets;\n    // Possible StretchRect filters\n    pCaps->StretchRectFilterCaps      = D3DPTFILTERCAPS_MINFPOINT\n                                      | D3DPTFILTERCAPS_MINFLINEAR\n                                   /* | D3DPTFILTERCAPS_MINFANISOTROPIC */\n                                   /* | D3DPTFILTERCAPS_MINFPYRAMIDALQUAD */\n                                   /* | D3DPTFILTERCAPS_MINFGAUSSIANQUAD */\n                                   /* | D3DPTFILTERCAPS_MIPFPOINT */\n                                   /* | D3DPTFILTERCAPS_MIPFLINEAR */\n                                   /* | D3DPTFILTERCAPS_CONVOLUTIONMONO */\n                                      | D3DPTFILTERCAPS_MAGFPOINT\n                                      | D3DPTFILTERCAPS_MAGFLINEAR\n                                   /* | D3DPTFILTERCAPS_MAGFANISOTROPIC */\n                                   /* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */\n                                   /* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */;\n\n    pCaps->VS20Caps.Caps                     = maxShaderModel >= 2 ? D3DVS20CAPS_PREDICATION : 0;\n    pCaps->VS20Caps.DynamicFlowControlDepth  = maxShaderModel >= 2 ? D3DVS20_MAX_DYNAMICFLOWCONTROLDEPTH : 0;\n    pCaps->VS20Caps.NumTemps                 = maxShaderModel >= 2 ? D3DVS20_MAX_NUMTEMPS : 0;\n    pCaps->VS20Caps.StaticFlowControlDepth   = maxShaderModel >= 2 ? D3DVS20_MAX_STATICFLOWCONTROLDEPTH : 0;\n\n    pCaps->PS20Caps.Caps                     = maxShaderModel >= 2 ? D3DPS20CAPS_ARBITRARYSWIZZLE\n                                                                   | D3DPS20CAPS_GRADIENTINSTRUCTIONS\n                                                                   | D3DPS20CAPS_PREDICATION\n                                                                   | D3DPS20CAPS_NODEPENDENTREADLIMIT\n                                                                   | D3DPS20CAPS_NOTEXINSTRUCTIONLIMIT : 0;\n    pCaps->PS20Caps.DynamicFlowControlDepth  = maxShaderModel >= 2 ? D3DPS20_MAX_DYNAMICFLOWCONTROLDEPTH : 0;\n    pCaps->PS20Caps.NumTemps                 = maxShaderModel >= 2 ? D3DPS20_MAX_NUMTEMPS : 0;\n    pCaps->PS20Caps.StaticFlowControlDepth   = maxShaderModel >= 2 ? D3DPS20_MAX_STATICFLOWCONTROLDEPTH : 0;\n    pCaps->PS20Caps.NumInstructionSlots      = maxShaderModel >= 2 ? D3DPS20_MAX_NUMINSTRUCTIONSLOTS : 0;\n\n    // Vertex texture samplers are only available as part of SM3, the caps are 0 otherwise.\n    pCaps->VertexTextureFilterCaps           = maxShaderModel == 3 ? D3DPTFILTERCAPS_MINFPOINT\n                                                                   | D3DPTFILTERCAPS_MINFLINEAR\n                                                                /* | D3DPTFILTERCAPS_MINFANISOTROPIC */\n                                                                /* | D3DPTFILTERCAPS_MINFPYRAMIDALQUAD */\n                                                                /* | D3DPTFILTERCAPS_MINFGAUSSIANQUAD */\n                                                                /* | D3DPTFILTERCAPS_MIPFPOINT */\n                                                                /* | D3DPTFILTERCAPS_MIPFLINEAR */\n                                                                /* | D3DPTFILTERCAPS_CONVOLUTIONMONO */\n                                                                   | D3DPTFILTERCAPS_MAGFPOINT\n                                                                   | D3DPTFILTERCAPS_MAGFLINEAR\n                                                                /* | D3DPTFILTERCAPS_MAGFANISOTROPIC */\n                                                                /* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */\n                                                                /* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */ : 0;\n\n    pCaps->MaxVShaderInstructionsExecuted    = maxShaderModel >= 2 ? 4294967295 : 0;\n    pCaps->MaxPShaderInstructionsExecuted    = maxShaderModel >= 2 ? 4294967295 : 0;\n\n    pCaps->MaxVertexShader30InstructionSlots = maxShaderModel == 3 ? 32768 : 0;\n    pCaps->MaxPixelShader30InstructionSlots  = maxShaderModel == 3 ? 32768 : 0;\n\n    return D3D_OK;\n  }\n\n\n  HMONITOR D3D9Adapter::GetMonitor() {\n    return wsi::getDefaultMonitor();\n  }\n\n\n  UINT D3D9Adapter::GetAdapterModeCountEx(const D3DDISPLAYMODEFILTER* pFilter) {\n    if (pFilter == nullptr)\n      return 0;\n\n    // We don't offer any interlaced formats here so early out and avoid destroying mode cache.\n    if (pFilter->ScanLineOrdering == D3DSCANLINEORDERING_INTERLACED)\n      return 0;\n\n    CacheModes(EnumerateFormat(pFilter->Format));\n    return m_modes.size();\n  }\n\n\n  HRESULT D3D9Adapter::EnumAdapterModesEx(\n    const D3DDISPLAYMODEFILTER* pFilter,\n          UINT                  Mode,\n          D3DDISPLAYMODEEX*     pMode) {\n    if (pMode == nullptr || pFilter == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    const D3D9Format format =\n      EnumerateFormat(pFilter->Format);\n\n    if (FAILED(CheckDeviceFormat(\n      D3DDEVTYPE_HAL, EnumerateFormat(pFilter->Format),\n      D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE,\n      EnumerateFormat(pFilter->Format))))\n      return D3DERR_INVALIDCALL;\n\n    CacheModes(format);\n\n    // We don't return any scanline orderings that aren't progressive,\n    // The format filtering is already handled for us by cache modes\n    // So we can early out here and then just index.\n    if (pFilter->ScanLineOrdering == D3DSCANLINEORDERING_INTERLACED)\n      return D3DERR_INVALIDCALL;\n\n    if (Mode >= m_modes.size())\n      return D3DERR_INVALIDCALL;\n\n    *pMode = m_modes[Mode];\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9Adapter::GetAdapterDisplayModeEx(\n          D3DDISPLAYMODEEX*   pMode,\n          D3DDISPLAYROTATION* pRotation) {\n    if (unlikely(pMode == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pMode->Size != sizeof(D3DDISPLAYMODEEX)))\n      return D3DERR_INVALIDCALL;\n\n    if (pRotation != nullptr)\n      *pRotation = D3DDISPLAYROTATION_IDENTITY;\n\n    wsi::WsiMode mode = { };\n\n    if (!wsi::getCurrentDisplayMode(wsi::getDefaultMonitor(), &mode)) {\n      Logger::err(\"D3D9Adapter::GetAdapterDisplayModeEx: Failed to enum display settings\");\n      return D3DERR_INVALIDCALL;\n    }\n\n    *pMode = ConvertDisplayMode(mode);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9Adapter::GetAdapterLUID(LUID* pLUID) {\n    if (pLUID == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    auto& vk11 = m_adapter->deviceProperties().vk11;\n\n    if (vk11.deviceLUIDValid)\n      *pLUID = bit::cast<LUID>(vk11.deviceLUID);\n    else\n      *pLUID = dxvk::GetAdapterLUID(m_ordinal);\n\n    return D3D_OK;\n  }\n\n\n  void D3D9Adapter::RefreshFormatsTable() const {\n    m_d3d9Formats->RefreshFormatSupport(this);\n  }\n\n\n  bool D3D9Adapter::IsExtended() const {\n    return m_parent->IsExtended();\n  }\n\n\n  bool D3D9Adapter::IsD3D8Compatible() const {\n    return m_parent->IsD3D8Compatible();\n  }\n\n\n  HRESULT D3D9Adapter::CheckDeviceVkFormat(\n          VkFormat        Format,\n          DWORD           Usage,\n          D3DRESOURCETYPE RType) {\n    VkFormatFeatureFlags2 checkFlags = 0;\n\n    if (RType != D3DRTYPE_SURFACE)\n      checkFlags |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT;\n\n    if (Usage & D3DUSAGE_RENDERTARGET) {\n      checkFlags |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT;\n\n      if (Usage & D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING)\n        checkFlags |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT;\n    }\n\n    if (Usage & D3DUSAGE_DEPTHSTENCIL)\n      checkFlags |= VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT;\n    else\n      checkFlags |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT;\n\n    VkFormatFeatureFlags2 checkFlagsMipGen = checkFlags;\n\n    if (Usage & D3DUSAGE_AUTOGENMIPMAP) {\n      checkFlagsMipGen |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT;\n      checkFlagsMipGen |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT;\n    }\n\n    DxvkFormatFeatures    fmtSupport  = m_adapter->getFormatFeatures(Format);\n    VkFormatFeatureFlags2 imgFeatures = fmtSupport.optimal | fmtSupport.linear;\n\n    if ((imgFeatures & checkFlags) != checkFlags)\n      return D3DERR_NOTAVAILABLE;\n\n    return ((imgFeatures & checkFlagsMipGen) != checkFlagsMipGen)\n      ? D3DOK_NOAUTOGEN\n      : D3D_OK;\n  }\n\n\n  void D3D9Adapter::CacheModes(D3D9Format Format) {\n    if (!m_modes.empty() && m_modeCacheFormat == Format)\n      return; // We already cached the modes for this format. No need to do it again.\n\n    m_modes.clear();\n    m_modeCacheFormat = Format;\n\n    // Skip unsupported formats\n    if (!IsSupportedModeFormat(Format))\n      return;\n\n    auto& options = m_parent->GetOptions();\n\n    // Filter the modes considering the config option filters.\n    FilterModesByFormat(Format, true);\n\n    // If no modes are returned based on the previous filtered\n    // search, then fall back to an unfiltered search.\n    if (unlikely((!options.forceAspectRatio.empty()\n                || options.forceRefreshRate\n                || options.modeCountCompatibility) && !m_modes.size())) {\n      Logger::warn(\"D3D9Adapter::CacheModes: No modes were found. Discarding filters.\");\n      FilterModesByFormat(Format, false);\n    }\n\n    // Sort display modes by width, height and refresh rate (descending), in that order.\n    // Some games rely on correct ordering, e.g. Prince of Persia (2008) expects the highest\n    // refresh rate to be listed first for a particular resolution.\n    std::sort(m_modes.begin(), m_modes.end(),\n      [](const D3DDISPLAYMODEEX& a, const D3DDISPLAYMODEEX& b) {\n        if (a.Width < b.Width)   return true;\n        if (a.Width > b.Width)   return false;\n\n        if (a.Height < b.Height) return true;\n        if (a.Height > b.Height) return false;\n\n        return a.RefreshRate > b.RefreshRate;\n    });\n  }\n\n\n  void D3D9Adapter::FilterModesByFormat(\n       D3D9Format Format,\n       const bool ApplyOptionsFilters) {\n    auto& options = m_parent->GetOptions();\n\n    const auto forcedRatio = Ratio<DWORD>(options.forceAspectRatio);\n\n    wsi::WsiMode currentMode = { };\n    wsi::WsiMode currentCompatibleMode = { };\n\n    if (options.modeCountCompatibility) {\n      wsi::getDesktopDisplayMode(wsi::getDefaultMonitor(), &currentMode);\n\n      if (likely(currentMode.width)) {\n        // Skip checking the compabilitiy refresh rate (60 Hz),\n        // if that's equal to the current desktop refresh rate.\n        if (currentMode.refreshRate.numerator / currentMode.refreshRate.denominator != 60) {\n          currentCompatibleMode = currentMode;\n          currentCompatibleMode.refreshRate.numerator = 60;\n          currentCompatibleMode.refreshRate.denominator = 1;\n        }\n      } else {\n        Logger::err(\"D3D9Adapter::CacheModes: Failed to determine desktop display mode\");\n      }\n    }\n\n    // Walk over all modes that the display supports and\n    // return those that match the requested format etc.\n    wsi::WsiMode devMode = { };\n\n    uint32_t modeIndex = 0;\n\n    while (wsi::getDisplayMode(wsi::getDefaultMonitor(), modeIndex++, &devMode)) {\n      // Skip interlaced modes altogether\n      if (devMode.interlaced)\n        continue;\n\n      // Skip modes with incompatible formats\n      if (devMode.bitsPerPixel != GetMonitorFormatBpp(Format))\n        continue;\n\n      if (!forcedRatio.undefined() &&\n          ApplyOptionsFilters &&\n          Ratio<DWORD>(devMode.width, devMode.height) != forcedRatio)\n        continue;\n\n      if (options.forceRefreshRate &&\n          ApplyOptionsFilters &&\n          devMode.refreshRate.numerator / devMode.refreshRate.denominator != options.forceRefreshRate)\n        continue;\n\n      if (options.modeCountCompatibility &&\n          ApplyOptionsFilters &&\n          !IsEquivalentMode(devMode, currentMode) &&\n          (!currentCompatibleMode.width || !IsEquivalentMode(devMode, currentCompatibleMode)) &&\n          !IsCountCompatibleMode(devMode))\n        continue;\n\n      D3DDISPLAYMODEEX mode = ConvertDisplayMode(devMode);\n      // Fix up the D3DFORMAT to match what we are enumerating\n      mode.Format = static_cast<D3DFORMAT>(Format);\n\n      if (std::count(m_modes.begin(), m_modes.end(), mode) == 0)\n        m_modes.push_back(mode);\n    }\n  }\n\n\n  void D3D9Adapter::CacheIdentifierInfo() {\n    auto& options = m_parent->GetOptions();\n\n    const auto& props = m_adapter->deviceProperties();\n\n    m_deviceGuid   = bit::cast<GUID>(props.vk11.deviceUUID);\n    m_vendorId     = props.core.properties.vendorID;\n    m_deviceId     = props.core.properties.deviceID;\n    m_deviceDesc   = props.core.properties.deviceName;\n\n    // Custom Vendor ID / Device ID / Device Description\n    if (options.customVendorId >= 0)\n      m_vendorId = uint32_t(options.customVendorId);\n\n    if (options.customDeviceId >= 0)\n      m_deviceId = uint32_t(options.customDeviceId);\n\n    if (!options.customDeviceDesc.empty())\n      m_deviceDesc = options.customDeviceDesc;\n\n    if (options.customVendorId < 0) {\n      bool isNonclassicalVendorId = m_vendorId != uint32_t(DxvkGpuVendor::Nvidia) &&\n                                    m_vendorId != uint32_t(DxvkGpuVendor::Amd) &&\n                                    m_vendorId != uint32_t(DxvkGpuVendor::Intel);\n\n      if (isNonclassicalVendorId)\n        Logger::info(str::format(\"D3D9: Detected nonclassical vendor ID: 0x\", std::hex, m_vendorId));\n\n      uint32_t     fallbackVendor = 0xdead;\n      uint32_t     fallbackDevice = 0xbeef;\n      const char*  fallbackDesc   = \"Generic Graphics Card\";\n\n      if (!options.hideAmdGpu) {\n        // AMD RX 6700 XT\n        fallbackVendor = uint32_t(DxvkGpuVendor::Amd);\n        fallbackDevice = 0x73df;\n        fallbackDesc   = \"AMD Radeon RX 6700 XT\";\n      } else if (!options.hideNvidiaGpu) {\n        // Nvidia RTX 3060\n        fallbackVendor = uint32_t(DxvkGpuVendor::Nvidia);\n        fallbackDevice = 0x2487;\n        fallbackDesc   = \"NVIDIA GeForce RTX 3060\";\n      }\n\n      bool hideNvidiaGpu = props.vk12.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY\n        ? options.hideNvidiaGpu : options.hideNvkGpu;\n\n      bool hideGpu = (m_vendorId == uint32_t(DxvkGpuVendor::Nvidia) && hideNvidiaGpu)\n                  || (m_vendorId == uint32_t(DxvkGpuVendor::Amd) && options.hideAmdGpu)\n                  || (m_vendorId == uint32_t(DxvkGpuVendor::Intel) && options.hideIntelGpu)\n                  // Hide the GPU by default for other vendors (default to reporting AMD)\n                  || isNonclassicalVendorId;\n\n      if (hideGpu) {\n        m_vendorId = fallbackVendor;\n\n        if (options.customDeviceId < 0)\n          m_deviceId = fallbackDevice;\n\n        if (options.customDeviceDesc.empty())\n          m_deviceDesc = fallbackDesc;\n\n        Logger::info(str::format(\"D3D9: Hiding actual GPU, reporting:\\n\",\n                                 \"  vendor ID: 0x\", std::hex, m_vendorId, \"\\n\",\n                                 \"  device ID: 0x\", std::hex, m_deviceId, \"\\n\",\n                                 \"  device description: \", m_deviceDesc, \"\\n\"));\n      }\n    }\n\n    m_deviceDriver = GetDriverDLL(DxvkGpuVendor(m_vendorId));\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_adapter.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n\n#include \"d3d9_options.h\"\n#include \"d3d9_format.h\"\n\n#include \"../dxvk/dxvk_adapter.h\"\n\n#include \"../wsi/wsi_monitor.h\"\n\nnamespace dxvk {\n\n  class D3D9InterfaceEx;\n\n  class D3D9Adapter {\n\n  public:\n\n    D3D9Adapter(\n            D3D9InterfaceEx* pParent,\n      const D3D9ON12_ARGS*   p9On12Args,\n            Rc<DxvkAdapter>  Adapter,\n            UINT             Ordinal,\n            UINT             DisplayIndex);\n\n    HRESULT GetAdapterIdentifier(\n            DWORD                   Flags,\n            D3DADAPTER_IDENTIFIER9* pIdentifier);\n\n    HRESULT CheckDeviceType(\n            D3DDEVTYPE DevType,\n            D3D9Format AdapterFormat,\n            D3D9Format BackBufferFormat,\n            BOOL       bWindowed);\n\n    HRESULT CheckDeviceFormat(\n            D3DDEVTYPE      DeviceType,\n            D3D9Format      AdapterFormat,\n            DWORD           Usage,\n            D3DRESOURCETYPE RType,\n            D3D9Format      CheckFormat);\n\n    HRESULT CheckDeviceMultiSampleType(\n            D3DDEVTYPE          DeviceType,\n            D3D9Format          SurfaceFormat,\n            BOOL                Windowed,\n            D3DMULTISAMPLE_TYPE MultiSampleType,\n            DWORD*              pQualityLevels);\n\n    HRESULT CheckDepthStencilMatch(\n            D3DDEVTYPE DeviceType,\n            D3D9Format AdapterFormat,\n            D3D9Format RenderTargetFormat,\n            D3D9Format DepthStencilFormat);\n\n    HRESULT CheckDeviceFormatConversion(\n            D3DDEVTYPE DeviceType,\n            D3D9Format SourceFormat,\n            D3D9Format TargetFormat);\n\n    HRESULT GetDeviceCaps(\n            D3DDEVTYPE DeviceType,\n            D3DCAPS9*  pCaps);\n\n    HMONITOR GetMonitor();\n\n    UINT GetAdapterModeCountEx(const D3DDISPLAYMODEFILTER* pFilter);\n\n    HRESULT EnumAdapterModesEx(\n      const D3DDISPLAYMODEFILTER* pFilter,\n            UINT                  Mode,\n            D3DDISPLAYMODEEX*     pMode);\n\n    HRESULT GetAdapterDisplayModeEx(\n            D3DDISPLAYMODEEX*   pMode,\n            D3DDISPLAYROTATION* pRotation);\n\n    HRESULT GetAdapterLUID(LUID* pLUID);\n\n    UINT GetOrdinal() { return m_ordinal; }\n\n    Rc<DxvkAdapter> GetDXVKAdapter() { return m_adapter; }\n\n    uint32_t GetVendorId() const {\n      return m_vendorId;\n    }\n\n    D3D9_VK_FORMAT_MAPPING GetFormatMapping(D3D9Format Format) const {\n      return m_d3d9Formats->GetFormatMapping(Format);\n    }\n\n    const DxvkFormatInfo* GetUnsupportedFormatInfo(D3D9Format Format) const {\n      return m_d3d9Formats->GetUnsupportedFormatInfo(Format);\n    }\n\n    D3D9ON12_ARGS Get9On12Args() const {\n      return m_9On12Args;\n    }\n\n    void RefreshFormatsTable() const;\n\n    bool IsExtended() const;\n\n    bool IsD3D8Compatible() const;\n\n  private:\n\n    // used as a global filter when mode count compatibility is enabled\n    inline bool IsCountCompatibleMode(const wsi::WsiMode& wsiMode) {\n      if (wsiMode.refreshRate.numerator / wsiMode.refreshRate.denominator != 60)\n        return false;\n\n      return (wsiMode.width == 640  && wsiMode.height == 480)\n          || (wsiMode.width == 800  && wsiMode.height == 600)\n          || (wsiMode.width == 1024 && wsiMode.height == 768)\n          || (wsiMode.width == 1280 && wsiMode.height == 1024)\n          || (wsiMode.width == 1280 && wsiMode.height == 720)\n          || (wsiMode.width == 1920 && wsiMode.height == 1080);\n    }\n\n    inline bool IsEquivalentMode(const wsi::WsiMode& wsiModeA, const wsi::WsiMode& wsiModeB) {\n      return wsiModeA.width  == wsiModeB.width  &&\n             wsiModeA.height == wsiModeB.height &&\n             (wsiModeA.refreshRate.numerator / wsiModeA.refreshRate.denominator ==\n              wsiModeB.refreshRate.numerator / wsiModeB.refreshRate.denominator);\n    }\n\n    HRESULT CheckDeviceVkFormat(\n          VkFormat        Format,\n          DWORD           Usage,\n          D3DRESOURCETYPE RType);\n\n    void CacheModes(D3D9Format Format);\n\n    void FilterModesByFormat(\n          D3D9Format Format,\n          const bool ApplyOptionsFilter);\n\n    void CacheIdentifierInfo();\n\n    D3D9InterfaceEx*              m_parent;\n\n    Rc<DxvkAdapter>               m_adapter;\n    UINT                          m_ordinal;\n    UINT                          m_displayIndex;\n\n    GUID                          m_deviceGuid;\n    uint32_t                      m_vendorId;\n    uint32_t                      m_deviceId;\n    std::string                   m_deviceDesc;\n    std::string                   m_deviceDriver;\n    D3D9ON12_ARGS                 m_9On12Args = { };\n\n    std::vector<D3DDISPLAYMODEEX>            m_modes;\n    D3D9Format                               m_modeCacheFormat;\n\n    std::unique_ptr<D3D9VkFormatTable>       m_d3d9Formats;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_annotation.cpp",
    "content": "#include \"d3d9_annotation.h\"\n\nnamespace dxvk {\n\n  ////////////////////////////\n  // D3D9GlobalAnnotationList\n  ////////////////////////////\n\n  D3D9GlobalAnnotationList::D3D9GlobalAnnotationList()\n    : m_shouldAnnotate(false)\n    , m_eventDepth(0)\n  {}\n\n\n  D3D9GlobalAnnotationList::~D3D9GlobalAnnotationList()\n  {}\n\n\n  void D3D9GlobalAnnotationList::RegisterAnnotator(IDXVKUserDefinedAnnotation* annotation) {\n    auto lock = std::unique_lock(m_mutex);\n    m_shouldAnnotate = true;\n    m_annotations.push_back(annotation);\n  }\n\n\n  void D3D9GlobalAnnotationList::UnregisterAnnotator(IDXVKUserDefinedAnnotation* annotation) {\n    auto lock = std::unique_lock(m_mutex);\n    auto iter = std::find(m_annotations.begin(), m_annotations.end(), annotation);\n    if (iter != m_annotations.end())\n        m_annotations.erase(iter);\n  }\n\n\n  INT D3D9GlobalAnnotationList::BeginEvent(D3DCOLOR color, LPCWSTR name) {\n    if (!m_shouldAnnotate)\n      return 0;\n\n    auto lock = std::unique_lock(m_mutex);\n    for (auto* annotation : m_annotations)\n      annotation->BeginEvent(color, name);\n\n    return m_eventDepth++;\n  }\n\n\n  INT D3D9GlobalAnnotationList::EndEvent() {\n    if (!m_shouldAnnotate)\n      return 0;\n\n    auto lock = std::unique_lock(m_mutex);\n    for (auto* annotation : m_annotations)\n      annotation->EndEvent();\n\n    return m_eventDepth--;\n  }\n\n\n  void D3D9GlobalAnnotationList::SetMarker(D3DCOLOR color, LPCWSTR name) {\n    if (!m_shouldAnnotate)\n      return;\n\n    auto lock = std::unique_lock(m_mutex);\n    for (auto* annotation : m_annotations)\n      annotation->SetMarker(color, name);\n  }\n\n\n  void D3D9GlobalAnnotationList::SetRegion(D3DCOLOR color, LPCWSTR name) {\n    // This, by the documentation, does nothing.\n  }\n\n\n  BOOL D3D9GlobalAnnotationList::QueryRepeatFrame() const {\n    // This, by the documentation, does nothing.\n    // It's meant to return TRUE if the profiler/debugger\n    // wants a frame to be repeated, but we never need that.\n    return FALSE;\n  }\n\n\n  void D3D9GlobalAnnotationList::SetOptions(DWORD options) {\n    // This is used to say that the app should\n    // not be debugged/profiled.\n  }\n\n\n  DWORD D3D9GlobalAnnotationList::GetStatus() const {\n    // This returns whether the app is being\n    // profiled / debugged.\n    // Some apps may rely on this to emit\n    // debug markers.\n    return m_shouldAnnotate ? 1 : 0;\n  }\n\n  ////////////////////////////\n  // D3D9UserDefinedAnnotation\n  ////////////////////////////\n\n  D3D9UserDefinedAnnotation::D3D9UserDefinedAnnotation(D3D9DeviceEx* device)\n    : m_container(device) {\n    D3D9GlobalAnnotationList::Instance().RegisterAnnotator(this);\n  }\n\n\n  D3D9UserDefinedAnnotation::~D3D9UserDefinedAnnotation() {\n    D3D9GlobalAnnotationList::Instance().UnregisterAnnotator(this);\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D9UserDefinedAnnotation::AddRef() {\n    return m_container->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3D9UserDefinedAnnotation::Release() {\n    return m_container->Release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9UserDefinedAnnotation::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_container->QueryInterface(riid, ppvObject);\n  }\n\n\n  INT STDMETHODCALLTYPE D3D9UserDefinedAnnotation::BeginEvent(\n          D3DCOLOR                Color,\n          LPCWSTR                 Name) {\n    D3D9DeviceLock lock = m_container->LockDevice();\n    m_container->EmitCs([color = Color, labelName = dxvk::str::fromws(Name)](DxvkContext *ctx) {\n      VkDebugUtilsLabelEXT label;\n      label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;\n      label.pNext = nullptr;\n      label.pLabelName = labelName.c_str();\n      DecodeD3DCOLOR(color, label.color);\n\n      ctx->beginDebugLabel(label);\n    });\n\n    // Handled by the global list.\n    return 0;\n  }\n\n\n  INT STDMETHODCALLTYPE D3D9UserDefinedAnnotation::EndEvent() {\n    D3D9DeviceLock lock = m_container->LockDevice();\n    m_container->EmitCs([](DxvkContext *ctx) {\n      ctx->endDebugLabel();\n    });\n\n    // Handled by the global list.\n    return 0;\n  }\n\n\n  void STDMETHODCALLTYPE D3D9UserDefinedAnnotation::SetMarker(\n          D3DCOLOR                Color,\n          LPCWSTR                 Name) {\n    D3D9DeviceLock lock = m_container->LockDevice();\n    m_container->EmitCs([color = Color, labelName = dxvk::str::fromws(Name)](DxvkContext *ctx) {\n      VkDebugUtilsLabelEXT label;\n      label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;\n      label.pNext = nullptr;\n      label.pLabelName = labelName.c_str();\n      DecodeD3DCOLOR(color, label.color);\n\n      ctx->insertDebugLabel(label);\n    });\n  }\n\n\n  BOOL STDMETHODCALLTYPE D3D9UserDefinedAnnotation::GetStatus() {\n    return TRUE;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_annotation.h",
    "content": "#pragma once\n\n#include \"d3d9_device.h\"\n#include \"d3d9_include.h\"\n\n#include \"../dxvk/dxvk_annotation.h\"\n\n#include \"../util/thread.h\"\n\n#include <atomic>\n#include <mutex>\n#include <vector>\n\nnamespace dxvk {\n\n  class D3D9GlobalAnnotationList {\n\n  public:\n\n    D3D9GlobalAnnotationList();\n    ~D3D9GlobalAnnotationList();\n\n    void RegisterAnnotator(IDXVKUserDefinedAnnotation* annotation);\n    void UnregisterAnnotator(IDXVKUserDefinedAnnotation* annotation);\n\n    INT BeginEvent(D3DCOLOR color, LPCWSTR name);\n    INT EndEvent();\n\n    void SetMarker(D3DCOLOR color, LPCWSTR name);\n\n    void SetRegion(D3DCOLOR color, LPCWSTR name);\n\n    BOOL QueryRepeatFrame() const;\n\n    void SetOptions(DWORD options);\n\n    DWORD GetStatus() const;\n\n    static D3D9GlobalAnnotationList& Instance() {\n      return s_instance;\n    }\n\n  private:\n\n    static D3D9GlobalAnnotationList s_instance;\n\n    std::atomic<bool> m_shouldAnnotate;\n\n    dxvk::mutex m_mutex;\n    std::vector<IDXVKUserDefinedAnnotation*> m_annotations;\n\n    // Provide our own event depth as we\n    // may have multiple annotators which could get out of sync.\n    int32_t m_eventDepth;\n\n  };\n\n  class D3D9UserDefinedAnnotation final : public IDXVKUserDefinedAnnotation {\n\n  public:\n\n    D3D9UserDefinedAnnotation(D3D9DeviceEx* device);\n    ~D3D9UserDefinedAnnotation();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    INT STDMETHODCALLTYPE BeginEvent(\n            D3DCOLOR                Color,\n            LPCWSTR                 Name);\n\n    INT STDMETHODCALLTYPE EndEvent();\n\n    void STDMETHODCALLTYPE SetMarker(\n            D3DCOLOR                Color,\n            LPCWSTR                 Name);\n\n    BOOL STDMETHODCALLTYPE GetStatus();\n\n  private:\n\n    D3D9DeviceEx* m_container;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_bridge.cpp",
    "content": "\n#include \"d3d9_device.h\"\n#include \"d3d9_interface.h\"\n#include \"d3d9_bridge.h\"\n#include \"d3d9_swapchain.h\"\n#include \"d3d9_surface.h\"\n#include \"d3d9_format.h\"\n\nnamespace dxvk {\n\n  DxvkD3D8Bridge::DxvkD3D8Bridge(D3D9DeviceEx* pDevice)\n    : m_device(pDevice) {\n  }\n\n  DxvkD3D8Bridge::~DxvkD3D8Bridge() {\n  }\n\n  ULONG STDMETHODCALLTYPE DxvkD3D8Bridge::AddRef() {\n    return m_device->AddRef();\n  }\n\n  ULONG STDMETHODCALLTYPE DxvkD3D8Bridge::Release() {\n    return m_device->Release();\n  }\n\n  HRESULT STDMETHODCALLTYPE DxvkD3D8Bridge::QueryInterface(\n          REFIID  riid,\n          void** ppvObject) {\n    return m_device->QueryInterface(riid, ppvObject);\n  }\n\n  HRESULT DxvkD3D8Bridge::UpdateTextureFromBuffer(\n        IDirect3DSurface9*  pDestSurface,\n        IDirect3DSurface9*  pSrcSurface,\n        const RECT*         pSrcRect,\n        const POINT*        pDestPoint) {\n    auto lock = m_device->LockDevice();\n\n    D3D9Surface* dst = static_cast<D3D9Surface*>(pDestSurface);\n    D3D9Surface* src = static_cast<D3D9Surface*>(pSrcSurface);\n\n    if (unlikely(dst == nullptr || src == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // CopyRects will not pass a null pSrcRect, but check anyway\n    if (unlikely(pSrcRect == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // validate dimensions to ensure we calculate a meaningful srcOffset & extent\n    if (unlikely(pSrcRect->left < 0\n              || pSrcRect->top  < 0\n              || pSrcRect->right  <= pSrcRect->left\n              || pSrcRect->bottom <= pSrcRect->top))\n      return D3DERR_INVALIDCALL;\n\n    // CopyRects will not pass a null pDestPoint, but check anyway\n    if (unlikely(pDestPoint == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // validate dimensions to ensure we caculate a meaningful dstOffset\n    if (unlikely(pDestPoint->x < 0\n              || pDestPoint->y < 0))\n      return D3DERR_INVALIDCALL;\n\n    D3D9CommonTexture* srcTextureInfo = src->GetCommonTexture();\n    D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture();\n\n    VkOffset3D srcOffset = { pSrcRect->left,\n                             pSrcRect->top,\n                             0u };\n\n    VkExtent3D extent = { uint32_t(pSrcRect->right - pSrcRect->left), uint32_t(pSrcRect->bottom - pSrcRect->top), 1 };\n\n    VkOffset3D dstOffset = { pDestPoint->x,\n                             pDestPoint->y,\n                             0u };\n\n    m_device->UpdateTextureFromBuffer(\n      srcTextureInfo, dstTextureInfo,\n      src->GetSubresource(), dst->GetSubresource(),\n      srcOffset, extent, dstOffset\n    );\n\n    dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true);\n\n    if (dstTextureInfo->IsAutomaticMip())\n      m_device->MarkTextureMipsDirty(dstTextureInfo);\n\n    return D3D_OK;\n  }\n\n  bool DxvkD3D8Bridge::IsSupportedSurfaceFormat(D3DFORMAT Format) {\n    auto mapping = m_device->LookupFormat(EnumerateFormat(Format));\n    return mapping.IsValid();\n  }\n\n  DxvkD3D8InterfaceBridge::DxvkD3D8InterfaceBridge(D3D9InterfaceEx* pObject)\n    : m_interface(pObject) {\n  }\n\n  DxvkD3D8InterfaceBridge::~DxvkD3D8InterfaceBridge() {\n  }\n\n  ULONG STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::AddRef() {\n    return m_interface->AddRef();\n  }\n\n  ULONG STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::Release() {\n    return m_interface->Release();\n  }\n\n  HRESULT STDMETHODCALLTYPE DxvkD3D8InterfaceBridge::QueryInterface(\n          REFIID  riid,\n          void** ppvObject) {\n    return m_interface->QueryInterface(riid, ppvObject);\n  }\n\n  void DxvkD3D8InterfaceBridge::EnableD3D8CompatibilityMode() {\n    m_interface->EnableD3D8CompatibilityMode();\n  }\n\n  const Config* DxvkD3D8InterfaceBridge::GetConfig() const {\n    return &m_interface->GetInstance()->config();\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_bridge.h",
    "content": "#pragma once\n\n#include <windows.h>\n#include \"../util/config/config.h\"\n\n/**\n * The D3D9 bridge allows D3D8 to access DXVK internals.\n * For Vulkan interop without needing DXVK internals, see d3d9_interop.h.\n *\n * NOTE: You must include \"d3d9_include.h\" or \"d3d8_include.h\" before this header.\n */\n\n/**\n * \\brief D3D9 device interface for D3D8 interop\n */\nMIDL_INTERFACE(\"D3D9D3D8-42A9-4C1E-AA97-BEEFCAFE2000\")\nIDxvkD3D8Bridge : public IUnknown {\n\n  // D3D8 keeps D3D9 objects contained in a namespace.\n  #ifdef DXVK_D3D9_NAMESPACE\n    using IDirect3DSurface9 = d3d9::IDirect3DSurface9;\n    using D3DFORMAT = d3d9::D3DFORMAT;\n  #endif\n\n  /**\n   * \\brief Updates a D3D9 surface from a D3D9 buffer\n   *\n   * \\param [in] pDestSurface Destination surface (typically in VRAM)\n   * \\param [in] pSrcSurface  Source surface (typically in system memory)\n   * \\param [in] pSrcRect     Source rectangle\n   * \\param [in] pDestPoint   Destination (top-left) point\n   */\n  virtual HRESULT UpdateTextureFromBuffer(\n      IDirect3DSurface9*        pDestSurface,\n      IDirect3DSurface9*        pSrcSurface,\n      const RECT*               pSrcRect,\n      const POINT*              pDestPoint) = 0;\n\n  /**\n   * \\brief Checks if a particular surface format is supported by D3D9\n   *\n   * \\param [in] Format D3DFORMAT value to be checked\n   */\n  virtual bool IsSupportedSurfaceFormat(D3DFORMAT Format) = 0;\n};\n\n/**\n * \\brief D3D9 instance interface for D3D8 interop\n */\nMIDL_INTERFACE(\"D3D9D3D8-A407-773E-18E9-CAFEBEEF3000\")\nIDxvkD3D8InterfaceBridge : public IUnknown {\n  /**\n   * \\brief Enforces D3D8-specific features and validations\n   */\n  virtual void EnableD3D8CompatibilityMode() = 0;\n\n  /**\n   * \\brief Retrieves the DXVK configuration\n   *\n   * \\returns The DXVK Config object\n   */\n  virtual const dxvk::Config* GetConfig() const = 0;\n};\n\n#ifndef _MSC_VER\n__CRT_UUID_DECL(IDxvkD3D8Bridge, 0xD3D9D3D8, 0x42A9, 0x4C1E, 0xAA, 0x97, 0xBE, 0xEF, 0xCA, 0xFE, 0x20, 0x00);\n__CRT_UUID_DECL(IDxvkD3D8InterfaceBridge, 0xD3D9D3D8, 0xA407, 0x773E, 0x18, 0xE9, 0xCA, 0xFE, 0xBE, 0xEF, 0x30, 0x00);\n#endif\n\nnamespace dxvk {\n\n  class D3D9DeviceEx;\n  class D3D9InterfaceEx;\n\n  class DxvkD3D8Bridge : public IDxvkD3D8Bridge {\n\n  public:\n\n    DxvkD3D8Bridge(D3D9DeviceEx* pDevice);\n\n    ~DxvkD3D8Bridge();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    ULONG STDMETHODCALLTYPE Release();\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void** ppvObject);\n\n    HRESULT UpdateTextureFromBuffer(\n        IDirect3DSurface9*        pDestSurface,\n        IDirect3DSurface9*        pSrcSurface,\n        const RECT*               pSrcRect,\n        const POINT*              pDestPoint);\n\n    bool IsSupportedSurfaceFormat(D3DFORMAT Format);\n\n  private:\n\n    D3D9DeviceEx* m_device;\n\n  };\n\n  class DxvkD3D8InterfaceBridge : public IDxvkD3D8InterfaceBridge {\n\n  public:\n\n    DxvkD3D8InterfaceBridge(D3D9InterfaceEx* pObject);\n\n    ~DxvkD3D8InterfaceBridge();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    ULONG STDMETHODCALLTYPE Release();\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void** ppvObject);\n\n    void EnableD3D8CompatibilityMode();\n\n    const Config* GetConfig() const;\n\n  protected:\n\n    D3D9InterfaceEx* m_interface;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_buffer.cpp",
    "content": "#include \"d3d9_buffer.h\"\n\nnamespace dxvk {\n\n  ////////////////////////\n  // D3D9VertexBuffer\n  ////////////////////////\n\n  D3D9VertexBuffer::D3D9VertexBuffer(\n          D3D9DeviceEx*      pDevice,\n    const D3D9_BUFFER_DESC*  pDesc,\n    const bool               Extended)\n  : D3D9VertexBufferBase(pDevice, pDesc, Extended) {\n\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9VertexBuffer::QueryInterface(\n          REFIID  riid,\n          void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DResource9)\n     || riid == __uuidof(IDirect3DVertexBuffer9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DVertexBuffer9), riid)) {\n      Logger::warn(\"D3D9VertexBuffer::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D9VertexBuffer::GetType() {\n    return D3DRTYPE_VERTEXBUFFER;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9VertexBuffer::GetDesc(\n          D3DVERTEXBUFFER_DESC* pDesc) {\n    if (pDesc == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    const D3D9_BUFFER_DESC* desc = m_buffer.Desc();\n\n    pDesc->Format = static_cast<D3DFORMAT>(desc->Format);\n    pDesc->Type   = desc->Type;\n    pDesc->Usage  = desc->Usage;\n    pDesc->Pool   = desc->Pool;\n    pDesc->Size   = desc->Size;\n    pDesc->FVF    = desc->FVF;\n\n    return D3D_OK;\n  }\n\n\n  //////////////////////\n  // D3D9IndexBuffer\n  //////////////////////\n\n\n  D3D9IndexBuffer::D3D9IndexBuffer(\n          D3D9DeviceEx*      pDevice,\n    const D3D9_BUFFER_DESC*  pDesc,\n    const bool               Extended)\n  : D3D9IndexBufferBase(pDevice, pDesc, Extended) {\n\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9IndexBuffer::QueryInterface(\n          REFIID  riid,\n          void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DResource9)\n     || riid == __uuidof(IDirect3DIndexBuffer9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DIndexBuffer9), riid)) {\n      Logger::warn(\"D3D9IndexBuffer::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D9IndexBuffer::GetType() {\n    return D3DRTYPE_INDEXBUFFER;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9IndexBuffer::GetDesc(\n          D3DINDEXBUFFER_DESC* pDesc) {\n    if (pDesc == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    const D3D9_BUFFER_DESC* desc = m_buffer.Desc();\n\n    pDesc->Format = static_cast<D3DFORMAT>(desc->Format);\n    pDesc->Type   = desc->Type;\n    pDesc->Usage  = desc->Usage;\n    pDesc->Pool   = desc->Pool;\n    pDesc->Size   = desc->Size;\n\n    return D3D_OK;\n  }\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_buffer.h",
    "content": "#pragma once\n\n#include \"d3d9_resource.h\"\n\n#include \"d3d9_common_buffer.h\"\n\nnamespace dxvk {\n\n  template <typename... Type>\n  class D3D9Buffer : public D3D9Resource<Type...> {\n\n  public:\n\n    D3D9Buffer(\n            D3D9DeviceEx*      pDevice,\n      const D3D9_BUFFER_DESC*  pDesc,\n      const bool               Extended)\n    : D3D9Resource<Type...> (pDevice, pDesc->Pool, Extended ),\n      m_buffer              (pDevice, pDesc) {\n\n    }\n\n    HRESULT STDMETHODCALLTYPE Lock(\n            UINT   OffsetToLock,\n            UINT   SizeToLock,\n            void** ppbData,\n            DWORD  Flags) final {\n      return m_buffer.Lock(\n        OffsetToLock,\n        SizeToLock,\n        ppbData,\n        Flags);\n    }\n\n    HRESULT STDMETHODCALLTYPE Unlock() final {\n      return m_buffer.Unlock();\n    }\n\n    void STDMETHODCALLTYPE PreLoad() final {\n      m_buffer.PreLoad();\n    }\n\n    D3D9CommonBuffer* GetCommonBuffer() {\n      return &m_buffer;\n    }\n\n  protected:\n\n    D3D9CommonBuffer m_buffer;\n\n  };\n\n\n  using D3D9VertexBufferBase = D3D9Buffer<IDirect3DVertexBuffer9>;\n  class D3D9VertexBuffer final : public D3D9VertexBufferBase {\n\n  public:\n\n    D3D9VertexBuffer(\n            D3D9DeviceEx*      pDevice,\n      const D3D9_BUFFER_DESC*  pDesc,\n      const bool               Extended);\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void** ppvObject);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType();\n\n    HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc);\n\n  };\n\n  using D3D9IndexBufferBase = D3D9Buffer<IDirect3DIndexBuffer9>;\n  class D3D9IndexBuffer final : public D3D9IndexBufferBase {\n\n  public:\n\n    D3D9IndexBuffer(\n            D3D9DeviceEx*      pDevice,\n      const D3D9_BUFFER_DESC*  pDesc,\n      const bool               Extended);\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void** ppvObject);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType();\n\n    HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc);\n\n  };\n\n  template <typename T>\n  inline D3D9CommonBuffer* GetCommonBuffer(const T& pResource) {\n    return pResource != nullptr ? pResource->GetCommonBuffer() : nullptr;\n  }\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_caps.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n\nnamespace dxvk::caps {\n\n  constexpr uint32_t MaxClipPlanes                = 6;\n  constexpr uint32_t MaxSamplers                  = 16;\n  constexpr uint32_t MaxStreams                   = 16;\n  constexpr uint32_t MaxSimultaneousTextures      = 8;\n  constexpr uint32_t MaxTextureBlendStages        = MaxSimultaneousTextures;\n  constexpr uint32_t MaxSimultaneousRenderTargets = D3D_MAX_SIMULTANEOUS_RENDERTARGETS;\n\n  constexpr uint32_t MaxFloatConstantsVS          = 256;\n  constexpr uint32_t MaxSM1FloatConstantsPS       = 8;\n  constexpr uint32_t MaxSM2FloatConstantsPS       = 32;\n  constexpr uint32_t MaxSM3FloatConstantsPS       = 224;\n  constexpr uint32_t MaxOtherConstants            = 16;\n  constexpr uint32_t MaxFloatConstantsSoftware    = 8192;\n  constexpr uint32_t MaxOtherConstantsSoftware    = 2048;\n\n  constexpr uint32_t InputRegisterCount           = 16;\n\n  constexpr uint32_t MaxTextureDimension          = 16384;\n  constexpr uint32_t MaxMipLevels                 = 15;\n  constexpr uint32_t MaxSubresources              = 15 * 6;\n\n  constexpr uint32_t MaxTransforms                = 10 + 256;\n\n  constexpr uint32_t TextureStageCount            = MaxSimultaneousTextures;\n\n  constexpr uint32_t MaxEnabledLights             = 8;\n\n  constexpr uint32_t MaxTexturesVS                = 4;\n  constexpr uint32_t MaxTexturesPS                = 16;\n  constexpr uint32_t MaxTextures                  = MaxTexturesVS + MaxTexturesPS + 1; // 1 additional texture for the dmap sampler\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_common_buffer.cpp",
    "content": "#include \"d3d9_common_buffer.h\"\n\n#include \"d3d9_device.h\"\n#include \"d3d9_util.h\"\n\nnamespace dxvk {\n\n  D3D9CommonBuffer::D3D9CommonBuffer(\n          D3D9DeviceEx*      pDevice,\n    const D3D9_BUFFER_DESC*  pDesc)\n    : m_parent ( pDevice ), m_desc ( *pDesc ),\n      m_mapMode(DetermineMapMode(pDevice->GetOptions())) {\n    m_buffer = CreateBuffer();\n    if (m_mapMode == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER)\n      m_stagingBuffer = CreateStagingBuffer();\n\n    m_allocation = GetMapBuffer()->storage();\n\n    if (m_desc.Pool != D3DPOOL_DEFAULT)\n      m_dirtyRange = D3D9Range(0, m_desc.Size);\n  }\n\n  D3D9CommonBuffer::~D3D9CommonBuffer() {\n    if (m_desc.Pool == D3DPOOL_DEFAULT)\n      m_parent->DecrementLosableCounter();\n  }\n\n\n  HRESULT D3D9CommonBuffer::Lock(\n          UINT   OffsetToLock,\n          UINT   SizeToLock,\n          void** ppbData,\n          DWORD  Flags) {\n    return m_parent->LockBuffer(\n      this,\n      OffsetToLock,\n      SizeToLock,\n      ppbData,\n      Flags);\n  }\n\n\n  HRESULT D3D9CommonBuffer::Unlock() {\n    return m_parent->UnlockBuffer(this);\n  }\n\n\n  HRESULT D3D9CommonBuffer::ValidateBufferProperties(const D3D9_BUFFER_DESC* pDesc, const bool IsExtended) {\n    if (unlikely(pDesc->Size == 0))\n      return D3DERR_INVALIDCALL;\n\n    // Neither vertex nor index buffers can be created in D3DPOOL_SCRATCH\n    // or in D3DPOOL_MANAGED with D3DUSAGE_DYNAMIC. On extended devices,\n    // D3DPOOL_MANAGED can not be used at all, regardless of usage flags.\n    if (unlikely(pDesc->Pool == D3DPOOL_SCRATCH\n             || (pDesc->Pool == D3DPOOL_MANAGED && (IsExtended ||\n                                                    pDesc->Usage & D3DUSAGE_DYNAMIC))))\n      return D3DERR_INVALIDCALL;\n\n    // D3DUSAGE_AUTOGENMIPMAP, D3DUSAGE_DEPTHSTENCIL and D3DUSAGE_RENDERTARGET\n    // are not permitted on index or vertex buffers.\n    if (unlikely((pDesc->Usage & D3DUSAGE_AUTOGENMIPMAP)\n              || (pDesc->Usage & D3DUSAGE_DEPTHSTENCIL)\n              || (pDesc->Usage & D3DUSAGE_RENDERTARGET)))\n      return D3DERR_INVALIDCALL;\n\n    return D3D_OK;\n  }\n\n\n  void D3D9CommonBuffer::PreLoad() {\n    if (IsPoolManaged(m_desc.Pool)) {\n      auto lock = m_parent->LockDevice();\n\n      if (NeedsUpload())\n        m_parent->FlushBuffer(this);\n    }\n  }\n\n  \n  D3D9_COMMON_BUFFER_MAP_MODE D3D9CommonBuffer::DetermineMapMode(const D3D9Options* options) const {\n    if (m_desc.Pool != D3DPOOL_DEFAULT)\n      return D3D9_COMMON_BUFFER_MAP_MODE_BUFFER;\n\n    // CSGO keeps vertex buffers locked across multiple frames and writes to it. It uses them for drawing without unlocking first.\n    // Tests show that D3D9 DEFAULT + USAGE_DYNAMIC behaves like a directly mapped buffer even when unlocked.\n    // DEFAULT + WRITEONLY does not behave like a directly mapped buffer EXCEPT if its locked at the moment.\n    // TODO: Work around that by adding option that maps WRITEONLY directly or disables the staging buffer for buffer uploads.\n\n    if (!(m_desc.Usage & D3DUSAGE_DYNAMIC))\n      return D3D9_COMMON_BUFFER_MAP_MODE_BUFFER;\n\n    if (!options->allowDirectBufferMapping)\n      return D3D9_COMMON_BUFFER_MAP_MODE_BUFFER;\n\n    return D3D9_COMMON_BUFFER_MAP_MODE_DIRECT;\n  }\n\n\n  Rc<DxvkBuffer> D3D9CommonBuffer::CreateBuffer() const {\n    DxvkBufferCreateInfo  info;\n    info.size   = m_desc.Size;\n    info.usage  = 0;\n    info.stages = 0;\n    info.access = 0;\n\n    VkMemoryPropertyFlags memoryFlags = 0;\n\n    if (m_desc.Type == D3DRTYPE_VERTEXBUFFER) {\n      info.usage  |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;\n      info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;\n      info.access |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;\n\n      if (m_parent->SupportsSWVP()) {\n        info.usage  |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n        info.stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;\n        info.access |= VK_ACCESS_SHADER_WRITE_BIT;\n      }\n    }\n    else if (m_desc.Type == D3DRTYPE_INDEXBUFFER) {\n      info.usage  |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;\n      info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;\n      info.access |= VK_ACCESS_INDEX_READ_BIT;\n    }\n\n    if (m_mapMode == D3D9_COMMON_BUFFER_MAP_MODE_DIRECT) {\n      info.stages |= VK_PIPELINE_STAGE_HOST_BIT;\n      info.access |= VK_ACCESS_HOST_WRITE_BIT;\n\n      memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n                  |  VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;\n\n      if ((m_desc.Usage & D3DUSAGE_WRITEONLY) == 0\n        || DoPerDrawUpload()\n        || m_parent->CanOnlySWVP()\n        || m_parent->GetOptions()->cachedWriteOnlyBuffers) {\n        // Never use uncached memory on devices that support SWVP because we might end up reading from it.\n\n        info.access |= VK_ACCESS_HOST_READ_BIT;\n        memoryFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;\n      } else {\n        memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n      }\n    }\n    else {\n      info.stages |= VK_PIPELINE_STAGE_TRANSFER_BIT;\n      info.usage  |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;\n      info.access |= VK_ACCESS_TRANSFER_WRITE_BIT;\n\n      memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n    }\n\n    return m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags);\n  }\n\n\n  Rc<DxvkBuffer> D3D9CommonBuffer::CreateStagingBuffer() const {\n    DxvkBufferCreateInfo  info;\n    info.size   = m_desc.Size;\n    info.stages = VK_PIPELINE_STAGE_HOST_BIT\n                | VK_PIPELINE_STAGE_TRANSFER_BIT;\n\n    info.usage  = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n\n    info.access = VK_ACCESS_HOST_WRITE_BIT\n                | VK_ACCESS_TRANSFER_READ_BIT;\n\n    if (!(m_desc.Usage & D3DUSAGE_WRITEONLY))\n      info.access |= VK_ACCESS_HOST_READ_BIT;\n\n    VkMemoryPropertyFlags memoryFlags = \n      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n    | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT\n    | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;\n\n    return m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags);\n  }\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_common_buffer.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n#include \"../dxvk/dxvk_cs.h\"\n\n#include \"d3d9_device_child.h\"\n#include \"d3d9_format.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Buffer map mode\n   */\n  enum D3D9_COMMON_BUFFER_MAP_MODE {\n    D3D9_COMMON_BUFFER_MAP_MODE_BUFFER,\n    D3D9_COMMON_BUFFER_MAP_MODE_DIRECT\n  };\n\n  /**\n   * \\brief Common buffer descriptor\n   */\n  struct D3D9_BUFFER_DESC {\n    D3DRESOURCETYPE Type;\n    UINT            Size;\n    DWORD           Usage;\n    D3D9Format      Format;\n    D3DPOOL         Pool;\n    DWORD           FVF;\n  };\n\n  /**\n   * \\brief The type of buffer you want to use\n   */\n  enum D3D9_COMMON_BUFFER_TYPE {\n    D3D9_COMMON_BUFFER_TYPE_MAPPING,\n    D3D9_COMMON_BUFFER_TYPE_STAGING,\n    D3D9_COMMON_BUFFER_TYPE_REAL\n  };\n\n  struct D3D9Range {\n    D3D9Range() { Clear(); }\n\n    D3D9Range(uint32_t min, uint32_t max)\n    : min(min),\n      max(max) {\n\n    }\n\n    inline bool IsDegenerate() const { return min == max; }\n\n    inline void Conjoin(D3D9Range range) {\n      if (IsDegenerate())\n        *this = range;\n      else {\n        min = std::min(range.min, min);\n        max = std::max(range.max, max);\n      }\n    }\n\n    inline bool Overlaps(D3D9Range range) {\n      if (IsDegenerate())\n        return false;\n\n      return range.max > min && range.min < max;\n    }\n\n    inline void Clear() { min = 0; max = 0; }\n\n    uint32_t min = 0;\n    uint32_t max = 0;\n  };\n\n  class D3D9CommonBuffer {\n    static constexpr VkDeviceSize BufferSliceAlignment = 64;\n  public:\n\n    D3D9CommonBuffer(\n            D3D9DeviceEx*      pDevice,\n      const D3D9_BUFFER_DESC*  pDesc);\n\n    ~D3D9CommonBuffer();\n\n    HRESULT Lock(\n            UINT   OffsetToLock,\n            UINT   SizeToLock,\n            void** ppbData,\n            DWORD  Flags);\n\n    HRESULT Unlock();\n\n    /**\n    * \\brief Determine the mapping mode of the buffer, (ie. direct mapping or backed)\n    */\n    D3D9_COMMON_BUFFER_MAP_MODE DetermineMapMode(const D3D9Options* options) const;\n\n    /**\n    * \\brief Get the mapping mode of the buffer, (ie. direct mapping or backed)\n    */\n    inline D3D9_COMMON_BUFFER_MAP_MODE GetMapMode() const {\n      return m_mapMode;\n    }\n\n    /**\n    * \\brief Abstraction for getting a type of buffer (mapping/staging/the real buffer) across mapping modes.\n    */\n    template <D3D9_COMMON_BUFFER_TYPE Type>\n    inline const Rc<DxvkBuffer>& GetBuffer() const {\n      if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_MAPPING)\n        return GetMapBuffer();\n      else if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_STAGING)\n        return GetStagingBuffer();\n      else //if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_REAL)\n        return GetRealBuffer();\n    }\n\n    template <D3D9_COMMON_BUFFER_TYPE Type>\n    inline DxvkBufferSlice GetBufferSlice() const {\n      return GetBufferSlice<Type>(0, m_desc.Size);\n    }\n\n    template <D3D9_COMMON_BUFFER_TYPE Type>\n    inline DxvkBufferSlice GetBufferSlice(VkDeviceSize offset) const {\n      return GetBufferSlice<Type>(offset, m_desc.Size - offset);\n    }\n\n    template <D3D9_COMMON_BUFFER_TYPE Type>\n    inline DxvkBufferSlice GetBufferSlice(VkDeviceSize offset, VkDeviceSize length) const {\n     if (likely(length && offset < m_desc.Size)) {\n        return DxvkBufferSlice(GetBuffer<Type>(), offset,\n          std::min<VkDeviceSize>(m_desc.Size - offset, length));\n     }\n\n      return DxvkBufferSlice();\n    }\n\n    inline Rc<DxvkResourceAllocation> DiscardMapSlice() {\n      m_allocation = GetMapBuffer()->allocateStorage();\n      return m_allocation;\n    }\n\n    inline Rc<DxvkResourceAllocation> GetMappedSlice() const {\n      return m_allocation;\n    }\n\n    inline DWORD GetMapFlags() const      { return m_mapFlags; }\n    inline void SetMapFlags(DWORD Flags)  { m_mapFlags = Flags; }\n\n    inline const D3D9_BUFFER_DESC* Desc() const { return &m_desc; }\n\n    static HRESULT ValidateBufferProperties(const D3D9_BUFFER_DESC* pDesc, const bool IsExtended);\n\n    /**\n     * \\brief The range of the buffer that was changed using Lock calls\n     */\n    inline D3D9Range& DirtyRange()  { return m_dirtyRange; }\n\n    /**\n    * \\brief Whether or not the buffer was written to by the GPU (in IDirect3DDevice9::ProcessVertices)\n    */\n    inline bool NeedsReadback() const     { return m_needsReadback; }\n\n    /**\n    * \\brief Sets whether or not the buffer was written to by the GPU\n    */\n    inline void SetNeedsReadback(bool state) { m_needsReadback = state; }\n\n    inline uint32_t IncrementLockCount() { return ++m_lockCount; }\n    inline uint32_t DecrementLockCount() {\n      if (m_lockCount == 0)\n        return 0;\n\n      return --m_lockCount;\n    }\n    inline uint32_t GetLockCount() const { return m_lockCount; }\n\n    /**\n     * \\brief Whether or not the staging buffer needs to be copied to the actual buffer\n     */\n    inline bool NeedsUpload() const { return m_desc.Pool != D3DPOOL_DEFAULT && !m_dirtyRange.IsDegenerate(); }\n\n    void PreLoad();\n\n    bool HasSequenceNumber() const {\n      return m_mapMode != D3D9_COMMON_BUFFER_MAP_MODE_DIRECT;\n    }\n\n     /**\n     * \\brief Tracks sequence number\n     *\n     * Stores which CS chunk the resource was last used on.\n     * \\param [in] Seq Sequence number\n     */\n    void TrackMappingBufferSequenceNumber(uint64_t Seq) {\n      m_seq = Seq;\n    }\n\n\n    /**\n     * \\brief Queries sequence number\n     *\n     * Returns which CS chunk the resource was last used on.\n     * \\returns Sequence number\n     */\n    uint64_t GetMappingBufferSequenceNumber() const {\n      return HasSequenceNumber() ? m_seq\n        : DxvkCsThread::SynchronizeAll;\n    }\n\n    bool DoPerDrawUpload() const {\n      return m_desc.Pool == D3DPOOL_SYSTEMMEM && (m_desc.Usage & D3DUSAGE_DYNAMIC) != 0;\n    }\n\n  private:\n\n    Rc<DxvkBuffer> CreateBuffer() const;\n    Rc<DxvkBuffer> CreateStagingBuffer() const;\n\n    inline const Rc<DxvkBuffer>& GetMapBuffer() const {\n      return m_stagingBuffer != nullptr ? m_stagingBuffer : m_buffer;\n    }\n\n    inline const Rc<DxvkBuffer>& GetStagingBuffer() const {\n      return m_stagingBuffer;\n    }\n\n    inline const Rc<DxvkBuffer>& GetRealBuffer() const {\n      return m_buffer;\n    }\n\n    D3D9DeviceEx*               m_parent;\n    const D3D9_BUFFER_DESC      m_desc;\n    DWORD                       m_mapFlags = 0;\n    bool                        m_needsReadback = false;\n    D3D9_COMMON_BUFFER_MAP_MODE m_mapMode;\n\n    Rc<DxvkBuffer>              m_buffer;\n    Rc<DxvkBuffer>              m_stagingBuffer;\n\n    Rc<DxvkResourceAllocation>  m_allocation;\n\n    D3D9Range                   m_dirtyRange;\n\n    uint32_t                    m_lockCount = 0;\n\n    uint64_t                    m_seq = 0ull;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_common_texture.cpp",
    "content": "#include \"d3d9_common_texture.h\"\n\n#include \"d3d9_util.h\"\n#include \"d3d9_device.h\"\n\n#include \"../util/util_shared_res.h\"\n#include \"../util/util_win32_compat.h\"\n\n#include <algorithm>\n\nnamespace dxvk {\n\n  D3D9CommonTexture::D3D9CommonTexture(\n          D3D9DeviceEx*             pDevice,\n          IUnknown*                 pInterface,\n    const D3D9_COMMON_TEXTURE_DESC* pDesc,\n          D3DRESOURCETYPE           ResourceType,\n          HANDLE*                   pSharedHandle)\n    : m_device(pDevice), m_desc(*pDesc), m_type(ResourceType), m_d3d9Interop(pInterface, this) {\n    if (m_desc.Format == D3D9Format::Unknown)\n      m_desc.Format = (m_desc.Usage & D3DUSAGE_DEPTHSTENCIL)\n                    ? D3D9Format::D24X8\n                    : D3D9Format::X8R8G8B8;\n\n    m_exposedMipLevels = m_desc.MipLevels;\n\n    if (m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP)\n      m_exposedMipLevels = 1;\n\n    for (uint32_t i = 0; i < m_dirtyBoxes.size(); i++) {\n      AddDirtyBox(nullptr, i);\n    }\n\n    if (IsPoolManaged(m_desc.Pool)) {\n      SetAllNeedUpload();\n    }\n\n    m_mapping = pDevice->LookupFormat(m_desc.Format);\n\n    m_mapMode        = DetermineMapMode();\n    m_shadow         = DetermineShadowState();\n    m_upgradedToD32f = ConvertFormatUnfixed(m_desc.Format).FormatColor != m_mapping.FormatColor &&\n                       (m_mapping.FormatColor == VK_FORMAT_D32_SFLOAT_S8_UINT || m_mapping.FormatColor == VK_FORMAT_D32_SFLOAT);\n    m_supportsFetch4 = DetermineFetch4Compatibility();\n\n    if (TextureUsesImage(&m_desc)) {\n      m_image = CreatePrimaryImage(ResourceType, pSharedHandle);\n\n      if (unlikely(m_image == nullptr && (m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP))) {\n        // AUTOGENMIPMAP is supposed to be treated like a hint according to the docs.\n        // So if creating an image with it fails, create one without it.\n        m_desc.Usage &= ~D3DUSAGE_AUTOGENMIPMAP;\n        m_desc.MipLevels = 1;\n        m_image = CreatePrimaryImage(ResourceType, pSharedHandle);\n      }\n\n      if (unlikely(m_image == nullptr)) {\n        throw DxvkError(str::format(\n          \"D3D9: Cannot create texture:\",\n          \"\\n  Type:    0x\", std::hex, ResourceType, std::dec,\n          \"\\n  Format:  \", m_desc.Format,\n          \"\\n  Extent:  \", m_desc.Width,\n                      \"x\", m_desc.Height,\n                      \"x\", m_desc.Depth,\n          \"\\n  Samples: \", m_desc.MultiSample,\n          \"\\n  Layers:  \", m_desc.ArraySize,\n          \"\\n  Levels:  \", m_desc.MipLevels,\n          \"\\n  Usage:   0x\", std::hex, m_desc.Usage, std::dec,\n          \"\\n  Pool:    0x\", std::hex, m_desc.Pool, std::dec));\n      }\n\n      if (pSharedHandle && *pSharedHandle == nullptr) {\n        *pSharedHandle = m_image->sharedHandle();\n        ExportImageInfo();\n      }\n\n      if ((m_image->info().usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0)\n        CreateSampleView(0);\n\n      if (!IsManaged()) {\n        m_size = m_image->getMemoryInfo().size;\n        if (!m_device->ChangeReportedMemory(-m_size))\n          throw DxvkError(\"D3D9: Reporting out of memory from tracking.\");\n      }\n    }\n\n    for (uint32_t i = 0; i < CountSubresources(); i++) {\n      m_memoryOffset[i] = m_totalSize;\n      m_totalSize += GetMipSize(i);\n    }\n\n    // Add a tiny amount of padding at the end because some games read/write OOB\n    // Medieval: Total War 1 for example seems to have an off-by-one bug in copying data for a managed texture.\n    uint32_t paddedSize = align(m_totalSize + 1, CACHE_LINE_SIZE);\n\n    // Initialization is handled by D3D9Initializer\n    if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)\n      m_data = m_device->GetAllocator()->Alloc(paddedSize);\n    else if (m_mapMode != D3D9_COMMON_TEXTURE_MAP_MODE_NONE && m_desc.Pool != D3DPOOL_DEFAULT)\n      CreateBuffer(false, paddedSize);\n  }\n\n\n  D3D9CommonTexture::~D3D9CommonTexture() {\n    if (m_size != 0)\n      m_device->ChangeReportedMemory(m_size);\n\n    m_device->RemoveMappedTexture(this);\n\n    if (m_desc.Pool == D3DPOOL_DEFAULT)\n      m_device->DecrementLosableCounter();\n  }\n\n\n  VkImageSubresource D3D9CommonTexture::GetSubresourceFromIndex(\n          VkImageAspectFlags    Aspect,\n          UINT                  Subresource) const {\n    VkImageSubresource result;\n    result.aspectMask     = Aspect;\n    result.mipLevel       = Subresource % m_desc.MipLevels;\n    result.arrayLayer     = Subresource / m_desc.MipLevels;\n    return result;\n  }\n  \n\n  HRESULT D3D9CommonTexture::NormalizeTextureProperties(\n          D3D9DeviceEx*             pDevice,\n          D3DRESOURCETYPE           ResourceType,\n          D3D9_COMMON_TEXTURE_DESC* pDesc) {\n    auto* options = pDevice->GetOptions();\n\n    //////////////////////\n    // Mapping Validation\n    auto mapping = pDevice->LookupFormat(pDesc->Format);\n\n    // Handle DisableA8RT hack for The Sims 2\n    if (pDesc->Format == D3D9Format::A8       &&\n       (pDesc->Usage & D3DUSAGE_RENDERTARGET) &&\n        options->disableA8RT)\n      return D3DERR_INVALIDCALL;\n\n    // Cube textures with depth formats are not supported on any native\n    // driver, and allowing them triggers a broken code path in Gothic 3.\n    if (ResourceType == D3DRTYPE_CUBETEXTURE && mapping.Aspect != VK_IMAGE_ASPECT_COLOR_BIT)\n      return D3DERR_INVALIDCALL;\n\n    // If the mapping is invalid then lets return invalid\n    // Some edge cases:\n    // NULL format does not map to anything, but should succeed\n    // SCRATCH textures can still be made if the device does not support\n    // the format at all.\n\n    if (!mapping.IsValid() && pDesc->Format != D3D9Format::NULL_FORMAT) {\n      auto info = pDevice->UnsupportedFormatInfo(pDesc->Format);\n\n      if (pDesc->Pool != D3DPOOL_SCRATCH || info->elementSize == 0)\n        return D3DERR_INVALIDCALL;\n    }\n\n    ///////////////////\n    // Desc Validation\n\n    // Resources can't be created in D3DPOOL_MANAGED\n    // when using extended devices. Note that the D3DPOOL\n    // value of 6 (D3DPOOL_MANAGED_EX) can be used.\n    if (pDevice->IsExtended() && pDesc->Pool == D3DPOOL_MANAGED)\n      return D3DERR_INVALIDCALL;\n\n    if (pDesc->Width == 0 || pDesc->Height == 0 || pDesc->Depth == 0)\n      return D3DERR_INVALIDCALL;\n\n    // Native drivers won't allow the creation of DXT format\n    // textures that aren't aligned to block dimensions.\n    if (IsDXTFormat(pDesc->Format)) {\n      D3D9_FORMAT_BLOCK_SIZE blockSize = GetFormatAlignedBlockSize(pDesc->Format);\n\n      if ((blockSize.Width  && (pDesc->Width  & (blockSize.Width  - 1)))\n       || (blockSize.Height && (pDesc->Height & (blockSize.Height - 1))))\n        return D3DERR_INVALIDCALL;\n    }\n\n    // Sample counts need to be valid and supported\n    VkSampleCountFlagBits sampleCount;\n    HRESULT hr = DecodeMultiSampleType(pDesc->MultiSample, pDesc->MultisampleQuality, &sampleCount);\n    if (FAILED(hr))\n      return hr;\n\n    // We never create an image for SCRATCH, SYSTEMMEM or NULL FMT D3D9 textures, so there's no point in checking\n    // whether the GPU supports the specified sampled count for those.\n    if (ResourceType == D3DRTYPE_SURFACE && TextureUsesImage(pDesc)) {\n      D3DMULTISAMPLE_TYPE d3dSampleCount = D3DMULTISAMPLE_TYPE(sampleCount);\n      // VK_SAMPLE_COUNT_1_BIT = 1 but the D3D9 equivalent we need is D3DMULTISAMPLE_NONE = 0\n      if (d3dSampleCount == D3DMULTISAMPLE_NONMASKABLE)\n        d3dSampleCount = D3DMULTISAMPLE_NONE;\n\n      hr = pDevice->GetAdapter()->CheckDeviceMultiSampleType(\n        D3DDEVTYPE_HAL,\n        pDesc->Format,\n        false,\n        d3dSampleCount,\n        nullptr\n      );\n      if (FAILED(hr))\n        return hr;\n    } else if (sampleCount != VK_SAMPLE_COUNT_1_BIT) {\n      // D3D9 only supports MSAA for surfaces\n      return D3DERR_INVALIDCALL;\n    }\n\n    // Using MANAGED pool with DYNAMIC usage is illegal\n    if (IsPoolManaged(pDesc->Pool) && (pDesc->Usage & D3DUSAGE_DYNAMIC))\n      return D3DERR_INVALIDCALL;\n\n    // D3DUSAGE_WRITEONLY doesn't apply to textures.\n    if (pDesc->Usage & D3DUSAGE_WRITEONLY)\n      return D3DERR_INVALIDCALL;\n\n    constexpr DWORD usageRTOrDS = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL;\n\n    // RENDERTARGET and DEPTHSTENCIL must be default pool\n    if (pDesc->Pool != D3DPOOL_DEFAULT && (pDesc->Usage & usageRTOrDS))\n      return D3DERR_INVALIDCALL;\n\n    // RENDERTARGET and DEPTHSTENCIL in D3DPOOL_DEFAULT\n    // can not also have DYNAMIC usage\n    if (pDesc->Pool == D3DPOOL_DEFAULT &&\n        (pDesc->Usage & usageRTOrDS) &&\n        (pDesc->Usage & D3DUSAGE_DYNAMIC))\n      return D3DERR_INVALIDCALL;\n\n    const bool isPlainSurface = ResourceType == D3DRTYPE_SURFACE && !(pDesc->Usage & usageRTOrDS);\n    const bool isDepthStencilFormat = IsDepthStencilFormat(pDesc->Format);\n\n    // With the exception of image surfaces (d3d8)\n    // or plain offscreen surfaces (d3d9), depth stencil\n    // formats can only be used in D3DPOOL_DEFAULT\n    if (!isPlainSurface && pDesc->Pool != D3DPOOL_DEFAULT && isDepthStencilFormat)\n      return D3DERR_INVALIDCALL;\n\n    // Depth stencil formats can not have RENDERTARGET\n    // usage, and nothing except depth stencil formats\n    // can have DEPTHSTENCIL usage\n    if (( isDepthStencilFormat && (pDesc->Usage & D3DUSAGE_RENDERTARGET)) ||\n        (!isDepthStencilFormat && (pDesc->Usage & D3DUSAGE_DEPTHSTENCIL)))\n      return D3DERR_INVALIDCALL;\n\n    // Volume textures can not be used as render targets\n    if (ResourceType == D3DRTYPE_VOLUMETEXTURE &&\n        (pDesc->Usage & D3DUSAGE_RENDERTARGET))\n      return D3DERR_INVALIDCALL;\n\n    // Volume textures in D3DPOOL_SCRATCH must not have DYNAMIC usage\n    if (ResourceType == D3DRTYPE_VOLUMETEXTURE\n      && pDesc->Pool == D3DPOOL_SCRATCH\n      && (pDesc->Usage & D3DUSAGE_DYNAMIC))\n      return D3DERR_INVALIDCALL;\n\n    // ATI2 can not be used for render targets, or for\n    // plain surfaces outside of D3DPOOL_SCRATCH in D3D9Ex\n    if (pDesc->Format == D3D9Format::ATI2\n     && (pDesc->Usage & D3DUSAGE_RENDERTARGET ||\n        (pDevice->IsExtended() && isPlainSurface && pDesc->Pool != D3DPOOL_SCRATCH)))\n      return D3DERR_INVALIDCALL;\n\n    // Auto-Mipgen is only valid on textures (for obvious reasons)\n    if ((pDesc->Usage & D3DUSAGE_AUTOGENMIPMAP) && ResourceType == D3DRTYPE_SURFACE)\n      return D3DERR_INVALIDCALL;\n\n    // Use the maximum possible mip level count if the supplied\n    // mip level count is either unspecified (0) or invalid\n    const uint32_t maxMipLevelCount = pDesc->MultiSample <= D3DMULTISAMPLE_NONMASKABLE\n      ? util::computeMipLevelCount({ pDesc->Width, pDesc->Height, pDesc->Depth })\n      : 1u;\n\n    if (pDesc->Usage & D3DUSAGE_AUTOGENMIPMAP)\n      pDesc->MipLevels = 0;\n    \n    if (pDesc->MipLevels == 0 || pDesc->MipLevels > maxMipLevelCount)\n      pDesc->MipLevels = maxMipLevelCount;\n\n    if (unlikely(pDesc->Discard)) {\n      if (!isDepthStencilFormat)\n        return D3DERR_INVALIDCALL;\n\n      if (pDesc->Format == D3D9Format::D32_LOCKABLE\n       || pDesc->Format == D3D9Format::D32F_LOCKABLE\n       || pDesc->Format == D3D9Format::D16_LOCKABLE\n       || pDesc->Format == D3D9Format::S8_LOCKABLE)\n        return D3DERR_INVALIDCALL;\n    }\n\n    // A multisample RT/DS must not be lockable\n    if (pDesc->IsLockable && sampleCount > VK_SAMPLE_COUNT_1_BIT)\n        return D3DERR_INVALIDCALL;\n\n    return D3D_OK;\n  }\n\n\n  void* D3D9CommonTexture::GetData(UINT Subresource) {\n    if (unlikely(m_buffer != nullptr))\n      return m_buffer->mapPtr(m_memoryOffset[Subresource]);\n\n    m_data.Map();\n    uint8_t* ptr = reinterpret_cast<uint8_t*>(m_data.Ptr());\n    if (ptr == nullptr)\n      return nullptr;\n    ptr += m_memoryOffset[Subresource];\n    return ptr;\n  }\n\n\n  void D3D9CommonTexture::CreateBuffer(bool Initialize, uint32_t Size) {\n    if (likely(m_buffer != nullptr))\n      return;\n\n    DxvkBufferCreateInfo info;\n    info.size   = Size;\n    info.usage  = VK_BUFFER_USAGE_TRANSFER_SRC_BIT\n                | VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n    info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                | VK_PIPELINE_STAGE_HOST_BIT;\n    info.access = VK_ACCESS_TRANSFER_READ_BIT\n                | VK_ACCESS_TRANSFER_WRITE_BIT\n                | VK_ACCESS_HOST_WRITE_BIT\n                | VK_ACCESS_HOST_READ_BIT;\n\n    if (m_mapping.ConversionFormatInfo.FormatType != D3D9ConversionFormat_None) {\n      info.usage  |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n      info.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;\n    }\n\n    VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n                                  | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT\n                                  | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;\n\n    m_buffer = m_device->GetDXVKDevice()->createBuffer(info, memType);\n\n    if (Initialize) {\n      if (m_data) {\n        m_data.Map();\n        std::memcpy(m_buffer->mapPtr(0), m_data.Ptr(), m_totalSize);\n      } else {\n        std::memset(m_buffer->mapPtr(0), 0, m_totalSize);\n      }\n    }\n    m_data = {};\n  }\n\n\n  VkDeviceSize D3D9CommonTexture::GetMipSize(UINT Subresource) const {\n    const UINT MipLevel = Subresource % m_desc.MipLevels;\n\n    const DxvkFormatInfo* formatInfo = m_mapping.FormatColor != VK_FORMAT_UNDEFINED\n      ? lookupFormatInfo(m_mapping.FormatColor)\n      : m_device->UnsupportedFormatInfo(m_desc.Format);\n\n    const VkExtent3D mipExtent = util::computeMipLevelExtent(\n      GetExtent(), MipLevel);\n\n    VkExtent3D blockSize = formatInfo->blockSize;\n    uint32_t elementSize = formatInfo->elementSize;\n    if (unlikely(formatInfo->flags.test(DxvkFormatFlag::MultiPlane))) {\n      // D3D9 doesn't allow specifying the plane when locking a texture.\n      // So the subsampled planes inherit the pitch of the first plane.\n      // That means the size is the size of plane 0 * plane count\n      elementSize = formatInfo->planes[0].elementSize;\n      blockSize = { formatInfo->planes[0].blockSize.width, formatInfo->planes[0].blockSize.height, 1u };\n    }\n\n    const VkExtent3D blockCount = util::computeBlockCount(\n      mipExtent, blockSize);\n\n    return std::min(GetPlaneCount(), 2u)\n         * align(elementSize * blockCount.width, 4)\n         * blockCount.height\n         * blockCount.depth;\n  }\n\n\n  Rc<DxvkImage> D3D9CommonTexture::CreatePrimaryImage(D3DRESOURCETYPE ResourceType, HANDLE* pSharedHandle) const {\n    DxvkImageCreateInfo imageInfo;\n    imageInfo.type            = GetImageTypeFromResourceType(ResourceType);\n    imageInfo.format          = m_mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED\n                              ? m_mapping.ConversionFormatInfo.FormatColor\n                              : m_mapping.FormatColor;\n    imageInfo.flags           = 0;\n    imageInfo.sampleCount     = VK_SAMPLE_COUNT_1_BIT;\n    imageInfo.extent.width    = m_desc.Width;\n    imageInfo.extent.height   = m_desc.Height;\n    imageInfo.extent.depth    = m_desc.Depth;\n    imageInfo.numLayers       = m_desc.ArraySize;\n    imageInfo.mipLevels       = m_desc.MipLevels;\n    imageInfo.usage           = VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n                              | VK_IMAGE_USAGE_TRANSFER_DST_BIT\n                              | m_desc.ImageUsage;\n    imageInfo.stages          = VK_PIPELINE_STAGE_TRANSFER_BIT\n                              | m_device->GetEnabledShaderStages();\n    imageInfo.access          = VK_ACCESS_TRANSFER_READ_BIT\n                              | VK_ACCESS_TRANSFER_WRITE_BIT\n                              | VK_ACCESS_SHADER_READ_BIT;\n    imageInfo.tiling          = VK_IMAGE_TILING_OPTIMAL;\n    imageInfo.layout          = VK_IMAGE_LAYOUT_GENERAL;\n    imageInfo.shared          = m_desc.IsBackBuffer;\n\n    if (pSharedHandle) {\n      imageInfo.sharing.type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;\n      imageInfo.sharing.mode = (*pSharedHandle == INVALID_HANDLE_VALUE || *pSharedHandle == nullptr)\n        ? DxvkSharedHandleMode::Export\n        : DxvkSharedHandleMode::Import;\n      imageInfo.sharing.handle = *pSharedHandle;\n      imageInfo.shared = true;\n      // TODO: validate metadata?\n    }\n\n    if (m_mapping.ConversionFormatInfo.FormatType != D3D9ConversionFormat_None) {\n      imageInfo.usage  |= VK_IMAGE_USAGE_STORAGE_BIT;\n      imageInfo.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;\n      imageInfo.access |= VK_ACCESS_SHADER_WRITE_BIT;\n      imageInfo.shared = true;\n    }\n\n    DecodeMultiSampleType(m_desc.MultiSample, m_desc.MultisampleQuality, &imageInfo.sampleCount);\n\n    // The image must be marked as mutable if it can be reinterpreted\n    // by a view with a different format. Depth-stencil formats cannot\n    // be reinterpreted in Vulkan, so we'll ignore those.\n    auto formatProperties = lookupFormatInfo(m_mapping.FormatColor);\n\n    bool isMutable     = m_mapping.FormatSrgb != VK_FORMAT_UNDEFINED;\n    bool isColorFormat = (formatProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;\n\n    if (isMutable && isColorFormat) {\n      imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;\n\n      imageInfo.viewFormatCount = 2;\n      imageInfo.viewFormats     = m_mapping.Formats;\n    }\n\n    const bool isRT = m_desc.Usage & D3DUSAGE_RENDERTARGET;\n    const bool isDS = m_desc.Usage & D3DUSAGE_DEPTHSTENCIL;\n    const bool isAutoGen = m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP;\n\n    if (!m_desc.IsAttachmentOnly || (!isRT && !isDS))\n      imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;\n\n    // Are we an RT, need to gen mips or an offscreen plain surface?\n    if (isRT || isAutoGen) {\n      imageInfo.usage  |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n      imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n      imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT\n                       |  VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n    }\n\n    if (isDS) {\n      imageInfo.usage  |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n      imageInfo.stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n                       |  VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n      imageInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT\n                       |  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n    }\n\n    if (ResourceType == D3DRTYPE_CUBETEXTURE)\n      imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;\n\n    // Some image formats (i.e. the R32G32B32 ones) are\n    // only supported with linear tiling on most GPUs\n    if (!CheckImageSupport(&imageInfo, VK_IMAGE_TILING_OPTIMAL))\n      imageInfo.tiling = VK_IMAGE_TILING_LINEAR;\n\n    // We must keep LINEAR images in GENERAL layout, but we\n    // can choose a better layout for the image based on how\n    // it is going to be used by the game.\n    if (imageInfo.tiling == VK_IMAGE_TILING_OPTIMAL && imageInfo.sharing.mode == DxvkSharedHandleMode::None)\n      imageInfo.layout = OptimizeLayout(imageInfo.usage);\n\n    // Check if we can actually create the image\n    if (!CheckImageSupport(&imageInfo, imageInfo.tiling))\n      return nullptr;\n\n    return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n  }\n\n\n  Rc<DxvkImage> D3D9CommonTexture::CreateResolveImage() const {\n    DxvkImageCreateInfo imageInfo = m_image->info();\n    imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;\n\n    return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n  }\n\n\n  BOOL D3D9CommonTexture::DetermineShadowState() const {\n    constexpr std::array<D3D9Format, 3> blacklist = {\n      D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24\n    };\n\n    return IsDepthFormat(m_desc.Format)\n        && std::find(blacklist.begin(), blacklist.end(), m_desc.Format) == blacklist.end();\n  }\n\n\n  BOOL D3D9CommonTexture::DetermineFetch4Compatibility() const {\n    constexpr std::array<D3D9Format, 8> singleChannelFormats = {\n      D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24,\n      D3D9Format::R16F, D3D9Format::R32F, D3D9Format::A8,\n      D3D9Format::L8, D3D9Format::L16\n    };\n\n    return std::find(singleChannelFormats.begin(), singleChannelFormats.end(), m_desc.Format) != singleChannelFormats.end();\n  }\n\n\n  BOOL D3D9CommonTexture::CheckImageSupport(\n    const DxvkImageCreateInfo*  pImageInfo,\n          VkImageTiling         Tiling) const {\n    DxvkFormatQuery formatQuery = { };\n    formatQuery.format = pImageInfo->format;\n    formatQuery.type = pImageInfo->type;\n    formatQuery.tiling = Tiling;\n    formatQuery.usage = pImageInfo->usage;\n    formatQuery.flags = pImageInfo->flags;\n\n    auto properties = m_device->GetDXVKDevice()->getFormatLimits(formatQuery);\n    \n    if (!properties)\n      return FALSE;\n    \n    return (pImageInfo->extent.width  <= properties->maxExtent.width)\n        && (pImageInfo->extent.height <= properties->maxExtent.height)\n        && (pImageInfo->extent.depth  <= properties->maxExtent.depth)\n        && (pImageInfo->numLayers     <= properties->maxArrayLayers)\n        && (pImageInfo->mipLevels     <= properties->maxMipLevels)\n        && (pImageInfo->sampleCount    & properties->sampleCounts);\n  }\n\n\n  VkImageType D3D9CommonTexture::GetImageTypeFromResourceType(D3DRESOURCETYPE Type) {\n    switch (Type) {\n      case D3DRTYPE_SURFACE:\n      case D3DRTYPE_TEXTURE:       return VK_IMAGE_TYPE_2D;\n      case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_TYPE_3D;\n      case D3DRTYPE_CUBETEXTURE:   return VK_IMAGE_TYPE_2D;\n      default: throw DxvkError(\"D3D9CommonTexture: Unhandled resource type\");\n    }\n  }\n\n\n  VkImageViewType D3D9CommonTexture::GetImageViewTypeFromResourceType(\n          D3DRESOURCETYPE  Dimension,\n          UINT             Layer) {\n    switch (Dimension) {\n      case D3DRTYPE_SURFACE:\n      case D3DRTYPE_TEXTURE:       return VK_IMAGE_VIEW_TYPE_2D;\n      case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_VIEW_TYPE_3D;\n      case D3DRTYPE_CUBETEXTURE:   return Layer == AllLayers\n                                        ? VK_IMAGE_VIEW_TYPE_CUBE\n                                        : VK_IMAGE_VIEW_TYPE_2D;\n      default: throw DxvkError(\"D3D9CommonTexture: Unhandled resource type\");\n    }\n  }\n\n\n  VkImageLayout D3D9CommonTexture::OptimizeLayout(VkImageUsageFlags Usage) const {\n    // Filter out unnecessary flags. Transfer operations\n    // are handled by the backend in a transparent manner.\n    // Feedback loops are handled by hazard tracking.\n    Usage &= ~(VK_IMAGE_USAGE_TRANSFER_DST_BIT\n             | VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n             | VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT);\n\n    // Storage images require GENERAL.\n    if (Usage & VK_IMAGE_USAGE_STORAGE_BIT)\n      return VK_IMAGE_LAYOUT_GENERAL;\n\n    // If the image is used only as an attachment, we never\n    // have to transform the image back to a different layout.\n    if (Usage == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)\n      return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n    \n    if (Usage == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)\n      return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\n    // Fall back to GENERAL if the image is not shader-readable\n    if (!(Usage & VK_IMAGE_USAGE_SAMPLED_BIT))\n      return VK_IMAGE_LAYOUT_GENERAL;\n\n    // Otherwise, pick a layout that can be used for reading.\n    // Use DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL for a DS texture\n    // so it can be used as DS and sampled at the same time without transitioning.\n    return (Usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)\n      ? VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL\n      : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n  }\n\n  D3D9_COMMON_TEXTURE_MAP_MODE D3D9CommonTexture::DetermineMapMode() const {\n    if (m_desc.Format == D3D9Format::NULL_FORMAT)\n      return D3D9_COMMON_TEXTURE_MAP_MODE_NONE;\n\n#ifdef D3D9_ALLOW_UNMAPPING\n    if (m_device->GetOptions()->textureMemory != 0 && m_desc.Pool != D3DPOOL_DEFAULT)\n      return D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE;\n#endif\n\n    if (m_desc.Pool == D3DPOOL_SYSTEMMEM || m_desc.Pool == D3DPOOL_SCRATCH)\n      return D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM;\n\n    return D3D9_COMMON_TEXTURE_MAP_MODE_BACKED;\n  }\n\n  void D3D9CommonTexture::ExportImageInfo() {\n    /* From MSDN:\n      Textures being shared from D3D9 to D3D11 have the following restrictions.\n\n      - Textures must be 2D\n      - Only 1 mip level is allowed\n      - Texture must have default usage\n      - Texture must be write only\n      - MSAA textures are not allowed\n      - Bind flags must have SHADER_RESOURCE and RENDER_TARGET set\n      - Only R10G10B10A2_UNORM, R16G16B16A16_FLOAT and R8G8B8A8_UNORM formats are allowed\n    */\n    DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN;\n\n    switch (m_desc.Format) {\n      case D3D9Format::A2B10G10R10: dxgiFormat = DXGI_FORMAT_R10G10B10A2_UNORM; break;\n      case D3D9Format::A16B16G16R16F: dxgiFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; break;\n      case D3D9Format::A8B8G8R8: dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; break;\n      case D3D9Format::X8B8G8R8: dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; break; /* No RGBX in DXGI */\n      case D3D9Format::A8R8G8B8: dxgiFormat = DXGI_FORMAT_B8G8R8A8_UNORM; break;\n      case D3D9Format::X8R8G8B8: dxgiFormat = DXGI_FORMAT_B8G8R8X8_UNORM; break;\n      default:\n        Logger::warn(str::format(\"D3D9: Unsupported format for shared textures: \", m_desc.Format));\n        return;\n    }\n\n    struct d3dkmt_d3d9_desc desc = { };\n    desc.dxgi.size = sizeof(desc);\n    desc.dxgi.version = 1;\n    desc.dxgi.width = m_desc.Width;\n    desc.dxgi.height = m_desc.Height;\n    desc.dxgi.format = dxgiFormat;\n    desc.dxgi.unknown_0 = 1;\n    desc.format = static_cast<D3DFORMAT>(m_desc.Format);\n    desc.type = m_type;\n    desc.usage = m_desc.Usage | 0x8000000;\n\n    switch (m_type) {\n      case D3DRTYPE_TEXTURE:\n        desc.texture.width = m_desc.Width;\n        desc.texture.height = m_desc.Height;\n        desc.texture.levels = m_desc.MipLevels;\n        break;\n      case D3DRTYPE_SURFACE:\n        desc.surface.width = m_desc.Width;\n        desc.surface.height = m_desc.Height;\n        break;\n      default:\n        Logger::warn(str::format(\"D3D9: Unsupported type for shared textures:\", m_type));\n        break;\n    }\n\n    D3DKMT_ESCAPE escape = { };\n    escape.Type = D3DKMT_ESCAPE_UPDATE_RESOURCE_WINE;\n    escape.pPrivateDriverData = &desc;\n    escape.PrivateDriverDataSize = sizeof(desc);\n    escape.hContext = m_image->storage()->kmtLocal();\n\n    if (!D3DKMTEscape(&escape))\n      return;\n\n    /* try the legacy Proton shared resource implementation */\n\n    if (m_desc.Depth == 1 && m_desc.MipLevels == 1 && m_desc.MultiSample == D3DMULTISAMPLE_NONE &&\n        m_desc.Usage & D3DUSAGE_RENDERTARGET && dxgiFormat != DXGI_FORMAT_UNKNOWN) {\n      HANDLE ntHandle = openKmtHandle(m_image->sharedHandle());\n\n      DxvkSharedTextureMetadata metadata;\n\n      metadata.Width              = m_desc.Width;\n      metadata.Height             = m_desc.Height;\n      metadata.MipLevels          = m_desc.MipLevels;\n      metadata.ArraySize          = m_desc.ArraySize;\n      metadata.Format             = dxgiFormat;\n      metadata.SampleDesc.Count   = 1;\n      metadata.SampleDesc.Quality = 0;\n      metadata.Usage              = D3D11_USAGE_DEFAULT;\n      metadata.BindFlags          = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;\n      metadata.CPUAccessFlags     = 0;\n      metadata.MiscFlags          = D3D11_RESOURCE_MISC_SHARED;\n      metadata.TextureLayout      = D3D11_TEXTURE_LAYOUT_UNDEFINED;\n\n      if (ntHandle == INVALID_HANDLE_VALUE || !setSharedMetadata(ntHandle, &metadata, sizeof(metadata)))\n        Logger::warn(\"D3D9: Failed to write shared resource info for a texture\");\n\n      if (ntHandle != INVALID_HANDLE_VALUE)\n        ::CloseHandle(ntHandle);\n    }\n  }\n\n\n  Rc<DxvkImageView> D3D9CommonTexture::CreateView(\n          UINT                   Layer,\n          UINT                   Lod,\n          VkImageUsageFlags      UsageFlags,\n          VkImageLayout          Layout,\n          bool                   Srgb) {\n    DxvkImageViewKey viewInfo;\n    viewInfo.format    = m_mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED\n                       ? PickSRGB(m_mapping.ConversionFormatInfo.FormatColor, m_mapping.ConversionFormatInfo.FormatSrgb, Srgb)\n                       : PickSRGB(m_mapping.FormatColor, m_mapping.FormatSrgb, Srgb);\n    viewInfo.layout    = Layout;\n    viewInfo.aspects   = lookupFormatInfo(viewInfo.format)->aspectMask;\n    viewInfo.usage     = UsageFlags;\n    viewInfo.viewType  = GetImageViewTypeFromResourceType(m_type, Layer);\n    viewInfo.mipIndex  = Lod;\n    viewInfo.mipCount  = m_desc.MipLevels - Lod;\n    viewInfo.layerIndex = Layer == AllLayers ? 0 : Layer;\n    viewInfo.layerCount = Layer == AllLayers ? m_desc.ArraySize : 1;\n    viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(m_mapping.Swizzle);\n\n    // Remove the stencil aspect if we are trying to create a regular image\n    // view of a depth stencil format\n    if (!(UsageFlags & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))\n      viewInfo.aspects &= ~VK_IMAGE_ASPECT_STENCIL_BIT;\n\n    if (UsageFlags & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |\n        VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))\n      viewInfo.mipCount = 1;\n\n    // Remove swizzle on depth views.\n    if (UsageFlags & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)\n      viewInfo.packedSwizzle = 0u;\n\n    // Create the underlying image view object\n    return GetImage()->createView(viewInfo);\n  }\n\n\n  void D3D9CommonTexture::PreLoadAll() {\n    if (!IsManaged())\n      return;\n\n    auto lock = m_device->LockDevice();\n    m_device->UploadManagedTexture(this);\n    m_device->MarkTextureUploaded(this);\n  }\n\n\n  void D3D9CommonTexture::PreLoadSubresource(UINT Subresource) {\n    if (IsManaged()) {\n      auto lock = m_device->LockDevice();\n\n      if (NeedsUpload(Subresource)) {\n        m_device->FlushImage(this, Subresource);\n        SetNeedsUpload(Subresource, false);\n\n        if (!NeedsAnyUpload())\n          m_device->MarkTextureUploaded(this);\n      }\n    }\n  }\n\n\n  void D3D9CommonTexture::CreateSampleView(UINT Lod) {\n    // This will be a no-op for SYSTEMMEM types given we\n    // don't expose the cap to allow texturing with them.\n    if (unlikely(m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM))\n      return;\n\n    // The backend will ignore the view layout anyway for images\n    // that have GENERAL (or FEEDBACK_LOOP) as their layout.\n    // This will always be the case for images that can be sampled.\n    // So just pick UNDEFINED here.\n\n    VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n    // We default to DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL for DS images that can be sampled.\n    // The backend defaults to DS_READ_ONLY, so we need to set the layout explicitly.\n    if (IsDepthStencil())\n      layout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL;\n\n    m_sampleView.Color = CreateView(AllLayers, Lod,\n      VK_IMAGE_USAGE_SAMPLED_BIT, layout, false);\n\n    if (IsSrgbCompatible()) {\n      m_sampleView.Srgb = CreateView(AllLayers, Lod,\n        VK_IMAGE_USAGE_SAMPLED_BIT, layout, true);\n    }\n  }\n\n\n  const Rc<DxvkBuffer>& D3D9CommonTexture::GetBuffer() {\n    return m_buffer;\n  }\n\n\n  DxvkBufferSlice D3D9CommonTexture::GetBufferSlice(UINT Subresource) {\n    return DxvkBufferSlice(GetBuffer(), m_memoryOffset[Subresource], GetMipSize(Subresource));\n  }\n\n  \n  uint32_t D3D9CommonTexture::GetPlaneCount() const {\n    const DxvkFormatInfo* formatInfo = m_mapping.FormatColor != VK_FORMAT_UNDEFINED\n      ? lookupFormatInfo(m_mapping.FormatColor)\n      : m_device->UnsupportedFormatInfo(m_desc.Format);\n\n    return vk::getPlaneCount(formatInfo->aspectMask);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_common_texture.h",
    "content": "#pragma once\n\n#include \"d3d9_format.h\"\n#include \"d3d9_util.h\"\n#include \"d3d9_caps.h\"\n#include \"d3d9_mem.h\"\n#include \"d3d9_interop.h\"\n\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../util/util_bit.h\"\n\nnamespace dxvk {\n\n  class D3D9DeviceEx;\n\n  /**\n   * \\brief Image memory mapping mode\n   * \n   * Determines how exactly \\c LockBox will\n   * behave when mapping an image.\n   */\n  enum D3D9_COMMON_TEXTURE_MAP_MODE {\n    D3D9_COMMON_TEXTURE_MAP_MODE_NONE,      ///< No mapping available\n    D3D9_COMMON_TEXTURE_MAP_MODE_BACKED,    ///< Mapped image through buffer\n    D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM, ///< Only a buffer - no image\n    D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE,   ///< Non-Vulkan memory that can be unmapped\n  };\n  \n  /**\n   * \\brief Common texture description\n   * \n   * Contains all members that can be\n   * defined for 2D, Cube and 3D textures.\n   */\n  struct D3D9_COMMON_TEXTURE_DESC {\n    UINT                Width;\n    UINT                Height;\n    UINT                Depth;\n    UINT                ArraySize;\n    UINT                MipLevels;\n    DWORD               Usage;\n    D3D9Format          Format;\n    D3DPOOL             Pool;\n    D3DMULTISAMPLE_TYPE MultiSample;\n    DWORD               MultisampleQuality;\n    bool                Discard;\n    bool                IsBackBuffer;\n    bool                IsAttachmentOnly;\n    bool                IsLockable;\n\n    // Additional parameters for ID3D9VkInteropDevice\n    VkImageUsageFlags   ImageUsage = 0;\n  };\n\n  struct D3D9ColorView {\n    inline Rc<DxvkImageView>& Pick(bool Srgb) {\n      return Srgb ? this->Srgb : this->Color;\n    }\n\n    inline const Rc<DxvkImageView>& Pick(bool Srgb) const {\n      return Srgb ? this->Srgb : this->Color;\n    }\n\n    Rc<DxvkImageView> Color;\n    Rc<DxvkImageView> Srgb;\n  };\n\n  template <typename T>\n  using D3D9SubresourceArray = std::array<T, caps::MaxSubresources>;\n\n  using D3D9SubresourceBitset = bit::bitset<caps::MaxSubresources>;\n\n  class D3D9CommonTexture {\n\n  public:\n\n    static constexpr UINT AllLayers = std::numeric_limits<uint32_t>::max();\n\n    D3D9CommonTexture(\n            D3D9DeviceEx*             pDevice,\n            IUnknown*                 pInterface,\n      const D3D9_COMMON_TEXTURE_DESC* pDesc,\n            D3DRESOURCETYPE           ResourceType,\n            HANDLE*                   pSharedHandle);\n\n    ~D3D9CommonTexture();\n\n    /**\n     * \\brief Device\n     * \\returns The parent device\n     */\n    D3D9DeviceEx* Device() const {\n      return m_device;\n    }\n\n    /**\n      * \\brief Texture properties\n      *\n      * The returned data can be used to fill in\n      * \\c D3D11_TEXTURE2D_DESC and similar structs.\n      * \\returns Pointer to texture description\n      */\n    const D3D9_COMMON_TEXTURE_DESC* Desc() const {\n      return &m_desc;\n    }\n\n    /**\n     * \\brief Vulkan Format\n     * \\returns The Vulkan format of the resource\n     */\n    const D3D9_VK_FORMAT_MAPPING& GetFormatMapping() const {\n      return m_mapping;\n    }\n\n    /**\n     * \\brief Counts number of subresources\n     * \\returns Number of subresources\n     */\n    UINT CountSubresources() const {\n      return m_desc.ArraySize * m_desc.MipLevels;\n    }\n\n    /**\n     * \\brief Map mode\n     * \\returns Map mode\n     */\n    D3D9_COMMON_TEXTURE_MAP_MODE GetMapMode() const {\n      return m_mapMode;\n    }\n\n    /**\n     * \\brief The DXVK image\n     * Note, this will be nullptr if the map mode is D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM\n     * \\returns The DXVK image\n     */\n    const Rc<DxvkImage>& GetImage() const {\n      return m_image;\n    }\n\n    /**\n     * \\brief Get a copy of the main image, but with a single sample\n     * This function will allocate/reuse an image with the same info\n     * as the main image\n     * \\returns An image with identical info, but 1 sample\n     */\n    const Rc<DxvkImage>& GetResolveImage() {\n      if (unlikely(m_resolveImage == nullptr))\n        m_resolveImage = CreateResolveImage();\n\n      return m_resolveImage;\n    }\n\n    /**\n     * \\brief Returns a pointer to the internal data used for LockRect/LockBox\n     *\n     * This works regardless of the map mode used by this texture\n     * and will map the memory if necessary.\n     * \\param [in] Subresource Subresource index\n     * @return Pointer to locking data\n     */\n    void* GetData(UINT Subresource);\n\n    const Rc<DxvkBuffer>& GetBuffer();\n\n    DxvkBufferSlice GetBufferSlice(UINT Subresource);\n\n    /**\n     * \\brief Computes subresource from the subresource index\n     *\n     * Used by some functions that operate on only\n     * one subresource, such as \\c UpdateSurface.\n     * \\param [in] Aspect The image aspect\n     * \\param [in] Subresource Subresource index\n     * \\returns The Vulkan image subresource\n     */\n    VkImageSubresource GetSubresourceFromIndex(\n            VkImageAspectFlags    Aspect,\n            UINT                  Subresource) const;\n\n    /**\n     * \\brief Normalizes and validates texture description\n     * \n     * Fills in undefined values and validates the texture\n     * parameters. Any error returned by this method should\n     * be forwarded to the application.\n     * \\param [in] pDevice D3D9 device\n     * \\param [in] ResourceType Resource type\n     * \\param [in,out] pDesc Texture description\n     * \\returns \\c S_OK if the parameters are valid\n     */\n    static HRESULT NormalizeTextureProperties(\n            D3D9DeviceEx*              pDevice,\n            D3DRESOURCETYPE            ResourceType,\n            D3D9_COMMON_TEXTURE_DESC*  pDesc);\n\n    /**\n     * \\brief Returns whether a Vulkan image is used for this D3D9 texture\n     *\n     * \\param pDesc The texture description\n     */\n    static bool TextureUsesImage(const D3D9_COMMON_TEXTURE_DESC* pDesc) {\n      return pDesc->Pool   != D3DPOOL_SYSTEMMEM\n          && pDesc->Pool   != D3DPOOL_SCRATCH\n          && pDesc->Format != D3D9Format::NULL_FORMAT;\n    }\n\n    /**\n     * \\brief Shadow\n     * \\returns Whether the texture is to be depth compared\n     */\n    bool IsShadow() const {\n      return m_shadow;\n    }\n\n    /**\n     * \\brief Cube\n     * \\returns Whether the texture is a cube map\n     */\n    bool IsCube() const {\n      return m_type == D3DRTYPE_CUBETEXTURE;\n    }\n\n    /**\n     * \\brief Dref Clamp\n     * \\returns Whether the texture emulates an UNORM format with D32f\n     */\n    bool IsUpgradedToD32f() const {\n      return m_upgradedToD32f;\n    }\n\n    /**\n     * \\brief FETCH4 compatibility\n     * \\returns Whether the format of the texture supports the FETCH4 hack\n     */\n    bool SupportsFetch4() const {\n      return m_supportsFetch4;\n    }\n\n    /**\n     * \\brief Null\n     * \\returns Whether the texture is D3DFMT_NULL or not\n     */\n    bool IsNull() const {\n      return m_desc.Format == D3D9Format::NULL_FORMAT;\n    }\n\n    /**\n     * \\brief Subresource\n     * \\returns The subresource idx of a given face and mip level\n     */\n    UINT CalcSubresource(UINT Face, UINT MipLevel) const {\n      return Face * m_desc.MipLevels + MipLevel;\n    }\n\n    void UnmapData() {\n      m_data.Unmap();\n    }\n\n    /**\n     * \\brief Destroys a buffer\n     * Destroys mapping and staging buffers for a given subresource\n     */\n    void DestroyBuffer() {\n      m_buffer = nullptr;\n      MarkAllNeedReadback();\n    }\n\n    bool IsDynamic() const {\n      return m_desc.Usage & D3DUSAGE_DYNAMIC;\n    }\n\n    /**\n     * \\brief Managed\n     * \\returns Whether a resource is managed (pool) or not\n     */\n    bool IsManaged() const {\n      return IsPoolManaged(m_desc.Pool);\n    }\n\n    /**\n     * \\brief Render Target\n     * \\returns Whether a resource is a render target or not\n     */\n    bool IsRenderTarget() const {\n      return m_desc.Usage & D3DUSAGE_RENDERTARGET;\n    }\n\n    /**\n     * \\brief Depth stencil\n     * \\returns Whether a resource is a depth stencil or not\n     */\n    bool IsDepthStencil() const {\n      return m_desc.Usage & D3DUSAGE_DEPTHSTENCIL;\n    }\n\n    /**\n     * \\brief Autogen Mipmap\n     * \\returns Whether the texture is to have automatic mip generation\n     */\n    bool IsAutomaticMip() const {\n      return m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP;\n    }\n\n    /**\n     * \\brief Checks whether sRGB views can be created\n     * \\returns Whether the format is sRGB compatible.\n     */\n    bool IsSrgbCompatible() const {\n      return m_mapping.FormatSrgb;\n    }\n\n    /**\n     * \\brief Recreate main image view\n     * Recreates the main view of the sampler w/ a specific LOD.\n     * SetLOD only works on MANAGED textures so this is A-okay.\n     */\n    void CreateSampleView(UINT Lod);\n\n    /**\n     * \\brief Extent\n     * \\returns The extent of the top-level mip\n     */\n    VkExtent3D GetExtent() const {\n      return VkExtent3D{ m_desc.Width, m_desc.Height, m_desc.Depth };\n    }\n\n    /**\n     * \\brief Mip Extent\n     * \\returns The extent of a mip or subresource\n     */\n    VkExtent3D GetExtentMip(UINT Subresource) const {\n      UINT MipLevel = Subresource % m_desc.MipLevels;\n      return util::computeMipLevelExtent(GetExtent(), MipLevel);\n    }\n\n    bool MarkTransitionedToHazardLayout() {\n      return std::exchange(m_transitionedToHazardLayout, true);\n    }\n\n    bool HasBeenTransitionedToHazardLayout() const {\n      return m_transitionedToHazardLayout;\n    }\n\n    D3DRESOURCETYPE GetType() const {\n      return m_type;\n    }\n\n    uint32_t GetPlaneCount() const;\n\n    D3DPOOL GetPool() const { return m_desc.Pool; }\n\n    const D3D9_VK_FORMAT_MAPPING& GetMapping() { return m_mapping; }\n\n    void SetLocked(UINT Subresource, bool value) { m_locked.set(Subresource, value); }\n\n    bool GetLocked(UINT Subresource) const { return m_locked.get(Subresource); }\n\n    bool IsAnySubresourceLocked() const { return m_locked.any(); }\n\n    void SetNeedsReadback(UINT Subresource, bool value) { m_needsReadback.set(Subresource, value); }\n\n    bool NeedsReadback(UINT Subresource) const { return m_needsReadback.get(Subresource); }\n\n    void MarkAllNeedReadback() { m_needsReadback.setAll(); }\n\n    const Rc<DxvkImageView>& GetSampleView(bool srgb) const {\n      return m_sampleView.Pick(srgb && IsSrgbCompatible());\n    }\n\n    Rc<DxvkImageView> CreateView(\n            UINT                   Layer,\n            UINT                   Lod,\n            VkImageUsageFlags      UsageFlags,\n            VkImageLayout          Layout,\n            bool                   Srgb);\n    D3D9SubresourceBitset& GetUploadBitmask() { return m_needsUpload; }\n\n    void SetAllNeedUpload() {\n      if (likely(!IsAutomaticMip())) {\n        m_needsUpload.setAll();\n      } else {\n        for (uint32_t a = 0; a < m_desc.ArraySize; a++) {\n          for (uint32_t m = 0; m < ExposedMipLevels(); m++) {\n            SetNeedsUpload(CalcSubresource(a, m), true);\n          }\n        }\n      }\n    }\n    void SetNeedsUpload(UINT Subresource, bool upload) { m_needsUpload.set(Subresource, upload); }\n    bool NeedsUpload(UINT Subresource) const { return m_needsUpload.get(Subresource); }\n    bool NeedsAnyUpload() { return m_needsUpload.any(); }\n    void ClearNeedsUpload() { return m_needsUpload.clearAll();  }\n\n    void SetNeedsMipGen(bool value) { m_needsMipGen = value; }\n    bool NeedsMipGen() const { return m_needsMipGen; }\n\n    DWORD ExposedMipLevels() const { return m_exposedMipLevels; }\n\n    void SetMipFilter(D3DTEXTUREFILTERTYPE filter) { m_mipFilter = filter; }\n    D3DTEXTUREFILTERTYPE GetMipFilter() const { return m_mipFilter; }\n\n    void PreLoadAll();\n    void PreLoadSubresource(UINT Subresource);\n\n    void AddDirtyBox(CONST D3DBOX* pDirtyBox, uint32_t layer) {\n      if (pDirtyBox) {\n        D3DBOX box = *pDirtyBox;\n        if (box.Right <= box.Left\n          || box.Bottom <= box.Top\n          || box.Back <= box.Front)\n          return;\n\n        box.Right = std::min(box.Right, m_desc.Width);\n        box.Bottom = std::min(box.Bottom, m_desc.Height);\n        box.Back = std::min(box.Back, m_desc.Depth);\n\n        D3DBOX& dirtyBox = m_dirtyBoxes[layer];\n        if (dirtyBox.Left == dirtyBox.Right) {\n          dirtyBox = box;\n        } else {\n          dirtyBox.Left    = std::min(dirtyBox.Left,   box.Left);\n          dirtyBox.Right   = std::max(dirtyBox.Right,  box.Right);\n          dirtyBox.Top     = std::min(dirtyBox.Top,    box.Top);\n          dirtyBox.Bottom  = std::max(dirtyBox.Bottom, box.Bottom);\n          dirtyBox.Front   = std::min(dirtyBox.Front,  box.Front);\n          dirtyBox.Back    = std::max(dirtyBox.Back,   box.Back);\n        }\n      } else {\n        m_dirtyBoxes[layer] = { 0, 0, m_desc.Width, m_desc.Height, 0, m_desc.Depth };\n      }\n    }\n\n    void ClearDirtyBoxes() {\n      for (uint32_t i = 0; i < m_dirtyBoxes.size(); i++) {\n        m_dirtyBoxes[i] = { 0, 0, 0, 0, 0, 0 };\n      }\n    }\n\n    const D3DBOX& GetDirtyBox(uint32_t layer) const {\n      return m_dirtyBoxes[layer];\n    }\n\n    static VkImageType GetImageTypeFromResourceType(\n            D3DRESOURCETYPE  Dimension);\n\n    static VkImageViewType GetImageViewTypeFromResourceType(\n            D3DRESOURCETYPE  Dimension,\n            UINT             Layer);\n\n     /**\n     * \\brief Tracks sequence number for a given subresource\n     *\n     * Stores which CS chunk the resource was last used on.\n     * \\param [in] Subresource Subresource index\n     * \\param [in] Seq Sequence number\n     */\n    void TrackMappingBufferSequenceNumber(UINT Subresource, uint64_t Seq) {\n      if (Subresource < m_seqs.size())\n        m_seqs[Subresource] = Seq;\n    }\n\n    /**\n     * \\brief Queries sequence number for a given subresource\n     *\n     * Returns which CS chunk the resource was last used on.\n     * \\param [in] Subresource Subresource index\n     * \\returns Sequence number for the given subresource\n     */\n    uint64_t GetMappingBufferSequenceNumber(UINT Subresource) {\n      return Subresource < m_seqs.size()\n        ? m_seqs[Subresource]\n        : 0ull;\n    }\n\n    /**\n     * \\brief Mip level\n     * \\returns Size of packed mip level in bytes\n     */\n    VkDeviceSize GetMipSize(UINT Subresource) const;\n\n    uint32_t GetTotalSize() const {\n      return m_totalSize;\n    }\n\n    /**\n     * \\brief Creates a buffer\n     * Creates the mapping buffer if necessary\n     * \\param [in] Initialize Whether to copy over existing data (or clear if there is no data)\n     * \\param [in] Size The size of the buffer\n     * \\returns Whether an allocation happened\n     */\n    void CreateBuffer(bool Initialize, uint32_t Size);\n\n    ID3D9VkInteropTexture* GetVkInterop() { return &m_d3d9Interop; }\n\n  private:\n\n    D3D9DeviceEx*                 m_device;\n    D3D9_COMMON_TEXTURE_DESC      m_desc;\n    D3DRESOURCETYPE               m_type;\n    D3D9_COMMON_TEXTURE_MAP_MODE  m_mapMode;\n\n    Rc<DxvkImage>                 m_image;\n    Rc<DxvkImage>                 m_resolveImage;\n    Rc<DxvkBuffer>                m_buffer;\n    D3D9Memory                    m_data = { };\n\n    D3D9SubresourceArray<\n      uint64_t>                   m_seqs = { };\n\n    D3D9SubresourceArray<\n      uint32_t>                   m_memoryOffset = { };\n    \n    uint32_t                      m_totalSize = 0;\n\n    D3D9_VK_FORMAT_MAPPING        m_mapping;\n\n    bool                          m_shadow; //< Depth Compare-ness\n    bool                          m_upgradedToD32f; // Dref Clamp\n    bool                          m_supportsFetch4;\n\n    int64_t                       m_size = 0;\n\n    bool                          m_transitionedToHazardLayout = false;\n\n    D3D9ColorView                 m_sampleView;\n\n    D3D9SubresourceBitset         m_locked = { };\n\n    D3D9SubresourceBitset         m_needsReadback = { };\n\n    D3D9SubresourceBitset         m_needsUpload = { };\n\n    DWORD                         m_exposedMipLevels = 0;\n\n    bool                          m_needsMipGen = false;\n\n    D3DTEXTUREFILTERTYPE          m_mipFilter = D3DTEXF_LINEAR;\n\n    std::array<D3DBOX, 6>         m_dirtyBoxes;\n\n    D3D9VkInteropTexture          m_d3d9Interop;\n\n    Rc<DxvkImage> CreatePrimaryImage(D3DRESOURCETYPE ResourceType, HANDLE* pSharedHandle) const;\n\n    Rc<DxvkImage> CreateResolveImage() const;\n\n    BOOL DetermineShadowState() const;\n\n    BOOL DetermineFetch4Compatibility() const;\n\n    BOOL CheckImageSupport(\n      const DxvkImageCreateInfo*  pImageInfo,\n            VkImageTiling         Tiling) const;\n\n    D3D9_COMMON_TEXTURE_MAP_MODE DetermineMapMode() const;\n\n    VkImageLayout OptimizeLayout(\n            VkImageUsageFlags         Usage) const;\n\n    void ExportImageInfo();\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_constant_buffer.cpp",
    "content": "#include \"d3d9_constant_buffer.h\"\n#include \"d3d9_device.h\"\n\nnamespace dxvk {\n\n  D3D9ConstantBuffer::D3D9ConstantBuffer() {\n\n  }\n\n\n  D3D9ConstantBuffer::D3D9ConstantBuffer(\n          D3D9DeviceEx*         pDevice,\n          D3D9ShaderType        ShaderStage,\n          DxsoConstantBuffers   BufferType,\n          VkDeviceSize          Size)\n  : D3D9ConstantBuffer(pDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, GetShaderStage(ShaderStage),\n      computeResourceSlotId(ShaderStage, DxsoBindingType::ConstantBuffer, BufferType),\n      Size) {\n\n  }\n\n\n  D3D9ConstantBuffer::D3D9ConstantBuffer(\n          D3D9DeviceEx*         pDevice,\n          VkBufferUsageFlags    Usage,\n          VkShaderStageFlags    Stages,\n          uint32_t              ResourceSlot,\n          VkDeviceSize          Size)\n  : m_device    (pDevice)\n  , m_binding   (ResourceSlot)\n  , m_usage     (Usage)\n  , m_stages    (Stages)\n  , m_size      (Size)\n  , m_align     (getAlignment(pDevice->GetDXVKDevice())) {\n\n  }\n\n\n  D3D9ConstantBuffer::~D3D9ConstantBuffer() {\n\n  }\n\n\n  void* D3D9ConstantBuffer::Alloc(VkDeviceSize size) {\n    if (unlikely(m_buffer == nullptr))\n      m_slice = this->createBuffer();\n\n    size = align(size, m_align);\n\n    if (unlikely(m_offset + size > m_size)) {\n      m_slice = m_buffer->allocateStorage();\n      m_offset = 0;\n\n      m_device->EmitCs([\n        cBuffer = m_buffer,\n        cSlice  = m_slice\n      ] (DxvkContext* ctx) mutable {\n        ctx->invalidateBuffer(cBuffer, std::move(cSlice));\n      });\n    }\n\n    m_device->EmitCs([\n      cStages   = m_stages,\n      cBinding  = m_binding,\n      cOffset   = m_offset,\n      cLength   = size\n    ] (DxvkContext* ctx) {\n      ctx->bindUniformBufferRange(cStages, cBinding, cOffset, cLength);\n    });\n\n    void* mapPtr = reinterpret_cast<char*>(m_slice->mapPtr()) + m_offset;\n    m_offset += size;\n    return mapPtr;\n  }\n\n\n  void* D3D9ConstantBuffer::AllocSlice() {\n    if (unlikely(m_buffer == nullptr))\n      m_slice = this->createBuffer();\n    else\n      m_slice = m_buffer->allocateStorage();\n\n    m_device->EmitCs([\n      cBuffer = m_buffer,\n      cSlice  = m_slice\n    ] (DxvkContext* ctx) mutable {\n      ctx->invalidateBuffer(cBuffer, std::move(cSlice));\n    });\n\n    return m_slice->mapPtr();\n  }\n\n\n  Rc<DxvkResourceAllocation> D3D9ConstantBuffer::createBuffer() {\n    auto options = m_device->GetOptions();\n\n    // Buffer usage and access flags don't make much of a difference\n    // in the backend, so set both STORAGE and UNIFORM usage/access.\n    DxvkBufferCreateInfo bufferInfo;\n    bufferInfo.size   = align(m_size, m_align);\n    bufferInfo.usage  = m_usage;\n    bufferInfo.access = 0;\n    bufferInfo.stages = util::pipelineStages(m_stages);\n    bufferInfo.debugName = \"Constant buffer\";\n\n    if (m_usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)\n      bufferInfo.access |= VK_ACCESS_UNIFORM_READ_BIT;\n    if (m_usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)\n      bufferInfo.access |= VK_ACCESS_SHADER_READ_BIT;\n\n    VkMemoryPropertyFlags memoryFlags\n      = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n      | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;\n\n    if (options->deviceLocalConstantBuffers)\n      memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n    m_buffer = m_device->GetDXVKDevice()->createBuffer(bufferInfo, memoryFlags);\n\n    m_device->EmitCs([\n      cStages   = m_stages,\n      cBinding  = m_binding,\n      cSlice    = DxvkBufferSlice(m_buffer)\n    ] (DxvkContext* ctx) mutable {\n      ctx->bindUniformBuffer(cStages, cBinding, std::move(cSlice));\n    });\n\n    return m_buffer->storage();\n  }\n\n\n  VkDeviceSize D3D9ConstantBuffer::getAlignment(const Rc<DxvkDevice>& device) const {\n    return std::max(std::max(\n      device->properties().core.properties.limits.minUniformBufferOffsetAlignment,\n      device->properties().core.properties.limits.minStorageBufferOffsetAlignment),\n      device->properties().extRobustness2.robustUniformBufferAccessSizeAlignment);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_constant_buffer.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_buffer.h\"\n\n#include \"../dxso/dxso_util.h\"\n\n#include \"../util/util_math.h\"\n\n#include \"d3d9_include.h\"\n\nnamespace dxvk {\n\n  class D3D9DeviceEx;\n\n  /**\n   * \\brief Constant buffer\n   */\n  class D3D9ConstantBuffer {\n\n  public:\n\n    D3D9ConstantBuffer();\n\n    D3D9ConstantBuffer(\n            D3D9DeviceEx*         pDevice,\n            D3D9ShaderType        ShaderStage,\n            DxsoConstantBuffers   BufferType,\n            VkDeviceSize          Size);\n\n    D3D9ConstantBuffer(\n            D3D9DeviceEx*         pDevice,\n            VkBufferUsageFlags    Usage,\n            VkShaderStageFlags    Stages,\n            uint32_t              ResourceSlot,\n            VkDeviceSize          Size);\n\n    ~D3D9ConstantBuffer();\n\n    /**\n     * \\brief Queries alignment\n     *\n     * Useful to pad copies with initialized data.\n     * \\returns Data alignment\n     */\n    VkDeviceSize GetAlignment() const {\n      return m_align;\n    }\n    \n    /**\n     * \\brief Allocates a given amount of memory\n     *\n     * \\param [in] size Number of bytes to allocate\n     * \\returns Map pointer of the allocated region\n     */\n    void* Alloc(VkDeviceSize size);\n\n    /**\n     * \\brief Allocates a full buffer slice\n     *\n     * This must not be called if \\ref Alloc is used.\n     * \\returns Map pointer of the allocated region\n     */\n    void* AllocSlice();\n\n  private:\n\n    D3D9DeviceEx*         m_device  = nullptr;\n\n    uint32_t              m_binding = 0u;\n    VkBufferUsageFlags    m_usage   = 0u;\n    VkShaderStageFlags    m_stages  = 0u;\n    VkDeviceSize          m_size    = 0ull;\n    VkDeviceSize          m_align   = 0ull;\n    VkDeviceSize          m_offset  = 0ull;\n\n    Rc<DxvkBuffer>        m_buffer  = nullptr;\n    Rc<DxvkResourceAllocation> m_slice = nullptr;\n\n    Rc<DxvkResourceAllocation> createBuffer();\n\n    VkDeviceSize getAlignment(const Rc<DxvkDevice>& device) const;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_constant_layout.h",
    "content": "#pragma once\n\n#include <cstdint>\n\n#include \"d3d9_caps.h\"\n\nnamespace dxvk {\n\n  struct D3D9ConstantLayout {\n    uint32_t floatCount;\n    uint32_t intCount;\n    uint32_t boolCount;\n    uint32_t bitmaskCount;\n\n    uint32_t floatSize()     const { return floatCount   * 4 * sizeof(float); }\n    uint32_t intSize()       const { return intCount     * 4 * sizeof(int); }\n    uint32_t bitmaskSize()   const {\n      // Account for SWVP (non SWVP uses a spec constant)\n      return bitmaskCount != 1\n        ? bitmaskCount * 1 * sizeof(uint32_t)\n        : 0;\n    }\n\n    uint32_t intOffset()     const { return 0; }\n    uint32_t floatOffset()   const { return intOffset() + intSize(); }\n    uint32_t bitmaskOffset() const { return floatOffset() + floatSize(); }\n\n    uint32_t totalSize()     const { return floatSize() + intSize() + bitmaskSize(); }\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_constant_set.h",
    "content": "#pragma once\n\n#include \"d3d9_caps.h\"\n#include \"d3d9_constant_buffer.h\"\n#include \"d3d9_constant_layout.h\"\n\n\n#include \"../dxso/dxso_isgn.h\"\n\n#include \"../util/util_vector.h\"\n\n#include <cstdint>\n\nnamespace dxvk {\n\n  enum class D3D9ConstantType {\n    Float,\n    Int,\n    Bool\n  };\n\n  // We make an assumption later based on the packing of this struct for copying.\n  struct D3D9ShaderConstantsVSSoftware {\n    Vector4i iConsts[caps::MaxOtherConstantsSoftware];\n    Vector4  fConsts[caps::MaxFloatConstantsSoftware];\n    uint32_t bConsts[caps::MaxOtherConstantsSoftware / 32];\n  };\n\n  struct D3D9ShaderConstantsVSHardware {\n    Vector4i iConsts[caps::MaxOtherConstants];\n    Vector4  fConsts[caps::MaxFloatConstantsVS];\n    uint32_t bConsts[1];\n  };\n\n  struct D3D9ShaderConstantsPS {\n    Vector4i iConsts[caps::MaxOtherConstants];\n    Vector4  fConsts[caps::MaxSM3FloatConstantsPS];\n    uint32_t bConsts[1];\n  };\n\n  struct D3D9SwvpConstantBuffers {\n    D3D9ConstantBuffer        intBuffer;\n    D3D9ConstantBuffer        boolBuffer;\n  };\n\n  struct D3D9ConstantSets {\n    D3D9ConstantLayout        layout;\n    D3D9SwvpConstantBuffers   swvp;\n    D3D9ConstantBuffer        buffer;\n    DxsoShaderMetaInfo        meta  = {};\n    bool                      dirty = true;\n    uint32_t                  maxChangedConstF = 0;\n    uint32_t                  maxChangedConstI = 0;\n    uint32_t                  maxChangedConstB = 0;\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_cursor.cpp",
    "content": "#include \"d3d9_cursor.h\"\n#include \"d3d9_util.h\"\n\n#include <utility>\n\nnamespace dxvk {\n\n#ifdef _WIN32\n  void D3D9Cursor::ResetCursor() {\n    m_visible = FALSE;\n    ShowCursor(m_visible);\n\n    if (IsHardwareCursor())\n      ResetHardwareCursor();\n    else if (IsActiveSoftwareCursor())\n      ResetSoftwareCursor();\n  }\n\n\n  void D3D9Cursor::ResetHardwareCursor() {\n    ::DestroyCursor(m_hCursor);\n    m_hCursor = nullptr;\n  }\n\n\n  void D3D9Cursor::ResetSoftwareCursor() {\n    m_sCursor.DrawCursor  = false;\n    m_sCursor.ClearCursor = true;\n  }\n\n\n  void D3D9Cursor::UpdateCursor(int X, int Y) {\n    // SetCursorPosition is used to directly update the position of software cursors,\n    // but keep track of the cursor position even when using hardware cursors, in order\n    // to ensure a smooth transition/overlap from one type to the other.\n    m_sCursor.X = X;\n    m_sCursor.Y = Y;\n\n    if (IsActiveSoftwareCursor())\n      return;\n\n    POINT currentPos = { };\n    if (::GetCursorPos(&currentPos) && currentPos == POINT{ X, Y })\n        return;\n\n    ::SetCursorPos(X, Y);\n  }\n\n\n  BOOL D3D9Cursor::ShowCursor(BOOL bShow) {\n    // Cursor visibility remains unchanged (typically FALSE) if the cursor isn't set.\n    if (unlikely(!IsHardwareCursor() && !IsActiveSoftwareCursor()))\n      return m_visible;\n\n    if (IsHardwareCursor())\n      ::SetCursor(bShow ? m_hCursor : nullptr);\n    else\n      m_sCursor.DrawCursor = bShow;\n\n    return std::exchange(m_visible, bShow);\n  }\n\n\n  void D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) {\n    if (IsActiveSoftwareCursor())\n      ResetSoftwareCursor();\n\n    CursorMask mask;\n    std::memset(mask, ~0, sizeof(mask));\n\n    ICONINFO info;\n    info.fIcon    = FALSE;\n    info.xHotspot = XHotSpot;\n    info.yHotspot = YHotSpot;\n    info.hbmMask  = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 1,  &mask[0]);\n    info.hbmColor = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 32, &bitmap[0]);\n\n    if (IsHardwareCursor())\n      ::DestroyCursor(m_hCursor);\n\n    m_hCursor = ::CreateIconIndirect(&info);\n\n    ::DeleteObject(info.hbmMask);\n    ::DeleteObject(info.hbmColor);\n\n    ShowCursor(m_visible);\n  }\n\n\n  void D3D9Cursor::SetSoftwareCursor(UINT XHotSpot, UINT YHotSpot, UINT Width, UINT Height) {\n    // Make sure to hide the win32 cursor.\n    ::SetCursor(nullptr);\n\n    if (IsHardwareCursor())\n      ResetHardwareCursor();\n\n    m_sCursor.Width       = Width;\n    m_sCursor.Height      = Height;\n    m_sCursor.XHotSpot    = XHotSpot;\n    m_sCursor.YHotSpot    = YHotSpot;\n    m_sCursor.ClearCursor = false;\n\n    ShowCursor(m_visible);\n  }\n\n#else\n  void D3D9Cursor::ResetCursor() {\n    Logger::warn(\"D3D9Cursor::ResetCursor: Not supported on current platform.\");\n  }\n\n\n  void D3D9Cursor::ResetHardwareCursor() {\n    Logger::warn(\"D3D9Cursor::ResetHardwareCursor: Not supported on current platform.\");\n  }\n\n\n  void D3D9Cursor::ResetSoftwareCursor() {\n    Logger::warn(\"D3D9Cursor::ResetSoftwareCursor: Not supported on current platform.\");\n  }\n\n\n  void D3D9Cursor::UpdateCursor(int X, int Y) {\n    Logger::warn(\"D3D9Cursor::UpdateCursor: Not supported on current platform.\");\n  }\n\n\n  BOOL D3D9Cursor::ShowCursor(BOOL bShow) {\n    Logger::warn(\"D3D9Cursor::ShowCursor: Not supported on current platform.\");\n    return std::exchange(m_visible, bShow);\n  }\n\n\n  void D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) {\n    Logger::warn(\"D3D9Cursor::SetHardwareCursor: Not supported on current platform.\");\n  }\n\n\n  void D3D9Cursor::SetSoftwareCursor(UINT XHotSpot, UINT YHotSpot, UINT Width, UINT Height) {\n    Logger::warn(\"D3D9Cursor::SetSoftwareCursor: Not supported on current platform.\");\n  }\n#endif\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_cursor.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief D3D9 Software Cursor\n   */\n  struct D3D9_SOFTWARE_CURSOR {\n    UINT Width       = 0;\n    UINT Height      = 0;\n    UINT XHotSpot    = 0;\n    UINT YHotSpot    = 0;\n    int32_t X        = 0;\n    int32_t Y        = 0;\n    bool DrawCursor  = false;\n    bool ClearCursor = false;\n  };\n\n  constexpr uint32_t HardwareCursorWidth      = 32u;\n  constexpr uint32_t HardwareCursorHeight     = 32u;\n  constexpr uint32_t HardwareCursorFormatSize = 4u;\n  constexpr uint32_t HardwareCursorPitch      = HardwareCursorWidth * HardwareCursorFormatSize;\n\n  // Format Size of 4 bytes (ARGB)\n  using CursorBitmap = uint8_t[HardwareCursorHeight * HardwareCursorPitch];\n  // Monochrome mask (1 bit)\n  using CursorMask   = uint8_t[HardwareCursorHeight * HardwareCursorWidth / 8];\n\n  class D3D9Cursor {\n\n  public:\n\n#ifdef _WIN32\n    ~D3D9Cursor() {\n      if (m_hCursor != nullptr)\n        ::DestroyCursor(m_hCursor);\n    }\n#endif\n\n    void ResetCursor();\n\n    void ResetHardwareCursor();\n\n    void ResetSoftwareCursor();\n\n    void UpdateCursor(int X, int Y);\n\n    BOOL ShowCursor(BOOL bShow);\n\n    void SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap);\n\n    void SetSoftwareCursor(UINT XHotSpot, UINT YHotSpot, UINT Width, UINT Height);\n\n    D3D9_SOFTWARE_CURSOR* GetSoftwareCursor() {\n      return &m_sCursor;\n    }\n\n    bool IsSoftwareCursor() const {\n      return m_sCursor.Width > 0 && m_sCursor.Height > 0;\n    }\n\n    inline bool IsActiveSoftwareCursor() const {\n      return IsSoftwareCursor() && !m_sCursor.ClearCursor;\n    }\n\n#ifdef _WIN32\n    inline bool IsHardwareCursor() const {\n      return m_hCursor != nullptr;\n    }\n#endif\n\n  private:\n\n    BOOL                  m_visible = FALSE;\n    D3D9_SOFTWARE_CURSOR  m_sCursor;\n\n#ifdef _WIN32\n    HCURSOR               m_hCursor = nullptr;\n#endif\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_device.cpp",
    "content": "#include \"d3d9_device.h\"\n\n#include \"d3d9_annotation.h\"\n#include \"d3d9_common_texture.h\"\n#include \"d3d9_interface.h\"\n#include \"d3d9_swapchain.h\"\n#include \"d3d9_caps.h\"\n#include \"d3d9_util.h\"\n#include \"d3d9_texture.h\"\n#include \"d3d9_buffer.h\"\n#include \"d3d9_vertex_declaration.h\"\n#include \"d3d9_shader.h\"\n#include \"d3d9_query.h\"\n#include \"d3d9_stateblock.h\"\n#include \"d3d9_monitor.h\"\n#include \"d3d9_spec_constants.h\"\n#include \"d3d9_names.h\"\n#include \"d3d9_format_helpers.h\"\n\n#include \"../dxvk/dxvk_adapter.h\"\n#include \"../dxvk/dxvk_instance.h\"\n\n#include \"../util/util_bit.h\"\n#include \"../util/util_math.h\"\n\n#include \"d3d9_initializer.h\"\n\n#include <algorithm>\n#include <cfloat>\n#ifdef MSC_VER\n#pragma fenv_access (on)\n#endif\n\nnamespace dxvk {\n\n  D3D9DeviceEx::D3D9DeviceEx(\n          D3D9InterfaceEx*       pParent,\n          D3D9Adapter*           pAdapter,\n          D3DDEVTYPE             DeviceType,\n          HWND                   hFocusWindow,\n          DWORD                  BehaviorFlags,\n          Rc<DxvkDevice>         dxvkDevice)\n    : m_parent             ( pParent )\n    , m_d3d9Options        ( dxvkDevice, pParent->GetInstance()->config() )\n    , m_deviceType         ( DeviceType )\n    , m_window             ( hFocusWindow )\n    , m_behaviorFlags      ( BehaviorFlags )\n    , m_adapter            ( pAdapter )\n    , m_dxvkDevice         ( dxvkDevice )\n    , m_memoryAllocator    ( )\n    , m_shaderAllocator    ( )\n    , m_ffModules          ( this )\n    , m_shaderModules      ( new D3D9ShaderModuleSet )\n    , m_stagingBuffer      ( dxvkDevice, StagingBufferSize )\n    , m_stagingBufferFence ( new sync::Fence() )\n    , m_multithread        ( BehaviorFlags & D3DCREATE_MULTITHREADED )\n    , m_isSWVP             ( (BehaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) != 0 )\n    , m_isD3D8Compatible   ( pParent->IsD3D8Compatible() )\n    , m_csThread           ( dxvkDevice, dxvkDevice->createContext() )\n    , m_csChunk            ( AllocCsChunk() )\n    , m_submissionFence    ( new sync::Fence() )\n    , m_flushTracker       ( GetMaxFlushType() )\n    , m_d3d9Interop        ( this )\n    , m_d3d9On12Args       ( pAdapter->Get9On12Args() )\n    , m_d3d9On12           ( this )\n    , m_d3d8Bridge         ( this ) {\n\n    // If we can SWVP, then we use an extended constant set\n    // as SWVP has many more slots available than HWVP.\n    bool canSWVP = CanSWVP();\n    DetermineConstantLayouts(canSWVP);\n\n    if (canSWVP)\n      Logger::info(\"D3D9DeviceEx: Using extended constant set for software vertex processing.\");\n\n    if (m_dxvkDevice->debugFlags().test(DxvkDebugFlag::Markers))\n      m_annotation = new D3D9UserDefinedAnnotation(this);\n\n    m_initializer      = new D3D9Initializer(this);\n    m_converter        = new D3D9FormatHelper(m_dxvkDevice);\n\n    EmitCs([\n      cDevice = m_dxvkDevice\n    ] (DxvkContext* ctx) {\n      ctx->beginRecording(cDevice->createCommandList());\n\n      // Disable logic op once and for all.\n      DxvkLogicOpState loState = { };\n      ctx->setLogicOpState(loState);\n    });\n\n    SynchronizeCsThread(DxvkCsThread::SynchronizeAll);\n\n    if (!(BehaviorFlags & D3DCREATE_FPU_PRESERVE))\n      SetupFPU();\n\n    m_dxsoOptions = DxsoOptions(this, m_d3d9Options);\n\n    // Check if VK_EXT_robustness2 is supported, so we can optimize the number of constants we need to copy.\n    // Also check the required alignments.\n    const bool supportsRobustness2 = m_dxvkDevice->features().extRobustness2.robustBufferAccess2;\n    bool useRobustConstantAccess = supportsRobustness2;\n    D3D9ConstantSets& vsConstSet = m_consts[uint32_t(D3D9ShaderType::VertexShader)];\n    D3D9ConstantSets& psConstSet = m_consts[uint32_t(D3D9ShaderType::PixelShader)];\n    if (useRobustConstantAccess) {\n      m_robustSSBOAlignment = m_dxvkDevice->properties().extRobustness2.robustStorageBufferAccessSizeAlignment;\n      m_robustUBOAlignment  = m_dxvkDevice->properties().extRobustness2.robustUniformBufferAccessSizeAlignment;\n      if (canSWVP) {\n        const uint32_t floatBufferAlignment = m_dxsoOptions.vertexFloatConstantBufferAsSSBO ? m_robustSSBOAlignment : m_robustUBOAlignment;\n\n        useRobustConstantAccess &= vsConstSet.layout.floatSize() % floatBufferAlignment == 0;\n        useRobustConstantAccess &= vsConstSet.layout.intSize() % m_robustUBOAlignment == 0;\n        useRobustConstantAccess &= vsConstSet.layout.bitmaskSize() % m_robustUBOAlignment == 0;\n      } else {\n        useRobustConstantAccess &= vsConstSet.layout.totalSize() % m_robustUBOAlignment == 0;\n      }\n      useRobustConstantAccess &= psConstSet.layout.totalSize() % m_robustUBOAlignment == 0;\n    }\n\n    if (!useRobustConstantAccess) {\n      // Disable optimized constant copies, we always have to copy all constants.\n      vsConstSet.maxChangedConstF = vsConstSet.layout.floatCount;\n      vsConstSet.maxChangedConstI = vsConstSet.layout.intCount;\n      vsConstSet.maxChangedConstB = vsConstSet.layout.boolCount;\n      psConstSet.maxChangedConstF = psConstSet.layout.floatCount;\n\n      if (supportsRobustness2) {\n        Logger::warn(\"Disabling robust constant buffer access because of alignment.\");\n      }\n    }\n\n    // Check for VK_EXT_graphics_pipeline_libraries\n    m_usingGraphicsPipelines = dxvkDevice->features().extGraphicsPipelineLibrary.graphicsPipelineLibrary;\n\n    // Check for VK_EXT_depth_bias_control and set up initial state\n    m_depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false };\n    if (dxvkDevice->features().extDepthBiasControl.depthBiasControl) {\n      if (dxvkDevice->features().extDepthBiasControl.depthBiasExact)\n        m_depthBiasRepresentation.depthBiasExact = true;\n\n      if (dxvkDevice->features().extDepthBiasControl.floatRepresentation) {\n        m_depthBiasRepresentation.depthBiasRepresentation = VK_DEPTH_BIAS_REPRESENTATION_FLOAT_EXT;\n        m_depthBiasScale = 1.0f;\n      }\n      else if (dxvkDevice->features().extDepthBiasControl.leastRepresentableValueForceUnormRepresentation)\n        m_depthBiasRepresentation.depthBiasRepresentation = VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT;\n    }\n\n    EmitCs([\n      cRepresentation = m_depthBiasRepresentation\n    ] (DxvkContext* ctx) {\n      ctx->setDepthBiasRepresentation(cRepresentation);\n    });\n\n    CreateConstantBuffers();\n\n    m_availableMemory = DetermineInitialTextureMemory();\n\n    m_hazardLayout = dxvkDevice->features().extAttachmentFeedbackLoopLayout.attachmentFeedbackLoopLayout\n      ? VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT\n      : VK_IMAGE_LAYOUT_GENERAL;\n\n    // Initially set all the dirty flags so we\n    // always end up giving the backend *something* to work with.\n    m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n    m_dirty.set(D3D9DeviceDirtyFlag::ClipPlanes);\n    m_dirty.set(D3D9DeviceDirtyFlag::DepthStencilState);\n    m_dirty.set(D3D9DeviceDirtyFlag::BlendState);\n    m_dirty.set(D3D9DeviceDirtyFlag::RasterizerState);\n    m_dirty.set(D3D9DeviceDirtyFlag::DepthBias);\n    m_dirty.set(D3D9DeviceDirtyFlag::AlphaTestState);\n    m_dirty.set(D3D9DeviceDirtyFlag::InputLayout);\n    m_dirty.set(D3D9DeviceDirtyFlag::ViewportScissor);\n    m_dirty.set(D3D9DeviceDirtyFlag::MultiSampleState);\n\n    m_dirty.set(D3D9DeviceDirtyFlag::FogState);\n    m_dirty.set(D3D9DeviceDirtyFlag::FogColor);\n    m_dirty.set(D3D9DeviceDirtyFlag::FogDensity);\n    m_dirty.set(D3D9DeviceDirtyFlag::FogScale);\n    m_dirty.set(D3D9DeviceDirtyFlag::FogEnd);\n\n    m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n    m_dirty.set(D3D9DeviceDirtyFlag::FFVertexBlend);\n    m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n    m_dirty.set(D3D9DeviceDirtyFlag::FFPixelShader);\n    m_dirty.set(D3D9DeviceDirtyFlag::FFViewport);\n    m_dirty.set(D3D9DeviceDirtyFlag::FFPixelData);\n    m_dirty.set(D3D9DeviceDirtyFlag::SharedPixelShaderData);\n    m_dirty.set(D3D9DeviceDirtyFlag::DepthBounds);\n    m_dirty.set(D3D9DeviceDirtyFlag::PointScale);\n\n    m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n\n    m_specInfo.set<SpecDrefScaling, uint32_t>(m_d3d9Options.drefScaling);\n\n    BindFFUbershader<D3D9ShaderType::VertexShader>();\n    BindFFUbershader<D3D9ShaderType::PixelShader>();\n\n    m_unlockAdditionalFormats = m_parent->HasFormatsUnlocked();\n  }\n\n\n  D3D9DeviceEx::~D3D9DeviceEx() {\n    // Avoids hanging when in this state, see comment\n    // in DxvkDevice::~DxvkDevice.\n    if (this_thread::isInModuleDetachment())\n      return;\n\n    Flush();\n    SynchronizeCsThread(DxvkCsThread::SynchronizeAll);\n\n    if (m_annotation)\n      delete m_annotation;\n\n    delete m_initializer;\n    delete m_converter;\n\n    m_dxvkDevice->waitForIdle(); // Sync Device\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    bool extended = m_parent->IsExtended()\n                 && riid == __uuidof(IDirect3DDevice9Ex);\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DDevice9)\n     || extended) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(IDxvkD3D8Bridge)) {\n      *ppvObject = ref(&m_d3d8Bridge);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D9VkInteropDevice)) {\n      *ppvObject = ref(&m_d3d9Interop);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(IDirect3DDevice9On12)) {\n      if (m_d3d9On12Args.Enable9On12) {\n        *ppvObject = ref(&m_d3d9On12);\n        return S_OK;\n      } else if (logQueryInterfaceError(__uuidof(IDirect3DDevice9), riid)) {\n        Logger::warn(\"D3D9DeviceEx::QueryInterface: IDirect3DDevice9On12 queried, but 9On12 not enabled for device\");\n        return E_NOINTERFACE;\n      }\n    }\n\n    // We want to ignore this if the extended device is queried and we weren't made extended.\n    if (riid == __uuidof(IDirect3DDevice9Ex))\n      return E_NOINTERFACE;\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DDevice9), riid)) {\n      Logger::warn(\"D3D9DeviceEx::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::TestCooperativeLevel() {\n    D3D9DeviceLock lock = LockDevice();\n\n    // Equivelant of D3D11/DXGI present tests. We can always present.\n    if (likely(m_deviceLostState == D3D9DeviceLostState::Ok)) {\n      return D3D_OK;\n    } else if (m_deviceLostState == D3D9DeviceLostState::NotReset) {\n      return D3DERR_DEVICENOTRESET;\n    } else {\n      return D3DERR_DEVICELOST;\n    }\n  }\n\n\n  UINT    STDMETHODCALLTYPE D3D9DeviceEx::GetAvailableTextureMem() {\n    // This is not meant to be accurate.\n    // The values are also wildly incorrect in d3d9... But some games rely\n    // on this inaccurate value...\n\n    // Clamp to megabyte range, as per spec.\n    constexpr UINT range = 0xfff00000;\n\n    // Can't have negative memory!\n    // Ensure the maximum is returned if available memory overflows the u32\n    int64_t memory = std::min(std::max<int64_t>(m_availableMemory.load(), 0), static_cast<int64_t>(range));\n\n    return UINT(memory) & range;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::EvictManagedResources() {\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDirect3D(IDirect3D9** ppD3D9) {\n    if (ppD3D9 == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    *ppD3D9 = m_parent.ref();\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDeviceCaps(D3DCAPS9* pCaps) {\n    if (pCaps == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    m_adapter->GetDeviceCaps(m_deviceType, pCaps);\n\n    // When in SWVP mode, 256 matrices can be used for indexed vertex blending\n    pCaps->MaxVertexBlendMatrixIndex = m_isSWVP ? 255 : 8;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode) {\n    if (unlikely(iSwapChain != 0))\n      return D3DERR_INVALIDCALL;\n\n    return m_implicitSwapchain->GetDisplayMode(pMode);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS *pParameters) {\n    if (pParameters == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    pParameters->AdapterOrdinal = m_adapter->GetOrdinal();\n    pParameters->BehaviorFlags  = m_behaviorFlags;\n    pParameters->DeviceType     = m_deviceType;\n    pParameters->hFocusWindow   = m_window;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetCursorProperties(\n          UINT               XHotSpot,\n          UINT               YHotSpot,\n          IDirect3DSurface9* pCursorBitmap) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pCursorBitmap == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    auto* cursorTex = GetCommonTexture(pCursorBitmap);\n    if (unlikely(cursorTex->Desc()->Format != D3D9Format::A8R8G8B8))\n      return D3DERR_INVALIDCALL;\n\n    const uint32_t inputWidth  = cursorTex->Desc()->Width;\n    const uint32_t inputHeight = cursorTex->Desc()->Height;\n\n    // Check if surface dimensions are powers of two.\n    if (unlikely((inputWidth  && (inputWidth  & (inputWidth  - 1)))\n              || (inputHeight && (inputHeight & (inputHeight - 1)))))\n      return D3DERR_INVALIDCALL;\n\n    // It makes no sense to have a hotspot outside of the bitmap.\n    if (unlikely((inputWidth  && (XHotSpot > inputWidth  - 1))\n              || (inputHeight && (YHotSpot > inputHeight - 1))))\n      return D3DERR_INVALIDCALL;\n\n    // For some reason the cursor bitmap size validation is done\n    // against the display mode dimensions (which makes for an\n    // interesting situation on windowed swapchains...)\n    D3DDISPLAYMODEEX mode = { };\n    mode.Size = sizeof(D3DDISPLAYMODEEX);\n    m_adapter->GetAdapterDisplayModeEx(&mode, nullptr);\n\n    if (unlikely(inputWidth  > mode.Width\n              || inputHeight > mode.Height))\n      return D3DERR_INVALIDCALL;\n\n    D3DPRESENT_PARAMETERS params;\n    m_implicitSwapchain->GetPresentParameters(&params);\n\n    // Use a hardware cursor if w/h == 32 px or when windowed.\n    const bool hwCursor = (inputWidth  == HardwareCursorWidth &&\n                           inputHeight == HardwareCursorHeight)\n                          || params.Windowed;\n\n    D3DLOCKED_BOX lockedBox;\n    HRESULT hr = LockImage(cursorTex, 0, 0, &lockedBox, nullptr, D3DLOCK_READONLY);\n    if (unlikely(FAILED(hr)))\n      return hr;\n\n    const uint8_t* data = reinterpret_cast<const uint8_t*>(lockedBox.pBits);\n\n    if (hwCursor) {\n      CursorBitmap bitmap = { 0 };\n      // We need to consider applications that might misbehave in\n      // windowed mode, setting a cursor smaller or larger than 32 x 32 px.\n      const size_t copyPitch = std::min<size_t>(\n        HardwareCursorPitch,\n        inputWidth * HardwareCursorFormatSize);\n      const size_t copyHeight = std::min<size_t>(\n        HardwareCursorHeight,\n        inputHeight);\n\n      // Windows works with a stride of 128, let's respect that.\n      for (uint32_t h = 0; h < copyHeight; h++)\n        std::memcpy(&bitmap[h * HardwareCursorPitch], &data[h * lockedBox.RowPitch], copyPitch);\n\n      hr = UnlockImage(cursorTex, 0, 0);\n      if (unlikely(FAILED(hr)))\n        return hr;\n\n      m_cursor.SetHardwareCursor(XHotSpot, YHotSpot, bitmap);\n    } else {\n      const size_t copyPitch = inputWidth * HardwareCursorFormatSize;\n      std::vector<uint8_t> bitmap(inputHeight * copyPitch, 0);\n\n      for (uint32_t h = 0; h < inputHeight; h++)\n        std::memcpy(&bitmap[h * copyPitch], &data[h * lockedBox.RowPitch], copyPitch);\n\n      hr = UnlockImage(cursorTex, 0, 0);\n      if (unlikely(FAILED(hr)))\n        return hr;\n\n      m_implicitSwapchain->SetCursorTexture(inputWidth, inputHeight, &bitmap[0]);\n\n      m_cursor.SetSoftwareCursor(XHotSpot, YHotSpot, inputWidth, inputHeight);\n    }\n\n    return D3D_OK;\n  }\n\n\n  void    STDMETHODCALLTYPE D3D9DeviceEx::SetCursorPosition(int X, int Y, DWORD Flags) {\n    D3D9DeviceLock lock = LockDevice();\n\n    // I was not able to find an instance\n    // where the cursor update was not immediate.\n\n    // Fullscreen + Windowed seem to have the same\n    // behaviour here.\n\n    // Hence we ignore the flag D3DCURSOR_IMMEDIATE_UPDATE.\n\n    m_cursor.UpdateCursor(X, Y);\n  }\n\n\n  BOOL    STDMETHODCALLTYPE D3D9DeviceEx::ShowCursor(BOOL bShow) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return m_cursor.ShowCursor(bShow);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateAdditionalSwapChain(\n          D3DPRESENT_PARAMETERS* pPresentationParameters,\n          IDirect3DSwapChain9**  ppSwapChain) {\n    return CreateAdditionalSwapChainEx(pPresentationParameters, nullptr, ppSwapChain);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain) {\n    D3D9DeviceLock lock = LockDevice();\n\n    InitReturnPtr(pSwapChain);\n\n    if (unlikely(pSwapChain == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // This only returns the implicit swapchain...\n\n    if (unlikely(iSwapChain != 0))\n      return D3DERR_INVALIDCALL;\n\n    *pSwapChain = static_cast<IDirect3DSwapChain9*>(m_implicitSwapchain.ref());\n\n    return D3D_OK;\n  }\n\n\n  UINT    STDMETHODCALLTYPE D3D9DeviceEx::GetNumberOfSwapChains() {\n    // This only counts the implicit swapchain...\n\n    return 1;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) {\n    D3D9DeviceLock lock = LockDevice();\n\n    Logger::info(\"Device reset\");\n    m_deviceLostState = D3D9DeviceLostState::Ok;\n\n    HRESULT hr;\n    // Black Desert creates a D3DDEVTYPE_NULLREF device and\n    // expects reset to work despite passing invalid parameters.\n    if (likely(m_deviceType != D3DDEVTYPE_NULLREF)) {\n      hr = m_parent->ValidatePresentationParameters(pPresentationParameters);\n\n      if (unlikely(FAILED(hr)))\n        return hr;\n    }\n\n    if (!IsExtended()) {\n      // The internal references are always cleared, regardless of whether the Reset call succeeds.\n      ResetState(pPresentationParameters);\n      m_implicitSwapchain->DestroyBackBuffers();\n      m_autoDepthStencil = nullptr;\n\n      // Unbind all buffers that were still bound to the backend to avoid leaks.\n      EmitCs([](DxvkContext* ctx) {\n        ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32);\n        for (uint32_t i = 0; i < caps::MaxStreams; i++) {\n          ctx->bindVertexBuffer(i, DxvkBufferSlice(), 0);\n        }\n      });\n\n      // Tests show that regular D3D9 ends the scene in Reset\n      // while D3D9Ex doesn't.\n      // Observed in Empires: Dawn of the Modern World (D3D8)\n      // and the OSU compatibility mode (D3D9Ex).\n      m_inScene = false;\n    } else {\n      // Extended devices will not reset the MinZ/MaxZ viewport values\n      const float MinZ = m_state.viewport.MinZ;\n      const float MaxZ = m_state.viewport.MaxZ;\n\n      // Extended devices only reset the bound render targets\n      for (uint32_t i = 0; i < caps::MaxSimultaneousRenderTargets; i++) {\n        SetRenderTargetInternal(i, nullptr);\n      }\n\n      // Previous MinZ/MaxZ values (saved above) need to be restored\n      m_state.viewport.MinZ = MinZ;\n      m_state.viewport.MaxZ = MaxZ;\n\n      SetDepthStencilSurface(nullptr);\n    }\n\n    m_cursor.ResetCursor();\n\n    /*\n      * Before calling the IDirect3DDevice9::Reset method for a device,\n      * an application should release any explicit render targets,\n      * depth stencil surfaces, additional swap chains, state blocks,\n      * and D3DPOOL_DEFAULT resources associated with the device.\n      *\n      * We have to check after ResetState clears the references held by SetTexture, etc.\n      * This matches what Windows D3D9 does.\n    */\n    if (unlikely(m_losableResourceCounter.load() != 0 && !IsExtended() && m_d3d9Options.countLosableResources)) {\n      Logger::warn(str::format(\"Device reset failed because device still has alive losable resources: Device not reset. Remaining resources: \", m_losableResourceCounter.load()));\n      m_deviceLostState = D3D9DeviceLostState::NotReset;\n      // D3D8 returns D3DERR_DEVICELOST here, whereas D3D9 returns D3DERR_INVALIDCALL.\n      return m_isD3D8Compatible ? D3DERR_DEVICELOST : D3DERR_INVALIDCALL;\n    }\n\n    hr = ResetSwapChain(pPresentationParameters, nullptr);\n    if (unlikely(FAILED(hr))) {\n      if (!IsExtended()) {\n        Logger::warn(\"Device reset failed: Device not reset\");\n        m_deviceLostState = D3D9DeviceLostState::NotReset;\n      }\n      return hr;\n    }\n\n    Flush();\n    SynchronizeCsThread(DxvkCsThread::SynchronizeAll);\n\n    if (m_d3d9Options.deferSurfaceCreation)\n      m_resetCtr++;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Present(\n    const RECT*    pSourceRect,\n    const RECT*    pDestRect,\n          HWND     hDestWindowOverride,\n    const RGNDATA* pDirtyRegion) {\n    return PresentEx(\n      pSourceRect,\n      pDestRect,\n      hDestWindowOverride,\n      pDirtyRegion,\n      0);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetBackBuffer(\n          UINT                iSwapChain,\n          UINT                iBackBuffer,\n          D3DBACKBUFFER_TYPE  Type,\n          IDirect3DSurface9** ppBackBuffer) {\n    InitReturnPtr(ppBackBuffer);\n\n    if (unlikely(iSwapChain != 0))\n      return D3DERR_INVALIDCALL;\n\n    return m_implicitSwapchain->GetBackBuffer(iBackBuffer, Type, ppBackBuffer);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus) {\n    if (unlikely(iSwapChain != 0))\n      return D3DERR_INVALIDCALL;\n\n    return m_implicitSwapchain->GetRasterStatus(pRasterStatus);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetDialogBoxMode(BOOL bEnableDialogs) {\n    return m_implicitSwapchain->SetDialogBoxMode(bEnableDialogs);\n  }\n\n\n  void    STDMETHODCALLTYPE D3D9DeviceEx::SetGammaRamp(\n          UINT          iSwapChain,\n          DWORD         Flags,\n    const D3DGAMMARAMP* pRamp) {\n    if (unlikely(iSwapChain != 0))\n      return;\n\n    m_implicitSwapchain->SetGammaRamp(Flags, pRamp);\n  }\n\n\n  void    STDMETHODCALLTYPE D3D9DeviceEx::GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp) {\n    if (unlikely(iSwapChain != 0))\n      return;\n\n    m_implicitSwapchain->GetGammaRamp(pRamp);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateTexture(\n          UINT                Width,\n          UINT                Height,\n          UINT                Levels,\n          DWORD               Usage,\n          D3DFORMAT           Format,\n          D3DPOOL             Pool,\n          IDirect3DTexture9** ppTexture,\n          HANDLE*             pSharedHandle) {\n    InitReturnPtr(ppTexture);\n\n    if (unlikely(ppTexture == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D9_COMMON_TEXTURE_DESC desc;\n    desc.Width              = Width;\n    desc.Height             = Height;\n    desc.Depth              = 1;\n    desc.ArraySize          = 1;\n    desc.MipLevels          = Levels;\n    desc.Usage              = Usage;\n    desc.Format             = EnumerateFormat(Format);\n    desc.Pool               = Pool;\n    desc.Discard            = FALSE;\n    desc.MultiSample        = D3DMULTISAMPLE_NONE;\n    desc.MultisampleQuality = 0;\n    desc.IsBackBuffer       = FALSE;\n    desc.IsAttachmentOnly   = FALSE;\n    // Docs:\n    // Textures placed in the D3DPOOL_DEFAULT pool cannot be locked\n    // unless they are dynamic textures or they are private, FOURCC, driver formats.\n    desc.IsLockable         = Pool != D3DPOOL_DEFAULT\n                            || (Usage & D3DUSAGE_DYNAMIC)\n                            || IsVendorFormat(EnumerateFormat(Format));\n\n    HRESULT hr = D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_TEXTURE, &desc);\n    if (FAILED(hr))\n      return hr;\n\n    try {\n      void* initialData = nullptr;\n\n      // On Windows Vista (so most likely D3D9Ex), pSharedHandle can be used to pass initial data\n      // for a texture, but only for a very specific type of texture.\n      if (unlikely(pSharedHandle != nullptr && Pool == D3DPOOL_SYSTEMMEM && Levels == 1)) {\n        initialData = *(reinterpret_cast<void**>(pSharedHandle));\n        pSharedHandle = nullptr;\n      }\n\n      // Shared textures have to be in POOL_DEFAULT\n      if (unlikely(pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT))\n        return D3DERR_INVALIDCALL;\n\n      // Shared resource handle has to be a D3DKMT global handle */\n      if (unlikely(pSharedHandle != nullptr && *pSharedHandle != nullptr &&\n                   !ValidateSharedTexture(*pSharedHandle, D3DRTYPE_TEXTURE, desc)))\n        return E_INVALIDARG;\n\n      const Com<D3D9Texture2D> texture = new D3D9Texture2D(this, &desc, IsExtended(), pSharedHandle);\n\n      m_initializer->InitTexture(texture->GetCommonTexture(), initialData);\n      *ppTexture = texture.ref();\n\n      if (desc.Pool == D3DPOOL_DEFAULT)\n        m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_OUTOFVIDEOMEMORY;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateVolumeTexture(\n          UINT                      Width,\n          UINT                      Height,\n          UINT                      Depth,\n          UINT                      Levels,\n          DWORD                     Usage,\n          D3DFORMAT                 Format,\n          D3DPOOL                   Pool,\n          IDirect3DVolumeTexture9** ppVolumeTexture,\n          HANDLE*                   pSharedHandle) {\n    InitReturnPtr(ppVolumeTexture);\n\n    if (unlikely(ppVolumeTexture == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pSharedHandle))\n        Logger::err(\"CreateVolumeTexture: Shared volume textures not supported\");\n\n    D3D9_COMMON_TEXTURE_DESC desc;\n    desc.Width              = Width;\n    desc.Height             = Height;\n    desc.Depth              = Depth;\n    desc.ArraySize          = 1;\n    desc.MipLevels          = Levels;\n    desc.Usage              = Usage;\n    desc.Format             = EnumerateFormat(Format);\n    desc.Pool               = Pool;\n    desc.Discard            = FALSE;\n    desc.MultiSample        = D3DMULTISAMPLE_NONE;\n    desc.MultisampleQuality = 0;\n    desc.IsBackBuffer       = FALSE;\n    desc.IsAttachmentOnly   = FALSE;\n    // Docs:\n    // Textures placed in the D3DPOOL_DEFAULT pool cannot be locked\n    // unless they are dynamic textures. Volume textures do not\n    // exempt private, FOURCC, driver formats from these checks.\n    desc.IsLockable         = Pool != D3DPOOL_DEFAULT\n                            || (Usage & D3DUSAGE_DYNAMIC);\n\n    HRESULT hr = D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_VOLUMETEXTURE, &desc);\n    if (FAILED(hr))\n      return hr;\n\n    if (unlikely(pSharedHandle != nullptr && *pSharedHandle != nullptr &&\n                 !ValidateSharedTexture(*pSharedHandle, D3DRTYPE_VOLUMETEXTURE, desc)))\n      return E_INVALIDARG;\n\n    try {\n      const Com<D3D9Texture3D> texture = new D3D9Texture3D(this, &desc, IsExtended());\n      m_initializer->InitTexture(texture->GetCommonTexture());\n      *ppVolumeTexture = texture.ref();\n\n      // The device cannot be reset if there's any remaining default resources\n      if (desc.Pool == D3DPOOL_DEFAULT)\n        m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_OUTOFVIDEOMEMORY;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateCubeTexture(\n          UINT                    EdgeLength,\n          UINT                    Levels,\n          DWORD                   Usage,\n          D3DFORMAT               Format,\n          D3DPOOL                 Pool,\n          IDirect3DCubeTexture9** ppCubeTexture,\n          HANDLE*                 pSharedHandle) {\n    InitReturnPtr(ppCubeTexture);\n\n    if (unlikely(ppCubeTexture == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pSharedHandle))\n        Logger::err(\"CreateCubeTexture: Shared cube textures not supported\");\n\n    D3D9_COMMON_TEXTURE_DESC desc;\n    desc.Width              = EdgeLength;\n    desc.Height             = EdgeLength;\n    desc.Depth              = 1;\n    desc.ArraySize          = 6; // A cube has 6 faces, wowwie!\n    desc.MipLevels          = Levels;\n    desc.Usage              = Usage;\n    desc.Format             = EnumerateFormat(Format);\n    desc.Pool               = Pool;\n    desc.Discard            = FALSE;\n    desc.MultiSample        = D3DMULTISAMPLE_NONE;\n    desc.MultisampleQuality = 0;\n    desc.IsBackBuffer       = FALSE;\n    desc.IsAttachmentOnly   = FALSE;\n    // Docs:\n    // Textures placed in the D3DPOOL_DEFAULT pool cannot be locked\n    // unless they are dynamic textures or they are private, FOURCC, driver formats.\n    desc.IsLockable         = Pool != D3DPOOL_DEFAULT\n                            || (Usage & D3DUSAGE_DYNAMIC)\n                            || IsVendorFormat(EnumerateFormat(Format));\n\n    HRESULT hr = D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_CUBETEXTURE, &desc);\n    if (FAILED(hr))\n      return hr;\n\n    if (unlikely(pSharedHandle != nullptr && *pSharedHandle != nullptr &&\n                 !ValidateSharedTexture(*pSharedHandle, D3DRTYPE_CUBETEXTURE, desc)))\n      return E_INVALIDARG;\n\n    try {\n      const Com<D3D9TextureCube> texture = new D3D9TextureCube(this, &desc, IsExtended());\n      m_initializer->InitTexture(texture->GetCommonTexture());\n      *ppCubeTexture = texture.ref();\n\n      // The device cannot be reset if there's any remaining default resources\n      if (desc.Pool == D3DPOOL_DEFAULT)\n        m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_OUTOFVIDEOMEMORY;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateVertexBuffer(\n          UINT                     Length,\n          DWORD                    Usage,\n          DWORD                    FVF,\n          D3DPOOL                  Pool,\n          IDirect3DVertexBuffer9** ppVertexBuffer,\n          HANDLE*                  pSharedHandle) {\n    InitReturnPtr(ppVertexBuffer);\n\n    if (unlikely(ppVertexBuffer == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT))\n      return D3DERR_NOTAVAILABLE;\n\n    if (unlikely(pSharedHandle))\n        Logger::err(\"CreateVertexBuffer: Shared vertex buffers not supported\");\n\n    D3D9_BUFFER_DESC desc;\n    desc.Format = D3D9Format::VERTEXDATA;\n    desc.FVF    = FVF;\n    desc.Pool   = Pool;\n    desc.Size   = Length;\n    desc.Type   = D3DRTYPE_VERTEXBUFFER;\n    desc.Usage  = Usage;\n\n    if (FAILED(D3D9CommonBuffer::ValidateBufferProperties(&desc, IsExtended())))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pSharedHandle != nullptr && *pSharedHandle != nullptr &&\n                 !ValidateSharedBuffer(*pSharedHandle, desc)))\n      return E_INVALIDARG;\n\n    try {\n      const Com<D3D9VertexBuffer> buffer = new D3D9VertexBuffer(this, &desc, IsExtended());\n      m_initializer->InitBuffer(buffer->GetCommonBuffer());\n      *ppVertexBuffer = buffer.ref();\n\n      // The device cannot be reset if there's any remaining default resources\n      if (desc.Pool == D3DPOOL_DEFAULT)\n        m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError & e) {\n      Logger::err(e.message());\n      return D3DERR_INVALIDCALL;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateIndexBuffer(\n          UINT                    Length,\n          DWORD                   Usage,\n          D3DFORMAT               Format,\n          D3DPOOL                 Pool,\n          IDirect3DIndexBuffer9** ppIndexBuffer,\n          HANDLE*                 pSharedHandle) {\n    InitReturnPtr(ppIndexBuffer);\n\n    if (unlikely(ppIndexBuffer == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT))\n      return D3DERR_NOTAVAILABLE;\n\n    if (unlikely(pSharedHandle))\n        Logger::err(\"CreateIndexBuffer: Shared index buffers not supported\");\n\n    D3D9_BUFFER_DESC desc;\n    desc.Format = EnumerateFormat(Format);\n    desc.Pool   = Pool;\n    desc.Size   = Length;\n    desc.Type   = D3DRTYPE_INDEXBUFFER;\n    desc.Usage  = Usage;\n\n    if (FAILED(D3D9CommonBuffer::ValidateBufferProperties(&desc, IsExtended())))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pSharedHandle != nullptr && *pSharedHandle != nullptr &&\n                 !ValidateSharedBuffer(*pSharedHandle, desc)))\n      return E_INVALIDARG;\n\n    try {\n      const Com<D3D9IndexBuffer> buffer = new D3D9IndexBuffer(this, &desc, IsExtended());\n      m_initializer->InitBuffer(buffer->GetCommonBuffer());\n      *ppIndexBuffer = buffer.ref();\n\n      // The device cannot be reset if there's any remaining default resources\n      if (desc.Pool == D3DPOOL_DEFAULT)\n        m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError & e) {\n      Logger::err(e.message());\n      return D3DERR_INVALIDCALL;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateRenderTarget(\n          UINT                Width,\n          UINT                Height,\n          D3DFORMAT           Format,\n          D3DMULTISAMPLE_TYPE MultiSample,\n          DWORD               MultisampleQuality,\n          BOOL                Lockable,\n          IDirect3DSurface9** ppSurface,\n          HANDLE*             pSharedHandle) {\n    return CreateRenderTargetEx(\n      Width,\n      Height,\n      Format,\n      MultiSample,\n      MultisampleQuality,\n      Lockable,\n      ppSurface,\n      pSharedHandle,\n      0);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateDepthStencilSurface(\n          UINT                Width,\n          UINT                Height,\n          D3DFORMAT           Format,\n          D3DMULTISAMPLE_TYPE MultiSample,\n          DWORD               MultisampleQuality,\n          BOOL                Discard,\n          IDirect3DSurface9** ppSurface,\n          HANDLE*             pSharedHandle) {\n    return CreateDepthStencilSurfaceEx(\n      Width,\n      Height,\n      Format,\n      MultiSample,\n      MultisampleQuality,\n      Discard,\n      ppSurface,\n      pSharedHandle,\n      0);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::UpdateSurface(\n          IDirect3DSurface9* pSourceSurface,\n    const RECT*              pSourceRect,\n          IDirect3DSurface9* pDestinationSurface,\n    const POINT*             pDestPoint) {\n    D3D9DeviceLock lock = LockDevice();\n\n    D3D9Surface* src = static_cast<D3D9Surface*>(pSourceSurface);\n    D3D9Surface* dst = static_cast<D3D9Surface*>(pDestinationSurface);\n\n    if (unlikely(src == nullptr || dst == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D9CommonTexture* srcTextureInfo = src->GetCommonTexture();\n    D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture();\n\n    if (unlikely(srcTextureInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM || dstTextureInfo->Desc()->Pool != D3DPOOL_DEFAULT))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(srcTextureInfo->Desc()->Format != dstTextureInfo->Desc()->Format))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(srcTextureInfo->Desc()->MultiSample != D3DMULTISAMPLE_NONE))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(dstTextureInfo->Desc()->MultiSample != D3DMULTISAMPLE_NONE))\n      return D3DERR_INVALIDCALL;\n\n    const DxvkFormatInfo* formatInfo = lookupFormatInfo(dstTextureInfo->GetFormatMapping().FormatColor);\n\n    VkOffset3D srcOffset = { 0u, 0u, 0u };\n    VkOffset3D dstOffset = { 0u, 0u, 0u };\n    VkExtent3D texLevelExtent = srcTextureInfo->GetExtentMip(src->GetSubresource());\n    VkExtent3D extent = texLevelExtent;\n\n    if (pSourceRect != nullptr) {\n      srcOffset = { pSourceRect->left,\n                    pSourceRect->top,\n                    0u };\n\n      extent = { uint32_t(pSourceRect->right - pSourceRect->left), uint32_t(pSourceRect->bottom - pSourceRect->top), 1 };\n\n      const bool extentAligned = extent.width % formatInfo->blockSize.width == 0\n        && extent.height % formatInfo->blockSize.height == 0;\n\n      if (pSourceRect->left < 0\n        || pSourceRect->top < 0\n        || pSourceRect->right <= pSourceRect->left\n        || pSourceRect->bottom <= pSourceRect->top\n        || pSourceRect->left % formatInfo->blockSize.width != 0\n        || pSourceRect->top % formatInfo->blockSize.height != 0\n        || (extent != texLevelExtent && !extentAligned))\n        return D3DERR_INVALIDCALL;\n    }\n\n    if (pDestPoint != nullptr) {\n      if (pDestPoint->x % formatInfo->blockSize.width != 0\n        || pDestPoint->y % formatInfo->blockSize.height != 0\n        || pDestPoint->x < 0\n        || pDestPoint->y < 0)\n        return D3DERR_INVALIDCALL;\n\n      dstOffset = { pDestPoint->x,\n                    pDestPoint->y,\n                    0u };\n    }\n\n    // The source surface must be in D3DPOOL_SYSTEMMEM so we just treat it as just another texture upload except with a different source.\n    UpdateTextureFromBuffer(dstTextureInfo, srcTextureInfo, dst->GetSubresource(), src->GetSubresource(), srcOffset, extent, dstOffset);\n\n    // The contents of the mapping no longer match the image.\n    dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true);\n\n    if (dstTextureInfo->IsAutomaticMip())\n      MarkTextureMipsDirty(dstTextureInfo);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::UpdateTexture(\n          IDirect3DBaseTexture9* pSourceTexture,\n          IDirect3DBaseTexture9* pDestinationTexture) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (!pDestinationTexture || !pSourceTexture)\n      return D3DERR_INVALIDCALL;\n\n    D3D9CommonTexture* dstTexInfo = GetCommonTexture(pDestinationTexture);\n    D3D9CommonTexture* srcTexInfo = GetCommonTexture(pSourceTexture);\n\n    if (unlikely(srcTexInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM || dstTexInfo->Desc()->Pool != D3DPOOL_DEFAULT))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(srcTexInfo->Desc()->MipLevels < dstTexInfo->Desc()->MipLevels && !dstTexInfo->IsAutomaticMip()))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(dstTexInfo->Desc()->Format != srcTexInfo->Desc()->Format))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(srcTexInfo->IsAutomaticMip() && !dstTexInfo->IsAutomaticMip()))\n      return D3DERR_INVALIDCALL;\n\n    const Rc<DxvkImage> dstImage  = dstTexInfo->GetImage();\n    uint32_t srcMipLevels = srcTexInfo->IsAutomaticMip() ? 1 : srcTexInfo->Desc()->MipLevels;\n    uint32_t dstMipLevels = dstTexInfo->IsAutomaticMip() ? 1 : dstTexInfo->Desc()->MipLevels;\n    uint32_t arraySlices = std::min(srcTexInfo->Desc()->ArraySize, dstTexInfo->Desc()->ArraySize);\n\n    uint32_t srcMipOffset = 0;\n    VkExtent3D srcFirstMipExtent = srcTexInfo->GetExtent();\n    VkExtent3D dstFirstMipExtent = dstTexInfo->GetExtent();\n\n    if (srcMipLevels > 1 || dstMipLevels > 1) {\n      // UpdateTexture does not validate dimensions for textures with only one mip\n      if (srcFirstMipExtent != dstFirstMipExtent) {\n        // UpdateTexture can be used with textures that have different mip lengths.\n        // It will either match the the top mips or the bottom ones.\n        // If the largest mip maps don't match in size, we try to take the smallest ones\n        // of the source.\n\n        srcMipOffset = srcTexInfo->Desc()->MipLevels - dstMipLevels;\n        srcFirstMipExtent = util::computeMipLevelExtent(srcTexInfo->GetExtent(), srcMipOffset);\n        dstFirstMipExtent = dstTexInfo->GetExtent();\n      }\n\n      if (srcFirstMipExtent != dstFirstMipExtent)\n        return D3DERR_INVALIDCALL;\n    } else {\n      if (unlikely(srcFirstMipExtent.width  > dstFirstMipExtent.width\n                || srcFirstMipExtent.height > dstFirstMipExtent.height\n                || srcFirstMipExtent.depth  > dstFirstMipExtent.depth))\n        Logger::warn(\"D3D9DeviceEx::UpdateTexture: Source dimensions exceed the destination\");\n    }\n\n    for (uint32_t a = 0; a < arraySlices; a++) {\n      // The docs claim that the dirty box is just a performance optimization, however in practice games rely on it.\n      const D3DBOX& box = srcTexInfo->GetDirtyBox(a);\n      if (box.Left >= box.Right || box.Top >= box.Bottom || box.Front >= box.Back)\n        continue;\n\n      // The dirty box is only tracked for mip level 0\n      VkExtent3D mip0Extent = {\n        uint32_t(box.Right - box.Left),\n        uint32_t(box.Bottom - box.Top),\n        uint32_t(box.Back - box.Front)\n      };\n      VkOffset3D mip0Offset = { int32_t(box.Left), int32_t(box.Top), int32_t(box.Front) };\n\n      for (uint32_t dstMip = 0; dstMip < dstMipLevels; dstMip++) {\n        // Scale the dirty box for the respective mip level\n        uint32_t srcMip = dstMip + srcMipOffset;\n        uint32_t srcSubresource = srcTexInfo->CalcSubresource(a, srcMip);\n        uint32_t dstSubresource = dstTexInfo->CalcSubresource(a, dstMip);\n        VkExtent3D extent = util::computeMipLevelExtent(mip0Extent, srcMip);\n        VkOffset3D offset = util::computeMipLevelOffset(mip0Offset, srcMip);\n\n        // The source surface must be in D3DPOOL_SYSTEMMEM so we just treat it as just another texture upload except with a different source.\n        UpdateTextureFromBuffer(dstTexInfo, srcTexInfo, dstSubresource, srcSubresource, offset, extent, offset);\n\n        // The contents of the mapping no longer match the image.\n        dstTexInfo->SetNeedsReadback(dstSubresource, true);\n      }\n    }\n\n    srcTexInfo->ClearDirtyBoxes();\n    if (dstTexInfo->IsAutomaticMip() && dstMipLevels != dstTexInfo->Desc()->MipLevels)\n      MarkTextureMipsDirty(dstTexInfo);\n\n    ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetRenderTargetData(\n          IDirect3DSurface9* pRenderTarget,\n          IDirect3DSurface9* pDestSurface) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(IsDeviceLost())) {\n      return D3DERR_DEVICELOST;\n    }\n\n    D3D9Surface* src = static_cast<D3D9Surface*>(pRenderTarget);\n    D3D9Surface* dst = static_cast<D3D9Surface*>(pDestSurface);\n\n    if (unlikely(src == nullptr || dst == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (pRenderTarget == pDestSurface)\n      return D3D_OK;\n\n    D3D9CommonTexture* dstTexInfo = GetCommonTexture(dst);\n    D3D9CommonTexture* srcTexInfo = GetCommonTexture(src);\n\n    if (srcTexInfo->Desc()->Format != dstTexInfo->Desc()->Format)\n      return D3DERR_INVALIDCALL;\n\n    if (src->GetSurfaceExtent() != dst->GetSurfaceExtent())\n      return D3DERR_INVALIDCALL;\n\n    if (dstTexInfo->Desc()->Pool == D3DPOOL_DEFAULT)\n      return this->StretchRect(pRenderTarget, nullptr, pDestSurface, nullptr, D3DTEXF_NONE);\n\n    VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel());\n    VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(src->GetMipLevel());\n\n    const bool clearDst = dstTexInfo->Desc()->MipLevels > 1\n                       || dstTexExtent.width > srcTexExtent.width\n                       || dstTexExtent.height > srcTexExtent.height;\n\n    dstTexInfo->CreateBuffer(clearDst, dstTexInfo->GetTotalSize());\n    DxvkBufferSlice dstBufferSlice      = dstTexInfo->GetBufferSlice(dst->GetSubresource());\n    Rc<DxvkImage> srcImage              = srcTexInfo->GetImage();\n    const DxvkFormatInfo* srcFormatInfo = lookupFormatInfo(srcImage->info().format);\n\n    const VkImageSubresource srcSubresource = srcTexInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, src->GetSubresource());\n    VkImageSubresourceLayers srcSubresourceLayers = {\n      srcSubresource.aspectMask,\n      srcSubresource.mipLevel,\n      srcSubresource.arrayLayer, 1 };\n\n    EmitCs([\n      cBufferSlice  = std::move(dstBufferSlice),\n      cImage        = srcImage,\n      cSubresources = srcSubresourceLayers,\n      cLevelExtent  = srcTexExtent\n    ] (DxvkContext* ctx) {\n      ctx->copyImageToBuffer(cBufferSlice.buffer(), cBufferSlice.offset(),\n        4, 0, VK_FORMAT_UNDEFINED, cImage, cSubresources,\n        VkOffset3D { 0, 0, 0 }, cLevelExtent);\n    });\n\n    dstTexInfo->SetNeedsReadback(dst->GetSubresource(), true);\n    TrackTextureMappingBufferSequenceNumber(dstTexInfo, dst->GetSubresource());\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface) {\n    if (unlikely(iSwapChain != 0))\n      return D3DERR_INVALIDCALL;\n\n    D3D9DeviceLock lock = LockDevice();\n\n    // In windowed mode, GetFrontBufferData takes a screenshot of the entire screen.\n    // We use the last used swapchain as a workaround.\n    // Total War: Medieval 2 relies on this.\n    return m_mostRecentlyUsedSwapchain->GetFrontBufferData(pDestSurface);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::StretchRect(\n          IDirect3DSurface9*   pSourceSurface,\n    const RECT*                pSourceRect,\n          IDirect3DSurface9*   pDestSurface,\n    const RECT*                pDestRect,\n          D3DTEXTUREFILTERTYPE Filter) {\n    D3D9DeviceLock lock = LockDevice();\n\n    D3D9Surface* dst = static_cast<D3D9Surface*>(pDestSurface);\n    D3D9Surface* src = static_cast<D3D9Surface*>(pSourceSurface);\n\n    if (unlikely(src == nullptr || dst == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(src == dst))\n      return D3DERR_INVALIDCALL;\n\n    bool fastPath = true;\n\n    D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture();\n    D3D9CommonTexture* srcTextureInfo = src->GetCommonTexture();\n\n    if (unlikely(dstTextureInfo->Desc()->Pool != D3DPOOL_DEFAULT ||\n                 srcTextureInfo->Desc()->Pool != D3DPOOL_DEFAULT))\n      return D3DERR_INVALIDCALL;\n\n    Rc<DxvkImage> dstImage = dstTextureInfo->GetImage();\n    Rc<DxvkImage> srcImage = srcTextureInfo->GetImage();\n\n    if (dstImage == nullptr || srcImage == nullptr)\n        return D3DERR_INVALIDCALL;\n\n    const DxvkFormatInfo* dstFormatInfo = lookupFormatInfo(dstImage->info().format);\n    const DxvkFormatInfo* srcFormatInfo = lookupFormatInfo(srcImage->info().format);\n\n    const VkImageSubresource dstSubresource = dstTextureInfo->GetSubresourceFromIndex(dstFormatInfo->aspectMask, dst->GetSubresource());\n    const VkImageSubresource srcSubresource = srcTextureInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, src->GetSubresource());\n\n    if (unlikely(Filter != D3DTEXF_NONE && Filter != D3DTEXF_LINEAR && Filter != D3DTEXF_POINT))\n      return D3DERR_INVALIDCALL;\n\n    VkExtent3D srcExtent = srcImage->mipLevelExtent(srcSubresource.mipLevel);\n    VkExtent3D dstExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel);\n\n    D3D9Format srcFormat = srcTextureInfo->Desc()->Format;\n    D3D9Format dstFormat = dstTextureInfo->Desc()->Format;\n\n    // We may only fast path copy non identicals one way!\n    // We don't know what garbage could be in the X8 data.\n    bool similar = AreFormatsSimilar(srcFormat, dstFormat);\n\n    // Copies are only supported on similar formats.\n    fastPath &= similar;\n\n    // Copies are only supported if the sample count matches,\n    // otherwise we need to resolve.\n    auto needsResolve = false;\n    if (srcImage->info().sampleCount != dstImage->info().sampleCount) {\n      needsResolve = srcImage->info().sampleCount != VK_SAMPLE_COUNT_1_BIT;\n      auto fbBlit = dstImage->info().sampleCount != VK_SAMPLE_COUNT_1_BIT;\n      fastPath &= !fbBlit;\n    }\n\n    // Copies would only work if we are block aligned.\n    if (pSourceRect != nullptr) {\n      fastPath       &=  (pSourceRect->left   % srcFormatInfo->blockSize.width  == 0);\n      fastPath       &=  (pSourceRect->right  % srcFormatInfo->blockSize.width  == 0);\n      fastPath       &=  (pSourceRect->top    % srcFormatInfo->blockSize.height == 0);\n      fastPath       &=  (pSourceRect->bottom % srcFormatInfo->blockSize.height == 0);\n    }\n\n    if (pDestRect != nullptr) {\n      fastPath       &=  (pDestRect->left     % dstFormatInfo->blockSize.width  == 0);\n      fastPath       &=  (pDestRect->top      % dstFormatInfo->blockSize.height == 0);\n    }\n\n    VkImageSubresourceLayers dstSubresourceLayers = {\n      dstSubresource.aspectMask,\n      dstSubresource.mipLevel,\n      dstSubresource.arrayLayer, 1 };\n\n    VkImageSubresourceLayers srcSubresourceLayers = {\n      srcSubresource.aspectMask,\n      srcSubresource.mipLevel,\n      srcSubresource.arrayLayer, 1 };\n\n    VkImageBlit blitInfo;\n    blitInfo.dstSubresource = dstSubresourceLayers;\n    blitInfo.srcSubresource = srcSubresourceLayers;\n\n    blitInfo.dstOffsets[0] = pDestRect != nullptr\n      ? VkOffset3D{ int32_t(pDestRect->left), int32_t(pDestRect->top), 0 }\n      : VkOffset3D{ 0,                        0,                       0 };\n\n    blitInfo.dstOffsets[1] = pDestRect != nullptr\n      ? VkOffset3D{ int32_t(pDestRect->right), int32_t(pDestRect->bottom), 1 }\n      : VkOffset3D{ int32_t(dstExtent.width),  int32_t(dstExtent.height),  1 };\n\n    blitInfo.srcOffsets[0] = pSourceRect != nullptr\n      ? VkOffset3D{ int32_t(pSourceRect->left), int32_t(pSourceRect->top), 0 }\n      : VkOffset3D{ 0,                          0,                         0 };\n\n    blitInfo.srcOffsets[1] = pSourceRect != nullptr\n      ? VkOffset3D{ int32_t(pSourceRect->right), int32_t(pSourceRect->bottom), 1 }\n      : VkOffset3D{ int32_t(srcExtent.width),    int32_t(srcExtent.height),    1 };\n\n    if (unlikely(IsBlitRegionInvalid(blitInfo.srcOffsets, srcExtent)))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(IsBlitRegionInvalid(blitInfo.dstOffsets, dstExtent)))\n      return D3DERR_INVALIDCALL;\n\n    VkExtent3D srcCopyExtent =\n    { uint32_t(blitInfo.srcOffsets[1].x - blitInfo.srcOffsets[0].x),\n      uint32_t(blitInfo.srcOffsets[1].y - blitInfo.srcOffsets[0].y),\n      uint32_t(blitInfo.srcOffsets[1].z - blitInfo.srcOffsets[0].z) };\n\n    VkExtent3D dstCopyExtent =\n    { uint32_t(blitInfo.dstOffsets[1].x - blitInfo.dstOffsets[0].x),\n      uint32_t(blitInfo.dstOffsets[1].y - blitInfo.dstOffsets[0].y),\n      uint32_t(blitInfo.dstOffsets[1].z - blitInfo.dstOffsets[0].z) };\n\n    bool srcIsDS = IsDepthStencilFormat(srcFormat);\n    bool dstIsDS = IsDepthStencilFormat(dstFormat);\n    if (unlikely(srcIsDS || dstIsDS)) {\n      if (unlikely(!srcIsDS || !dstIsDS))\n        return D3DERR_INVALIDCALL;\n\n      if (unlikely(srcTextureInfo->Desc()->Discard || dstTextureInfo->Desc()->Discard))\n        return D3DERR_INVALIDCALL;\n\n      if (unlikely(srcCopyExtent.width != srcExtent.width || srcCopyExtent.height != srcExtent.height))\n        return D3DERR_INVALIDCALL;\n\n      if (unlikely(m_inScene))\n        return D3DERR_INVALIDCALL;\n    }\n\n    // Copies would only work if the extents match. (ie. no stretching)\n    bool stretch = srcCopyExtent != dstCopyExtent;\n\n    bool dstHasAttachmentUsage = (dstTextureInfo->Desc()->Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0;\n    bool dstIsSurface = dstTextureInfo->GetType() == D3DRTYPE_SURFACE;\n    if (stretch) {\n      if (unlikely(pSourceSurface == pDestSurface))\n        return D3DERR_INVALIDCALL;\n\n      if (unlikely(dstIsDS))\n        return D3DERR_INVALIDCALL;\n\n      // The docs say that stretching is only allowed if the destination is either a render target surface or a render target texture.\n      // However in practice, using an offscreen plain surface in D3DPOOL_DEFAULT as the destination works fine.\n      // Using a texture without USAGE_RENDERTARGET as destination however does not.\n      if (unlikely(!dstIsSurface && !dstHasAttachmentUsage))\n        return D3DERR_INVALIDCALL;\n    } else {\n      bool srcIsSurface = srcTextureInfo->GetType() == D3DRTYPE_SURFACE;\n      bool srcHasAttachmentUsage = (srcTextureInfo->Desc()->Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0;\n\n      // D3D9Ex allows StretchRect to regular (non-RT) textures if it is a simple copy.\n      bool isCopy = IsExtended()\n        && pSourceRect == nullptr && pDestRect == nullptr // Yes, the rects have to be null. Even passing a rect that is the same size as the texture is invalid.\n        && srcTextureInfo->Desc()->Pool == D3DPOOL_DEFAULT\n        && dstTextureInfo->Desc()->Pool == D3DPOOL_DEFAULT\n        && srcTextureInfo->Desc()->Format == dstTextureInfo->Desc()->Format;\n\n      // Non-stretching copies are only allowed if:\n      // - the destination is either a render target surface or a render target texture\n      // - both destination and source are depth stencil surfaces\n      // - both destination and source are offscreen plain surfaces.\n      // The only way to get a surface with resource type D3DRTYPE_SURFACE without USAGE_RT or USAGE_DS is CreateOffscreenPlainSurface.\n      if (unlikely((!dstHasAttachmentUsage && (!dstIsSurface || !srcIsSurface || srcHasAttachmentUsage)) && !m_isD3D8Compatible && !isCopy))\n        return D3DERR_INVALIDCALL;\n    }\n\n    fastPath &= !stretch;\n\n    if (!fastPath || needsResolve) {\n      // Compressed destination formats are forbidden for blits.\n      if (dstFormatInfo->flags.test(DxvkFormatFlag::BlockCompressed))\n        return D3DERR_INVALIDCALL;\n    }\n\n    if (fastPath) {\n      if (needsResolve) {\n        VkImageResolve region;\n        region.srcSubresource = blitInfo.srcSubresource;\n        region.srcOffset      = blitInfo.srcOffsets[0];\n        region.dstSubresource = blitInfo.dstSubresource;\n        region.dstOffset      = blitInfo.dstOffsets[0];\n        region.extent         = srcCopyExtent;\n\n        EmitCs([\n          cDstImage    = dstImage,\n          cSrcImage    = srcImage,\n          cRegion      = region\n        ] (DxvkContext* ctx) {\n          // Deliberately use AVERAGE even for depth resolves here\n          ctx->resolveImage(cDstImage, cSrcImage, cRegion, cSrcImage->info().format,\n            VK_RESOLVE_MODE_AVERAGE_BIT, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT);\n        });\n      } else {\n        EmitCs([\n          cDstImage  = dstImage,\n          cSrcImage  = srcImage,\n          cDstLayers = blitInfo.dstSubresource,\n          cSrcLayers = blitInfo.srcSubresource,\n          cDstOffset = blitInfo.dstOffsets[0],\n          cSrcOffset = blitInfo.srcOffsets[0],\n          cExtent    = srcCopyExtent\n        ] (DxvkContext* ctx) {\n          ctx->copyImage(\n            cDstImage, cDstLayers, cDstOffset,\n            cSrcImage, cSrcLayers, cSrcOffset,\n            cExtent);\n        });\n      }\n    }\n    else {\n      DxvkImageViewKey dstViewInfo;\n      dstViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n      dstViewInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n      dstViewInfo.format = dstImage->info().format;\n      dstViewInfo.aspects = blitInfo.dstSubresource.aspectMask;\n      dstViewInfo.mipIndex = blitInfo.dstSubresource.mipLevel;\n      dstViewInfo.mipCount = 1;\n      dstViewInfo.layerIndex = blitInfo.dstSubresource.baseArrayLayer;\n      dstViewInfo.layerCount = blitInfo.dstSubresource.layerCount;\n      dstViewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(dstTextureInfo->GetMapping().Swizzle);\n\n      DxvkImageViewKey srcViewInfo;\n      srcViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n      srcViewInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;\n      srcViewInfo.format = srcImage->info().format;\n      srcViewInfo.aspects = blitInfo.srcSubresource.aspectMask;\n      srcViewInfo.mipIndex = blitInfo.srcSubresource.mipLevel;\n      srcViewInfo.mipCount = 1;\n      srcViewInfo.layerIndex = blitInfo.srcSubresource.baseArrayLayer;\n      srcViewInfo.layerCount = blitInfo.srcSubresource.layerCount;\n      srcViewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(srcTextureInfo->GetMapping().Swizzle);\n\n      EmitCs([\n        cDstView  = dstImage->createView(dstViewInfo),\n        cSrcView  = srcImage->createView(srcViewInfo),\n        cBlitInfo = blitInfo,\n        cFilter   = stretch ? DecodeFilter(Filter) : VK_FILTER_NEAREST\n      ] (DxvkContext* ctx) {\n        ctx->blitImageView(\n          cDstView, cBlitInfo.dstOffsets,\n          cSrcView, cBlitInfo.srcOffsets,\n          cFilter);\n      });\n    }\n\n    dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true);\n\n    if (dstTextureInfo->IsAutomaticMip())\n      MarkTextureMipsDirty(dstTextureInfo);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ColorFill(\n          IDirect3DSurface9* pSurface,\n    const RECT*              pRect,\n          D3DCOLOR           Color) {\n    D3D9DeviceLock lock = LockDevice();\n\n    D3D9Surface* dst = static_cast<D3D9Surface*>(pSurface);\n\n    if (unlikely(dst == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture();\n\n    if (dstTextureInfo->IsNull())\n      return D3D_OK;\n\n    if (unlikely(dstTextureInfo->Desc()->Pool != D3DPOOL_DEFAULT))\n      return D3DERR_INVALIDCALL;\n\n    VkExtent3D mipExtent = dstTextureInfo->GetExtentMip(dst->GetSubresource());\n\n    VkOffset3D offset = VkOffset3D{ 0u, 0u, 0u };\n    VkExtent3D extent = mipExtent;\n\n    if (pRect != nullptr)\n      ConvertRect(*pRect, offset, extent);\n\n    VkClearValue clearValue = { };\n    DecodeD3DCOLOR(Color, clearValue.color.float32);\n\n    Rc<DxvkImage> image = dstTextureInfo->GetImage();\n\n    if (image->formatInfo()->aspectMask != VK_IMAGE_ASPECT_COLOR_BIT)\n      return D3DERR_INVALIDCALL;\n\n    VkImageSubresourceLayers subresource = { };\n    subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    subresource.mipLevel = dst->GetMipLevel();\n\n    if (dst->GetFace() == D3D9CommonTexture::AllLayers) {\n      subresource.baseArrayLayer = 0u;\n      subresource.layerCount = image->info().numLayers;\n    } else {\n      subresource.baseArrayLayer = dst->GetFace();\n      subresource.layerCount = 1u;\n    }\n\n    if (image->formatInfo()->flags.test(DxvkFormatFlag::BlockCompressed)) {\n      EmitCs([\n        cImage      = std::move(image),\n        cSubresource = subresource,\n        cOffset     = offset,\n        cExtent     = extent,\n        cClearValue = clearValue\n      ] (DxvkContext* ctx) {\n        auto formatInfo = cImage->formatInfo();\n\n        VkFormat blockFormat = formatInfo->elementSize == 16u\n          ? VK_FORMAT_R32G32B32A32_UINT\n          : VK_FORMAT_R32G32_UINT;\n\n        DxvkImageUsageInfo usage = { };\n        usage.usage = VK_IMAGE_USAGE_STORAGE_BIT;\n        usage.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT\n                    | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT\n                    | VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT;\n        usage.viewFormatCount = 1;\n        usage.viewFormats = &blockFormat;\n        usage.layout = VK_IMAGE_LAYOUT_GENERAL;\n\n        ctx->ensureImageCompatibility(cImage, usage);\n\n        DxvkImageViewKey viewKey = { };\n        viewKey.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n        viewKey.format = blockFormat;\n        viewKey.usage = VK_IMAGE_USAGE_STORAGE_BIT;\n        viewKey.aspects = cSubresource.aspectMask;\n        viewKey.mipIndex = cSubresource.mipLevel;\n        viewKey.mipCount = 1u;\n        viewKey.layerIndex = cSubresource.baseArrayLayer;\n        viewKey.layerCount = cSubresource.layerCount;\n\n        Rc<DxvkImageView> view = cImage->createView(viewKey);\n\n        VkClearValue clearBlock = { };\n        clearBlock.color = util::encodeClearBlockValue(cImage->info().format, cClearValue.color);\n\n        VkOffset3D offset = util::computeBlockOffset(cOffset, formatInfo->blockSize);\n        VkExtent3D extent = util::computeBlockExtent(cExtent, formatInfo->blockSize);\n\n        ctx->clearImageView(view, offset, extent,\n          VK_IMAGE_ASPECT_COLOR_BIT, clearBlock);\n      });\n    } else {\n      EmitCs([\n        cImage      = std::move(image),\n        cSubresource = subresource,\n        cOffset     = offset,\n        cExtent     = extent,\n        cClearValue = clearValue\n      ] (DxvkContext* ctx) {\n        DxvkImageUsageInfo usage = { };\n        usage.usage  = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n        usage.stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        usage.access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\n        ctx->ensureImageCompatibility(cImage, usage);\n\n        DxvkImageViewKey viewKey = { };\n        viewKey.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n        viewKey.format = cImage->info().format;\n        viewKey.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n        viewKey.aspects = cSubresource.aspectMask;\n        viewKey.mipIndex = cSubresource.mipLevel;\n        viewKey.mipCount = 1u;\n        viewKey.layerIndex = cSubresource.baseArrayLayer;\n        viewKey.layerCount = cSubresource.layerCount;\n\n        Rc<DxvkImageView> view = cImage->createView(viewKey);\n\n        if (cOffset == VkOffset3D() && cExtent == cImage->mipLevelExtent(viewKey.mipIndex)) {\n          ctx->clearRenderTarget(view, cSubresource.aspectMask, cClearValue, 0u);\n        } else {\n          ctx->clearImageView(view, cOffset, cExtent,\n            cSubresource.aspectMask, cClearValue);\n        }\n      });\n    }\n\n    dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true);\n\n    if (dstTextureInfo->IsAutomaticMip())\n      MarkTextureMipsDirty(dstTextureInfo);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateOffscreenPlainSurface(\n    UINT Width,\n    UINT Height,\n    D3DFORMAT Format,\n    D3DPOOL Pool,\n    IDirect3DSurface9** ppSurface,\n    HANDLE* pSharedHandle) {\n    return CreateOffscreenPlainSurfaceEx(\n      Width,     Height,\n      Format,    Pool,\n      ppSurface, pSharedHandle,\n      0);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetRenderTarget(\n          DWORD              RenderTargetIndex,\n          IDirect3DSurface9* pRenderTarget) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pRenderTarget == nullptr && RenderTargetIndex == 0))\n      return D3DERR_INVALIDCALL;\n\n    // We need to make sure the render target was created using this device.\n    D3D9Surface* rt = static_cast<D3D9Surface*>(pRenderTarget);\n    if (unlikely(rt != nullptr && rt->GetDevice() != this))\n      return D3DERR_INVALIDCALL;\n\n    return SetRenderTargetInternal(RenderTargetIndex, pRenderTarget);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetRenderTargetInternal(\n          DWORD              RenderTargetIndex,\n          IDirect3DSurface9* pRenderTarget) {\n    if (unlikely(RenderTargetIndex >= caps::MaxSimultaneousRenderTargets))\n      return D3DERR_INVALIDCALL;\n\n    D3D9Surface* rt = static_cast<D3D9Surface*>(pRenderTarget);\n    D3D9CommonTexture* texInfo = rt != nullptr\n      ? rt->GetCommonTexture()\n      : nullptr;\n\n    if (unlikely(rt != nullptr && !(texInfo->Desc()->Usage & D3DUSAGE_RENDERTARGET)))\n      return D3DERR_INVALIDCALL;\n\n    if (RenderTargetIndex == 0) {\n      // Setting Render target 0 changes viewport & scissor\n      // even if it gets changed to the one that's already bound.\n      D3DVIEWPORT9 viewport;\n      viewport.X       = 0;\n      viewport.Y       = 0;\n      viewport.MinZ    = 0.0f;\n      viewport.MaxZ    = 1.0f;\n\n      RECT scissorRect;\n      scissorRect.left    = 0;\n      scissorRect.top     = 0;\n\n      if (likely(rt != nullptr)) {\n        auto rtSize = rt->GetSurfaceExtent();\n        viewport.Width  = rtSize.width;\n        viewport.Height = rtSize.height;\n        scissorRect.right  = rtSize.width;\n        scissorRect.bottom = rtSize.height;\n      } else {\n        viewport.Width  = 0;\n        viewport.Height = 0;\n        scissorRect.right  = 0;\n        scissorRect.bottom = 0;\n      }\n\n      if (m_state.viewport != viewport) {\n        m_dirty.set(D3D9DeviceDirtyFlag::FFViewport);\n        m_dirty.set(D3D9DeviceDirtyFlag::PointScale);\n        m_dirty.set(D3D9DeviceDirtyFlag::ViewportScissor);\n        m_state.viewport = viewport;\n      }\n\n      if (m_state.scissorRect != scissorRect) {\n        m_dirty.set(D3D9DeviceDirtyFlag::ViewportScissor);\n        m_state.scissorRect = scissorRect;\n      }\n    }\n\n    if (m_state.renderTargets[RenderTargetIndex] == rt)\n      return D3D_OK;\n\n    m_state.renderTargets[RenderTargetIndex] = rt;\n\n    // Do a strong flush if the first render target is changed.\n    ConsiderFlush(RenderTargetIndex == 0\n      ? GpuFlushType::ImplicitStrongHint\n      : GpuFlushType::ImplicitWeakHint);\n\n    m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n\n    // Update tracking bitmasks\n    uint32_t oldAlphaSwizzleRTs = m_rtSlotTracking.hasAlphaSwizzle;\n    const uint32_t bit = 1u << RenderTargetIndex;\n    m_rtSlotTracking.canBeSampled    &= ~bit;\n    m_rtSlotTracking.hasAlphaSwizzle &= ~bit;\n\n    if (texInfo != nullptr) {\n      // Update render target sampling usage bitmask for hazard tracking\n      m_rtSlotTracking.canBeSampled |= uint8_t(!texInfo->IsNull() &&\n        rt->GetBaseTexture() != nullptr) << RenderTargetIndex;\n\n      // Update render target alpha swizzle bitmask if we need to fix up the alpha channel\n      // for XRGB formats\n      m_rtSlotTracking.hasAlphaSwizzle |= uint8_t(texInfo->GetMapping().Swizzle.a == VK_COMPONENT_SWIZZLE_ONE) << RenderTargetIndex;\n\n      if (texInfo->IsAutomaticMip())\n        texInfo->SetNeedsMipGen(true);\n    }\n\n    // Update hazards now that the RT has changed\n    UpdateActiveHazardsRT(std::numeric_limits<uint32_t>::max());\n\n    // The blend factors need to get adjusted to the swizzle.\n    if (oldAlphaSwizzleRTs != m_rtSlotTracking.hasAlphaSwizzle)\n      m_dirty.set(D3D9DeviceDirtyFlag::BlendState);\n\n    if (RenderTargetIndex == 0) {\n      // Changing RT0 can disable ATOC and\n      // potentially enable alpha test, so we\n      // need to keep track of the state.\n      UpdateAlphaToCoverangeAndAlphaTest();\n\n      if (likely(texInfo != nullptr)) {\n        // We need to recalculate the alpha test precision for the potentially changed RT format.\n        // Updating the precision is cheap, so there's no need to compare the previous format to the new one.\n        if (m_alphaTestEnabled)\n          m_dirty.set(D3D9DeviceDirtyFlag::AlphaTestState);\n\n        bool oldValidSampleMask = m_validSampleMask;\n        m_validSampleMask = texInfo->Desc()->MultiSample > D3DMULTISAMPLE_NONMASKABLE;\n        // We need to update the multisample state to account for the changed sample mask.\n        if (m_validSampleMask != oldValidSampleMask)\n          m_dirty.set(D3D9DeviceDirtyFlag::MultiSampleState);\n      } else {\n        m_validSampleMask = false;\n        m_dirty.set(D3D9DeviceDirtyFlag::MultiSampleState);\n        m_dirty.set(D3D9DeviceDirtyFlag::AlphaTestState);\n      }\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetRenderTarget(\n          DWORD               RenderTargetIndex,\n          IDirect3DSurface9** ppRenderTarget) {\n    D3D9DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppRenderTarget);\n\n    if (unlikely(ppRenderTarget == nullptr || RenderTargetIndex > caps::MaxSimultaneousRenderTargets))\n      return D3DERR_INVALIDCALL;\n\n    if (m_state.renderTargets[RenderTargetIndex] == nullptr)\n      return D3DERR_NOTFOUND;\n\n    *ppRenderTarget = m_state.renderTargets[RenderTargetIndex].ref();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil) {\n    D3D9DeviceLock lock = LockDevice();\n\n    D3D9Surface* ds = static_cast<D3D9Surface*>(pNewZStencil);\n\n    if (unlikely(ds && !(ds->GetCommonTexture()->Desc()->Usage & D3DUSAGE_DEPTHSTENCIL)))\n      return D3DERR_INVALIDCALL;\n\n    if (m_state.depthStencil == ds)\n      return D3D_OK;\n\n    ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n    m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n\n    // Update depth bias if necessary\n    if (ds != nullptr && m_depthBiasRepresentation.depthBiasRepresentation != VK_DEPTH_BIAS_REPRESENTATION_FLOAT_EXT) {\n      const int32_t vendorId = m_dxvkDevice->adapter()->deviceProperties().core.properties.vendorID;\n      const bool exact = m_depthBiasRepresentation.depthBiasExact;\n      const bool forceUnorm = m_depthBiasRepresentation.depthBiasRepresentation == VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT;\n      const float rValue = GetDepthBufferRValue(ds->GetCommonTexture()->GetFormatMapping().FormatColor, vendorId, exact, forceUnorm);\n      if (m_depthBiasScale != rValue) {\n        m_depthBiasScale = rValue;\n        m_dirty.set(D3D9DeviceDirtyFlag::DepthBias);\n      }\n    }\n\n    m_state.depthStencil = ds;\n\n    UpdateActiveHazardsDS(std::numeric_limits<uint32_t>::max());\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface) {\n    D3D9DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppZStencilSurface);\n\n    if (unlikely(ppZStencilSurface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (m_state.depthStencil == nullptr)\n      return D3DERR_NOTFOUND;\n\n    *ppZStencilSurface = m_state.depthStencil.ref();\n\n    return D3D_OK;\n  }\n\n  // The Begin/EndScene functions actually do nothing.\n  // Some games don't even call them.\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::BeginScene() {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(m_inScene))\n      return D3DERR_INVALIDCALL;\n\n    m_inScene = true;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::EndScene() {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(!m_inScene))\n      return D3DERR_INVALIDCALL;\n\n    ConsiderFlush(GpuFlushType::ImplicitStrongHint);\n\n    m_inScene = false;\n\n    // D3D9 resets the internally bound vertex buffers and index buffer in EndScene if they were unbound in the meantime.\n    // We have to ignore unbinding those buffers because of Operation Flashpoint Red River,\n    // so we should also clear the bindings here, to avoid leaks.\n    if (m_state.indices == nullptr) {\n      EmitCs([](DxvkContext* ctx) {\n        ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32);\n      });\n    }\n\n    for (uint32_t i : bit::BitMask(~static_cast<uint32_t>(m_vbSlotTracking.bound) & ((1 << 16) - 1))) {\n      if (m_state.vertexBuffers[i].vertexBuffer == nullptr) {\n        EmitCs([cIndex = i](DxvkContext* ctx) {\n          ctx->bindVertexBuffer(cIndex, DxvkBufferSlice(), 0);\n        });\n      }\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Clear(\n          DWORD    Count,\n    const D3DRECT* pRects,\n          DWORD    Flags,\n          D3DCOLOR Color,\n          float    Z,\n          DWORD    Stencil) {\n    if (unlikely(!Count && pRects))\n      return D3D_OK;\n\n    D3D9DeviceLock lock = LockDevice();\n    BindFramebuffer();\n\n    // D3DCLEAR_ZBUFFER and D3DCLEAR_STENCIL are invalid flags\n    // if there is no currently bound DS (which can be the autoDS)\n    if (unlikely(m_state.depthStencil == nullptr\n            && (Flags & (D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL))))\n      return D3DERR_INVALIDCALL;\n\n    const auto& vp = m_state.viewport;\n    const auto& sc = m_state.scissorRect;\n\n    bool srgb      = m_state.renderStates[D3DRS_SRGBWRITEENABLE];\n    bool scissor   = m_state.renderStates[D3DRS_SCISSORTESTENABLE];\n\n    VkOffset3D offset = { int32_t(vp.X),    int32_t(vp.Y),      0  };\n    VkExtent3D extent = {         vp.Width,         vp.Height,  1u };\n\n    if (scissor) {\n      offset.x = std::max<int32_t> (offset.x, sc.left);\n      offset.y = std::max<int32_t> (offset.y, sc.top);\n\n      extent.width  = std::min<uint32_t>(extent.width,  sc.right  - offset.x);\n      extent.height = std::min<uint32_t>(extent.height, sc.bottom - offset.y);\n    }\n\n    // This becomes pretty unreadable in one singular if statement...\n    if (Count) {\n      // If pRects is null, or our first rect encompasses the viewport:\n      if (!pRects)\n        Count = 0;\n      else if (pRects[0].x1 <= offset.x                         && pRects[0].y1 <= offset.y\n            && pRects[0].x2 >= offset.x + int32_t(extent.width) && pRects[0].y2 >= offset.y + int32_t(extent.height))\n        Count = 0;\n    }\n\n    // Here, Count of 0 will denote whether or not to care about user rects.\n    VkClearValue clearValueDepth;\n    clearValueDepth.depthStencil.depth   = Z;\n    clearValueDepth.depthStencil.stencil = Stencil;\n\n    VkClearValue clearValueColor;\n    DecodeD3DCOLOR(Color, clearValueColor.color.float32);\n\n    VkImageAspectFlags depthAspectMask = 0;\n    if (m_state.depthStencil != nullptr) {\n      if (Flags & D3DCLEAR_ZBUFFER)\n        depthAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;\n\n      if (Flags & D3DCLEAR_STENCIL)\n        depthAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;\n\n      depthAspectMask &= lookupFormatInfo(m_state.depthStencil->GetCommonTexture()->GetFormatMapping().FormatColor)->aspectMask;\n    }\n\n    auto ClearImageView = [this](\n      uint32_t                 alignment,\n      VkOffset3D               offset,\n      VkExtent3D               extent,\n      const Rc<DxvkImageView>& imageView,\n      VkImageAspectFlags       aspectMask,\n      VkClearValue             clearValue) {\n\n      VkExtent3D imageExtent = imageView->mipLevelExtent(0);\n      extent.width = std::min(imageExtent.width, extent.width);\n      extent.height = std::min(imageExtent.height, extent.height);\n\n      if (unlikely(uint32_t(offset.x) >= imageExtent.width || uint32_t(offset.y) >= imageExtent.height))\n        return;\n\n      const bool fullClear = align(extent.width, alignment) == align(imageExtent.width, alignment)\n        && align(extent.height, alignment) == align(imageExtent.height, alignment)\n        && offset.x == 0\n        && offset.y == 0;\n\n      if (fullClear) {\n        EmitCs([\n          cClearValue = clearValue,\n          cAspectMask = aspectMask,\n          cImageView  = imageView\n        ] (DxvkContext* ctx) {\n          ctx->clearRenderTarget(cImageView,\n            cAspectMask, cClearValue, 0u);\n        });\n      }\n      else {\n        EmitCs([\n          cClearValue = clearValue,\n          cAspectMask = aspectMask,\n          cImageView  = imageView,\n          cOffset     = offset,\n          cExtent     = extent\n        ] (DxvkContext* ctx) {\n          ctx->clearImageView(\n            cImageView,\n            cOffset, cExtent,\n            cAspectMask,\n            cClearValue);\n        });\n      }\n    };\n\n    auto ClearViewRect = [&](\n      uint32_t           alignment,\n      VkOffset3D         offset,\n      VkExtent3D         extent) {\n      // Clear depth if we need to.\n      if (depthAspectMask != 0)\n        ClearImageView(alignment, offset, extent, m_state.depthStencil->GetDepthStencilView(true), depthAspectMask, clearValueDepth);\n\n      // Clear render targets if we need to.\n      if (Flags & D3DCLEAR_TARGET) {\n        for (uint32_t rt = 0u; rt < m_state.renderTargets.size(); rt++) {\n          if (!HasRenderTargetBound(rt))\n            continue;\n          const auto& rts = m_state.renderTargets[rt];\n          const auto& rtv = rts->GetRenderTargetView(srgb);\n\n          if (likely(rtv != nullptr)) {\n            ClearImageView(alignment, offset, extent, rtv, VK_IMAGE_ASPECT_COLOR_BIT, clearValueColor);\n\n            D3D9CommonTexture* dstTexture = rts->GetCommonTexture();\n\n            if (dstTexture->IsAutomaticMip())\n              MarkTextureMipsDirty(dstTexture);\n          }\n        }\n      }\n    };\n\n    // A Hat in Time and other UE3 games only gets partial clears here\n    // because of an oversized rt height due to their weird alignment...\n    // This works around that.\n    uint32_t alignment = m_d3d9Options.lenientClear ? 8 : 1;\n\n    if (extent.width == 0 || extent.height == 0) {\n      return D3D_OK;\n    }\n\n    if (!Count) {\n      // Clear our viewport & scissor minified region in this rendertarget.\n      ClearViewRect(alignment, offset, extent);\n    }\n    else {\n      // Clear the application provided rects.\n      for (uint32_t i = 0; i < Count; i++) {\n        VkOffset3D rectOffset = {\n          std::max<int32_t>(pRects[i].x1, offset.x),\n          std::max<int32_t>(pRects[i].y1, offset.y),\n          0\n        };\n\n        if (std::min<int32_t>(pRects[i].x2, offset.x + extent.width) <= rectOffset.x\n          || std::min<int32_t>(pRects[i].y2, offset.y + extent.height) <= rectOffset.y) {\n          continue;\n        }\n\n        VkExtent3D rectExtent = {\n          std::min<uint32_t>(pRects[i].x2, offset.x + extent.width)  - rectOffset.x,\n          std::min<uint32_t>(pRects[i].y2, offset.y + extent.height) - rectOffset.y,\n          1u\n        };\n\n        ClearViewRect(alignment, rectOffset, rectExtent);\n      }\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) {\n    return SetStateTransform(GetTransformIndex(State), pMatrix);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pMatrix == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pMatrix = bit::cast<D3DMATRIX>(m_state.transforms[GetTransformIndex(State)]);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix) {\n    D3D9DeviceLock lock = LockDevice();\n\n    const uint32_t idx = GetTransformIndex(TransformState);\n\n    m_state.transforms[idx] = m_state.transforms[idx] * ConvertMatrix(pMatrix);\n\n    m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n\n    if (idx == GetTransformIndex(D3DTS_VIEW) || idx >= GetTransformIndex(D3DTS_WORLD))\n      m_dirty.set(D3D9DeviceDirtyFlag::FFVertexBlend);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetViewport(const D3DVIEWPORT9* pViewport) {\n    D3D9DeviceLock lock = LockDevice();\n\n    // Outright crashes on native, but let's be\n    // somewhat more elegant about it.\n    if (unlikely(pViewport == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetViewport(pViewport);\n\n    if (m_state.viewport == *pViewport)\n      return D3D_OK;\n\n    m_state.viewport.X = pViewport->X;\n    m_state.viewport.Y = pViewport->Y;\n    m_state.viewport.Width = pViewport->Width;\n    m_state.viewport.Height = pViewport->Height;\n    m_state.viewport.MinZ = pViewport->MinZ;\n    m_state.viewport.MaxZ = pViewport->MinZ < pViewport->MaxZ ? pViewport->MaxZ : pViewport->MinZ + 0.001f;\n\n    m_dirty.set(D3D9DeviceDirtyFlag::ViewportScissor);\n    m_dirty.set(D3D9DeviceDirtyFlag::FFViewport);\n    m_dirty.set(D3D9DeviceDirtyFlag::PointScale);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetViewport(D3DVIEWPORT9* pViewport) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pViewport == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pViewport = m_state.viewport;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetMaterial(const D3DMATERIAL9* pMaterial) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pMaterial == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetMaterial(pMaterial);\n\n    m_state.material = *pMaterial;\n    m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetMaterial(D3DMATERIAL9* pMaterial) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pMaterial == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pMaterial = m_state.material;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetLight(DWORD Index, const D3DLIGHT9* pLight) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pLight == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ShouldRecord())) {\n      m_recorder->SetLight(Index, pLight);\n      return D3D_OK;\n    }\n\n    if (Index >= m_state.lights.size())\n      m_state.lights.resize(Index + 1);\n\n    m_state.lights[Index] = *pLight;\n\n    if (m_state.IsLightEnabled(Index))\n      m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetLight(DWORD Index, D3DLIGHT9* pLight) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pLight == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(Index >= m_state.lights.size() || !m_state.lights[Index]))\n      return D3DERR_INVALIDCALL;\n\n    *pLight = m_state.lights[Index].value();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::LightEnable(DWORD Index, BOOL Enable) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(ShouldRecord())) {\n      m_recorder->LightEnable(Index, Enable);\n      return D3D_OK;\n    }\n\n    if (unlikely(Index >= m_state.lights.size()))\n      m_state.lights.resize(Index + 1);\n\n    if (unlikely(!m_state.lights[Index]))\n      m_state.lights[Index] = DefaultLight;\n\n    if (m_state.IsLightEnabled(Index) == !!Enable)\n      return D3D_OK;\n\n    uint32_t searchIndex = std::numeric_limits<uint32_t>::max();\n    uint32_t setIndex    = Index;\n\n    if (!Enable)\n      std::swap(searchIndex, setIndex);\n\n    for (auto& idx : m_state.enabledLightIndices) {\n      if (idx == searchIndex) {\n        idx = setIndex;\n        m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n        m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n        break;\n      }\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetLightEnable(DWORD Index, BOOL* pEnable) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pEnable == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(Index >= m_state.lights.size() || !m_state.lights[Index]))\n      return D3DERR_INVALIDCALL;\n\n    *pEnable = m_state.IsLightEnabled(Index) ? 128 : 0; // Weird quirk but OK.\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetClipPlane(DWORD Index, const float* pPlane) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(!pPlane))\n      return D3DERR_INVALIDCALL;\n\n    // Higher indexes will be capped to the last valid index\n    if (unlikely(Index >= caps::MaxClipPlanes))\n      Index = caps::MaxClipPlanes - 1;\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetClipPlane(Index, pPlane);\n\n    bool dirty = false;\n\n    for (uint32_t i = 0; i < 4; i++) {\n      dirty |= m_state.clipPlanes[Index].coeff[i] != pPlane[i];\n      m_state.clipPlanes[Index].coeff[i] = pPlane[i];\n    }\n\n    bool enabled = m_state.renderStates[D3DRS_CLIPPLANEENABLE] & (1u << Index);\n    dirty &= enabled;\n\n    if (dirty)\n      m_dirty.set(D3D9DeviceDirtyFlag::ClipPlanes);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetClipPlane(DWORD Index, float* pPlane) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(!pPlane))\n      return D3DERR_INVALIDCALL;\n\n    // Higher indexes will be capped to the last valid index\n    if (unlikely(Index >= caps::MaxClipPlanes))\n      Index = caps::MaxClipPlanes - 1;\n\n    for (uint32_t i = 0; i < 4; i++)\n      pPlane[i] = m_state.clipPlanes[Index].coeff[i];\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) {\n    D3D9DeviceLock lock = LockDevice();\n\n    // D3D9 only allows reading for values 0 and 7-255 so we don't need to do anything but return OK\n    if (unlikely(State > 255 || (State < D3DRS_ZENABLE && State != 0))) {\n      return D3D_OK;\n    }\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetRenderState(State, Value);\n\n    auto& states         = m_state.renderStates;\n    const DWORD oldValue = states[State];\n\n    if (likely(Value != oldValue)) {\n      constexpr uint32_t nvidiaVendorId = uint32_t(DxvkGpuVendor::Nvidia);\n      constexpr uint32_t amdVendorId    = uint32_t(DxvkGpuVendor::Amd);\n      constexpr uint32_t intelVendorId  = uint32_t(DxvkGpuVendor::Intel);\n\n      states[State] = Value;\n\n      switch (State) {\n        case D3DRS_SEPARATEALPHABLENDENABLE:\n        case D3DRS_ALPHABLENDENABLE:\n        case D3DRS_BLENDOP:\n        case D3DRS_BLENDOPALPHA:\n        case D3DRS_DESTBLEND:\n        case D3DRS_DESTBLENDALPHA:\n        case D3DRS_SRCBLEND:\n        case D3DRS_SRCBLENDALPHA:\n          m_dirty.set(D3D9DeviceDirtyFlag::BlendState);\n          break;\n\n        case D3DRS_COLORWRITEENABLE:\n          if (likely(!Value != !oldValue))\n            UpdateAnyColorWrites<0>();\n          m_dirty.set(D3D9DeviceDirtyFlag::BlendState);\n          break;\n        case D3DRS_COLORWRITEENABLE1:\n          if (likely(!Value != !oldValue))\n            UpdateAnyColorWrites<1>();\n          m_dirty.set(D3D9DeviceDirtyFlag::BlendState);\n          break;\n        case D3DRS_COLORWRITEENABLE2:\n          if (likely(!Value != !oldValue))\n            UpdateAnyColorWrites<2>();\n          m_dirty.set(D3D9DeviceDirtyFlag::BlendState);\n          break;\n        case D3DRS_COLORWRITEENABLE3:\n          if (likely(!Value != !oldValue))\n            UpdateAnyColorWrites<3>();\n          m_dirty.set(D3D9DeviceDirtyFlag::BlendState);\n          break;\n\n        case D3DRS_ALPHATESTENABLE: {\n          UpdateAlphaToCoverangeAndAlphaTest();\n          break;\n        }\n\n        case D3DRS_ALPHAFUNC:\n          m_dirty.set(D3D9DeviceDirtyFlag::AlphaTestState);\n          break;\n\n        case D3DRS_BLENDFACTOR:\n          BindBlendFactor();\n          break;\n\n        case D3DRS_MULTISAMPLEMASK:\n          if (m_validSampleMask)\n            m_dirty.set(D3D9DeviceDirtyFlag::MultiSampleState);\n          break;\n\n        case D3DRS_ZWRITEENABLE:\n          if (likely(!Value != !oldValue)) {\n            if (likely(m_state.depthStencil != nullptr\n              && m_state.renderStates[D3DRS_ZENABLE])) {\n              // Whether we write the depth has been changed => check for hazards\n              UpdateActiveHazardsDS(std::numeric_limits<uint32_t>::max());\n            }\n\n            m_dirty.set(D3D9DeviceDirtyFlag::DepthStencilState);\n          }\n          break;\n\n        case D3DRS_STENCILENABLE:\n          if (likely(!Value != !oldValue)) {\n            m_dirty.set(D3D9DeviceDirtyFlag::DepthStencilState);\n          }\n          break;\n\n        case D3DRS_ZENABLE:\n          if (likely(!Value != !oldValue)) {\n            if (likely(m_state.depthStencil != nullptr)) {\n              // The depth test has been enabled or disabled => check for hazards\n              UpdateActiveHazardsDS(std::numeric_limits<uint32_t>::max());\n            }\n\n            m_dirty.set(D3D9DeviceDirtyFlag::DepthStencilState);\n          }\n          break;\n\n        case D3DRS_ZFUNC:\n        case D3DRS_TWOSIDEDSTENCILMODE:\n        case D3DRS_STENCILFAIL:\n        case D3DRS_STENCILZFAIL:\n        case D3DRS_STENCILPASS:\n        case D3DRS_STENCILFUNC:\n        case D3DRS_CCW_STENCILFAIL:\n        case D3DRS_CCW_STENCILZFAIL:\n        case D3DRS_CCW_STENCILPASS:\n        case D3DRS_CCW_STENCILFUNC:\n        case D3DRS_STENCILMASK:\n        case D3DRS_STENCILWRITEMASK:\n          m_dirty.set(D3D9DeviceDirtyFlag::DepthStencilState);\n          break;\n\n        case D3DRS_STENCILREF:\n          BindDepthStencilReference();\n          break;\n\n        case D3DRS_SCISSORTESTENABLE:\n          m_dirty.set(D3D9DeviceDirtyFlag::ViewportScissor);\n          break;\n\n        case D3DRS_SRGBWRITEENABLE:\n          m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n          break;\n\n        case D3DRS_DEPTHBIAS:\n        case D3DRS_SLOPESCALEDEPTHBIAS:\n          m_dirty.set(D3D9DeviceDirtyFlag::DepthBias);\n          break;\n\n        case D3DRS_CULLMODE:\n        case D3DRS_FILLMODE:\n        case D3DRS_MULTISAMPLEANTIALIAS:\n          m_dirty.set(D3D9DeviceDirtyFlag::RasterizerState);\n          break;\n\n        case D3DRS_CLIPPLANEENABLE:\n          if (!Value != !oldValue)\n            m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n\n          m_dirty.set(D3D9DeviceDirtyFlag::ClipPlanes);\n          break;\n\n        case D3DRS_ALPHAREF:\n          UpdatePushConstant<D3D9RenderStateItem::AlphaRef>();\n          break;\n\n        case D3DRS_TEXTUREFACTOR:\n          m_dirty.set(D3D9DeviceDirtyFlag::FFPixelData);\n          break;\n\n        case D3DRS_DIFFUSEMATERIALSOURCE:\n        case D3DRS_AMBIENTMATERIALSOURCE:\n        case D3DRS_SPECULARMATERIALSOURCE:\n        case D3DRS_EMISSIVEMATERIALSOURCE:\n        case D3DRS_COLORVERTEX:\n        case D3DRS_LIGHTING:\n        case D3DRS_NORMALIZENORMALS:\n        case D3DRS_LOCALVIEWER:\n          m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n          break;\n\n        case D3DRS_AMBIENT:\n          m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n          break;\n\n        case D3DRS_SPECULARENABLE:\n          m_dirty.set(D3D9DeviceDirtyFlag::FFPixelShader);\n          m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n          break;\n\n        case D3DRS_FOGENABLE:\n        case D3DRS_FOGVERTEXMODE:\n        case D3DRS_FOGTABLEMODE:\n          m_dirty.set(D3D9DeviceDirtyFlag::FogState);\n          break;\n\n        case D3DRS_RANGEFOGENABLE:\n          m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n          break;\n\n        case D3DRS_FOGCOLOR:\n          m_dirty.set(D3D9DeviceDirtyFlag::FogColor);\n          break;\n\n        case D3DRS_FOGSTART:\n          m_dirty.set(D3D9DeviceDirtyFlag::FogScale);\n          break;\n\n        case D3DRS_FOGEND:\n          m_dirty.set(D3D9DeviceDirtyFlag::FogScale);\n          m_dirty.set(D3D9DeviceDirtyFlag::FogEnd);\n          break;\n\n        case D3DRS_FOGDENSITY:\n          m_dirty.set(D3D9DeviceDirtyFlag::FogDensity);\n          break;\n\n        case D3DRS_POINTSIZE: {\n          const uint32_t vendorId = m_adapter->GetVendorId();\n\n          // AMD's driver hack for ATOC, RESZ, INST and CENT (also supported on Nvidia)\n          if (likely(vendorId != intelVendorId)) {\n            // ATOC (AMD specific)\n            constexpr uint32_t AlphaToCoverageEnable  = uint32_t(D3D9Format::A2M1);\n            constexpr uint32_t AlphaToCoverageDisable = uint32_t(D3D9Format::A2M0);\n\n            if ((Value == AlphaToCoverageEnable\n              || Value == AlphaToCoverageDisable) &&\n                 vendorId == amdVendorId &&\n                 !m_isD3D8Compatible) {\n              UpdateAlphaToCoverangeAndAlphaTest();\n              break;\n            }\n\n            // RESZ (AMD specific, also advertised and exposed\n            // in D3D8 - once supported by Intel as well,\n            // however modern drivers do not expose it)\n            constexpr uint32_t RESZ = 0x7fa05000;\n            if (unlikely(Value == RESZ && vendorId == amdVendorId)) {\n              ResolveZ();\n              break;\n            }\n\n            // INST (AMD specific)\n            if (unlikely(Value == uint32_t(D3D9Format::INST) &&\n                         vendorId == amdVendorId &&\n                         !m_isD3D8Compatible)) {\n              // Geometry instancing is supported by SM3, but ATI/AMD\n              // exposed this hack to retroactively enable it on their\n              // SM2-capable hardware. It's esentially a no-op.\n              break;\n            }\n\n            // CENT (AMD & Nvidia)\n            if (unlikely(Value == uint32_t(D3D9Format::CENT) &&\n                         !m_isD3D8Compatible)) {\n              // Centroid (alternate pixel center) hack.\n              // Taken into account anyway, so yet another no-op.\n              break;\n            }\n          }\n\n          UpdatePushConstant<D3D9RenderStateItem::PointSize>();\n          break;\n        }\n\n        case D3DRS_POINTSIZE_MIN:\n          UpdatePushConstant<D3D9RenderStateItem::PointSizeMin>();\n          break;\n\n        case D3DRS_POINTSIZE_MAX:\n          UpdatePushConstant<D3D9RenderStateItem::PointSizeMax>();\n          break;\n\n        case D3DRS_POINTSCALE_A:\n        case D3DRS_POINTSCALE_B:\n        case D3DRS_POINTSCALE_C:\n          m_dirty.set(D3D9DeviceDirtyFlag::PointScale);\n          break;\n\n        case D3DRS_POINTSCALEENABLE:\n        case D3DRS_POINTSPRITEENABLE:\n          // Nothing to do here!\n          // This is handled in UpdatePointMode.\n          break;\n\n        case D3DRS_SHADEMODE:\n          m_dirty.set(D3D9DeviceDirtyFlag::RasterizerState);\n          break;\n\n        case D3DRS_TWEENFACTOR:\n          m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n          break;\n\n        case D3DRS_VERTEXBLEND:\n          m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n          break;\n\n        case D3DRS_INDEXEDVERTEXBLENDENABLE:\n          if (CanSWVP() && Value)\n            m_dirty.set(D3D9DeviceDirtyFlag::FFVertexBlend);\n\n          m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n          break;\n\n        case D3DRS_ADAPTIVETESS_Y: {\n          const uint32_t vendorId = m_adapter->GetVendorId();\n\n          // Nvidia's driver hack for ATOC (also supported on Intel), COPM and SSAA\n          if (likely(vendorId != amdVendorId && !m_isD3D8Compatible)) {\n            // ATOC (Nvidia & Intel)\n            constexpr uint32_t AlphaToCoverageEnable  = uint32_t(D3D9Format::ATOC);\n            // Disabling both ATOC and SSAA is done using D3DFMT_UNKNOWN (0)\n            constexpr uint32_t AlphaToCoverageDisable = 0;\n\n            if (Value == AlphaToCoverageEnable\n             || Value == AlphaToCoverageDisable) {\n              UpdateAlphaToCoverangeAndAlphaTest();\n              break;\n            }\n\n            // COPM (Nvidia specific)\n            // UE3 calls this MinimalNVIDIADriverShaderOptimization\n            if (unlikely(Value == uint32_t(D3D9Format::COPM) &&\n                         vendorId == nvidiaVendorId)) {\n              static bool s_copmErrorShown;\n\n              if (!std::exchange(s_copmErrorShown, true))\n                Logger::info(\"D3D9DeviceEx::SetRenderState: MinimalNVIDIADriverShaderOptimization is unsupported\");\n\n              break;\n            }\n\n            // SSAA (Nvidia specific)\n            if (unlikely(Value == uint32_t(D3D9Format::SSAA) &&\n                         vendorId == nvidiaVendorId)) {\n              static bool s_ssaaErrorShown;\n\n              if (!std::exchange(s_ssaaErrorShown, true))\n                Logger::warn(\"D3D9DeviceEx::SetRenderState: Transparency supersampling (SSAA) is unsupported\");\n\n              break;\n            }\n          }\n          break;\n        }\n\n        case D3DRS_ADAPTIVETESS_X:\n        case D3DRS_ADAPTIVETESS_Z:\n        case D3DRS_ADAPTIVETESS_W: {\n          const uint32_t vendorId = m_adapter->GetVendorId();\n\n          // Nvidia specific depth bounds test hack\n          const bool nvdbEnabled = vendorId == nvidiaVendorId && IsNVDepthBoundsTestEnabled();\n\n          if (nvdbEnabled || m_nvdbEnabled) {\n            m_dirty.set(D3D9DeviceDirtyFlag::DepthBounds);\n\n            if (likely(IsZTestEnabled()))\n              m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n\n            // NVDB state changes happen on D3DRS_ADAPTIVETESS_X,\n            // whereas D3DRS_ADAPTIVETESS_Z and D3DRS_ADAPTIVETESS_W\n            // are used to set the values for the depth bounds test\n            if (State == D3DRS_ADAPTIVETESS_X && nvdbEnabled != m_nvdbEnabled)\n              m_nvdbEnabled = nvdbEnabled;\n          }\n          break;\n        }\n\n        default:\n          static bool s_errorShown[256];\n\n          if (!std::exchange(s_errorShown[State], true))\n            Logger::warn(str::format(\"D3D9DeviceEx::SetRenderState: Unhandled render state \", State));\n          break;\n      }\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pValue == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(State > 255 || (State < D3DRS_ZENABLE && State != 0))) {\n      return D3DERR_INVALIDCALL;\n    }\n\n    if (State < D3DRS_ZENABLE || State > D3DRS_BLENDOPALPHA)\n      *pValue = 0;\n    else\n      *pValue = m_state.renderStates[State];\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateStateBlock(\n          D3DSTATEBLOCKTYPE      Type,\n          IDirect3DStateBlock9** ppSB) {\n    D3D9DeviceLock lock = LockDevice();\n\n    // A state block can not be created while another is being recorded.\n    if (unlikely(ShouldRecord()))\n      return D3DERR_INVALIDCALL;\n\n    InitReturnPtr(ppSB);\n\n    if (unlikely(ppSB == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D9StateBlockType stateBlockType = ConvertStateBlockType(Type);\n\n    if (unlikely(stateBlockType == D3D9StateBlockType::Unknown)) {\n      Logger::warn(str::format(\"D3D9DeviceEx::CreateStateBlock: Invalid state block type: \", Type));\n      return D3DERR_INVALIDCALL;\n    }\n\n    try {\n      const Com<D3D9StateBlock> sb = new D3D9StateBlock(this, stateBlockType);\n      *ppSB = sb.ref();\n      if (!m_isD3D8Compatible)\n        m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError & e) {\n      Logger::err(e.message());\n      return D3DERR_INVALIDCALL;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::BeginStateBlock() {\n    D3D9DeviceLock lock = LockDevice();\n\n    // Only one state block can be recorded at a given time.\n    if (unlikely(ShouldRecord()))\n      return D3DERR_INVALIDCALL;\n\n    m_recorder = new D3D9StateBlock(this, D3D9StateBlockType::None);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::EndStateBlock(IDirect3DStateBlock9** ppSB) {\n    D3D9DeviceLock lock = LockDevice();\n\n    // Recording a state block can't end if recording hasn't been started.\n    if (unlikely(ppSB == nullptr || !ShouldRecord()))\n      return D3DERR_INVALIDCALL;\n\n    InitReturnPtr(ppSB);\n\n    *ppSB = m_recorder.ref();\n    if (!m_isD3D8Compatible)\n      m_losableResourceCounter++;\n    m_recorder = nullptr;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetClipStatus(const D3DCLIPSTATUS9* pClipStatus) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pClipStatus == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    m_state.clipStatus = *pClipStatus;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetClipStatus(D3DCLIPSTATUS9* pClipStatus) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pClipStatus == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pClipStatus = m_state.clipStatus;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (ppTexture == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    *ppTexture = nullptr;\n\n    if (unlikely(InvalidSampler(Stage)))\n      return D3D_OK;\n\n    DWORD stateSampler = RemapSamplerState(Stage);\n\n    *ppTexture = ref(m_state.textures[stateSampler]);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture) {\n    if (unlikely(InvalidSampler(Stage)))\n      return D3D_OK;\n\n    DWORD stateSampler = RemapSamplerState(Stage);\n\n    return SetStateTexture(stateSampler, pTexture);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetTextureStageState(\n          DWORD                    Stage,\n          D3DTEXTURESTAGESTATETYPE Type,\n          DWORD*                   pValue) {\n    auto dxvkType = RemapTextureStageStateType(Type);\n\n    if (unlikely(pValue == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    Stage = std::min(Stage, DWORD(caps::TextureStageCount - 1));\n    dxvkType = std::min(dxvkType, D3D9TextureStageStateTypes(DXVK_TSS_COUNT - 1));\n\n    *pValue = m_state.textureStages[Stage][dxvkType];\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetTextureStageState(\n          DWORD                    Stage,\n          D3DTEXTURESTAGESTATETYPE Type,\n          DWORD                    Value) {\n    return SetStateTextureStageState(Stage, RemapTextureStageStateType(Type), Value);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetSamplerState(\n          DWORD               Sampler,\n          D3DSAMPLERSTATETYPE Type,\n          DWORD*              pValue) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pValue == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pValue = 0;\n\n    if (unlikely(InvalidSampler(Sampler)))\n      return D3D_OK;\n\n    Sampler = RemapSamplerState(Sampler);\n\n    *pValue = m_state.samplerStates[Sampler][Type];\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetSamplerState(\n          DWORD               Sampler,\n          D3DSAMPLERSTATETYPE Type,\n          DWORD               Value) {\n    if (unlikely(InvalidSampler(Sampler)))\n      return D3D_OK;\n\n    uint32_t stateSampler = RemapSamplerState(Sampler);\n\n    return SetStateSamplerState(stateSampler, Type, Value);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ValidateDevice(DWORD* pNumPasses) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (pNumPasses != nullptr)\n      *pNumPasses = 1;\n\n    return IsDeviceLost() ? D3DERR_DEVICELOST : D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pEntries == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    auto texturePalettesIter = m_state.texturePalettes.find(PaletteNumber);\n\n    // if the palette doesn't already exist (likely), create a new element\n    if (likely(texturePalettesIter == m_state.texturePalettes.end())) {\n      // D3D9 documentation: \"IDirect3DDevice9::SetPaletteEntries updates all of a palette's\n      // 256 entries. Each entry is a PALETTEENTRY structure of the format D3DFMT_A8R8G8B8.\"\n      std::array<PALETTEENTRY, PaletteEntryCount> paletteEntry;\n      memcpy(&paletteEntry[0], pEntries, sizeof(PALETTEENTRY) * PaletteEntryCount);\n\n      m_state.texturePalettes.emplace(std::piecewise_construct,\n                                      std::forward_as_tuple(PaletteNumber),\n                                      std::forward_as_tuple(paletteEntry));\n    // if the pallete already exists, update the palette entry array\n    } else {\n      memcpy(&texturePalettesIter->second[0], pEntries, sizeof(PALETTEENTRY) * PaletteEntryCount);\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pEntries == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    auto texturePalettesIter = m_state.texturePalettes.find(PaletteNumber);\n\n    if (unlikely(texturePalettesIter == m_state.texturePalettes.end())) {\n      Logger::warn(str::format(\"D3D9DeviceEx::GetPaletteEntries: Invalid PaletteNumber: \", PaletteNumber));\n      return D3DERR_INVALIDCALL;\n    }\n\n    memcpy(pEntries, &texturePalettesIter->second[0], sizeof(PALETTEENTRY) * PaletteEntryCount);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetCurrentTexturePalette(UINT PaletteNumber) {\n    D3D9DeviceLock lock = LockDevice();\n\n    // TODO: Use the PaletteNumber and coresponding texture palette entries\n    // to translate all paletted textures for all active texture stages.\n    // See: https://learn.microsoft.com/en-us/windows/win32/direct3d9/texture-palettes\n    m_state.texturePaletteNumber = PaletteNumber;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetCurrentTexturePalette(UINT *PaletteNumber) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(PaletteNumber == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *PaletteNumber = m_state.texturePaletteNumber;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetScissorRect(const RECT* pRect) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pRect == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetScissorRect(pRect);\n\n    if (m_state.scissorRect == *pRect)\n      return D3D_OK;\n\n    m_state.scissorRect = *pRect;\n\n    m_dirty.set(D3D9DeviceDirtyFlag::ViewportScissor);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetScissorRect(RECT* pRect) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pRect == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pRect = m_state.scissorRect;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetSoftwareVertexProcessing(BOOL bSoftware) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (bSoftware && !CanSWVP())\n      return D3DERR_INVALIDCALL;\n\n    if (!bSoftware && (m_behaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING))\n      return D3DERR_INVALIDCALL;\n\n    m_isSWVP = bSoftware;\n\n    return D3D_OK;\n  }\n\n\n  BOOL    STDMETHODCALLTYPE D3D9DeviceEx::GetSoftwareVertexProcessing() {\n    D3D9DeviceLock lock = LockDevice();\n\n    return m_isSWVP;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetNPatchMode(float nSegments) {\n    D3D9DeviceLock lock = LockDevice();\n\n    m_state.nPatchSegments = nSegments;\n\n    return D3D_OK;\n  }\n\n\n  float   STDMETHODCALLTYPE D3D9DeviceEx::GetNPatchMode() {\n    D3D9DeviceLock lock = LockDevice();\n\n    return m_state.nPatchSegments;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawPrimitive(\n          D3DPRIMITIVETYPE PrimitiveType,\n          UINT             StartVertex,\n          UINT             PrimitiveCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(m_state.vertexDecl == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(!PrimitiveCount))\n      return D3D_OK;\n\n    bool dynamicSysmemVBOs;\n    uint32_t firstIndex     = 0;\n    int32_t baseVertexIndex = 0;\n    uint32_t vertexCount    = GetVertexCount(PrimitiveType, PrimitiveCount);\n    UploadPerDrawData(\n      StartVertex,\n      vertexCount,\n      firstIndex,\n      0,\n      baseVertexIndex,\n      &dynamicSysmemVBOs,\n      nullptr\n    );\n\n    PrepareDraw(PrimitiveType, !dynamicSysmemVBOs, false);\n\n    EmitCs([this,\n      cPrimType    = PrimitiveType,\n      cPrimCount   = PrimitiveCount,\n      cStartVertex = StartVertex\n    ](DxvkContext* ctx) {\n      uint32_t vertexCount = GetVertexCount(cPrimType, cPrimCount);\n\n      ApplyPrimitiveType(ctx, cPrimType);\n\n      // Tests on Windows show that D3D9 does not do non-indexed instanced draws.\n\n      VkDrawIndirectCommand draw = { };\n      draw.vertexCount   = vertexCount;\n      draw.instanceCount = 1u;\n      draw.firstVertex   = cStartVertex;\n\n      ctx->draw(1u, &draw);\n    });\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawIndexedPrimitive(\n          D3DPRIMITIVETYPE PrimitiveType,\n          INT              BaseVertexIndex,\n          UINT             MinVertexIndex,\n          UINT             NumVertices,\n          UINT             StartIndex,\n          UINT             PrimitiveCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(m_state.vertexDecl == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(!PrimitiveCount || !NumVertices))\n      return D3D_OK;\n\n    bool dynamicSysmemVBOs;\n    bool dynamicSysmemIBO;\n    uint32_t indexCount = GetVertexCount(PrimitiveType, PrimitiveCount);\n    UploadPerDrawData(\n      MinVertexIndex,\n      NumVertices,\n      StartIndex,\n      indexCount,\n      BaseVertexIndex,\n      &dynamicSysmemVBOs,\n      &dynamicSysmemIBO\n    );\n\n    PrepareDraw(PrimitiveType, !dynamicSysmemVBOs, !dynamicSysmemIBO);\n\n    EmitCs([this,\n      cPrimType        = PrimitiveType,\n      cPrimCount       = PrimitiveCount,\n      cStartIndex      = StartIndex,\n      cBaseVertexIndex = BaseVertexIndex,\n      cInstanceCount   = GetInstanceCount()\n    ](DxvkContext* ctx) {\n      auto drawInfo = GenerateDrawInfo(cPrimType, cPrimCount, cInstanceCount);\n\n      ApplyPrimitiveType(ctx, cPrimType);\n\n      VkDrawIndexedIndirectCommand draw = { };\n      draw.indexCount    = drawInfo.vertexCount;\n      draw.instanceCount = drawInfo.instanceCount;\n      draw.firstIndex    = cStartIndex;\n      draw.vertexOffset  = cBaseVertexIndex;\n\n      ctx->drawIndexed(1u, &draw);\n    });\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawPrimitiveUP(\n          D3DPRIMITIVETYPE PrimitiveType,\n          UINT             PrimitiveCount,\n    const void*            pVertexStreamZeroData,\n          UINT             VertexStreamZeroStride) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(!VertexStreamZeroStride))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(m_state.vertexDecl == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(!PrimitiveCount))\n      return D3D_OK;\n\n    PrepareDraw(PrimitiveType, false, false);\n\n    uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount);\n\n    const uint32_t dataSize = GetUPDataSize(vertexCount, VertexStreamZeroStride);\n    const uint32_t bufferSize = GetUPBufferSize(vertexCount, VertexStreamZeroStride);\n\n    auto upSlice = AllocUPBuffer(bufferSize);\n    FillUPVertexBuffer(upSlice.mapPtr, pVertexStreamZeroData, dataSize, bufferSize);\n\n    EmitCs([this,\n      cBufferSlice  = std::move(upSlice.slice),\n      cPrimType     = PrimitiveType,\n      cStride       = VertexStreamZeroStride,\n      cVertexCount  = vertexCount\n    ](DxvkContext* ctx) mutable {\n      ApplyPrimitiveType(ctx, cPrimType);\n\n      // Tests on Windows show that D3D9 does not do non-indexed instanced draws.\n      VkDrawIndirectCommand draw = { };\n      draw.vertexCount = cVertexCount;\n      draw.instanceCount = 1u;\n\n      ctx->bindVertexBuffer(0, std::move(cBufferSlice), cStride);\n      ctx->draw(1u, &draw);\n      ctx->bindVertexBuffer(0, DxvkBufferSlice(), 0);\n    });\n\n    m_state.vertexBuffers[0].vertexBuffer = nullptr;\n    m_state.vertexBuffers[0].offset       = 0;\n    m_state.vertexBuffers[0].stride       = 0;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawIndexedPrimitiveUP(\n          D3DPRIMITIVETYPE PrimitiveType,\n          UINT             MinVertexIndex,\n          UINT             NumVertices,\n          UINT             PrimitiveCount,\n    const void*            pIndexData,\n          D3DFORMAT        IndexDataFormat,\n    const void*            pVertexStreamZeroData,\n          UINT             VertexStreamZeroStride) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(!VertexStreamZeroStride))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(m_state.vertexDecl == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(!PrimitiveCount || !NumVertices))\n      return D3D_OK;\n\n    PrepareDraw(PrimitiveType, false, false);\n\n    uint32_t vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount);\n\n    const uint32_t vertexDataSize = GetUPDataSize(MinVertexIndex + NumVertices, VertexStreamZeroStride);\n    const uint32_t vertexBufferSize = GetUPBufferSize(MinVertexIndex + NumVertices, VertexStreamZeroStride);\n\n    const uint32_t indexSize = IndexDataFormat == D3DFMT_INDEX16 ? 2 : 4;\n    const uint32_t indicesSize = vertexCount * indexSize;\n\n    const uint32_t upSize = vertexBufferSize + indicesSize;\n\n    auto upSlice = AllocUPBuffer(upSize);\n    uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr);\n    FillUPVertexBuffer(data, pVertexStreamZeroData, vertexDataSize, vertexBufferSize);\n    std::memcpy(data + vertexBufferSize, pIndexData, indicesSize);\n\n    EmitCs([this,\n      cVertexSize   = vertexBufferSize,\n      cBufferSlice  = std::move(upSlice.slice),\n      cPrimType     = PrimitiveType,\n      cPrimCount    = PrimitiveCount,\n      cStride       = VertexStreamZeroStride,\n      cInstanceCount = GetInstanceCount(),\n      cIndexType    = DecodeIndexType(\n                        static_cast<D3D9Format>(IndexDataFormat))\n    ](DxvkContext* ctx) {\n      auto drawInfo = GenerateDrawInfo(cPrimType, cPrimCount, cInstanceCount);\n\n      ApplyPrimitiveType(ctx, cPrimType);\n\n      VkDrawIndexedIndirectCommand draw = { };\n      draw.indexCount    = drawInfo.vertexCount;\n      draw.instanceCount = drawInfo.instanceCount;\n\n      ctx->bindVertexBuffer(0, cBufferSlice.subSlice(0, cVertexSize), cStride);\n      ctx->bindIndexBuffer(cBufferSlice.subSlice(cVertexSize, cBufferSlice.length() - cVertexSize), cIndexType);\n      ctx->drawIndexed(1u, &draw);\n      ctx->bindVertexBuffer(0, DxvkBufferSlice(), 0);\n      ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32);\n    });\n\n    m_state.vertexBuffers[0].vertexBuffer = nullptr;\n    m_state.vertexBuffers[0].offset       = 0;\n    m_state.vertexBuffers[0].stride       = 0;\n\n    m_state.indices = nullptr;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ProcessVertices(\n          UINT                         SrcStartIndex,\n          UINT                         DestIndex,\n          UINT                         VertexCount,\n          IDirect3DVertexBuffer9*      pDestBuffer,\n          IDirect3DVertexDeclaration9* pVertexDecl,\n          DWORD                        Flags) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pDestBuffer == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // When vertex shader 3.0 or above is set as the current vertex shader,\n    // the output vertex declaration must be present.\n    if (UseProgrammableVS()) {\n      const auto& programInfo = GetCommonShader(m_state.vertexShader)->GetInfo();\n\n      if (unlikely(programInfo.majorVersion() >= 3) && (pVertexDecl == nullptr))\n        return D3DERR_INVALIDCALL;\n    }\n\n    if (!SupportsSWVP()) {\n      static bool s_errorShown = false;\n\n      if (!std::exchange(s_errorShown, true))\n        Logger::err(\"D3D9DeviceEx::ProcessVertices: SWVP emu unsupported (vertexPipelineStoresAndAtomics)\");\n\n      return D3D_OK;\n    }\n\n    if (unlikely(!VertexCount))\n      return D3D_OK;\n\n    D3D9CommonBuffer* dst  = static_cast<D3D9VertexBuffer*>(pDestBuffer)->GetCommonBuffer();\n    D3D9VertexDecl*   decl = static_cast<D3D9VertexDecl*>  (pVertexDecl);\n\n    bool dynamicSysmemVBOs;\n    uint32_t firstIndex     = 0;\n    int32_t baseVertexIndex = 0;\n    UploadPerDrawData(\n      SrcStartIndex,\n      VertexCount,\n      firstIndex,\n      0,\n      baseVertexIndex,\n      &dynamicSysmemVBOs,\n      nullptr\n    );\n\n    PrepareDraw(D3DPT_FORCE_DWORD, !dynamicSysmemVBOs, false);\n\n    if (decl == nullptr) {\n      DWORD FVF = dst->Desc()->FVF;\n\n      auto iter = m_fvfTable.find(FVF);\n\n      if (iter == m_fvfTable.end()) {\n        decl = new D3D9VertexDecl(this, FVF);\n        m_fvfTable.insert(std::make_pair(FVF, decl));\n      }\n      else\n        decl = iter->second.ptr();\n    }\n\n    uint32_t offset = DestIndex * decl->GetSize(0);\n\n    D3D9CompactVertexElements elements;\n    for (const D3DVERTEXELEMENT9& element : decl->GetElements()) {\n      elements.emplace_back(element);\n    }\n\n    EmitCs([this,\n      cVertexElements = std::move(elements),\n      cVertexCount    = VertexCount,\n      cStartIndex     = SrcStartIndex,\n      cInstanceCount  = GetInstanceCount(),\n      cBufferSlice    = dst->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>(),\n      cBufferOffset   = offset\n    ](DxvkContext* ctx) mutable {\n      Rc<DxvkShader> shader = m_swvpEmulator.GetShaderModule(this, std::move(cVertexElements));\n\n      auto drawInfo = GenerateDrawInfo(D3DPT_POINTLIST, cVertexCount, cInstanceCount);\n\n      if (drawInfo.instanceCount != 1) {\n        drawInfo.instanceCount = 1;\n\n        Logger::warn(\"D3D9DeviceEx::ProcessVertices: instancing unsupported\");\n      }\n\n      ApplyPrimitiveType(ctx, D3DPT_POINTLIST);\n\n      // We need to bind the buffer as a view rather than a raw buffer.\n      // In order to avoid view bloat, create a format-less view for\n      // the entire buffer and pass the offset in via a push constant.\n      DxvkBufferViewKey viewKey;\n      viewKey.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n      viewKey.offset = cBufferSlice.offset();\n      viewKey.size = cBufferSlice.length();\n\n      auto bufferView = cBufferSlice.buffer()->createView(viewKey);\n\n      // Unbind the pixel shader, we aren't drawing\n      // to avoid val errors / UB.\n      ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(nullptr);\n\n      VkDrawIndirectCommand draw = { };\n      draw.vertexCount   = drawInfo.vertexCount;\n      draw.instanceCount = drawInfo.instanceCount;\n      draw.firstVertex   = cStartIndex;\n\n      uint32_t byteOffset = cBufferOffset;\n\n      ctx->bindShader<VK_SHADER_STAGE_GEOMETRY_BIT>(std::move(shader));\n      ctx->bindResourceBufferView(VK_SHADER_STAGE_GEOMETRY_BIT, getSWVPBufferSlot(), std::move(bufferView));\n      ctx->pushData(VK_SHADER_STAGE_GEOMETRY_BIT, 0u, sizeof(byteOffset), &byteOffset);\n      ctx->draw(1u, &draw);\n      ctx->bindResourceBufferView(VK_SHADER_STAGE_GEOMETRY_BIT, getSWVPBufferSlot(), nullptr);\n      ctx->bindShader<VK_SHADER_STAGE_GEOMETRY_BIT>(nullptr);\n    });\n\n    // We unbound the pixel shader before,\n    // let's make sure that gets rebound.\n    if (m_state.pixelShader != nullptr) {\n      BindShader<D3D9ShaderType::PixelShader>(\n        GetCommonShader(m_state.pixelShader));\n    } else {\n      m_dirty.set(D3D9DeviceDirtyFlag::FFPixelShader);\n      BindFFUbershader<D3D9ShaderType::PixelShader>();\n    }\n\n    if (dst->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER) {\n      uint32_t copySize = VertexCount * decl->GetSize(0);\n\n      EmitCs([\n        cSrcBuffer = dst->GetBuffer<D3D9_COMMON_BUFFER_TYPE_REAL>(),\n        cDstBuffer = dst->GetBuffer<D3D9_COMMON_BUFFER_TYPE_MAPPING>(),\n        cOffset    = offset,\n        cCopySize  = copySize\n      ](DxvkContext* ctx) {\n        ctx->copyBuffer(cDstBuffer, cOffset, cSrcBuffer, cOffset, cCopySize);\n      });\n    }\n\n    dst->SetNeedsReadback(true);\n    TrackBufferMappingBufferSequenceNumber(dst);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateVertexDeclaration(\n    const D3DVERTEXELEMENT9*            pVertexElements,\n          IDirect3DVertexDeclaration9** ppDecl) {\n    InitReturnPtr(ppDecl);\n\n    if (unlikely(ppDecl == nullptr || pVertexElements == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    const D3DVERTEXELEMENT9* counter = pVertexElements;\n    while (counter->Stream != 0xFF)\n      counter++;\n\n    const uint32_t declCount = uint32_t(counter - pVertexElements);\n\n    try {\n      const Com<D3D9VertexDecl> decl = new D3D9VertexDecl(this, pVertexElements, declCount);\n      *ppDecl = decl.ref();\n      return D3D_OK;\n    }\n    catch (const DxvkError & e) {\n      Logger::err(e.message());\n      return D3DERR_INVALIDCALL;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl) {\n    D3D9DeviceLock lock = LockDevice();\n\n    D3D9VertexDecl* decl = static_cast<D3D9VertexDecl*>(pDecl);\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetVertexDeclaration(decl);\n\n    if (decl == m_state.vertexDecl.ptr())\n      return D3D_OK;\n\n    bool dirtyFFShader = decl == nullptr || m_state.vertexDecl == nullptr;\n    if (!dirtyFFShader)\n      dirtyFFShader |= decl->GetFlags()        != m_state.vertexDecl->GetFlags()\n                    || decl->GetTexcoordMask() != m_state.vertexDecl->GetTexcoordMask();\n\n    if (dirtyFFShader)\n      m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n\n    const bool wasUsingProgrammableVS = UseProgrammableVS();\n\n    m_state.vertexDecl = decl;\n\n    const bool usesProgrammableVS = UseProgrammableVS();\n\n    if (unlikely(usesProgrammableVS != wasUsingProgrammableVS)) {\n      if (usesProgrammableVS) {\n        BindShader<D3D9ShaderType::VertexShader>(GetCommonShader(m_state.vertexShader));\n      } else {\n        m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n        BindFFUbershader<D3D9ShaderType::VertexShader>();\n      }\n    }\n\n    m_dirty.set(D3D9DeviceDirtyFlag::InputLayout);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl) {\n    D3D9DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppDecl);\n\n    if (unlikely(ppDecl == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (m_state.vertexDecl == nullptr)\n      return D3D_OK;\n\n    *ppDecl = m_state.vertexDecl.ref();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetFVF(DWORD FVF) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (FVF == 0)\n      return D3D_OK;\n\n    D3D9VertexDecl* decl = nullptr;\n\n    auto iter = m_fvfTable.find(FVF);\n\n    if (iter == m_fvfTable.end()) {\n      decl = new D3D9VertexDecl(this, FVF);\n      m_fvfTable.insert(std::make_pair(FVF, decl));\n    }\n    else\n      decl = iter->second.ptr();\n\n    return this->SetVertexDeclaration(decl);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetFVF(DWORD* pFVF) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pFVF == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pFVF = m_state.vertexDecl != nullptr\n      ? m_state.vertexDecl->GetFVF()\n      : 0;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateVertexShader(\n    const DWORD*                   pFunction,\n          IDirect3DVertexShader9** ppShader) {\n    // CreateVertexShader does not init the\n    // return ptr unlike CreatePixelShader\n\n    if (unlikely(ppShader == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    DxsoModuleInfo moduleInfo;\n    moduleInfo.options = m_dxsoOptions;\n\n    D3D9CommonShader module;\n    uint32_t bytecodeLength;\n\n    if (FAILED(this->CreateShaderModule(&module,\n      &bytecodeLength,\n      VK_SHADER_STAGE_VERTEX_BIT,\n      pFunction,\n      &moduleInfo)))\n      return D3DERR_INVALIDCALL;\n\n    *ppShader = ref(new D3D9VertexShader(this,\n      &m_shaderAllocator,\n      module,\n      pFunction,\n      bytecodeLength));\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexShader(IDirect3DVertexShader9* pShader) {\n    D3D9DeviceLock lock = LockDevice();\n\n    D3D9VertexShader* shader = static_cast<D3D9VertexShader*>(pShader);\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetVertexShader(shader);\n\n    if (shader == m_state.vertexShader.ptr())\n      return D3D_OK;\n\n    auto* oldShader = GetCommonShader(m_state.vertexShader);\n    auto* newShader = GetCommonShader(shader);\n\n    bool oldCopies = oldShader && oldShader->GetMeta().needsConstantCopies;\n    bool newCopies = newShader && newShader->GetMeta().needsConstantCopies;\n\n    m_consts[uint32_t(D3D9ShaderType::VertexShader)].dirty |= oldCopies || newCopies || !oldShader;\n    m_consts[uint32_t(D3D9ShaderType::VertexShader)].meta  = newShader ? newShader->GetMeta() : DxsoShaderMetaInfo();\n\n    if (newShader && oldShader) {\n      m_consts[uint32_t(D3D9ShaderType::VertexShader)].dirty\n        |= newShader->GetMeta().maxConstIndexF > oldShader->GetMeta().maxConstIndexF\n        || newShader->GetMeta().maxConstIndexI > oldShader->GetMeta().maxConstIndexI\n        || newShader->GetMeta().maxConstIndexB > oldShader->GetMeta().maxConstIndexB;\n    }\n\n    const bool wasUsingProgrammableVS = UseProgrammableVS();\n\n    m_state.vertexShader = shader;\n\n    const bool usesProgrammableVS = UseProgrammableVS();\n\n    if (usesProgrammableVS) {\n      BindShader<D3D9ShaderType::VertexShader>(GetCommonShader(shader));\n\n      UpdateTextureTypeMismatchesForShader(newShader, VSShaderMasks().samplerMask, FirstVSSamplerSlot);\n    } else if (wasUsingProgrammableVS) {\n      m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n      BindFFUbershader<D3D9ShaderType::VertexShader>();\n    }\n\n    m_dirty.set(D3D9DeviceDirtyFlag::InputLayout);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexShader(IDirect3DVertexShader9** ppShader) {\n    D3D9DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppShader);\n\n    if (unlikely(ppShader == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *ppShader = m_state.vertexShader.ref();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexShaderConstantF(\n          UINT   StartRegister,\n    const float* pConstantData,\n          UINT   Vector4fCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return SetShaderConstants<\n      D3D9ShaderType::VertexShader,\n      D3D9ConstantType::Float>(\n        StartRegister,\n        pConstantData,\n        Vector4fCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexShaderConstantF(\n          UINT   StartRegister,\n          float* pConstantData,\n          UINT   Vector4fCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return GetShaderConstants<\n      D3D9ShaderType::VertexShader,\n      D3D9ConstantType::Float>(\n        StartRegister,\n        pConstantData,\n        Vector4fCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexShaderConstantI(\n          UINT StartRegister,\n    const int* pConstantData,\n          UINT Vector4iCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return SetShaderConstants<\n      D3D9ShaderType::VertexShader,\n      D3D9ConstantType::Int>(\n        StartRegister,\n        pConstantData,\n        Vector4iCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexShaderConstantI(\n          UINT StartRegister,\n          int* pConstantData,\n          UINT Vector4iCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return GetShaderConstants<\n      D3D9ShaderType::VertexShader,\n      D3D9ConstantType::Int>(\n        StartRegister,\n        pConstantData,\n        Vector4iCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetVertexShaderConstantB(\n          UINT  StartRegister,\n    const BOOL* pConstantData,\n          UINT  BoolCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return SetShaderConstants<\n      D3D9ShaderType::VertexShader,\n      D3D9ConstantType::Bool>(\n        StartRegister,\n        pConstantData,\n        BoolCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetVertexShaderConstantB(\n          UINT  StartRegister,\n          BOOL* pConstantData,\n          UINT  BoolCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return GetShaderConstants<\n      D3D9ShaderType::VertexShader,\n      D3D9ConstantType::Bool>(\n        StartRegister,\n        pConstantData,\n        BoolCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetStreamSource(\n          UINT                    StreamNumber,\n          IDirect3DVertexBuffer9* pStreamData,\n          UINT                    OffsetInBytes,\n          UINT                    Stride) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(StreamNumber >= caps::MaxStreams))\n      return D3DERR_INVALIDCALL;\n\n    D3D9VertexBuffer* buffer = static_cast<D3D9VertexBuffer*>(pStreamData);\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetStreamSource(\n        StreamNumber,\n        buffer,\n        OffsetInBytes,\n        Stride);\n\n    auto& vbo = m_state.vertexBuffers[StreamNumber];\n    bool needsUpdate = vbo.vertexBuffer != buffer;\n\n    if (needsUpdate)\n      vbo.vertexBuffer = buffer;\n\n    const uint32_t bit = 1u << StreamNumber;\n    m_vbSlotTracking.bound &= ~bit;\n    m_vbSlotTracking.uploadPerDraw &= ~bit;\n    m_vbSlotTracking.needsUpload &= ~bit;\n\n    if (buffer != nullptr) {\n      needsUpdate |= vbo.offset != OffsetInBytes\n                  || vbo.stride != Stride;\n\n      vbo.offset = OffsetInBytes;\n      vbo.stride = Stride;\n\n      const D3D9CommonBuffer* commonBuffer = GetCommonBuffer(buffer);\n      m_vbSlotTracking.bound |= bit;\n      if (commonBuffer->DoPerDrawUpload() || CanOnlySWVP())\n        m_vbSlotTracking.uploadPerDraw |= bit;\n      if (commonBuffer->NeedsUpload()) {\n        m_vbSlotTracking.needsUpload |= bit;\n      }\n    } else {\n      // D3D9 doesn't actually unbind any vertex buffer when passing null.\n      // Operation Flashpoint: Red River relies on this behavior.\n      needsUpdate = false;\n    }\n\n    if (needsUpdate)\n      BindVertexBuffer(StreamNumber, buffer, OffsetInBytes, Stride);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetStreamSource(\n          UINT                     StreamNumber,\n          IDirect3DVertexBuffer9** ppStreamData,\n          UINT*                    pOffsetInBytes,\n          UINT*                    pStride) {\n    D3D9DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppStreamData);\n\n    if (likely(pOffsetInBytes != nullptr))\n      *pOffsetInBytes = 0;\n\n    if (likely(pStride != nullptr))\n      *pStride = 0;\n\n    if (unlikely(ppStreamData == nullptr || pOffsetInBytes == nullptr || pStride == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(StreamNumber >= caps::MaxStreams))\n      return D3DERR_INVALIDCALL;\n\n    const auto& vbo = m_state.vertexBuffers[StreamNumber];\n\n    *ppStreamData   = vbo.vertexBuffer.ref();\n    *pOffsetInBytes = vbo.offset;\n    *pStride        = vbo.stride;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(StreamNumber >= caps::MaxStreams))\n      return D3DERR_INVALIDCALL;\n\n    const bool indexed  = Setting & D3DSTREAMSOURCE_INDEXEDDATA;\n    const bool instanced = Setting & D3DSTREAMSOURCE_INSTANCEDATA;\n\n    if (unlikely(StreamNumber == 0 && instanced))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(instanced && indexed))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(Setting == 0))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetStreamSourceFreq(StreamNumber, Setting);\n\n    if (m_state.streamFreq[StreamNumber] == Setting)\n      return D3D_OK;\n\n    m_state.streamFreq[StreamNumber] = Setting;\n\n    if (instanced)\n      m_vbSlotTracking.instanced |=   1u << StreamNumber;\n    else\n      m_vbSlotTracking.instanced &= ~(1u << StreamNumber);\n\n    m_dirty.set(D3D9DeviceDirtyFlag::InputLayout);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetStreamSourceFreq(UINT StreamNumber, UINT* pSetting) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(StreamNumber >= caps::MaxStreams))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pSetting == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pSetting = m_state.streamFreq[StreamNumber];\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetIndices(IDirect3DIndexBuffer9* pIndexData) {\n    D3D9DeviceLock lock = LockDevice();\n\n    D3D9IndexBuffer* buffer = static_cast<D3D9IndexBuffer*>(pIndexData);\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetIndices(buffer);\n\n    if (buffer == m_state.indices.ptr())\n      return D3D_OK;\n\n    m_state.indices = buffer;\n\n    // Don't unbind the buffer if the game sets a nullptr here.\n    // Operation Flashpoint Red River breaks if we do that.\n    // EndScene will clean it up if necessary.\n    if (buffer != nullptr)\n      BindIndices();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetIndices(IDirect3DIndexBuffer9** ppIndexData) {\n    D3D9DeviceLock lock = LockDevice();\n    InitReturnPtr(ppIndexData);\n\n    if (unlikely(ppIndexData == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *ppIndexData = m_state.indices.ref();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreatePixelShader(\n    const DWORD*                  pFunction,\n          IDirect3DPixelShader9** ppShader) {\n    InitReturnPtr(ppShader);\n\n    if (unlikely(ppShader == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    DxsoModuleInfo moduleInfo;\n    moduleInfo.options = m_dxsoOptions;\n\n    D3D9CommonShader module;\n    uint32_t bytecodeLength;\n\n    if (FAILED(this->CreateShaderModule(&module,\n      &bytecodeLength,\n      VK_SHADER_STAGE_FRAGMENT_BIT,\n      pFunction,\n      &moduleInfo)))\n      return D3DERR_INVALIDCALL;\n\n    *ppShader = ref(new D3D9PixelShader(this,\n      &m_shaderAllocator,\n      module,\n      pFunction,\n      bytecodeLength));\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPixelShader(IDirect3DPixelShader9* pShader) {\n    D3D9DeviceLock lock = LockDevice();\n\n    D3D9PixelShader* shader = static_cast<D3D9PixelShader*>(pShader);\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetPixelShader(shader);\n\n    if (shader == m_state.pixelShader.ptr())\n      return D3D_OK;\n\n    auto* oldShader = GetCommonShader(m_state.pixelShader);\n    auto* newShader = GetCommonShader(shader);\n\n    bool oldCopies = oldShader && oldShader->GetMeta().needsConstantCopies;\n    bool newCopies = newShader && newShader->GetMeta().needsConstantCopies;\n\n    m_consts[uint32_t(D3D9ShaderType::PixelShader)].dirty |= oldCopies || newCopies || !oldShader;\n    m_consts[uint32_t(D3D9ShaderType::PixelShader)].meta  = newShader ? newShader->GetMeta() : DxsoShaderMetaInfo();\n\n    if (newShader && oldShader) {\n      m_consts[uint32_t(D3D9ShaderType::PixelShader)].dirty\n        |= newShader->GetMeta().maxConstIndexF > oldShader->GetMeta().maxConstIndexF\n        || newShader->GetMeta().maxConstIndexI > oldShader->GetMeta().maxConstIndexI\n        || newShader->GetMeta().maxConstIndexB > oldShader->GetMeta().maxConstIndexB;\n    }\n\n    const D3D9ShaderMasks oldShaderMasks = PSShaderMasks();\n    m_state.pixelShader = shader;\n    const D3D9ShaderMasks newShaderMasks = PSShaderMasks();\n\n    if (shader != nullptr) {\n      BindShader<D3D9ShaderType::PixelShader>(newShader);\n\n      UpdateTextureTypeMismatchesForShader(newShader, newShaderMasks.samplerMask, 0);\n\n      bool dirty = m_specInfo.set<D3D9SpecConstantId::SpecFFLastActiveTextureStage>(0u);\n      dirty |= m_specInfo.set<D3D9SpecConstantId::SpecFFGlobalSpecularEnabled>(0u);\n      constexpr uint32_t perTextureStageSpecConsts = static_cast<uint32_t>(D3D9SpecConstantId::SpecFFTextureStage1ColorOp) - static_cast<uint32_t>(D3D9SpecConstantId::SpecFFTextureStage0ColorOp);\n      for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n        dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorOp + perTextureStageSpecConsts * i), 0u);\n        dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorArg1 + perTextureStageSpecConsts * i), 0u);\n        dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorArg2 + perTextureStageSpecConsts * i), 0u);\n        dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaOp + perTextureStageSpecConsts * i), 0u);\n        dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaArg1 + perTextureStageSpecConsts * i), 0u);\n        dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaArg2 + perTextureStageSpecConsts * i), 0u);\n        dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ResultIsTemp + perTextureStageSpecConsts * i), 0u);\n        // Color arg0 and alpha arg0 for all stages are packed after all the other FF spec consts\n        dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorArg0 + i), 0u);\n        dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaArg0 + i), 0u);\n      }\n      if (dirty) {\n        m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n      }\n    }\n    else {\n      m_dirty.set(D3D9DeviceDirtyFlag::FFPixelShader);\n      BindFFUbershader<D3D9ShaderType::PixelShader>();\n\n      // TODO: What fixed function textures are in use?\n      // Currently we are making all 8 of them as in use here.\n      // Fixed function always uses spec constants to decide the texture type.\n      m_textureSlotTracking.textureDirty |= newShaderMasks.samplerMask & m_textureSlotTracking.mismatchingTextureType;\n      m_textureSlotTracking.mismatchingTextureType &= ~newShaderMasks.samplerMask;\n    }\n\n    // Check whether the color output mask or the mask of the used samplers\n    // forces us to deal with hazards in a different way.\n    if (likely(oldShaderMasks.samplerMask != newShaderMasks.samplerMask ||\n      oldShaderMasks.rtMask != newShaderMasks.rtMask))\n      UpdateActiveHazardsRT(oldShaderMasks.samplerMask | newShaderMasks.samplerMask);\n\n    if (likely(oldShaderMasks.samplerMask != newShaderMasks.samplerMask))\n      UpdateActiveHazardsDS(oldShaderMasks.samplerMask | newShaderMasks.samplerMask);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPixelShader(IDirect3DPixelShader9** ppShader) {\n    D3D9DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppShader);\n\n    if (unlikely(ppShader == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *ppShader = m_state.pixelShader.ref();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPixelShaderConstantF(\n    UINT   StartRegister,\n    const float* pConstantData,\n    UINT   Vector4fCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return SetShaderConstants <\n      D3D9ShaderType::PixelShader,\n      D3D9ConstantType::Float>(\n        StartRegister,\n        pConstantData,\n        Vector4fCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPixelShaderConstantF(\n    UINT   StartRegister,\n    float* pConstantData,\n    UINT   Vector4fCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return GetShaderConstants<\n      D3D9ShaderType::PixelShader,\n      D3D9ConstantType::Float>(\n        StartRegister,\n        pConstantData,\n        Vector4fCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPixelShaderConstantI(\n    UINT StartRegister,\n    const int* pConstantData,\n    UINT Vector4iCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return SetShaderConstants<\n      D3D9ShaderType::PixelShader,\n      D3D9ConstantType::Int>(\n        StartRegister,\n        pConstantData,\n        Vector4iCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPixelShaderConstantI(\n    UINT StartRegister,\n    int* pConstantData,\n    UINT Vector4iCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return GetShaderConstants<\n      D3D9ShaderType::PixelShader,\n      D3D9ConstantType::Int>(\n        StartRegister,\n        pConstantData,\n        Vector4iCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetPixelShaderConstantB(\n    UINT  StartRegister,\n    const BOOL* pConstantData,\n    UINT  BoolCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return SetShaderConstants<\n      D3D9ShaderType::PixelShader,\n      D3D9ConstantType::Bool>(\n        StartRegister,\n        pConstantData,\n        BoolCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetPixelShaderConstantB(\n    UINT  StartRegister,\n    BOOL* pConstantData,\n    UINT  BoolCount) {\n    D3D9DeviceLock lock = LockDevice();\n\n    return GetShaderConstants<\n      D3D9ShaderType::PixelShader,\n      D3D9ConstantType::Bool>(\n        StartRegister,\n        pConstantData,\n        BoolCount);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawRectPatch(\n          UINT               Handle,\n    const float*             pNumSegs,\n    const D3DRECTPATCH_INFO* pRectPatchInfo) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9DeviceEx::DrawRectPatch: Stub\");\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DrawTriPatch(\n          UINT              Handle,\n    const float*            pNumSegs,\n    const D3DTRIPATCH_INFO* pTriPatchInfo) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9DeviceEx::DrawTriPatch: Stub\");\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::DeletePatch(UINT Handle) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9DeviceEx::DeletePatch: Stub\");\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery) {\n    HRESULT hr = D3D9Query::QuerySupported(this, Type);\n\n    if (ppQuery == nullptr || hr != D3D_OK)\n      return hr;\n\n    try {\n      *ppQuery = ref(new D3D9Query(this, Type));\n      return D3D_OK;\n    }\n    catch (const DxvkError & e) {\n      Logger::err(e.message());\n      return D3DERR_NOTAVAILABLE;\n    }\n  }\n\n\n  // Ex Methods\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetConvolutionMonoKernel(\n          UINT   width,\n          UINT   height,\n          float* rows,\n          float* columns) {\n    // We don't advertise support for this.\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ComposeRects(\n          IDirect3DSurface9*      pSrc,\n          IDirect3DSurface9*      pDst,\n          IDirect3DVertexBuffer9* pSrcRectDescs,\n          UINT                    NumRects,\n          IDirect3DVertexBuffer9* pDstRectDescs,\n          D3DCOMPOSERECTSOP       Operation,\n          int                     Xoffset,\n          int                     Yoffset) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9DeviceEx::ComposeRects: Stub\");\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetGPUThreadPriority(INT* pPriority) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9DeviceEx::GetGPUThreadPriority: Stub\");\n\n    if (unlikely(pPriority == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pPriority = 0;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetGPUThreadPriority(INT Priority) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9DeviceEx::SetGPUThreadPriority: Stub\");\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::WaitForVBlank(UINT iSwapChain) {\n    if (unlikely(iSwapChain != 0))\n      return D3DERR_INVALIDCALL;\n\n    return m_implicitSwapchain->WaitForVBlank();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CheckResourceResidency(IDirect3DResource9** pResourceArray, UINT32 NumResources) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9DeviceEx::CheckResourceResidency: Stub\");\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetMaximumFrameLatency(UINT MaxLatency) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(MaxLatency > 30))\n      return D3DERR_INVALIDCALL;\n\n    if (MaxLatency == 0)\n      MaxLatency = DefaultFrameLatency;\n\n    if (MaxLatency > MaxFrameLatency)\n      MaxLatency = MaxFrameLatency;\n\n    m_frameLatency = MaxLatency;\n\n    m_implicitSwapchain->SyncFrameLatency();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetMaximumFrameLatency(UINT* pMaxLatency) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(pMaxLatency == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pMaxLatency = m_frameLatency;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CheckDeviceState(HWND hDestinationWindow) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9DeviceEx::CheckDeviceState: Stub\");\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::PresentEx(\n    const RECT* pSourceRect,\n    const RECT* pDestRect,\n          HWND hDestWindowOverride,\n    const RGNDATA* pDirtyRegion,\n          DWORD dwFlags) {\n\n    if (m_cursor.IsSoftwareCursor()) {\n      D3D9_SOFTWARE_CURSOR* pSoftwareCursor = m_cursor.GetSoftwareCursor();\n\n      UINT cursorWidth  = pSoftwareCursor->DrawCursor ? pSoftwareCursor->Width  : 0;\n      UINT cursorHeight = pSoftwareCursor->DrawCursor ? pSoftwareCursor->Height : 0;\n\n      m_implicitSwapchain->SetCursorPosition(pSoftwareCursor->X - pSoftwareCursor->XHotSpot,\n                                             pSoftwareCursor->Y - pSoftwareCursor->YHotSpot,\n                                             cursorWidth, cursorHeight);\n\n      // Once a hardware cursor has been set or the device has been reset,\n      // we need to ensure that we render a 0-sized rectangle first, and\n      // only then fully clear the software cursor.\n      if (unlikely(pSoftwareCursor->ClearCursor)) {\n        pSoftwareCursor->Width       = 0;\n        pSoftwareCursor->Height      = 0;\n        pSoftwareCursor->XHotSpot    = 0;\n        pSoftwareCursor->YHotSpot    = 0;\n        pSoftwareCursor->ClearCursor = false;\n      }\n    }\n\n    return m_implicitSwapchain->Present(\n      pSourceRect,\n      pDestRect,\n      hDestWindowOverride,\n      pDirtyRegion,\n      dwFlags);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateRenderTargetEx(\n          UINT                Width,\n          UINT                Height,\n          D3DFORMAT           Format,\n          D3DMULTISAMPLE_TYPE MultiSample,\n          DWORD               MultisampleQuality,\n          BOOL                Lockable,\n          IDirect3DSurface9** ppSurface,\n          HANDLE*             pSharedHandle,\n          DWORD               Usage) {\n    InitReturnPtr(ppSurface);\n\n    if (unlikely(ppSurface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // The new Create functions added in 9Ex only accept the new USAGE flags added with 9Ex.\n    // Yes, it actually fails when explicitly passing D3DUSAGE_RENDERTARGET.\n    if (unlikely(Usage & ~(D3DUSAGE_RESTRICTED_CONTENT | D3DUSAGE_RESTRICT_SHARED_RESOURCE | D3DUSAGE_RESTRICT_SHARED_RESOURCE_DRIVER)))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely((Usage & (D3DUSAGE_RESTRICT_SHARED_RESOURCE | D3DUSAGE_RESTRICT_SHARED_RESOURCE_DRIVER)) != 0\n      && pSharedHandle == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D9_COMMON_TEXTURE_DESC desc;\n    desc.Width              = Width;\n    desc.Height             = Height;\n    desc.Depth              = 1;\n    desc.ArraySize          = 1;\n    desc.MipLevels          = 1;\n    desc.Usage              = Usage | D3DUSAGE_RENDERTARGET;\n    desc.Format             = EnumerateFormat(Format);\n    desc.Pool               = D3DPOOL_DEFAULT;\n    desc.Discard            = FALSE;\n    desc.MultiSample        = MultiSample;\n    desc.MultisampleQuality = MultisampleQuality;\n    desc.IsBackBuffer       = FALSE;\n    desc.IsAttachmentOnly   = TRUE;\n    desc.IsLockable         = Lockable;\n\n    HRESULT hr = D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc);\n    if (FAILED(hr))\n      return hr;\n\n    if (unlikely(pSharedHandle != nullptr && *pSharedHandle != nullptr &&\n                 !ValidateSharedTexture(*pSharedHandle, D3DRTYPE_SURFACE, desc)))\n      return E_INVALIDARG;\n\n    try {\n      const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, IsExtended(), nullptr, pSharedHandle);\n      m_initializer->InitTexture(surface->GetCommonTexture());\n      *ppSurface = surface.ref();\n      m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_OUTOFVIDEOMEMORY;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateOffscreenPlainSurfaceEx(\n          UINT                Width,\n          UINT                Height,\n          D3DFORMAT           Format,\n          D3DPOOL             Pool,\n          IDirect3DSurface9** ppSurface,\n          HANDLE*             pSharedHandle,\n          DWORD               Usage) {\n    InitReturnPtr(ppSurface);\n\n    if (unlikely(ppSurface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // The new Create functions added in 9Ex only accept the new USAGE flags added with 9Ex.\n    if (unlikely(Usage & ~(D3DUSAGE_RESTRICTED_CONTENT | D3DUSAGE_RESTRICT_SHARED_RESOURCE | D3DUSAGE_RESTRICT_SHARED_RESOURCE_DRIVER)))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely((Usage & (D3DUSAGE_RESTRICT_SHARED_RESOURCE | D3DUSAGE_RESTRICT_SHARED_RESOURCE_DRIVER)) != 0\n      && pSharedHandle == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D9_COMMON_TEXTURE_DESC desc;\n    desc.Width              = Width;\n    desc.Height             = Height;\n    desc.Depth              = 1;\n    desc.ArraySize          = 1;\n    desc.MipLevels          = 1;\n    desc.Usage              = Usage;\n    desc.Format             = EnumerateFormat(Format);\n    desc.Pool               = Pool;\n    desc.Discard            = FALSE;\n    desc.MultiSample        = D3DMULTISAMPLE_NONE;\n    desc.MultisampleQuality = 0;\n    desc.IsBackBuffer       = FALSE;\n    desc.IsAttachmentOnly   = TRUE;\n    // Docs: Off-screen plain surfaces are always lockable, regardless of their pool types.\n    desc.IsLockable         = TRUE;\n\n    // Because they are always lockable, image surfaces / offscreen plain surfaces\n    // are restricted to using lockable depth stencil formats.\n    if (unlikely(IsDepthStencilFormat(desc.Format) && !IsLockableDepthStencilFormat(desc.Format)))\n      return D3DERR_INVALIDCALL;\n\n    HRESULT hr = D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc);\n    if (FAILED(hr))\n      return hr;\n\n    try {\n      void* initialData = nullptr;\n\n      // On Windows Vista (so most likely D3D9Ex), pSharedHandle can be used to pass initial data\n      // for an offscreen plain surface, but only for a very specific type of offscreen plain surface.\n      if (unlikely(pSharedHandle != nullptr && Pool == D3DPOOL_SYSTEMMEM)) {\n        initialData = *(reinterpret_cast<void**>(pSharedHandle));\n        pSharedHandle = nullptr;\n      }\n\n      // Shared offscreen plain surfaces have to be in POOL_DEFAULT\n      if (unlikely(pSharedHandle != nullptr && Pool != D3DPOOL_DEFAULT))\n        return D3DERR_INVALIDCALL;\n\n      if (unlikely(pSharedHandle != nullptr && *pSharedHandle != nullptr &&\n                   !ValidateSharedTexture(*pSharedHandle, D3DRTYPE_SURFACE, desc)))\n        return E_INVALIDARG;\n\n      const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, IsExtended(), nullptr, pSharedHandle);\n      m_initializer->InitTexture(surface->GetCommonTexture(), initialData);\n      *ppSurface = surface.ref();\n\n      if (desc.Pool == D3DPOOL_DEFAULT)\n        m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_OUTOFVIDEOMEMORY;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateDepthStencilSurfaceEx(\n          UINT                Width,\n          UINT                Height,\n          D3DFORMAT           Format,\n          D3DMULTISAMPLE_TYPE MultiSample,\n          DWORD               MultisampleQuality,\n          BOOL                Discard,\n          IDirect3DSurface9** ppSurface,\n          HANDLE*             pSharedHandle,\n          DWORD               Usage) {\n    InitReturnPtr(ppSurface);\n\n    if (unlikely(ppSurface == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // The new Create functions added in 9Ex only accept the new USAGE flags added with 9Ex.\n    // Yes, it actually fails when explicitly passing D3DUSAGE_DEPTHSTENCIL.\n    if (unlikely(Usage & ~(D3DUSAGE_RESTRICTED_CONTENT | D3DUSAGE_RESTRICT_SHARED_RESOURCE | D3DUSAGE_RESTRICT_SHARED_RESOURCE_DRIVER)))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely((Usage & (D3DUSAGE_RESTRICT_SHARED_RESOURCE | D3DUSAGE_RESTRICT_SHARED_RESOURCE_DRIVER)) != 0\n      && pSharedHandle == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D9_COMMON_TEXTURE_DESC desc;\n    desc.Width              = Width;\n    desc.Height             = Height;\n    desc.Depth              = 1;\n    desc.ArraySize          = 1;\n    desc.MipLevels          = 1;\n    desc.Usage              = Usage | D3DUSAGE_DEPTHSTENCIL;\n    desc.Format             = EnumerateFormat(Format);\n    desc.Pool               = D3DPOOL_DEFAULT;\n    desc.Discard            = Discard;\n    desc.MultiSample        = MultiSample;\n    desc.MultisampleQuality = MultisampleQuality;\n    desc.IsBackBuffer       = FALSE;\n    desc.IsAttachmentOnly   = TRUE;\n    desc.IsLockable         = IsLockableDepthStencilFormat(desc.Format);\n\n    HRESULT hr = D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc);\n    if (FAILED(hr))\n      return hr;\n\n    if (unlikely(pSharedHandle != nullptr && *pSharedHandle != nullptr &&\n                 !ValidateSharedTexture(*pSharedHandle, D3DRTYPE_SURFACE, desc)))\n      return E_INVALIDARG;\n\n    try {\n      const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, IsExtended(), nullptr, pSharedHandle);\n      m_initializer->InitTexture(surface->GetCommonTexture());\n      *ppSurface = surface.ref();\n      m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_OUTOFVIDEOMEMORY;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ResetEx(\n          D3DPRESENT_PARAMETERS* pPresentationParameters,\n          D3DDISPLAYMODEEX*      pFullscreenDisplayMode) {\n    D3D9DeviceLock lock = LockDevice();\n\n    HRESULT hr;\n    if (likely(m_deviceType != D3DDEVTYPE_NULLREF)) {\n      hr = m_parent->ValidatePresentationParametersEx(pPresentationParameters, pFullscreenDisplayMode);\n\n      if (unlikely(FAILED(hr)))\n        return hr;\n    }\n\n    hr = ResetSwapChain(pPresentationParameters, pFullscreenDisplayMode);\n    if (FAILED(hr))\n      return hr;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetDisplayModeEx(\n          UINT                iSwapChain,\n          D3DDISPLAYMODEEX*   pMode,\n          D3DDISPLAYROTATION* pRotation) {\n    if (unlikely(iSwapChain != 0))\n      return D3DERR_INVALIDCALL;\n\n    return m_implicitSwapchain->GetDisplayModeEx(pMode, pRotation);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9DeviceEx::CreateAdditionalSwapChainEx(\n          D3DPRESENT_PARAMETERS* pPresentationParameters,\n    const D3DDISPLAYMODEEX*      pFullscreenDisplayMode,\n          IDirect3DSwapChain9**  ppSwapChain) {\n    D3D9DeviceLock lock = LockDevice();\n\n    InitReturnPtr(ppSwapChain);\n\n    if (ppSwapChain == nullptr || pPresentationParameters == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    // Additional fullscreen swapchains are forbidden.\n    if (!pPresentationParameters->Windowed)\n      return D3DERR_INVALIDCALL;\n\n    // We can't make another swapchain if we are fullscreen.\n    if (!m_implicitSwapchain->GetPresentParams()->Windowed)\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(IsDeviceLost())) {\n      return D3DERR_DEVICELOST;\n    }\n\n    m_implicitSwapchain->Invalidate(pPresentationParameters->hDeviceWindow);\n\n    try {\n      auto* swapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode, false);\n      *ppSwapChain = ref(swapchain);\n      m_losableResourceCounter++;\n    }\n    catch (const DxvkError & e) {\n      Logger::err(e.message());\n      return D3DERR_NOTAVAILABLE;\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9DeviceEx::SetStateSamplerState(\n    DWORD               StateSampler,\n    D3DSAMPLERSTATETYPE Type,\n    DWORD               Value) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetStateSamplerState(StateSampler, Type, Value);\n\n    auto& state = m_state.samplerStates;\n\n    if (state[StateSampler][Type] == Value)\n      return D3D_OK;\n\n    state[StateSampler][Type] = Value;\n\n    const uint32_t samplerBit = 1u << StateSampler;\n\n    m_textureSlotTracking.samplerStateDirty |= samplerBit;\n\n    if (Type == D3DSAMP_SRGBTEXTURE && (m_textureSlotTracking.bound & samplerBit))\n      m_textureSlotTracking.textureDirty |= samplerBit;\n\n    constexpr DWORD Fetch4Enabled  = MAKEFOURCC('G', 'E', 'T', '4');\n    constexpr DWORD Fetch4Disabled = MAKEFOURCC('G', 'E', 'T', '1');\n\n    if (unlikely(Type == D3DSAMP_MIPMAPLODBIAS)) {\n      if (unlikely(Value == Fetch4Enabled))\n        m_textureSlotTracking.fetch4SamplerState |= samplerBit;\n      else if (unlikely(Value == Fetch4Disabled))\n        m_textureSlotTracking.fetch4SamplerState &= ~samplerBit;\n\n      UpdateActiveFetch4(StateSampler);\n    }\n\n    if (unlikely(Type == D3DSAMP_MAGFILTER && (m_textureSlotTracking.fetch4SamplerState & samplerBit)))\n      UpdateActiveFetch4(StateSampler);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9DeviceEx::SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetStateTexture(StateSampler, pTexture);\n\n    if (m_state.textures[StateSampler] == pTexture)\n      return D3D_OK;\n\n    auto oldTexture = GetCommonTexture(m_state.textures[StateSampler]);\n    auto newTexture = GetCommonTexture(pTexture);\n\n    // We need to check our ops and disable respective stages.\n    // Given we have transition from a null resource to\n    // a valid resource or vice versa.\n    const bool isPSSampler = StateSampler < caps::MaxTexturesPS;\n    if (isPSSampler) {\n      // If we either bind a new texture or unbind the old one,\n      // we need to update the fixed function shader\n      // because we generate a different shader based on whether each texture is bound.\n      if (newTexture == nullptr || oldTexture == nullptr)\n        m_dirty.set(D3D9DeviceDirtyFlag::FFPixelShader);\n    }\n\n    // We pick a different address mode for cubemaps, so we might need to update the sampler state.\n    bool oldTextureIsCube = oldTexture != nullptr && oldTexture->IsCube();\n    bool newTextureIsCube = newTexture != nullptr && newTexture->IsCube();\n    if (unlikely(oldTextureIsCube != newTextureIsCube))\n      m_textureSlotTracking.samplerStateDirty |= 1u << StateSampler;\n\n    // We disable anisotropic filtering if the texture only has a single mip map,\n    // so wie might need to update the sampler state.\n    bool oldTextureHasMultipleMips = oldTexture != nullptr && oldTexture->Desc()->MipLevels > 1u;\n    bool newTextureHasMultipleMips = newTexture != nullptr && newTexture->Desc()->MipLevels > 1u;\n    if (unlikely(oldTextureHasMultipleMips != newTextureHasMultipleMips))\n      m_textureSlotTracking.samplerStateDirty |= 1u << StateSampler;\n\n    DWORD oldUsage = oldTexture != nullptr ? oldTexture->Desc()->Usage : 0;\n    DWORD newUsage = newTexture != nullptr ? newTexture->Desc()->Usage : 0;\n    DWORD combinedUsage = oldUsage | newUsage;\n    TextureChangePrivate(m_state.textures[StateSampler], pTexture);\n    m_textureSlotTracking.textureDirty |= 1u << StateSampler;\n    UpdateTextureBitmasks(StateSampler, combinedUsage);\n\n    // If the texture format changes and the corresponding sampler uses\n    // border colors, we may need to update the border color swizzle\n    if ((!oldTexture || !newTexture || oldTexture->Desc()->Format != newTexture->Desc()->Format)\n      && SamplerUsesBorderColor(StateSampler))\n        m_textureSlotTracking.samplerStateDirty |= 1u << StateSampler;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9DeviceEx::SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetStateTransform(idx, pMatrix);\n\n    m_state.transforms[idx] = ConvertMatrix(pMatrix);\n\n    m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n\n    if (idx == GetTransformIndex(D3DTS_VIEW) || idx >= GetTransformIndex(D3DTS_WORLD))\n      m_dirty.set(D3D9DeviceDirtyFlag::FFVertexBlend);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9DeviceEx::SetStateTextureStageState(\n          DWORD                      Stage,\n          D3D9TextureStageStateTypes Type,\n          DWORD                      Value) {\n\n    // Clamp values instead of checking and returning INVALID_CALL\n    // Matches tests + Dawn of Magic 2 relies on it.\n    Stage = std::min(Stage, DWORD(caps::TextureStageCount - 1));\n    Type = std::min(Type, D3D9TextureStageStateTypes(DXVK_TSS_COUNT - 1));\n\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetStateTextureStageState(Stage, Type, Value);\n\n    if (likely(m_state.textureStages[Stage][Type] != Value)) {\n      m_state.textureStages[Stage][Type] = Value;\n\n      switch (Type) {\n        case DXVK_TSS_COLOROP:\n        case DXVK_TSS_COLORARG0:\n        case DXVK_TSS_COLORARG1:\n        case DXVK_TSS_COLORARG2:\n        case DXVK_TSS_ALPHAOP:\n        case DXVK_TSS_ALPHAARG0:\n        case DXVK_TSS_ALPHAARG1:\n        case DXVK_TSS_ALPHAARG2:\n        case DXVK_TSS_RESULTARG:\n          m_dirty.set(D3D9DeviceDirtyFlag::FFPixelShader);\n          break;\n\n        case DXVK_TSS_TEXCOORDINDEX:\n          m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n          break;\n\n        case DXVK_TSS_TEXTURETRANSFORMFLAGS:\n          m_textureSlotTracking.projected &= ~(1 << Stage);\n          if (Value & D3DTTFF_PROJECTED)\n            m_textureSlotTracking.projected |= 1 << Stage;\n\n          m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n          m_dirty.set(D3D9DeviceDirtyFlag::FFPixelShader);\n          break;\n\n        case DXVK_TSS_BUMPENVMAT00:\n        case DXVK_TSS_BUMPENVMAT01:\n        case DXVK_TSS_BUMPENVMAT10:\n        case DXVK_TSS_BUMPENVMAT11:\n        case DXVK_TSS_BUMPENVLSCALE:\n        case DXVK_TSS_BUMPENVLOFFSET:\n        case DXVK_TSS_CONSTANT:\n          m_dirty.set(D3D9DeviceDirtyFlag::SharedPixelShaderData);\n          break;\n\n        default: break;\n      }\n    }\n\n    return D3D_OK;\n  }\n\n\n  bool D3D9DeviceEx::IsExtended() {\n    return m_parent->IsExtended();\n  }\n\n\n  bool D3D9DeviceEx::SupportsSWVP() {\n    return m_dxvkDevice->features().core.features.vertexPipelineStoresAndAtomics && m_dxvkDevice->features().vk12.shaderInt8;\n  }\n\n\n  bool D3D9DeviceEx::SupportsVCacheQuery() const {\n    return m_adapter->GetVendorId() == uint32_t(DxvkGpuVendor::Nvidia);\n  }\n\n\n  HWND D3D9DeviceEx::GetWindow() {\n    return m_window;\n  }\n\n\n  void D3D9DeviceEx::DetermineConstantLayouts(bool canSWVP) {\n    D3D9ConstantSets& vsConstSet    = m_consts[uint32_t(D3D9ShaderType::VertexShader)];\n    vsConstSet.layout.floatCount    = canSWVP ? caps::MaxFloatConstantsSoftware : caps::MaxFloatConstantsVS;\n    vsConstSet.layout.intCount      = canSWVP ? caps::MaxOtherConstantsSoftware : caps::MaxOtherConstants;\n    vsConstSet.layout.boolCount     = canSWVP ? caps::MaxOtherConstantsSoftware : caps::MaxOtherConstants;\n    vsConstSet.layout.bitmaskCount  = align(vsConstSet.layout.boolCount, 32) / 32;\n\n    D3D9ConstantSets& psConstSet   = m_consts[uint32_t(D3D9ShaderType::PixelShader)];\n    psConstSet.layout.floatCount   = caps::MaxSM3FloatConstantsPS;\n    psConstSet.layout.intCount     = caps::MaxOtherConstants;\n    psConstSet.layout.boolCount    = caps::MaxOtherConstants;\n    psConstSet.layout.bitmaskCount = align(psConstSet.layout.boolCount, 32) / 32;\n  }\n\n\n  D3D9BufferSlice D3D9DeviceEx::AllocUPBuffer(VkDeviceSize size) {\n    constexpr VkDeviceSize UPBufferSize = 1 << 20;\n\n    if (unlikely(m_upBuffer == nullptr || size > UPBufferSize)) {\n      VkMemoryPropertyFlags memoryFlags\n        = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT\n        | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n        | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n      DxvkBufferCreateInfo info;\n      info.size   = std::max(UPBufferSize, size);\n      info.usage  = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT\n                  | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;\n      info.access = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT\n                  | VK_ACCESS_INDEX_READ_BIT;\n      info.stages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;\n      info.debugName = \"UP buffer\";\n\n      Rc<DxvkBuffer> buffer = m_dxvkDevice->createBuffer(info, memoryFlags);\n      void* mapPtr = buffer->mapPtr(0);\n\n      if (size <= UPBufferSize) {\n        m_upBuffer = std::move(buffer);\n        m_upBufferMapPtr = mapPtr;\n      } else {\n        // Temporary buffer\n        D3D9BufferSlice result;\n        result.slice = DxvkBufferSlice(std::move(buffer), 0, size);\n        result.mapPtr = mapPtr;\n        return result;\n      }\n    }\n\n    VkDeviceSize alignedSize = align(size, CACHE_LINE_SIZE);\n\n    if (unlikely(m_upBufferOffset + alignedSize > UPBufferSize)) {\n      auto slice = m_upBuffer->allocateStorage();\n\n      m_upBufferOffset = 0;\n      m_upBufferMapPtr = slice->mapPtr();\n\n      EmitCs([\n        cBuffer = m_upBuffer,\n        cSlice  = std::move(slice)\n      ] (DxvkContext* ctx) mutable {\n        ctx->invalidateBuffer(cBuffer, std::move(cSlice));\n      });\n    }\n\n    D3D9BufferSlice result;\n    result.slice = DxvkBufferSlice(m_upBuffer, m_upBufferOffset, size);\n    result.mapPtr = reinterpret_cast<char*>(m_upBufferMapPtr) + m_upBufferOffset;\n\n    m_upBufferOffset += alignedSize;\n    return result;\n  }\n\n\n  D3D9BufferSlice D3D9DeviceEx::AllocStagingBuffer(VkDeviceSize size) {\n    D3D9BufferSlice result;\n    result.slice = m_stagingBuffer.alloc(size);\n    result.mapPtr = result.slice.mapPtr(0);\n    return result;\n  }\n\n\n  void D3D9DeviceEx::ThrottleAllocation() {\n    // Threshold at which to submit eagerly. This is useful to ensure\n    // that staging buffer memory gets recycled relatively soon.\n    constexpr VkDeviceSize MaxMemoryPerSubmission = (env::is32BitHostPlatform() ? 10u : 30u) << 20u;\n\n    // Treshold for staging memory in flight. Since the staging buffer granularity\n    // is somewhat coars, it is possible for one additional allocation to be in use,\n    // but otherwise this is a hard upper bound.\n    constexpr VkDeviceSize MaxMemoryInFlight = MaxMemoryPerSubmission * 3u;\n\n    DxvkStagingBufferStats stats = GetStagingMemoryStatistics();\n\n    VkDeviceSize stagingBufferAllocated = stats.allocatedTotal;\n\n    if (stagingBufferAllocated > m_stagingMemorySignaled + MaxMemoryPerSubmission) {\n      // Perform submission. If the amount of staging memory allocated since the\n      // last submission exceeds the hard limit, we need to submit to guarantee\n      // forward progress. Ideally, this should not happen very often.\n      GpuFlushType flushType = stagingBufferAllocated <= m_stagingMemorySignaled + MaxMemoryInFlight\n        ? GpuFlushType::ImplicitSynchronization\n        : GpuFlushType::ExplicitFlush;\n\n      ConsiderFlush(flushType);\n    }\n\n    // Wait for staging memory to get recycled.\n    if (stagingBufferAllocated > MaxMemoryInFlight)\n      m_dxvkDevice->waitForFence(*m_stagingBufferFence, stagingBufferAllocated - MaxMemoryInFlight);\n  }\n\n\n  DxvkStagingBufferStats D3D9DeviceEx::GetStagingMemoryStatistics() const {\n    DxvkStagingBufferStats stats = m_stagingBuffer.getStatistics();\n    stats.allocatedTotal += m_discardMemoryCounter;\n    stats.allocatedSinceLastReset += m_discardMemoryCounter - m_discardMemoryOnFlush;\n    return stats;\n  }\n\n\n  D3D9_VK_FORMAT_MAPPING D3D9DeviceEx::LookupFormat(\n    D3D9Format            Format) const {\n    return m_adapter->GetFormatMapping(Format);\n  }\n\n\n  const DxvkFormatInfo* D3D9DeviceEx::UnsupportedFormatInfo(\n    D3D9Format            Format) const {\n    return m_adapter->GetUnsupportedFormatInfo(Format);\n  }\n\n\n  bool D3D9DeviceEx::WaitForResource(\n    const DxvkPagedResource&                Resource,\n          uint64_t                          SequenceNumber,\n          DWORD                             MapFlags) {\n    // Wait for the any pending D3D9 command to be executed\n    // on the CS thread so that we can determine whether the\n    // resource is currently in use or not.\n\n    // Determine access type to wait for based on map mode\n    DxvkAccess access = (MapFlags & D3DLOCK_READONLY)\n      ? DxvkAccess::Write\n      : DxvkAccess::Read;\n\n    if (!Resource.isInUse(access))\n      SynchronizeCsThread(SequenceNumber);\n\n    if (Resource.isInUse(access)) {\n      if (MapFlags & D3DLOCK_DONOTWAIT) {\n        // We don't have to wait, but misbehaving games may\n        // still try to spin on `Map` until the resource is\n        // idle, so we should flush pending commands\n        ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n        return false;\n      }\n      else {\n        // Make sure pending commands using the resource get\n        // executed on the the GPU if we have to wait for it\n        Flush();\n        SynchronizeCsThread(SequenceNumber);\n\n        m_dxvkDevice->waitForResource(Resource, access);\n      }\n    }\n\n    return true;\n  }\n\n\n  uint32_t D3D9DeviceEx::CalcImageLockOffset(\n            uint32_t                SlicePitch,\n            uint32_t                RowPitch,\n      const DxvkFormatInfo*         FormatInfo,\n      const D3DBOX*                 pBox) {\n    if (pBox == nullptr)\n      return 0;\n\n    std::array<uint32_t, 3> offsets = { pBox->Front, pBox->Top, pBox->Left };\n\n    uint32_t elementSize = 1;\n\n    if (FormatInfo != nullptr) {\n      elementSize = FormatInfo->elementSize;\n      VkExtent3D blockSize = FormatInfo->blockSize;\n      if (unlikely(FormatInfo->flags.test(DxvkFormatFlag::MultiPlane))) {\n        elementSize = FormatInfo->planes[0].elementSize;\n        blockSize = { FormatInfo->planes[0].blockSize.width, FormatInfo->planes[0].blockSize.height, 1u };\n      }\n\n      offsets[0] = offsets[0] / blockSize.depth;\n      offsets[1] = offsets[1] / blockSize.height;\n      offsets[2] = offsets[2] / blockSize.width;\n    }\n\n    return offsets[0] * SlicePitch +\n           offsets[1] * RowPitch   +\n           offsets[2] * elementSize;\n  }\n\n\n  HRESULT D3D9DeviceEx::LockImage(\n            D3D9CommonTexture*      pResource,\n            UINT                    Face,\n            UINT                    MipLevel,\n            D3DLOCKED_BOX*          pLockedBox,\n      const D3DBOX*                 pBox,\n            DWORD                   Flags) {\n    D3D9DeviceLock lock = LockDevice();\n\n    UINT Subresource = pResource->CalcSubresource(Face, MipLevel);\n\n    // Don't allow multiple lockings.\n    if (unlikely(pResource->GetLocked(Subresource)))\n      return D3DERR_INVALIDCALL;\n\n    auto& desc = *(pResource->Desc());\n\n    if (unlikely((Flags & (D3DLOCK_DISCARD | D3DLOCK_READONLY)) == (D3DLOCK_DISCARD | D3DLOCK_READONLY)\n               && desc.Pool == D3DPOOL_DEFAULT))\n      return D3DERR_INVALIDCALL;\n\n    // We only ever wait for textures that were used with GetRenderTargetData or GetFrontBufferData anyway.\n    // Games like Beyond Good and Evil break if this doesn't succeed.\n    Flags &= ~D3DLOCK_DONOTWAIT;\n\n    if (unlikely((Flags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE)) == (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE)))\n      Flags &= ~D3DLOCK_DISCARD;\n\n    // Tests show that D3D9 drivers ignore DISCARD when the device is lost.\n    if (unlikely(m_deviceLostState != D3D9DeviceLostState::Ok))\n      Flags &= ~D3DLOCK_DISCARD;\n\n    if (unlikely(!desc.IsLockable))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(pBox != nullptr)) {\n      D3DRESOURCETYPE type = pResource->GetType();\n      D3D9_FORMAT_BLOCK_SIZE blockSize = GetFormatAlignedBlockSize(desc.Format);\n\n      bool isBlockAlignedFormat = blockSize.Width > 0 && blockSize.Height > 0;\n      bool isNotLeftAligned   = pBox->Left   && (pBox->Left   & (blockSize.Width  - 1));\n      bool isNotTopAligned    = pBox->Top    && (pBox->Top    & (blockSize.Height - 1));\n      bool isNotRightAligned  = pBox->Right  && (pBox->Right  & (blockSize.Width  - 1));\n      bool isNotBottomAligned = pBox->Bottom && (pBox->Bottom & (blockSize.Height - 1));\n\n      // LockImage calls on D3DPOOL_DEFAULT surfaces and volume textures with formats\n      // which need to be block aligned, must be validated for mip level 0.\n      if (MipLevel == 0 && isBlockAlignedFormat\n        && (type == D3DRTYPE_VOLUMETEXTURE ||\n            desc.Pool == D3DPOOL_DEFAULT)\n        && (isNotLeftAligned  || isNotTopAligned ||\n            isNotRightAligned || isNotBottomAligned))\n        return D3DERR_INVALIDCALL;\n    }\n\n    auto& formatMapping = pResource->GetFormatMapping();\n    const DxvkFormatInfo* formatInfo = formatMapping.IsValid()\n      ? lookupFormatInfo(formatMapping.FormatColor) : UnsupportedFormatInfo(pResource->Desc()->Format);\n\n    auto subresource = pResource->GetSubresourceFromIndex(\n        formatInfo->aspectMask, Subresource);\n\n    VkExtent3D levelExtent = pResource->GetExtentMip(MipLevel);\n    VkExtent3D blockCount  = util::computeBlockCount(levelExtent, formatInfo->blockSize);\n\n    bool fullResource = pBox == nullptr;\n    if (unlikely(!fullResource)) {\n      // Check whether the box passed as argument matches or exceeds the entire texture.\n      VkOffset3D lockOffset;\n      VkExtent3D lockExtent;\n\n      ConvertBox(*pBox, lockOffset, lockExtent);\n\n      fullResource = lockOffset == VkOffset3D{ 0, 0, 0 }\n                  && lockExtent.width  >= levelExtent.width\n                  && lockExtent.height >= levelExtent.height\n                  && lockExtent.depth  >= levelExtent.depth;\n    }\n\n    // If we are not locking the entire image\n    // a partial discard is meant to occur.\n    // We can't really implement that, so just ignore discard\n    // if we are not locking the full resource.\n\n    // DISCARD is also ignored for MANAGED and SYSTEMEM.\n    // DISCARD is not ignored for non-DYNAMIC unlike what the docs say.\n\n    if (!fullResource || desc.Pool != D3DPOOL_DEFAULT)\n      Flags &= ~D3DLOCK_DISCARD;\n\n    if (desc.Usage & D3DUSAGE_WRITEONLY)\n      Flags &= ~D3DLOCK_READONLY;\n\n    // If we recently wrote to the texture on the gpu,\n    // then we need to copy -> buffer\n    // We are also always dirty if we are a render target,\n    // a depth stencil, or auto generate mipmaps.\n    bool renderable = desc.Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL);\n    bool needsReadback = pResource->NeedsReadback(Subresource) || renderable;\n\n    // Skip readback if we discard is specified. We can only do this for textures that have an associated Vulkan image.\n    // Any other texture might write to the Vulkan staging buffer directly. (GetBackbufferData for example)\n    needsReadback &= pResource->GetImage() != nullptr || !(Flags & D3DLOCK_DISCARD);\n    pResource->SetNeedsReadback(Subresource, false);\n\n    if (unlikely(pResource->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED || needsReadback)) {\n      // Create mapping buffer if it doesn't exist yet. (POOL_DEFAULT)\n      pResource->CreateBuffer(!needsReadback, pResource->GetTotalSize());\n    }\n\n    // Don't use MapTexture here to keep the mapped list small while the resource is still locked.\n    void* mapPtr = pResource->GetData(Subresource);\n\n    if (unlikely(needsReadback)) {\n      // The texture was written to on the GPU.\n      // This can be either the image (for D3DPOOL_DEFAULT)\n      // or the buffer directly (for D3DPOOL_SYSTEMMEM).\n\n      DxvkBufferSlice mappedBufferSlice = pResource->GetBufferSlice(Subresource);\n      const Rc<DxvkBuffer> mappedBuffer = pResource->GetBuffer();\n\n      if (unlikely(pResource->GetFormatMapping().ConversionFormatInfo.FormatType != D3D9ConversionFormat_None)) {\n        Logger::err(str::format(\"Reading back format\", pResource->Desc()->Format, \" is not supported. It is uploaded using the fomrat converter.\"));\n      }\n\n      if (pResource->GetImage() != nullptr) {\n        Rc<DxvkImage> resourceImage = pResource->GetImage();\n\n        Rc<DxvkImage> mappedImage;\n        if (resourceImage->info().sampleCount != 1) {\n            mappedImage = pResource->GetResolveImage();\n        } else {\n            mappedImage = std::move(resourceImage);\n        }\n\n        // When using any map mode which requires the image contents\n        // to be preserved, and if the GPU has write access to the\n        // image, copy the current image contents into the buffer.\n        auto subresourceLayers = vk::makeSubresourceLayers(subresource);\n\n        // We need to resolve this, some games\n        // lock MSAA render targets even though\n        // that's entirely illegal and they explicitly\n        // tell us that they do NOT want to lock them...\n        //\n        // resourceImage is null because the image reference was moved to mappedImage\n        // for images that need to be resolved.\n        if (resourceImage != nullptr) {\n          EmitCs([\n            cMainImage    = resourceImage,\n            cResolveImage = mappedImage,\n            cSubresource  = subresourceLayers\n          ] (DxvkContext* ctx) {\n            VkFormat format = cMainImage->info().format;\n\n            VkImageResolve region;\n            region.srcSubresource = cSubresource;\n            region.srcOffset      = VkOffset3D { 0, 0, 0 };\n            region.dstSubresource = cSubresource;\n            region.dstOffset      = VkOffset3D { 0, 0, 0 };\n            region.extent         = cMainImage->mipLevelExtent(cSubresource.mipLevel);\n\n            ctx->resolveImage(cResolveImage, cMainImage, region, format,\n              getDefaultResolveMode(format), VK_RESOLVE_MODE_SAMPLE_ZERO_BIT);\n          });\n        }\n\n        // if packedFormat is VK_FORMAT_UNDEFINED\n        // DxvkContext::copyImageToBuffer will automatically take the format from the image\n        VkFormat packedFormat = GetPackedDepthStencilFormat(desc.Format);\n\n        EmitCs([\n          cImageBufferSlice = std::move(mappedBufferSlice),\n          cImage            = std::move(mappedImage),\n          cSubresources     = subresourceLayers,\n          cLevelExtent      = levelExtent,\n          cPackedFormat     = packedFormat\n        ] (DxvkContext* ctx) {\n          ctx->copyImageToBuffer(cImageBufferSlice.buffer(),\n            cImageBufferSlice.offset(), 4, 0, cPackedFormat,\n            cImage, cSubresources, VkOffset3D { 0, 0, 0 },\n            cLevelExtent);\n        });\n        TrackTextureMappingBufferSequenceNumber(pResource, Subresource);\n      }\n\n      // Wait until the buffer is idle which may include the copy (and resolve) we just issued.\n      if (!WaitForResource(*mappedBuffer, pResource->GetMappingBufferSequenceNumber(Subresource), Flags))\n        return D3DERR_WASSTILLDRAWING;\n    }\n\n    const bool atiHack = desc.Format == D3D9Format::ATI1 || desc.Format == D3D9Format::ATI2;\n    // Set up map pointer.\n    if (atiHack) {\n      // The API didn't treat this as a block compressed format here.\n      // So we need to lie here. The game is expected to use this info and do a workaround.\n      // It's stupid. I know.\n      pLockedBox->RowPitch   = align(std::max(desc.Width >> MipLevel, 1u), 4);\n      pLockedBox->SlicePitch = pLockedBox->RowPitch * std::max(desc.Height >> MipLevel, 1u);\n    }\n    else if (likely(!formatInfo->flags.test(DxvkFormatFlag::MultiPlane))) {\n      pLockedBox->RowPitch   = align(formatInfo->elementSize * blockCount.width, 4);\n      pLockedBox->SlicePitch = pLockedBox->RowPitch * blockCount.height;\n    } else {\n      auto plane = &formatInfo->planes[0];\n      uint32_t planeElementSize = plane->elementSize;\n      VkExtent3D planeBlockSize = { plane->blockSize.width, plane->blockSize.height, 1u };\n      VkExtent3D blockCount  = util::computeBlockCount(levelExtent, planeBlockSize);\n      pLockedBox->RowPitch   = align(planeElementSize * blockCount.width, 4);\n      pLockedBox->SlicePitch = pLockedBox->RowPitch * blockCount.height;\n    }\n\n    pResource->SetLocked(Subresource, true);\n\n    // Make sure the amount of mapped texture memory stays below the threshold.\n    UnmapTextures();\n\n    const bool readOnly = Flags & D3DLOCK_READONLY;\n    const bool noDirtyUpdate = Flags & D3DLOCK_NO_DIRTY_UPDATE;\n    if ((desc.Pool == D3DPOOL_DEFAULT || !noDirtyUpdate) && !readOnly) {\n      if (pBox && MipLevel != 0) {\n        D3DBOX scaledBox = *pBox;\n        scaledBox.Left   <<= MipLevel;\n        scaledBox.Right    = std::min(scaledBox.Right << MipLevel, pResource->Desc()->Width);\n        scaledBox.Top    <<= MipLevel;\n        scaledBox.Bottom   = std::min(scaledBox.Bottom << MipLevel, pResource->Desc()->Height);\n        scaledBox.Back   <<= MipLevel;\n        scaledBox.Front    = std::min(scaledBox.Front << MipLevel, pResource->Desc()->Depth);\n        pResource->AddDirtyBox(&scaledBox, Face);\n      } else {\n        pResource->AddDirtyBox(pBox, Face);\n      }\n    }\n\n    if (IsPoolManaged(desc.Pool) && !readOnly) {\n      // Managed textures are uploaded at draw time.\n      pResource->SetNeedsUpload(Subresource, true);\n\n      for (uint32_t i : bit::BitMask(m_textureSlotTracking.bound)) {\n        // Guaranteed to not be nullptr...\n        auto texInfo = GetCommonTexture(m_state.textures[i]);\n\n        if (texInfo == pResource) {\n          m_textureSlotTracking.needsUpload |= 1 << i;\n        }\n      }\n    }\n\n    const uint32_t offset = CalcImageLockOffset(\n      pLockedBox->SlicePitch,\n      pLockedBox->RowPitch,\n      (!atiHack) ? formatInfo : nullptr,\n      pBox);\n\n    uint8_t* data = reinterpret_cast<uint8_t*>(mapPtr);\n    data += offset;\n    pLockedBox->pBits = data;\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9DeviceEx::UnlockImage(\n        D3D9CommonTexture*      pResource,\n        UINT                    Face,\n        UINT                    MipLevel) {\n    D3D9DeviceLock lock = LockDevice();\n\n    UINT Subresource = pResource->CalcSubresource(Face, MipLevel);\n\n    // Don't allow multiple unlockings, except for D3DRTYPE_TEXTURE\n    if (unlikely(!pResource->GetLocked(Subresource))) {\n      if (pResource->GetType() == D3DRTYPE_TEXTURE)\n        return D3D_OK;\n      else\n        return D3DERR_INVALIDCALL;\n    }\n\n    MapTexture(pResource, Subresource); // Add it to the list of mapped resources\n    pResource->SetLocked(Subresource, false);\n\n    // Flush image contents from staging if we aren't read only\n    // and we aren't deferring for managed.\n    const D3DBOX& box = pResource->GetDirtyBox(Face);\n    bool shouldFlush  = pResource->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED;\n         shouldFlush &= box.Left < box.Right && box.Top < box.Bottom && box.Front < box.Back;\n         shouldFlush &= !pResource->IsManaged();\n\n    if (shouldFlush) {\n        this->FlushImage(pResource, Subresource);\n        if (!pResource->IsAnySubresourceLocked())\n          pResource->ClearDirtyBoxes();\n    }\n\n    // Toss our staging buffer if we're not dynamic\n    // and we aren't managed (for sysmem copy.)\n    bool shouldToss  = pResource->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED;\n         shouldToss &= !pResource->IsDynamic();\n         shouldToss &= !pResource->IsManaged();\n         shouldToss &= !pResource->IsAnySubresourceLocked();\n\n    // The texture converter cannot handle converting back. So just keep textures in memory as a workaround.\n    shouldToss &= pResource->GetFormatMapping().ConversionFormatInfo.FormatType == D3D9ConversionFormat_None;\n\n    if (shouldToss)\n      pResource->DestroyBuffer();\n\n    UnmapTextures();\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9DeviceEx::FlushImage(\n        D3D9CommonTexture*      pResource,\n        UINT                    Subresource) {\n\n    const Rc<DxvkImage> image = pResource->GetImage();\n    auto formatInfo  = lookupFormatInfo(image->info().format);\n    auto subresource = pResource->GetSubresourceFromIndex(\n      formatInfo->aspectMask, Subresource);\n\n    const D3DBOX& box = pResource->GetDirtyBox(subresource.arrayLayer);\n\n    // The dirty box is only tracked for mip 0. Scale it for the mip level we're gonna upload.\n    VkExtent3D mip0Extent = { box.Right - box.Left, box.Bottom - box.Top, box.Back - box.Front };\n    VkExtent3D extent = util::computeMipLevelExtent(mip0Extent, subresource.mipLevel);\n    VkOffset3D mip0Offset = { int32_t(box.Left), int32_t(box.Top), int32_t(box.Front) };\n    VkOffset3D offset = util::computeMipLevelOffset(mip0Offset, subresource.mipLevel);\n\n    UpdateTextureFromBuffer(pResource, pResource, Subresource, Subresource, offset, extent, offset);\n\n    if (pResource->IsAutomaticMip())\n      MarkTextureMipsDirty(pResource);\n\n    return D3D_OK;\n  }\n\n  void D3D9DeviceEx::UpdateTextureFromBuffer(\n    D3D9CommonTexture* pDestTexture,\n    D3D9CommonTexture* pSrcTexture,\n    UINT DestSubresource,\n    UINT SrcSubresource,\n    VkOffset3D SrcOffset,\n    VkExtent3D SrcExtent,\n    VkOffset3D DestOffset) {\n    // Wait until the amount of used staging memory is under a certain threshold to avoid using\n    // too much memory and even more so to avoid using too much address space.\n    ThrottleAllocation();\n\n    const Rc<DxvkImage> image = pDestTexture->GetImage();\n\n    // Now that data has been written into the buffer,\n    // we need to copy its contents into the image\n\n    auto formatInfo = lookupFormatInfo(pDestTexture->GetFormatMapping().FormatColor);\n    auto srcSubresource = pSrcTexture->GetSubresourceFromIndex(\n      formatInfo->aspectMask, SrcSubresource);\n\n    auto dstSubresource = pDestTexture->GetSubresourceFromIndex(\n      formatInfo->aspectMask, DestSubresource);\n    VkImageSubresourceLayers dstLayers = { dstSubresource.aspectMask, dstSubresource.mipLevel, dstSubresource.arrayLayer, 1 };\n\n    VkExtent3D dstTexLevelExtent = image->mipLevelExtent(dstSubresource.mipLevel);\n    VkExtent3D srcTexLevelExtent = util::computeMipLevelExtent(pSrcTexture->GetExtent(), srcSubresource.mipLevel);\n\n    auto convertFormat = pDestTexture->GetFormatMapping().ConversionFormatInfo;\n\n    if (unlikely(pSrcTexture->NeedsReadback(SrcSubresource))) {\n      // The src texutre has to be in POOL_SYSTEMEM, so it cannot use AUTOMIPGEN.\n      // That means that NeedsReadback is only true if the texture has been used with GetRTData or GetFrontbufferData before.\n      // Those functions create a buffer, so the buffer always exists here.\n      const Rc<DxvkBuffer>& buffer = pSrcTexture->GetBuffer();\n      WaitForResource(*buffer, pSrcTexture->GetMappingBufferSequenceNumber(SrcSubresource), 0);\n      pSrcTexture->SetNeedsReadback(SrcSubresource, false);\n    }\n\n    if (likely(convertFormat.FormatType == D3D9ConversionFormat_None)) {\n      // The texture does not use a format that needs to be converted in a compute shader.\n      // So we just need to make sure the passed size and offset are not out of range and properly aligned,\n      // copy the data to a staging buffer and then copy that on the GPU to the actual image.\n      VkOffset3D alignedDestOffset = {\n        int32_t(alignDown(DestOffset.x, formatInfo->blockSize.width)),\n        int32_t(alignDown(DestOffset.y, formatInfo->blockSize.height)),\n        int32_t(alignDown(DestOffset.z, formatInfo->blockSize.depth))\n      };\n      VkOffset3D alignedSrcOffset = {\n        int32_t(alignDown(SrcOffset.x, formatInfo->blockSize.width)),\n        int32_t(alignDown(SrcOffset.y, formatInfo->blockSize.height)),\n        int32_t(alignDown(SrcOffset.z, formatInfo->blockSize.depth))\n      };\n      SrcExtent.width += SrcOffset.x - alignedSrcOffset.x;\n      SrcExtent.height += SrcOffset.y - alignedSrcOffset.y;\n      SrcExtent.depth += SrcOffset.z - alignedSrcOffset.z;\n      VkExtent3D extentBlockCount = util::computeBlockCount(SrcExtent, formatInfo->blockSize);\n      VkExtent3D alignedExtent = util::computeBlockExtent(extentBlockCount, formatInfo->blockSize);\n\n      alignedExtent = util::snapExtent3D(alignedDestOffset, alignedExtent, dstTexLevelExtent);\n      alignedExtent = util::snapExtent3D(alignedSrcOffset, alignedExtent, srcTexLevelExtent);\n\n      VkOffset3D srcOffsetBlockCount = util::computeBlockOffset(alignedSrcOffset, formatInfo->blockSize);\n      VkExtent3D srcTexLevelExtentBlockCount = util::computeBlockCount(srcTexLevelExtent, formatInfo->blockSize);\n      VkDeviceSize pitch = align(srcTexLevelExtentBlockCount.width * formatInfo->elementSize, 4);\n      VkDeviceSize copySrcOffset = srcOffsetBlockCount.z * srcTexLevelExtentBlockCount.height * pitch\n          + srcOffsetBlockCount.y * pitch\n          + srcOffsetBlockCount.x * formatInfo->elementSize;\n\n      // Get the mapping pointer from MapTexture to map the texture and keep track of that\n      // in case it is unmappable.\n      const void* mapPtr = MapTexture(pSrcTexture, SrcSubresource);\n      VkDeviceSize dirtySize = extentBlockCount.width * extentBlockCount.height * extentBlockCount.depth * formatInfo->elementSize;\n      D3D9BufferSlice slice = AllocStagingBuffer(dirtySize);\n      const void* srcData = reinterpret_cast<const uint8_t*>(mapPtr) + copySrcOffset;\n      util::packImageData(\n        slice.mapPtr, srcData, extentBlockCount, formatInfo->elementSize,\n        pitch, pitch * srcTexLevelExtentBlockCount.height);\n\n      VkFormat packedDSFormat = GetPackedDepthStencilFormat(pDestTexture->Desc()->Format);\n\n      EmitCs([\n        cSrcSlice       = slice.slice,\n        cDstImage       = image,\n        cDstLayers      = dstLayers,\n        cDstLevelExtent = alignedExtent,\n        cOffset         = alignedDestOffset,\n        cPackedDSFormat = packedDSFormat\n      ] (DxvkContext* ctx) {\n        ctx->copyBufferToImage(\n          cDstImage,  cDstLayers,\n          cOffset, cDstLevelExtent,\n          cSrcSlice.buffer(), cSrcSlice.offset(),\n          0, 0, cPackedDSFormat);\n      });\n\n      TrackTextureMappingBufferSequenceNumber(pSrcTexture, SrcSubresource);\n    }\n    else {\n      // The texture uses a format which gets converted by a compute shader.\n      const void* mapPtr = MapTexture(pSrcTexture, SrcSubresource);\n\n      // The compute shader does not support only converting a subrect of the texture\n      if (unlikely(SrcOffset.x != 0 || SrcOffset.y != 0 || SrcOffset.z != 0\n        || DestOffset.x != 0 || DestOffset.y != 0 || DestOffset.z != 0\n        || SrcExtent != srcTexLevelExtent)) {\n        Logger::warn(\"Offset and rect not supported with the texture converter.\");\n      }\n\n      if (unlikely(srcTexLevelExtent != dstTexLevelExtent)) {\n        Logger::err(\"Different extents are not supported with the texture converter.\");\n        return;\n      }\n\n      uint32_t formatElementSize = formatInfo->elementSize;\n      VkExtent3D srcBlockSize = formatInfo->blockSize;\n      if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n        formatElementSize = formatInfo->planes[0].elementSize;\n        srcBlockSize = { formatInfo->planes[0].blockSize.width, formatInfo->planes[0].blockSize.height, 1u };\n      }\n      VkExtent3D srcBlockCount = util::computeBlockCount(srcTexLevelExtent, srcBlockSize);\n      srcBlockCount.height *= std::min(pSrcTexture->GetPlaneCount(), 2u);\n\n      // the converter can not handle the 4 aligned pitch so we always repack into a staging buffer\n      D3D9BufferSlice slice = AllocStagingBuffer(pSrcTexture->GetMipSize(SrcSubresource));\n      VkDeviceSize pitch = align(srcBlockCount.width * formatElementSize, 4);\n\n      const DxvkFormatInfo* convertedFormatInfo = lookupFormatInfo(convertFormat.FormatColor);\n      VkImageSubresourceLayers convertedDstLayers = { convertedFormatInfo->aspectMask, dstSubresource.mipLevel, dstSubresource.arrayLayer, 1 };\n\n      util::packImageData(\n        slice.mapPtr, mapPtr, srcBlockCount, formatElementSize,\n        pitch, std::min(pSrcTexture->GetPlaneCount(), 2u) * pitch * srcBlockCount.height);\n\n      EmitCs([this,\n        cConvertFormat    = convertFormat,\n        cDstImage         = std::move(image),\n        cDstLayers        = convertedDstLayers,\n        cSrcSlice         = std::move(slice.slice)\n      ] (DxvkContext* ctx) {\n        auto contextObjects = ctx->beginExternalRendering();\n\n        m_converter->ConvertFormat(contextObjects,\n          cConvertFormat, cDstImage, cDstLayers, cSrcSlice);\n      });\n    }\n    UnmapTextures();\n    ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n  }\n\n  void D3D9DeviceEx::EmitGenerateMips(\n    D3D9CommonTexture* pResource) {\n    if (pResource->IsManaged())\n      UploadManagedTexture(pResource);\n\n    EmitCs([\n      cImageView = pResource->GetSampleView(false),\n      cFilter    = pResource->GetMipFilter()\n    ] (DxvkContext* ctx) {\n      ctx->generateMipmaps(cImageView, DecodeFilter(cFilter));\n    });\n  }\n\n\n  HRESULT D3D9DeviceEx::LockBuffer(\n          D3D9CommonBuffer*       pResource,\n          UINT                    OffsetToLock,\n          UINT                    SizeToLock,\n          void**                  ppbData,\n          DWORD                   Flags) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (unlikely(ppbData == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(!m_d3d9Options.allowDiscard))\n      Flags &= ~D3DLOCK_DISCARD;\n\n    auto& desc = *pResource->Desc();\n\n    // Ignore DISCARD if NOOVERWRITE or READONLY is set\n    if (unlikely((Flags & (D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE | D3DLOCK_READONLY)) != D3DLOCK_DISCARD))\n      Flags &= ~D3DLOCK_DISCARD;\n\n    // Ignore DISCARD and NOOVERWRITE if the buffer is not DEFAULT pool (tests + Halo 2)\n    // The docs say DISCARD and NOOVERWRITE are ignored if the buffer is not DYNAMIC\n    // but tests say otherwise!\n    if (desc.Pool != D3DPOOL_DEFAULT || CanOnlySWVP())\n      Flags &= ~(D3DLOCK_DISCARD | D3DLOCK_NOOVERWRITE);\n\n    // Ignore DONOTWAIT if we are DYNAMIC\n    // Yes... D3D9 is a good API.\n    if (desc.Usage & D3DUSAGE_DYNAMIC)\n      Flags &= ~D3DLOCK_DONOTWAIT;\n\n    // Tests show that D3D9 drivers ignore DISCARD when the device is lost.\n    if (unlikely(m_deviceLostState != D3D9DeviceLostState::Ok))\n      Flags &= ~D3DLOCK_DISCARD;\n\n    // In SWVP mode, we always use the per-draw upload path.\n    // So the buffer will never be in use on the device.\n    // FVF Buffers are the exception. Those can be used as a destination for ProcessVertices.\n    if (unlikely(CanOnlySWVP() && !pResource->NeedsReadback()))\n      Flags |= D3DLOCK_NOOVERWRITE;\n\n    // READONLY is ignored for non-managed pools\n    if ((Flags & D3DLOCK_READONLY) && !IsPoolManaged(desc.Pool))\n      Flags &= ~D3DLOCK_READONLY;\n\n    // We only bounds check for MANAGED.\n    // (TODO: Apparently this is meant to happen for DYNAMIC too but I am not sure\n    //  how that works given it is meant to be a DIRECT access..?)\n    const bool respectUserBounds = !(Flags & D3DLOCK_DISCARD) &&\n                                    SizeToLock != 0 &&\n                                    (desc.Pool == D3DPOOL_MANAGED || (desc.Usage & D3DUSAGE_DYNAMIC));\n\n    // If we don't respect the bounds, encompass it all in our tests/checks\n    // These values may be out of range and don't get clamped.\n    uint32_t offset = respectUserBounds ? OffsetToLock : 0;\n    uint32_t size   = respectUserBounds ? std::min(SizeToLock, desc.Size - offset) : desc.Size;\n    D3D9Range lockRange = D3D9Range(offset, offset + size);\n\n    bool updateDirtyRange = (desc.Pool == D3DPOOL_DEFAULT || !(Flags & D3DLOCK_NO_DIRTY_UPDATE)) && !(Flags & D3DLOCK_READONLY);\n    if (updateDirtyRange) {\n      pResource->DirtyRange().Conjoin(lockRange);\n\n      for (uint32_t i : bit::BitMask(static_cast<uint32_t>(m_vbSlotTracking.bound))) {\n        auto commonBuffer = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);\n        if (commonBuffer == pResource) {\n          m_vbSlotTracking.needsUpload |= 1 << i;\n        }\n      }\n    }\n\n    const bool directMapping = pResource->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_DIRECT;\n    const bool needsReadback = pResource->NeedsReadback();\n\n    uint8_t* data = nullptr;\n\n    if ((Flags & D3DLOCK_DISCARD) && (directMapping || needsReadback)) {\n      // If we're not directly mapped and don't need readback,\n      // the buffer is not currently getting used anyway\n      // so there's no reason to waste memory by discarding.\n\n      m_discardMemoryCounter += desc.Size;\n      ThrottleAllocation();\n\n      // Allocate a new backing slice for the buffer and set\n      // it as the 'new' mapped slice. This assumes that the\n      // only way to invalidate a buffer is by mapping it.\n      Rc<DxvkBuffer> mappingBuffer = pResource->GetBuffer<D3D9_COMMON_BUFFER_TYPE_MAPPING>();\n      auto bufferSlice = pResource->DiscardMapSlice();\n      data = reinterpret_cast<uint8_t*>(bufferSlice->mapPtr());\n\n      EmitCs([\n        cBuffer      = std::move(mappingBuffer),\n        cBufferSlice = std::move(bufferSlice)\n      ] (DxvkContext* ctx) mutable {\n        ctx->invalidateBuffer(cBuffer, std::move(cBufferSlice));\n      });\n\n      pResource->SetNeedsReadback(false);\n    }\n    else {\n      // The application either didn't specify DISCARD or the buffer is guaranteed to be idle anyway.\n\n      // Use map pointer from previous map operation. This\n      // way we don't have to synchronize with the CS thread\n      // if the map mode is D3DLOCK_NOOVERWRITE.\n      data = reinterpret_cast<uint8_t*>(pResource->GetMappedSlice()->mapPtr());\n\n      const bool readOnly = Flags & D3DLOCK_READONLY;\n      // NOOVERWRITE promises that they will not write in a currently used area.\n      const bool noOverwrite = Flags & D3DLOCK_NOOVERWRITE;\n      const bool directMapping = pResource->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_DIRECT;\n\n      // If we're not directly mapped, we can rely on needsReadback to tell us if a sync is required.\n      const bool skipWait = (!needsReadback && (readOnly || !directMapping)) || noOverwrite;\n\n      if (!skipWait) {\n        const Rc<DxvkBuffer> mappingBuffer = pResource->GetBuffer<D3D9_COMMON_BUFFER_TYPE_MAPPING>();\n        if (!WaitForResource(*mappingBuffer, pResource->GetMappingBufferSequenceNumber(), Flags))\n          return D3DERR_WASSTILLDRAWING;\n\n        pResource->SetNeedsReadback(false);\n      }\n    }\n\n    // The offset/size is not clamped to or affected by the desc size.\n    data += OffsetToLock;\n    *ppbData = reinterpret_cast<void*>(data);\n\n    DWORD oldFlags = pResource->GetMapFlags();\n\n    // We need to remove the READONLY flags from the map flags\n    // if there was ever a non-readonly upload.\n    if (!(Flags & D3DLOCK_READONLY))\n      oldFlags &= ~D3DLOCK_READONLY;\n\n    pResource->SetMapFlags(Flags | oldFlags);\n    pResource->IncrementLockCount();\n\n    // We just mapped a buffer which may have come with an address space cost.\n    // Unmap textures if the amount of mapped texture memory is exceeding the threshold.\n    UnmapTextures();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9DeviceEx::FlushBuffer(\n        D3D9CommonBuffer*       pResource) {\n    // Wait until the amount of used staging memory is under a certain threshold to avoid using\n    // too much memory and even more so to avoid using too much address space.\n    ThrottleAllocation();\n\n    auto dstBuffer = pResource->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>();\n    auto srcSlice = pResource->GetMappedSlice();\n\n    D3D9Range& range = pResource->DirtyRange();\n\n    D3D9BufferSlice slice = AllocStagingBuffer(range.max - range.min);\n    void* srcData = reinterpret_cast<uint8_t*>(srcSlice->mapPtr()) + range.min;\n    memcpy(slice.mapPtr, srcData, range.max - range.min);\n\n    EmitCs([\n      cDstSlice  = dstBuffer,\n      cSrcSlice  = slice.slice,\n      cDstOffset = range.min,\n      cLength    = range.max - range.min\n    ] (DxvkContext* ctx) {\n      ctx->copyBuffer(\n        cDstSlice.buffer(),\n        cDstSlice.offset() + cDstOffset,\n        cSrcSlice.buffer(),\n        cSrcSlice.offset(),\n        cLength);\n    });\n\n    pResource->DirtyRange().Clear();\n    TrackBufferMappingBufferSequenceNumber(pResource);\n\n    UnmapTextures();\n    ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9DeviceEx::UnlockBuffer(\n        D3D9CommonBuffer*       pResource) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (pResource->DecrementLockCount() != 0)\n      return D3D_OK;\n\n    // Nothing else to do for directly mapped buffers. Those were already written.\n    if (pResource->GetMapMode() != D3D9_COMMON_BUFFER_MAP_MODE_BUFFER)\n      return D3D_OK;\n\n    // There is no part of the buffer that hasn't been uploaded yet.\n    // This shouldn't happen.\n    if (pResource->DirtyRange().IsDegenerate())\n      return D3D_OK;\n\n    pResource->SetMapFlags(0);\n\n    // Only D3DPOOL_DEFAULT buffers get uploaded in UnlockBuffer.\n    // D3DPOOL_SYSTEMMEM and D3DPOOL_MANAGED get uploaded at draw time.\n    if (pResource->Desc()->Pool != D3DPOOL_DEFAULT)\n      return D3D_OK;\n\n    FlushBuffer(pResource);\n\n    return D3D_OK;\n  }\n\n\n\n  void D3D9DeviceEx::UploadPerDrawData(\n          UINT&                   FirstVertexIndex,\n          UINT                    NumVertices,\n          UINT&                   FirstIndex,\n          UINT                    NumIndices,\n          INT&                    BaseVertexIndex,\n          bool*                   pDynamicVBOs,\n          bool*                   pDynamicIBO\n  ) {\n    const uint32_t usedBuffersMask = (m_state.vertexDecl != nullptr ? m_state.vertexDecl->GetStreamMask() : ~0u) & static_cast<uint32_t>(m_vbSlotTracking.bound);\n    bool dynamicSysmemVBOs = usedBuffersMask == m_vbSlotTracking.uploadPerDraw;\n\n    D3D9CommonBuffer* ibo = GetCommonBuffer(m_state.indices);\n    bool dynamicSysmemIBO = NumIndices != 0 && ibo != nullptr && (ibo->DoPerDrawUpload() || CanOnlySWVP());\n\n    *pDynamicVBOs = dynamicSysmemVBOs;\n\n    if (unlikely(pDynamicIBO))\n      *pDynamicIBO = dynamicSysmemIBO;\n\n    if (likely(!dynamicSysmemVBOs && !dynamicSysmemIBO))\n      return;\n\n    uint32_t vertexBuffersToUpload;\n    if (likely(dynamicSysmemVBOs))\n      vertexBuffersToUpload = m_vbSlotTracking.uploadPerDraw & usedBuffersMask;\n    else\n      vertexBuffersToUpload = 0;\n\n    // The UP buffer allocator will invalidate,\n    // so we can only use 1 UP buffer slice per draw.\n    // First we calculate the size of that UP buffer slice\n    // and store all sizes and offsets into it.\n\n    struct VBOCopy {\n      uint32_t srcOffset;\n      uint32_t dstOffset;\n      uint32_t copyBufferLength;\n      uint32_t copyElementCount;\n      uint32_t copyElementSize;\n      uint32_t copyElementStride;\n    };\n    uint32_t totalUpBufferSize = 0;\n    std::array<VBOCopy, caps::MaxStreams> vboCopies = {};\n\n    for (uint32_t i : bit::BitMask(vertexBuffersToUpload)) {\n      auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);\n      if (likely(vbo == nullptr)) {\n        continue;\n      }\n\n      if (unlikely(vbo->NeedsReadback())) {\n        // There's only one way the GPU might write new data to a vertex buffer:\n        // - Write to the primary buffer using ProcessVertices which gets copied over to the staging buffer at the end.\n        //   So it could end up writing to the buffer on the GPU while the same buffer gets read here on the CPU.\n        //   That is why we need to ensure the staging buffer is idle here.\n        WaitForResource(*vbo->GetBuffer<D3D9_COMMON_BUFFER_TYPE_STAGING>(), vbo->GetMappingBufferSequenceNumber(), D3DLOCK_READONLY);\n      }\n\n      const uint32_t vertexSize = m_state.vertexDecl->GetSize(i);\n      const uint32_t vertexStride = m_state.vertexBuffers[i].stride;\n      const uint32_t srcStride = vertexStride;\n      const uint32_t dstStride = std::min(vertexStride, vertexSize);\n\n      uint32_t elementCount = NumVertices;\n      if (m_state.streamFreq[i] & D3DSTREAMSOURCE_INSTANCEDATA) {\n        elementCount = GetInstanceCount();\n      }\n      const uint32_t vboOffset = m_state.vertexBuffers[i].offset;\n      const uint32_t vertexOffset = (FirstVertexIndex + BaseVertexIndex) * srcStride;\n      const uint32_t vertexBufferSize = vbo->Desc()->Size;\n      const uint32_t srcOffset = vboOffset + vertexOffset;\n\n      if (unlikely(srcOffset > vertexBufferSize)) {\n        // All vertices are out of bounds\n        vboCopies[i].copyBufferLength = 0;\n      } else if (unlikely(srcOffset + elementCount * srcStride > vertexBufferSize)) {\n        // Some vertices are (partially) out of bounds\n        uint32_t boundVertexBufferRange = vertexBufferSize - vboOffset;\n        elementCount = boundVertexBufferRange / srcStride;\n        // Copy all complete vertices\n        vboCopies[i].copyBufferLength = elementCount * dstStride;\n        // Copy the remaining partial vertex\n        vboCopies[i].copyBufferLength += std::min(dstStride, boundVertexBufferRange % srcStride);\n      } else {\n        // No vertices are out of bounds\n        vboCopies[i].copyBufferLength = elementCount * dstStride;\n      }\n\n      vboCopies[i].copyElementCount = elementCount;\n      vboCopies[i].copyElementStride = srcStride;\n      vboCopies[i].copyElementSize = dstStride;\n      vboCopies[i].srcOffset = srcOffset;\n      vboCopies[i].dstOffset = totalUpBufferSize;\n      totalUpBufferSize += vboCopies[i].copyBufferLength;\n    }\n\n    uint32_t iboUPBufferSize = 0;\n    uint32_t iboUPBufferOffset = 0;\n    if (dynamicSysmemIBO) {\n      auto* ibo = GetCommonBuffer(m_state.indices);\n      if (likely(ibo != nullptr)) {\n        uint32_t indexStride = ibo->Desc()->Format == D3D9Format::INDEX16 ? 2 : 4;\n        uint32_t offset = indexStride * FirstIndex;\n        uint32_t indexBufferSize = ibo->Desc()->Size;\n        if (offset < indexBufferSize) {\n          iboUPBufferSize = std::min(NumIndices * indexStride, indexBufferSize - offset);\n          iboUPBufferOffset = totalUpBufferSize;\n          totalUpBufferSize += iboUPBufferSize;\n        }\n      }\n    }\n\n    if (unlikely(totalUpBufferSize == 0)) {\n      *pDynamicVBOs = false;\n      if (pDynamicIBO)\n        *pDynamicIBO = false;\n\n      return;\n    }\n\n    auto upSlice = AllocUPBuffer(totalUpBufferSize);\n\n    // Now copy the actual data and bind it.\n    if (dynamicSysmemVBOs) {\n      for (uint32_t i : bit::BitMask(vertexBuffersToUpload)) {\n        const VBOCopy& copy = vboCopies[i];\n\n        if (likely(copy.copyBufferLength != 0)) {\n          const auto* vbo = GetCommonBuffer(m_state.vertexBuffers[i].vertexBuffer);\n          uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr) + copy.dstOffset;\n          const uint8_t* src = reinterpret_cast<uint8_t*>(vbo->GetMappedSlice()->mapPtr()) + copy.srcOffset;\n\n          if (likely(copy.copyElementStride == copy.copyElementSize)) {\n            std::memcpy(data, src, copy.copyBufferLength);\n          } else {\n            for (uint32_t j = 0; j < copy.copyElementCount; j++) {\n              std::memcpy(data + j * copy.copyElementSize, src + j * copy.copyElementStride, copy.copyElementSize);\n            }\n            if (unlikely(copy.copyBufferLength > copy.copyElementCount * copy.copyElementSize)) {\n              // Partial vertex at the end\n              std::memcpy(\n                data + copy.copyElementCount * copy.copyElementSize,\n                src + copy.copyElementCount * copy.copyElementStride,\n                copy.copyBufferLength - copy.copyElementCount * copy.copyElementSize);\n            }\n          }\n        }\n\n        auto vboSlice = upSlice.slice.subSlice(copy.dstOffset, copy.copyBufferLength);\n        EmitCs([\n          cStream      = i,\n          cBufferSlice = std::move(vboSlice),\n          cStride      = copy.copyElementSize\n        ](DxvkContext* ctx) mutable {\n          ctx->bindVertexBuffer(cStream, std::move(cBufferSlice), cStride);\n        });\n        m_dirty.set(D3D9DeviceDirtyFlag::VertexBuffers);\n      }\n\n      // Change the draw call parameters to reflect the changed vertex buffers\n      if (NumIndices != 0) {\n        BaseVertexIndex = -FirstVertexIndex;\n      } else {\n        FirstVertexIndex = 0;\n      }\n    }\n\n    if (dynamicSysmemIBO) {\n      if (unlikely(iboUPBufferSize == 0)) {\n        EmitCs([](DxvkContext* ctx) {\n          ctx->bindIndexBuffer(DxvkBufferSlice(), VK_INDEX_TYPE_UINT32);\n        });\n        m_dirty.set(D3D9DeviceDirtyFlag::IndexBuffer);\n      } else {\n        auto* ibo = GetCommonBuffer(m_state.indices);\n        uint32_t indexStride = ibo->Desc()->Format == D3D9Format::INDEX16 ? 2 : 4;\n        VkIndexType indexType = DecodeIndexType(ibo->Desc()->Format);\n        uint32_t offset = indexStride * FirstIndex;\n        uint8_t* data = reinterpret_cast<uint8_t*>(upSlice.mapPtr) + iboUPBufferOffset;\n        uint8_t* src = reinterpret_cast<uint8_t*>(ibo->GetMappedSlice()->mapPtr()) + offset;\n        std::memcpy(data, src, iboUPBufferSize);\n\n        auto iboSlice = upSlice.slice.subSlice(iboUPBufferOffset, iboUPBufferSize);\n        EmitCs([\n          cBufferSlice = std::move(iboSlice),\n          cIndexType = indexType\n        ](DxvkContext* ctx) mutable {\n          ctx->bindIndexBuffer(std::move(cBufferSlice), cIndexType);\n        });\n        m_dirty.set(D3D9DeviceDirtyFlag::IndexBuffer);\n      }\n\n      // Change the draw call parameters to reflect the changed index buffer\n      FirstIndex = 0;\n    }\n  }\n\n\n  void D3D9DeviceEx::InjectCsChunk(\n          DxvkCsChunkRef&&            Chunk,\n          bool                        Synchronize) {\n    m_csThread.injectChunk(DxvkCsQueue::HighPriority, std::move(Chunk), Synchronize);\n  }\n\n\n  void D3D9DeviceEx::EmitCsChunk(DxvkCsChunkRef&& chunk) {\n    // Flush init commands so that the CS thread\n    // can processe them before the first use.\n    m_initializer->FlushCsChunk();\n\n    m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk));\n  }\n\n\n  void D3D9DeviceEx::ConsiderFlush(GpuFlushType FlushType) {\n    uint64_t chunkId = GetCurrentSequenceNumber();\n    uint64_t submissionId = m_submissionFence->value();\n\n    if (m_flushTracker.considerFlush(FlushType, chunkId, submissionId, 0u))\n      Flush();\n  }\n\n\n  void D3D9DeviceEx::SynchronizeCsThread(uint64_t SequenceNumber) {\n    D3D9DeviceLock lock = LockDevice();\n\n    // Dispatch current chunk so that all commands\n    // recorded prior to this function will be run\n    if (SequenceNumber > m_csSeqNum)\n      FlushCsChunk();\n\n    m_csThread.synchronize(SequenceNumber);\n  }\n\n\n  void D3D9DeviceEx::SetupFPU() {\n    // Should match d3d9 float behaviour.\n\n#if defined(_MSC_VER)\n    // For MSVC we can use these cross arch and platform funcs to set the FPU.\n    // This will work on any platform, x86, x64, ARM, etc.\n\n    // Clear exceptions.\n    _clearfp();\n\n    // Disable exceptions\n    _controlfp(_MCW_EM, _MCW_EM);\n\n#ifndef _WIN64\n    // Use 24 bit precision\n    _controlfp(_PC_24, _MCW_PC);\n#endif\n\n    // Round to nearest\n    _controlfp(_RC_NEAR, _MCW_RC);\n#elif (defined(__GNUC__) || defined(__MINGW32__)) && (defined(__i386__) || (defined(__x86_64__) && !defined(__arm64ec__)) || defined(__ia64))\n    // For GCC/MinGW we can use inline asm to set it.\n    // This only works for x86 and x64 processors however.\n\n    uint16_t control;\n\n    // Get current control word.\n    __asm__ __volatile__(\"fnstcw %0\" : \"=m\" (*&control));\n\n    // Clear existing settings.\n    control &= 0xF0C0;\n\n    // Disable exceptions\n    // Use 24 bit precision\n    // Round to nearest\n    control |= 0x003F;\n\n    // Set new control word.\n    __asm__ __volatile__(\"fldcw %0\" : : \"m\" (*&control));\n#else\n    Logger::warn(\"D3D9DeviceEx::SetupFPU: not supported on this arch.\");\n#endif\n  }\n\n\n  int64_t D3D9DeviceEx::DetermineInitialTextureMemory() {\n    auto memoryProp = m_adapter->GetDXVKAdapter()->memoryProperties();\n\n    VkDeviceSize availableTextureMemory = 0;\n\n    for (uint32_t i = 0; i < memoryProp.memoryHeapCount; i++)\n      availableTextureMemory += memoryProp.memoryHeaps[i].size;\n\n    constexpr VkDeviceSize Megabytes = 1024 * 1024;\n    // Windows will typically \"reserve\" some amount of video memory,\n    // presumably for back buffers, which gets subtracted from the\n    // reported size, e.g. in case of 4 GB it will report a total of\n    // 4286578687 available bytes. The reserved amount varies depending\n    // on the number of back buffers and the back buffer resolution,\n    // however 8 MB has been generally observed for 1080p.\n    constexpr VkDeviceSize ReservedMemory = 8 * Megabytes;\n\n    // The value returned is a 32-bit value, so we need to clamp it.\n    VkDeviceSize maxMemory = (VkDeviceSize(m_d3d9Options.maxAvailableMemory) * Megabytes) - 1;\n    availableTextureMemory = std::min(availableTextureMemory, maxMemory) - ReservedMemory;\n\n    return int64_t(availableTextureMemory);\n  }\n\n\n  void D3D9DeviceEx::CreateConstantBuffers() {\n    constexpr VkDeviceSize DefaultConstantBufferSize  = 1024ull << 10;\n    constexpr VkDeviceSize SmallConstantBufferSize    =   64ull << 10;\n\n    m_consts[uint32_t(D3D9ShaderType::VertexShader)].buffer = D3D9ConstantBuffer(this,\n      D3D9ShaderType::VertexShader,\n      DxsoConstantBuffers::VSConstantBuffer,\n      DefaultConstantBufferSize);\n\n    m_consts[uint32_t(D3D9ShaderType::VertexShader)].swvp.intBuffer = D3D9ConstantBuffer(this,\n      D3D9ShaderType::VertexShader,\n      DxsoConstantBuffers::VSIntConstantBuffer,\n      SmallConstantBufferSize);\n\n    m_consts[uint32_t(D3D9ShaderType::VertexShader)].swvp.boolBuffer = D3D9ConstantBuffer(this,\n      D3D9ShaderType::VertexShader,\n      DxsoConstantBuffers::VSBoolConstantBuffer,\n      SmallConstantBufferSize);\n\n    m_consts[uint32_t(D3D9ShaderType::PixelShader)].buffer = D3D9ConstantBuffer(this,\n      D3D9ShaderType::PixelShader,\n      DxsoConstantBuffers::PSConstantBuffer,\n      DefaultConstantBufferSize);\n\n    m_vsClipPlanes = D3D9ConstantBuffer(this,\n      D3D9ShaderType::VertexShader,\n      DxsoConstantBuffers::VSClipPlanes,\n      caps::MaxClipPlanes * sizeof(D3D9ClipPlane));\n\n    m_vsFixedFunction = D3D9ConstantBuffer(this,\n      D3D9ShaderType::VertexShader,\n      DxsoConstantBuffers::VSFixedFunction,\n      sizeof(D3D9FixedFunctionVS));\n\n    m_psFixedFunction = D3D9ConstantBuffer(this,\n      D3D9ShaderType::PixelShader,\n      DxsoConstantBuffers::PSFixedFunction,\n      sizeof(D3D9FixedFunctionPS));\n\n    m_psShared = D3D9ConstantBuffer(this,\n      D3D9ShaderType::PixelShader,\n      DxsoConstantBuffers::PSShared,\n      sizeof(D3D9SharedPS));\n\n    m_vsVertexBlend = D3D9ConstantBuffer(this,\n      D3D9ShaderType::VertexShader,\n      DxsoConstantBuffers::VSVertexBlendData,\n      CanSWVP()\n        ? sizeof(D3D9FixedFunctionVertexBlendDataSW)\n        : sizeof(D3D9FixedFunctionVertexBlendDataHW));\n\n    // Allocate constant buffer for values that would otherwise get passed as spec constants for fast-linked pipelines to use.\n    if (m_usingGraphicsPipelines) {\n      m_specBuffer = D3D9ConstantBuffer(this,\n        VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,\n        VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,\n        getSpecConstantBufferSlot(),\n        D3D9SpecializationInfo::UBOSize);\n    }\n  }\n\n\n  inline void D3D9DeviceEx::UploadSoftwareConstantSet(const D3D9ShaderConstantsVSSoftware& Src, const D3D9ConstantLayout& Layout) {\n    /*\n     * SWVP raises the amount of constants by a lot.\n     * To avoid copying huge amounts of data for every draw call,\n     * we track the highest set constant and only use a buffer big enough\n     * to fit that. We rely on robustness to return 0 for OOB reads.\n    */\n\n    D3D9ConstantSets& constSet = m_consts[uint32_t(D3D9ShaderType::VertexShader)];\n\n    if (!constSet.dirty)\n      return;\n\n    constSet.dirty = false;\n\n    uint32_t floatCount = constSet.maxChangedConstF;\n    if (constSet.meta.needsConstantCopies) {\n      // If the shader requires us to preserve shader defined constants,\n      // we copy those over. We need to adjust the amount of used floats accordingly.\n      auto shader = GetCommonShader(m_state.vertexShader);\n      floatCount = std::max(floatCount, static_cast<uint32_t>(shader->GetMaxDefinedFloatConstant() + 1));\n    }\n    // If we statically know which is the last float constant accessed by the shader, we don't need to copy the rest.\n    floatCount = std::min(floatCount, constSet.meta.maxConstIndexF);\n\n    // Calculate data sizes for each constant type.\n    const uint32_t floatDataSize = floatCount * sizeof(Vector4);\n    const uint32_t intDataSize   = std::min(constSet.meta.maxConstIndexI, constSet.maxChangedConstI) * sizeof(Vector4i);\n    const uint32_t boolDataSize  = divCeil(std::min(constSet.meta.maxConstIndexB, constSet.maxChangedConstB), 32u) * uint32_t(sizeof(uint32_t));\n\n    // Max copy source size is 8192 * 16 => always aligned to any plausible value\n    // => we won't copy out of bounds\n    if (likely(constSet.meta.maxConstIndexF != 0)) {\n      auto mapPtr = CopySoftwareConstants(constSet.buffer, Src.fConsts, floatDataSize);\n\n      if (constSet.meta.needsConstantCopies) {\n        // Copy shader defined constants over so they can be accessed\n        // with relative addressing.\n        Vector4* data = reinterpret_cast<Vector4*>(mapPtr);\n\n        auto& shaderConsts = GetCommonShader(m_state.vertexShader)->GetConstants();\n\n        for (const auto& constant : shaderConsts) {\n          if (constant.uboIdx < constSet.meta.maxConstIndexF)\n            data[constant.uboIdx] = *reinterpret_cast<const Vector4*>(constant.float32);\n        }\n      }\n    }\n\n    // Max copy source size is 2048 * 16 => always aligned to any plausible value\n    // => we won't copy out of bounds\n    if (likely(constSet.meta.maxConstIndexI != 0))\n      CopySoftwareConstants(constSet.swvp.intBuffer, Src.iConsts, intDataSize);\n\n    if (likely(constSet.meta.maxConstIndexB != 0))\n      CopySoftwareConstants(constSet.swvp.boolBuffer, Src.bConsts, boolDataSize);\n  }\n\n\n  inline void* D3D9DeviceEx::CopySoftwareConstants(D3D9ConstantBuffer& dstBuffer, const void* src, uint32_t size) {\n    uint32_t alignment = dstBuffer.GetAlignment();\n    size = std::max(size, alignment);\n    size = align(size, alignment);\n\n    auto mapPtr = dstBuffer.Alloc(size);\n    std::memcpy(mapPtr, src, size);\n    return mapPtr;\n  }\n\n\n  template <D3D9ShaderType ShaderStage, typename HardwareLayoutType, typename SoftwareLayoutType, typename ShaderType>\n  inline void D3D9DeviceEx::UploadConstantSet(const SoftwareLayoutType& Src, const D3D9ConstantLayout& Layout, const ShaderType& Shader) {\n    /*\n     * We just copy the float constants that have been set by the application and rely on robustness\n     * to return 0 on OOB reads.\n    */\n    D3D9ConstantSets& constSet = m_consts[uint32_t(ShaderStage)];\n\n    if (!constSet.dirty)\n      return;\n\n    constSet.dirty = false;\n\n    uint32_t floatCount = constSet.maxChangedConstF;\n    if (constSet.meta.needsConstantCopies) {\n      // If the shader requires us to preserve shader defined constants,\n      // we copy those over. We need to adjust the amount of used floats accordingly.\n      auto shader = GetCommonShader(Shader);\n      floatCount = std::max(floatCount, static_cast<uint32_t>(shader->GetMaxDefinedFloatConstant() + 1));\n    }\n    // If we statically know which is the last float constant accessed by the shader, we don't need to copy the rest.\n    floatCount = std::min(constSet.meta.maxConstIndexF, floatCount);\n\n    // There are very few int constants, so we put those into the same buffer at the start.\n    // We always allocate memory for all possible int constants to make sure alignment works out.\n    const uint32_t intRange = caps::MaxOtherConstants * sizeof(Vector4i);\n    uint32_t floatDataSize = floatCount * sizeof(Vector4);\n    // Determine amount of floats and buffer size based on highest used float constant and alignment\n    const uint32_t alignment = constSet.buffer.GetAlignment();\n    const uint32_t bufferSize = align(std::max(floatDataSize + intRange, alignment), alignment);\n    floatDataSize = bufferSize - intRange;\n\n    void* mapPtr = constSet.buffer.Alloc(bufferSize);\n    auto* dst = reinterpret_cast<HardwareLayoutType*>(mapPtr);\n\n    const uint32_t intDataSize = constSet.meta.maxConstIndexI * sizeof(Vector4i);\n    if (constSet.meta.maxConstIndexI != 0)\n      std::memcpy(dst->iConsts, Src.iConsts, intDataSize);\n    if (constSet.meta.maxConstIndexF != 0)\n      std::memcpy(dst->fConsts, Src.fConsts, floatDataSize);\n\n    if (constSet.meta.needsConstantCopies) {\n      // Copy shader defined constants over so they can be accessed\n      // with relative addressing.\n      Vector4* data = reinterpret_cast<Vector4*>(dst->fConsts);\n\n      auto& shaderConsts = GetCommonShader(Shader)->GetConstants();\n\n      for (const auto& constant : shaderConsts) {\n        if (constant.uboIdx < constSet.meta.maxConstIndexF)\n          data[constant.uboIdx] = *reinterpret_cast<const Vector4*>(constant.float32);\n      }\n    }\n  }\n\n\n  template <D3D9ShaderType ShaderStage>\n  void D3D9DeviceEx::UploadConstants() {\n    if constexpr (ShaderStage == D3D9ShaderType::VertexShader) {\n      if (CanSWVP())\n        return UploadSoftwareConstantSet(m_state.vsConsts.get(), m_consts[uint32_t(ShaderStage)].layout);\n      else\n        return UploadConstantSet<ShaderStage, D3D9ShaderConstantsVSHardware>(m_state.vsConsts.get(), m_consts[uint32_t(ShaderStage)].layout, m_state.vertexShader);\n    } else {\n      return UploadConstantSet<ShaderStage, D3D9ShaderConstantsPS>(m_state.psConsts.get(), m_consts[uint32_t(ShaderStage)].layout, m_state.pixelShader);\n    }\n  }\n\n\n  void D3D9DeviceEx::UpdateClipPlanes() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::ClipPlanes);\n\n    auto mapPtr = m_vsClipPlanes.AllocSlice();\n    auto dst = reinterpret_cast<D3D9ClipPlane*>(mapPtr);\n\n    uint32_t clipPlaneCount = 0u;\n    for (uint32_t i = 0; i < caps::MaxClipPlanes; i++) {\n      D3D9ClipPlane clipPlane = (m_state.renderStates[D3DRS_CLIPPLANEENABLE] & (1 << i))\n        ? m_state.clipPlanes[i]\n        : D3D9ClipPlane();\n\n      if (clipPlane != D3D9ClipPlane())\n        dst[clipPlaneCount++] = clipPlane;\n    }\n\n    // Write the rest to 0 for GPL.\n    for (uint32_t i = clipPlaneCount; i < caps::MaxClipPlanes; i++)\n      dst[i] = D3D9ClipPlane();\n\n    if (m_specInfo.set<SpecClipPlaneCount>(clipPlaneCount))\n      m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n  }\n\n\n  template <uint32_t Offset, uint32_t Length>\n  void D3D9DeviceEx::UpdatePushConstant(const void* pData) {\n    struct ConstantData { uint8_t Data[Length]; };\n\n    const ConstantData* constData = reinterpret_cast<const ConstantData*>(pData);\n\n    EmitCs([\n      cData = *constData\n    ](DxvkContext* ctx) {\n      // Render state uses the shared push constant block\n      ctx->pushData(VK_SHADER_STAGE_ALL_GRAPHICS, Offset, Length, &cData);\n    });\n  }\n\n\n  template <D3D9RenderStateItem Item>\n  void D3D9DeviceEx::UpdatePushConstant() {\n    auto& rs = m_state.renderStates;\n\n    if constexpr (Item == D3D9RenderStateItem::AlphaRef) {\n      uint32_t alpha = rs[D3DRS_ALPHAREF] & 0xFF;\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, alphaRef), sizeof(uint32_t)>(&alpha);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::FogColor) {\n      Vector4 color;\n      DecodeD3DCOLOR(D3DCOLOR(rs[D3DRS_FOGCOLOR]), color.data);\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, fogColor), sizeof(D3D9RenderStateInfo::fogColor)>(&color);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::FogDensity) {\n      float density = bit::cast<float>(rs[D3DRS_FOGDENSITY]);\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, fogDensity), sizeof(float)>(&density);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::FogEnd) {\n      float end = bit::cast<float>(rs[D3DRS_FOGEND]);\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, fogEnd), sizeof(float)>(&end);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::FogScale) {\n      float end = bit::cast<float>(rs[D3DRS_FOGEND]);\n      float start = bit::cast<float>(rs[D3DRS_FOGSTART]);\n\n      float scale = 1.0f / (end - start);\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, fogScale), sizeof(float)>(&scale);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::PointSize) {\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, pointSize), sizeof(float)>(&rs[D3DRS_POINTSIZE]);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::PointSizeMin) {\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, pointSizeMin), sizeof(float)>(&rs[D3DRS_POINTSIZE_MIN]);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::PointSizeMax) {\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, pointSizeMax), sizeof(float)>(&rs[D3DRS_POINTSIZE_MAX]);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::PointScaleA) {\n      float scale = bit::cast<float>(rs[D3DRS_POINTSCALE_A]);\n      scale /= float(m_state.viewport.Height * m_state.viewport.Height);\n\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, pointScaleA), sizeof(float)>(&scale);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::PointScaleB) {\n      float scale = bit::cast<float>(rs[D3DRS_POINTSCALE_B]);\n      scale /= float(m_state.viewport.Height * m_state.viewport.Height);\n\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, pointScaleB), sizeof(float)>(&scale);\n    }\n    else if constexpr (Item == D3D9RenderStateItem::PointScaleC) {\n      float scale = bit::cast<float>(rs[D3DRS_POINTSCALE_C]);\n      scale /= float(m_state.viewport.Height * m_state.viewport.Height);\n\n      UpdatePushConstant<offsetof(D3D9RenderStateInfo, pointScaleC), sizeof(float)>(&scale);\n    }\n    else\n      Logger::warn(\"D3D9: Invalid push constant set to update.\");\n  }\n\n\n  template <bool Synchronize9On12>\n  void D3D9DeviceEx::ExecuteFlush() {\n    D3D9DeviceLock lock = LockDevice();\n\n    if constexpr (Synchronize9On12)\n      m_submitStatus.result = VK_NOT_READY;\n\n    // Update signaled staging buffer counter and signal the fence\n    m_stagingMemorySignaled = GetStagingMemoryStatistics().allocatedTotal;\n\n    // Reset counter for discarded memory in flight\n    m_discardMemoryOnFlush = m_discardMemoryCounter;\n\n    // Add commands to flush the threaded\n    // context, then flush the command list\n    uint64_t submissionId = ++m_submissionId;\n\n    EmitCs<false>([\n      cSubmissionFence  = m_submissionFence,\n      cSubmissionId     = submissionId,\n      cSubmissionStatus = Synchronize9On12 ? &m_submitStatus : nullptr,\n      cStagingBufferFence = m_stagingBufferFence,\n      cStagingBufferAllocated = m_stagingMemorySignaled\n    ] (DxvkContext* ctx) {\n      ctx->signal(cSubmissionFence, cSubmissionId);\n      ctx->signal(cStagingBufferFence, cStagingBufferAllocated);\n      ctx->flushCommandList(nullptr, cSubmissionStatus);\n    });\n\n    FlushCsChunk();\n\n    m_flushSeqNum = m_csSeqNum;\n    m_flushTracker.notifyFlush(m_flushSeqNum, submissionId);\n\n    // If necessary, block calling thread until the\n    // Vulkan queue submission is performed.\n    if constexpr (Synchronize9On12)\n      m_dxvkDevice->waitForSubmission(&m_submitStatus);\n\n    // Notify the device that the context has been flushed,\n    // this resets some resource initialization heuristics.\n    m_initializer->NotifyContextFlush();\n  }\n\n\n  void D3D9DeviceEx::Flush() {\n    ExecuteFlush<false>();\n  }\n\n\n  void D3D9DeviceEx::FlushAndSync9On12() {\n    ExecuteFlush<true>();\n  }\n\n\n  void D3D9DeviceEx::BeginFrame(Rc<DxvkLatencyTracker> LatencyTracker, uint64_t FrameId) {\n    D3D9DeviceLock lock = LockDevice();\n\n    EmitCs<false>([\n      cTracker = std::move(LatencyTracker),\n      cFrameId = FrameId\n    ] (DxvkContext* ctx) {\n      if (cTracker && cTracker->needsAutoMarkers())\n        ctx->beginLatencyTracking(cTracker, cFrameId);\n    });\n  }\n\n\n  void D3D9DeviceEx::EndFrame(Rc<DxvkLatencyTracker> LatencyTracker) {\n    D3D9DeviceLock lock = LockDevice();\n\n    EmitCs<false>([\n      cTracker = std::move(LatencyTracker)\n    ] (DxvkContext* ctx) {\n      ctx->endFrame();\n\n      if (cTracker && cTracker->needsAutoMarkers())\n        ctx->endLatencyTracking(cTracker);\n    });\n  }\n\n\n  template <uint32_t Index>\n  inline void D3D9DeviceEx::UpdateAnyColorWrites() {\n    // Writes to a render target have been enabled => check for hazards\n    UpdateActiveHazardsRT(std::numeric_limits<uint32_t>::max());\n\n    // Writes to render target 0 have been enabled and the RT might not be bound due to the 1x1 hack.\n    if (Index == 0 && m_state.depthStencil != nullptr)\n      m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n  }\n\n\n  inline void D3D9DeviceEx::UpdateTextureBitmasks(uint32_t index, DWORD combinedUsage) {\n    const uint32_t bit = 1 << index;\n\n    m_textureSlotTracking.rtUsage                &= ~bit;\n    m_textureSlotTracking.dsUsage                &= ~bit;\n    m_textureSlotTracking.bound                  &= ~bit;\n    m_textureSlotTracking.needsUpload            &= ~bit;\n    m_textureSlotTracking.needsMipGen            &= ~bit;\n    m_textureSlotTracking.mismatchingTextureType &= ~bit;\n\n    auto tex = GetCommonTexture(m_state.textures[index]);\n\n    if (likely(IsPSSampler(index))) {\n      const uint32_t textureType = tex != nullptr\n        ? uint32_t(tex->GetType() - D3DRTYPE_TEXTURE)\n        : 0;\n      // There are 3 texture types, so we need 2 bits.\n      const uint32_t offset = index * 2;\n      const uint32_t textureBitMask = 0b11u       << offset;\n      const uint32_t textureBits    = textureType << offset;\n\n      // In fixed function shaders and SM < 2 we put the type mask\n      // into a spec constant to select the used sampler type.\n      m_textureSlotTracking.textureType &= ~textureBitMask;\n      m_textureSlotTracking.textureType |=  textureBits;\n    }\n\n    if (likely(tex != nullptr)) {\n      m_textureSlotTracking.bound |= bit;\n\n      if (unlikely(tex->IsRenderTarget()))\n        m_textureSlotTracking.rtUsage |= bit;\n\n      if (unlikely(tex->IsDepthStencil()))\n        m_textureSlotTracking.dsUsage |= bit;\n\n      if (unlikely(tex->NeedsAnyUpload()))\n        m_textureSlotTracking.needsUpload |= bit;\n\n      if (unlikely(tex->NeedsMipGen()))\n        m_textureSlotTracking.needsMipGen |= bit;\n\n      // Update shadow sampler mask\n      const bool oldDepth = m_textureSlotTracking.depth & bit;\n      const bool newDepth = tex->IsShadow();\n\n      if (oldDepth != newDepth) {\n        m_textureSlotTracking.depth ^= bit;\n        m_textureSlotTracking.samplerStateDirty |= bit;\n      }\n\n      // Update dref clamp mask\n      m_textureSlotTracking.drefClamp &= ~bit;\n      m_textureSlotTracking.drefClamp |= uint32_t(tex->IsUpgradedToD32f()) << index;\n\n      if (unlikely(m_textureSlotTracking.fetch4SamplerState & bit))\n        UpdateActiveFetch4(index);\n\n      UpdateTextureTypeMismatchesForTexture(index);\n    } else {\n      if (unlikely(m_textureSlotTracking.fetch4 & bit))\n        UpdateActiveFetch4(index);\n    }\n\n    if (unlikely(combinedUsage & D3DUSAGE_RENDERTARGET)) {\n      UpdateActiveHazardsRT(bit);\n    } else {\n      m_textureSlotTracking.hazardRT             &= ~bit;\n      m_textureSlotTracking.unresolvableHazardRT &= ~bit;\n    }\n\n    if (unlikely(combinedUsage & D3DUSAGE_DEPTHSTENCIL)) {\n      UpdateActiveHazardsDS(bit);\n    } else {\n      m_textureSlotTracking.hazardDS             &= ~bit;\n      m_textureSlotTracking.unresolvableHazardDS &= ~bit;\n    }\n  }\n\n\n  inline void D3D9DeviceEx::UpdateActiveHazardsRT(uint32_t texMask) {\n    uint32_t oldHazardMask             = m_textureSlotTracking.hazardRT;\n    uint32_t oldUnresolvableHazardMask = m_textureSlotTracking.unresolvableHazardRT;\n    m_textureSlotTracking.hazardRT             &= ~texMask;\n    m_textureSlotTracking.unresolvableHazardRT &= ~texMask;\n\n    auto psMasks = PSShaderMasks();\n    uint32_t rtMask = m_rtSlotTracking.canBeSampled;\n    texMask &= m_textureSlotTracking.rtUsage;\n\n    for (uint32_t rtIdx : bit::BitMask(rtMask)) {\n      bool anyColorWrite = m_state.renderStates[ColorWriteIndex(rtIdx)] != 0;\n      bool shaderWritesToRt = (psMasks.rtMask & (1 << rtIdx)) != 0;\n      for (uint32_t samplerIdx : bit::BitMask(texMask)) {\n        D3D9Surface* rtSurf = m_state.renderTargets[rtIdx].ptr();\n\n        IDirect3DBaseTexture9* rtBase  = rtSurf->GetBaseTexture();\n        IDirect3DBaseTexture9* texBase = m_state.textures[samplerIdx];\n\n        // HACK: Don't mark for hazards if we aren't rendering to mip 0!\n        // Some games use screenspace passes like this for blurring\n        // Sampling from mip 0 (texture) -> mip 1 (rt)\n        // and we'd trigger the hazard path otherwise which is unnecessary,\n        // and would shove us into GENERAL and emitting readback barriers.\n        if (likely(rtSurf->GetMipLevel() != 0 || rtBase != texBase))\n          continue;\n\n        const bool sampledInShader = !!(psMasks.samplerMask & (1 << samplerIdx));\n        const bool wasHazard = !!(oldHazardMask & (1 << samplerIdx));\n\n        // If the shader doesn't actually use the texture, keep it marked as a hazard\n        // to avoid spilling the render pass over and over again because of shader changes.\n        if (unlikely(!sampledInShader && ((anyColorWrite && shaderWritesToRt) || !wasHazard)))\n          continue;\n\n        // We can resolve the hazard by unbinding the RT.\n        m_textureSlotTracking.hazardRT |= 1 << samplerIdx;\n\n        // Don't mark texture as an unresolvable hazard if the shader doesn't actually use it.\n        if (unlikely(!sampledInShader))\n          continue;\n\n        // The hazard can be resolved by not binding it.\n        if (likely(!anyColorWrite || !shaderWritesToRt))\n          continue;\n\n        // We have to bind the RT, so we need FEEDBACK_LOOP_LAYOUT.\n        m_textureSlotTracking.unresolvableHazardRT |= 1 << samplerIdx;\n      }\n    }\n\n    // Only dirty the framebuffer if we need to make changes for a new hazard\n    if (unlikely(m_textureSlotTracking.hazardRT != oldHazardMask\n      || m_textureSlotTracking.unresolvableHazardRT != oldUnresolvableHazardMask)) {\n      m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n    }\n  }\n\n\n  inline void D3D9DeviceEx::UpdateActiveHazardsDS(uint32_t texMask) {\n    uint32_t oldHazardMask             = m_textureSlotTracking.hazardDS;\n    uint32_t oldUnresolvableHazardMask = m_textureSlotTracking.unresolvableHazardDS;\n    m_textureSlotTracking.hazardDS             &= ~texMask;\n    m_textureSlotTracking.unresolvableHazardDS &= ~texMask;\n\n    if (m_state.depthStencil == nullptr ||\n        m_state.depthStencil->GetBaseTexture() == nullptr)\n      return;\n\n    auto psMasks = PSShaderMasks();\n    texMask &= m_textureSlotTracking.dsUsage;\n\n    const bool depthWrite = m_state.renderStates[D3DRS_ZENABLE] && m_state.renderStates[D3DRS_ZWRITEENABLE];\n\n    for (uint32_t samplerIdx : bit::BitMask(texMask)) {\n      IDirect3DBaseTexture9* dsBase  = m_state.depthStencil->GetBaseTexture();\n      IDirect3DBaseTexture9* texBase = m_state.textures[samplerIdx];\n\n      if (likely(dsBase != texBase))\n        continue;\n\n      const bool sampledInShader = !!(psMasks.samplerMask & (1 << samplerIdx));\n      const bool wasHazard = !!(oldHazardMask & (1 << samplerIdx));\n\n      // Don't mark it as a hazard if the current shader doesn't actually sample it\n      // but we need to render to it.\n      // If the shader doesn't actually sample the texture, we don't render to it\n      // and it was a hazard before, keep it marked as a hazard to avoid spilling\n      // the render pass over and over again because of shader changes.\n      if (unlikely(!sampledInShader && (depthWrite || !wasHazard)))\n        continue;\n\n      m_textureSlotTracking.hazardDS |= 1 << samplerIdx;\n\n      // Don't mark texture as an unresolvable hazard if the shader doesn't actually use it.\n      if (unlikely(!sampledInShader))\n        continue;\n\n      // The hazard can be resolved by binding it as READONLY.\n      if (unlikely(!depthWrite))\n        continue;\n\n      // We have to bind the DS as writable, so we need FEEDBACK_LOOP_LAYOUT.\n      m_textureSlotTracking.unresolvableHazardDS |= 1 << samplerIdx;\n    }\n\n    // Only dirty the framebuffer if we need to make changes for a new hazard\n    if (unlikely(m_textureSlotTracking.hazardDS != oldHazardMask\n      || m_textureSlotTracking.unresolvableHazardDS != oldUnresolvableHazardMask)) {\n      m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n    }\n  }\n\n\n  void D3D9DeviceEx::EmitFeedbackLoopBarriers() {\n    struct {\n      uint8_t RT : 1;\n      uint8_t DS : 1;\n    } hazardState;\n    hazardState.RT = m_textureSlotTracking.unresolvableHazardRT != 0;\n    hazardState.DS = m_textureSlotTracking.unresolvableHazardDS != 0;\n\n    EmitCs([\n      cHazardState = hazardState\n    ](DxvkContext* ctx) {\n      VkPipelineStageFlags srcStages = 0;\n      VkAccessFlags srcAccess = 0;\n\n      if (cHazardState.RT != 0) {\n        srcStages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        srcAccess |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n      }\n      if (cHazardState.DS != 0) {\n        srcStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n        srcAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n      }\n\n      ctx->emitGraphicsBarrier(\n        srcStages,\n        srcAccess,\n        VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,\n        VK_ACCESS_SHADER_READ_BIT);\n    });\n\n    for (uint32_t samplerIdx : bit::BitMask(m_textureSlotTracking.unresolvableHazardRT | m_textureSlotTracking.unresolvableHazardDS)) {\n      auto tex = GetCommonTexture(m_state.textures[samplerIdx]);\n\n      if (unlikely(!tex->MarkTransitionedToHazardLayout())) {\n        // Recreate image with feedback loop usage and layout\n        EmitCs([\n          cImage  = tex->GetImage(),\n          cLayout = m_hazardLayout\n        ] (DxvkContext* ctx) mutable {\n          DxvkImageUsageInfo usage = { };\n          usage.layout = cLayout;\n\n          if (cLayout == VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT)\n            usage.usage = VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;\n\n          ctx->ensureImageCompatibility(std::move(cImage), usage);\n        });\n\n        m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n      }\n    }\n  }\n\n\n  void D3D9DeviceEx::UpdateActiveFetch4(uint32_t stateSampler) {\n    auto& state = m_state.samplerStates;\n\n    const uint32_t samplerBit = 1u << stateSampler;\n\n    auto texture = GetCommonTexture(m_state.textures[stateSampler]);\n    const bool textureSupportsFetch4 = texture != nullptr && texture->SupportsFetch4();\n\n    const bool fetch4Enabled = m_textureSlotTracking.fetch4SamplerState & samplerBit;\n    const bool pointSampled  = state[stateSampler][D3DSAMP_MAGFILTER] == D3DTEXF_POINT;\n    const bool shouldFetch4  = fetch4Enabled && textureSupportsFetch4 && pointSampled;\n\n    if (unlikely(shouldFetch4 != !!(m_textureSlotTracking.fetch4 & samplerBit))) {\n      if (shouldFetch4)\n        m_textureSlotTracking.fetch4 |= samplerBit;\n      else\n        m_textureSlotTracking.fetch4 &= ~samplerBit;\n    }\n  }\n\n\n  void D3D9DeviceEx::UploadManagedTexture(D3D9CommonTexture* pResource) {\n    for (uint32_t subresource = 0; subresource < pResource->CountSubresources(); subresource++) {\n      if (!pResource->NeedsUpload(subresource))\n        continue;\n\n      this->FlushImage(pResource, subresource);\n    }\n\n    pResource->ClearDirtyBoxes();\n    pResource->ClearNeedsUpload();\n  }\n\n\n  void D3D9DeviceEx::UploadManagedTextures(uint32_t mask) {\n    // Guaranteed to not be nullptr...\n    for (uint32_t texIdx : bit::BitMask(mask))\n      UploadManagedTexture(GetCommonTexture(m_state.textures[texIdx]));\n\n    m_textureSlotTracking.needsUpload &= ~mask;\n  }\n\n\n  void D3D9DeviceEx::UpdateTextureTypeMismatchesForShader(const D3D9CommonShader* shader, uint32_t shaderSamplerMask, uint32_t shaderSamplerOffset) {\n    const uint32_t stageCorrectedShaderSamplerMask = shaderSamplerMask << shaderSamplerOffset;\n    if (unlikely(shader->GetInfo().majorVersion() < 2 || m_d3d9Options.forceSamplerTypeSpecConstants)) {\n      // SM 1 shaders don't define the texture type in the shader.\n      // We always use spec constants for those.\n      m_textureSlotTracking.textureDirty |= stageCorrectedShaderSamplerMask & m_textureSlotTracking.mismatchingTextureType;\n      m_textureSlotTracking.mismatchingTextureType &= ~stageCorrectedShaderSamplerMask;\n      return;\n    }\n\n    for (const uint32_t i : bit::BitMask(stageCorrectedShaderSamplerMask)) {\n      const D3D9CommonTexture* texture = GetCommonTexture(m_state.textures[i]);\n      if (unlikely(texture == nullptr)) {\n        // Unbound textures are not mismatching texture types\n        m_textureSlotTracking.textureDirty |= m_textureSlotTracking.mismatchingTextureType & (1 << i);\n        m_textureSlotTracking.mismatchingTextureType &= ~(1 << i);\n        continue;\n      }\n\n      VkImageViewType boundViewType  = D3D9CommonTexture::GetImageViewTypeFromResourceType(texture->GetType(), D3D9CommonTexture::AllLayers);\n      VkImageViewType shaderViewType = shader->GetImageViewType(i - shaderSamplerOffset);\n      if (unlikely(boundViewType != shaderViewType)) {\n        m_textureSlotTracking.textureDirty |= 1 << i;\n        m_textureSlotTracking.mismatchingTextureType |= 1 << i;\n      } else {\n        // The texture type is no longer mismatching, make sure we bind the texture now.\n        m_textureSlotTracking.textureDirty |= m_textureSlotTracking.mismatchingTextureType & (1 << i);\n        m_textureSlotTracking.mismatchingTextureType &= ~(1 << i);\n      }\n    }\n  }\n\n\n  void D3D9DeviceEx::UpdateTextureTypeMismatchesForTexture(uint32_t stateSampler) {\n    uint32_t shaderTextureIndex;\n    const D3D9CommonShader* shader;\n    if (likely(IsPSSampler(stateSampler))) {\n      shader = GetCommonShader(m_state.pixelShader);\n      shaderTextureIndex = stateSampler;\n    } else if (unlikely(IsVSSampler(stateSampler))) {\n      shader = GetCommonShader(m_state.vertexShader);\n      shaderTextureIndex = stateSampler - caps::MaxTexturesPS - 1;\n    } else {\n      // Do not type check the fixed function displacement map texture.\n      return;\n    }\n\n    if (unlikely(shader == nullptr || shader->GetInfo().majorVersion() < 2 || m_d3d9Options.forceSamplerTypeSpecConstants)) {\n      // This function only gets called by UpdateTextureBitmasks\n      // which clears the dirty and mismatching bits for the texture before anyway.\n      return;\n    }\n\n    const D3D9CommonTexture* tex = GetCommonTexture(m_state.textures[stateSampler]);\n    VkImageViewType boundViewType  = D3D9CommonTexture::GetImageViewTypeFromResourceType(tex->GetType(), D3D9CommonTexture::AllLayers);\n    VkImageViewType shaderViewType = shader->GetImageViewType(shaderTextureIndex);\n    // D3D9 does not have 1D textures. The value of VIEW_TYPE_1D is 0\n    // which is the default when there is no declaration for the type.\n    bool shaderUsesTexture = shaderViewType != VkImageViewType(0);\n    if (unlikely(boundViewType != shaderViewType && shaderUsesTexture)) {\n      const uint32_t samplerBit = 1u << stateSampler;\n      m_textureSlotTracking.mismatchingTextureType |= samplerBit;\n    }\n  }\n\n\n  void D3D9DeviceEx::GenerateTextureMips(uint32_t mask) {\n    for (uint32_t texIdx : bit::BitMask(mask)) {\n      // Guaranteed to not be nullptr...\n      auto texInfo = GetCommonTexture(m_state.textures[texIdx]);\n\n      if (likely(texInfo->NeedsMipGen())) {\n        this->EmitGenerateMips(texInfo);\n        if (likely(!IsTextureBoundAsAttachment(texInfo))) {\n          texInfo->SetNeedsMipGen(false);\n        }\n      }\n    }\n\n    m_textureSlotTracking.needsMipGen &= ~mask;\n  }\n\n\n  void D3D9DeviceEx::MarkTextureMipsDirty(D3D9CommonTexture* pResource) {\n    pResource->SetNeedsMipGen(true);\n\n    for (uint32_t i : bit::BitMask(m_textureSlotTracking.bound)) {\n      // Guaranteed to not be nullptr...\n      auto texInfo = GetCommonTexture(m_state.textures[i]);\n\n      if (texInfo == pResource) {\n        m_textureSlotTracking.needsMipGen |= 1 << i;\n        // We can early out here, no need to add another index for this.\n        break;\n      }\n    }\n  }\n\n\n  void D3D9DeviceEx::MarkTextureMipsUnDirty(D3D9CommonTexture* pResource) {\n    if (likely(!IsTextureBoundAsAttachment(pResource))) {\n      // We need to keep the texture marked as needing mipmap generation because we don't set that when rendering.\n      pResource->SetNeedsMipGen(false);\n\n      for (uint32_t i : bit::BitMask(m_textureSlotTracking.bound)) {\n        // Guaranteed to not be nullptr...\n        auto texInfo = GetCommonTexture(m_state.textures[i]);\n\n        if (unlikely(texInfo == pResource)) {\n          m_textureSlotTracking.needsMipGen &= ~(1 << i);\n        }\n      }\n    }\n  }\n\n\n  void D3D9DeviceEx::MarkTextureUploaded(D3D9CommonTexture* pResource) {\n    for (uint32_t i : bit::BitMask(m_textureSlotTracking.bound)) {\n      // Guaranteed to not be nullptr...\n      auto texInfo = GetCommonTexture(m_state.textures[i]);\n\n      if (texInfo == pResource)\n        m_textureSlotTracking.needsUpload &= ~(1 << i);\n    }\n  }\n\n\n  void D3D9DeviceEx::UpdatePointMode(bool pointList) {\n    if (!pointList) {\n      UpdatePointModeSpec(0);\n      return;\n    }\n\n    auto& rs = m_state.renderStates;\n\n    const bool scale  = rs[D3DRS_POINTSCALEENABLE] && !UseProgrammableVS();\n    const bool sprite = rs[D3DRS_POINTSPRITEENABLE];\n\n    const uint32_t scaleBit  = scale  ? 1u : 0u;\n    const uint32_t spriteBit = sprite ? 2u : 0u;\n\n    uint32_t mode = scaleBit | spriteBit;\n\n    if (rs[D3DRS_POINTSCALEENABLE] && m_dirty.test(D3D9DeviceDirtyFlag::PointScale)) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::PointScale);\n\n      UpdatePushConstant<D3D9RenderStateItem::PointScaleA>();\n      UpdatePushConstant<D3D9RenderStateItem::PointScaleB>();\n      UpdatePushConstant<D3D9RenderStateItem::PointScaleC>();\n    }\n\n    UpdatePointModeSpec(mode);\n  }\n\n\n  void D3D9DeviceEx::UpdateFog() {\n    auto& rs = m_state.renderStates;\n\n    bool fogEnabled = rs[D3DRS_FOGENABLE];\n\n    bool pixelFog   = rs[D3DRS_FOGTABLEMODE]  != D3DFOG_NONE && fogEnabled;\n    bool vertexFog  = rs[D3DRS_FOGVERTEXMODE] != D3DFOG_NONE && fogEnabled && !pixelFog;\n\n    auto UpdateFogConstants = [&](D3DFOGMODE FogMode) {\n      if (m_dirty.test(D3D9DeviceDirtyFlag::FogColor)) {\n        m_dirty.clr(D3D9DeviceDirtyFlag::FogColor);\n        UpdatePushConstant<D3D9RenderStateItem::FogColor>();\n      }\n\n      if (FogMode == D3DFOG_LINEAR) {\n        if (m_dirty.test(D3D9DeviceDirtyFlag::FogScale)) {\n          m_dirty.clr(D3D9DeviceDirtyFlag::FogScale);\n          UpdatePushConstant<D3D9RenderStateItem::FogScale>();\n        }\n\n        if (m_dirty.test(D3D9DeviceDirtyFlag::FogEnd)) {\n          m_dirty.clr(D3D9DeviceDirtyFlag::FogEnd);\n          UpdatePushConstant<D3D9RenderStateItem::FogEnd>();\n        }\n      }\n      else if (FogMode == D3DFOG_EXP || FogMode == D3DFOG_EXP2) {\n        if (m_dirty.test(D3D9DeviceDirtyFlag::FogDensity)) {\n          m_dirty.clr(D3D9DeviceDirtyFlag::FogDensity);\n          UpdatePushConstant<D3D9RenderStateItem::FogDensity>();\n        }\n      }\n    };\n\n    if (vertexFog) {\n      D3DFOGMODE mode = D3DFOGMODE(rs[D3DRS_FOGVERTEXMODE]);\n\n      UpdateFogConstants(mode);\n\n      if (m_dirty.test(D3D9DeviceDirtyFlag::FogState)) {\n        m_dirty.clr(D3D9DeviceDirtyFlag::FogState);\n\n        UpdateFogModeSpec(true, mode, D3DFOG_NONE);\n      }\n    }\n    else if (pixelFog) {\n      D3DFOGMODE mode = D3DFOGMODE(rs[D3DRS_FOGTABLEMODE]);\n\n      UpdateFogConstants(mode);\n\n      if (m_dirty.test(D3D9DeviceDirtyFlag::FogState)) {\n        m_dirty.clr(D3D9DeviceDirtyFlag::FogState);\n\n        UpdateFogModeSpec(true, D3DFOG_NONE, mode);\n      }\n    }\n    else {\n      if (fogEnabled)\n        UpdateFogConstants(D3DFOG_NONE);\n\n      if (m_dirty.test(D3D9DeviceDirtyFlag::FogState)) {\n        m_dirty.clr(D3D9DeviceDirtyFlag::FogState);\n\n        UpdateFogModeSpec(fogEnabled, D3DFOG_NONE, D3DFOG_NONE);\n      }\n    }\n  }\n\n\n  void D3D9DeviceEx::BindFramebuffer() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::Framebuffer);\n\n    DxvkRenderTargets attachments;\n\n    bool srgb = m_state.renderStates[D3DRS_SRGBWRITEENABLE];\n\n    // The extents and sample counts of all render targets need to match.\n    // There's a few exceptions for mismatching extents of depth stencil surfaces:\n    //   - It is allowed to be larger than RT0.\n    //   - It is allowed to be smaller than RT0 IF only one render target is bound, RT0 has the NULL format\n    //     or the extents 1x1 and the color write mask for RT0 is 0.\n\n    // Dead Space uses this behavior to render shadow maps if it detects AMD hardware.\n    // It detects AMD hardware by checking whether DF texture formats are supported.\n    // That's only the case on the AMD D3D9 driver.\n    // On AMD hardware Dead Space renders shadow maps by binding a 1x1 RT and setting the color write mask to 0.\n    // On Nvidia hardware it uses a render target with the NULL texture format.\n\n    // We also unbind render targets if they aren't used for rendering but get sampled.\n    // But we want to minimize frame buffer changes because those break up the current render pass,\n    // so we dont unbind for disabled color write masks unless the RT gets sampled.\n\n    const auto& psMasks = PSShaderMasks();\n\n    VkSampleCountFlags sampleCount = VK_SAMPLE_COUNT_1_BIT;\n    VkExtent2D renderArea = { 0u, 0u };\n    const D3D9CommonTexture* rt0 = GetCommonTexture(m_state.renderTargets[0].ptr());\n    if (likely(rt0 != nullptr && !rt0->IsNull())) {\n      const DxvkImageCreateInfo& rt0Info = rt0->GetImage()->info();\n      sampleCount = rt0Info.sampleCount;\n      renderArea = { rt0Info.extent.width, rt0Info.extent.height };\n    }\n\n    if (m_state.depthStencil != nullptr) {\n      const D3D9CommonTexture* ds = m_state.depthStencil->GetCommonTexture();\n      const DxvkImageCreateInfo& dsInfo = ds->GetImage()->info();\n\n      uint32_t boundRTCount = 0;\n      for (uint32_t i = 0; i < m_state.renderTargets.size(); i++) {\n        if (m_state.renderTargets[i] == nullptr) // NULL format textures are counted\n          continue;\n\n        boundRTCount++;\n      }\n\n      const bool rt0WrittenTo = (psMasks.rtMask & 1u) != 0\n          && m_state.renderStates[D3DRS_COLORWRITEENABLE] != 0;\n\n      // D3D9 has a special case for 1x1 textures. (Tested on Windows on the Nvidia D3D9 driver.)\n      const bool noRT0Bound = rt0 == nullptr || rt0->IsNull();\n      const bool ignoreRT0 = boundRTCount == 1\n          && renderArea.width == 1\n          && renderArea.height == 1\n          && !rt0WrittenTo;\n\n      // The depth stencil surface is allowed to be larger than the RTs.\n      const bool mismatch = dsInfo.extent.width < renderArea.width\n        || dsInfo.extent.height < renderArea.height\n        || dsInfo.sampleCount != sampleCount;\n      const bool bindDS = !mismatch || noRT0Bound || ignoreRT0;\n\n      if (likely(bindDS)) {\n        if (unlikely(noRT0Bound || ignoreRT0)) {\n          renderArea = { dsInfo.extent.width, dsInfo.extent.height };\n          sampleCount = dsInfo.sampleCount;\n        }\n\n        // If the DS is also bound as a texture for sampling\n        // and it's either unused as DS or not written to,\n        // use the readonly layout.\n        bool readOnly = m_textureSlotTracking.hazardDS != 0;\n        readOnly &= !m_state.renderStates[D3DRS_ZENABLE]\n          || !m_state.renderStates[D3DRS_ZWRITEENABLE];\n\n        // The layout of the view will be ignored by the backend anyway\n        // if it has been transitioned to the feedback loop layout.\n        readOnly &= !m_state.depthStencil->GetCommonTexture()->HasBeenTransitionedToHazardLayout();\n\n        attachments.depth.view = m_state.depthStencil->GetDepthStencilView(!readOnly);\n      }\n    }\n\n    for (uint32_t i = 0u; i < m_state.renderTargets.size(); i++) {\n      if (!HasRenderTargetBound(i))\n        continue;\n\n      const D3D9CommonTexture* rt = m_state.renderTargets[i]->GetCommonTexture();\n      const DxvkImageCreateInfo& rtInfo = rt->GetImage()->info();\n      if (unlikely(rtInfo.extent.width != renderArea.width\n          || rtInfo.extent.height != renderArea.height\n          || rtInfo.sampleCount != sampleCount))\n        continue;\n\n      // Check if the render target is also bound as a texture for sampling.\n      // If that's the case, check whether we can skip binding the render target\n      // because writing to it is disabled anyway\n      // (using the color write mask or by the current pixel shader).\n      bool hasHazard = false;\n      for (uint32_t samplerIdx : bit::BitMask(m_textureSlotTracking.hazardRT)) {\n        D3D9Surface* rtSurf = m_state.renderTargets[i].ptr();\n\n        IDirect3DBaseTexture9* rtBase  = rtSurf->GetBaseTexture();\n        IDirect3DBaseTexture9* texBase = m_state.textures[samplerIdx];\n\n        if (likely(rtSurf->GetMipLevel() == 0 && rtBase == texBase)) {\n          hasHazard = true;\n          break;\n        }\n      }\n\n      const uint32_t rtBit = 1u << i;\n      const bool writtenTo = (psMasks.rtMask & rtBit) != 0\n          && m_state.renderStates[ColorWriteIndex(i)] != 0;\n\n      if (hasHazard && !writtenTo)\n        continue;\n\n      attachments.color[i].view = m_state.renderTargets[i]->GetRenderTargetView(srgb);\n    }\n\n    // Work out feedback loop layouts based on bound render targets\n    VkImageAspectFlags feedbackLoopAspects = 0u;\n\n    if (m_hazardLayout == VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT) {\n      if (m_textureSlotTracking.unresolvableHazardRT != 0)\n        feedbackLoopAspects |= VK_IMAGE_ASPECT_COLOR_BIT;\n      if (m_textureSlotTracking.unresolvableHazardDS != 0)\n        feedbackLoopAspects |= VK_IMAGE_ASPECT_DEPTH_BIT;\n    }\n\n    // Create and bind the framebuffer object to the context\n    EmitCs([\n      cAttachments         = std::move(attachments),\n      cFeedbackLoopAspects = feedbackLoopAspects\n    ] (DxvkContext* ctx) mutable {\n      ctx->bindRenderTargets(std::move(cAttachments), cFeedbackLoopAspects);\n    });\n  }\n\n\n  void D3D9DeviceEx::BindViewportAndScissor() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::ViewportScissor);\n\n    // D3D9's coordinate system has its origin in the bottom left,\n    // but the viewport coordinates are aligned to the top-left\n    // corner so we can get away with flipping the viewport.\n    const D3DVIEWPORT9& vp = m_state.viewport;\n\n    // Correctness Factor for 1/2 texel offset\n    constexpr float cf = 0.5f;\n\n    // How much to bias MinZ by to avoid a depth\n    // degenerate viewport.\n    // Tests show that the bias is only applied below minZ values of 0.5\n    float zBias;\n    if (vp.MinZ >= 0.5f) {\n      zBias = 0.0f;\n    } else {\n      zBias = 0.001f;\n    }\n\n    DxvkViewport state = { };\n    state.viewport = VkViewport{\n      float(vp.X)     + cf,    float(vp.Height + vp.Y) + cf,\n      float(vp.Width),        -float(vp.Height),\n      std::clamp(vp.MinZ,                            0.0f, 1.0f),\n      std::clamp(std::max(vp.MaxZ, vp.MinZ + zBias), 0.0f, 1.0f),\n    };\n\n    // Scissor rectangles. Vulkan does not provide an easy way\n    // to disable the scissor test, so we'll have to set scissor\n    // rects that are at least as large as the framebuffer.\n    bool enableScissorTest = m_state.renderStates[D3DRS_SCISSORTESTENABLE];\n\n    if (enableScissorTest) {\n      RECT sr = m_state.scissorRect;\n\n      VkOffset2D srPosA;\n      srPosA.x = std::max<int32_t>(0, sr.left);\n      srPosA.x = std::max<int32_t>(vp.X, srPosA.x);\n      srPosA.y = std::max<int32_t>(0, sr.top);\n      srPosA.y = std::max<int32_t>(vp.Y, srPosA.y);\n\n      VkOffset2D srPosB;\n      srPosB.x = std::max<int32_t>(srPosA.x, sr.right);\n      srPosB.x = std::min<int32_t>(vp.X + vp.Width, srPosB.x);\n      srPosB.y = std::max<int32_t>(srPosA.y, sr.bottom);\n      srPosB.y = std::min<int32_t>(vp.Y + vp.Height, srPosB.y);\n\n      VkExtent2D srSize;\n      srSize.width  = uint32_t(srPosB.x - srPosA.x);\n      srSize.height = uint32_t(srPosB.y - srPosA.y);\n\n      state.scissor = VkRect2D{ srPosA, srSize };\n    }\n    else {\n      state.scissor = VkRect2D{\n        VkOffset2D { int32_t(vp.X), int32_t(vp.Y) },\n        VkExtent2D { vp.Width,      vp.Height     }};\n    }\n\n    EmitCs([\n      cViewport = state\n    ] (DxvkContext* ctx) {\n      ctx->setViewports(1, &cViewport);\n    });\n  }\n\n\n  void D3D9DeviceEx::UpdateAlphaToCoverangeAndAlphaTest() {\n    if (likely(!m_isD3D8Compatible)) {\n      // ATOC is not supported by D3D8\n      bool alphaToCoverageEnabled = true;\n\n      // Check render states\n      // The AMD ATOC enable state or the Nvidia ATOC enable state with alpha test\n      // enabled (also supported by Intel) could potentially enable ATOC overall\n      const bool isAMDATOCEnabled = m_state.renderStates[D3DRS_POINTSIZE]      == uint32_t(D3D9Format::A2M1);\n      const bool isNVATOCEnabled  = m_state.renderStates[D3DRS_ADAPTIVETESS_Y] == uint32_t(D3D9Format::ATOC)\n                                      && m_state.renderStates[D3DRS_ALPHATESTENABLE] != 0;\n      const bool isAMD = m_adapter->GetVendorId() == uint32_t(DxvkGpuVendor::Amd);\n      alphaToCoverageEnabled &= (isAMD && isAMDATOCEnabled) || (!isAMD && isNVATOCEnabled);\n\n      // Check sample count of RT 0\n      const D3D9CommonTexture* rt0 = GetCommonTexture(m_state.renderTargets[0].ptr());\n      const bool isMultisampled = rt0 != nullptr && (\n        rt0->Desc()->MultiSample >= D3DMULTISAMPLE_2_SAMPLES\n        || (rt0->Desc()->MultiSample == D3DMULTISAMPLE_NONMASKABLE && rt0->Desc()->MultisampleQuality > 0)\n      );\n      alphaToCoverageEnabled &= isMultisampled;\n\n      if (m_atocEnabled != alphaToCoverageEnabled) {\n        m_dirty.set(D3D9DeviceDirtyFlag::MultiSampleState);\n        m_atocEnabled = alphaToCoverageEnabled;\n      }\n    }\n\n    // Update alpha test state\n    bool alphaTestEnabled = m_state.renderStates[D3DRS_ALPHATESTENABLE] && !m_atocEnabled;\n    if (m_alphaTestEnabled != alphaTestEnabled) {\n      m_dirty.set(D3D9DeviceDirtyFlag::AlphaTestState);\n      m_alphaTestEnabled = alphaTestEnabled;\n    }\n  }\n\n\n  void D3D9DeviceEx::BindMultiSampleState() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::MultiSampleState);\n\n    DxvkMultisampleState msState = { };\n    msState.setSampleMask(m_validSampleMask\n      ? uint16_t(m_state.renderStates[D3DRS_MULTISAMPLEMASK])\n      : uint16_t(0xffffu));\n    msState.setAlphaToCoverage(m_atocEnabled);\n\n    EmitCs([\n      cState = msState\n    ] (DxvkContext* ctx) {\n      ctx->setMultisampleState(cState);\n    });\n  }\n\n\n  void D3D9DeviceEx::BindBlendState() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::BlendState);\n\n    auto& state = m_state.renderStates;\n\n    DxvkBlendMode mode = { };\n    mode.setBlendEnable(state[D3DRS_ALPHABLENDENABLE]);\n\n    D3D9BlendState color = { };\n\n    color.Src = D3DBLEND(state[D3DRS_SRCBLEND]);\n    color.Dst = D3DBLEND(state[D3DRS_DESTBLEND]);\n    color.Op  = D3DBLENDOP(state[D3DRS_BLENDOP]);\n    FixupBlendState(color);\n\n    D3D9BlendState alpha = color;\n\n    if (state[D3DRS_SEPARATEALPHABLENDENABLE]) {\n      alpha.Src = D3DBLEND(state[D3DRS_SRCBLENDALPHA]);\n      alpha.Dst = D3DBLEND(state[D3DRS_DESTBLENDALPHA]);\n      alpha.Op  = D3DBLENDOP(state[D3DRS_BLENDOPALPHA]);\n      FixupBlendState(alpha);\n    }\n\n    mode.setColorOp(DecodeBlendFactor(color.Src, false),\n                    DecodeBlendFactor(color.Dst, false),\n                    DecodeBlendOp(color.Op));\n\n    mode.setAlphaOp(DecodeBlendFactor(alpha.Src, true),\n                    DecodeBlendFactor(alpha.Dst, true),\n                    DecodeBlendOp(alpha.Op));\n\n    uint16_t writeMasks = 0;\n\n    for (uint32_t i = 0; i < 4; i++)\n      writeMasks |= (state[ColorWriteIndex(i)] & 0xfu) << (4u * i);\n\n    EmitCs([\n      cMode       = mode,\n      cWriteMasks = writeMasks,\n      cAlphaMasks = m_rtSlotTracking.hasAlphaSwizzle\n    ](DxvkContext* ctx) {\n      for (uint32_t i = 0; i < 4; i++) {\n        DxvkBlendMode mode = cMode;\n        mode.setWriteMask(cWriteMasks >> (4u * i));\n\n        // Adjust the blend factor based on the render target alpha swizzle bit mask.\n        // Specific formats such as the XRGB ones require a ONE swizzle for alpha\n        // which cannot be directly applied with the image view of the attachment.\n        if (cAlphaMasks & (1 << i)) {\n          auto NormalizeFactor = [] (VkBlendFactor Factor) {\n            if (Factor == VK_BLEND_FACTOR_DST_ALPHA)\n              return VK_BLEND_FACTOR_ONE;\n            else if (Factor == VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA)\n              return VK_BLEND_FACTOR_ZERO;\n            return Factor;\n          };\n\n          mode.setColorOp(NormalizeFactor(mode.colorSrcFactor()),\n                          NormalizeFactor(mode.colorDstFactor()), mode.colorBlendOp());\n          mode.setAlphaOp(NormalizeFactor(mode.alphaSrcFactor()),\n                          NormalizeFactor(mode.alphaDstFactor()), mode.alphaBlendOp());\n        }\n\n        mode.normalize();\n\n        ctx->setBlendMode(i, mode);\n      }\n    });\n  }\n\n\n  void D3D9DeviceEx::BindBlendFactor() {\n    DxvkBlendConstants blendConstants;\n    DecodeD3DCOLOR(\n      D3DCOLOR(m_state.renderStates[D3DRS_BLENDFACTOR]),\n      reinterpret_cast<float*>(&blendConstants));\n\n    EmitCs([\n      cBlendConstants = blendConstants\n    ](DxvkContext* ctx) {\n      ctx->setBlendConstants(cBlendConstants);\n    });\n  }\n\n\n  void D3D9DeviceEx::BindDepthStencilState() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::DepthStencilState);\n\n    auto& rs = m_state.renderStates;\n\n    bool stencil            = rs[D3DRS_STENCILENABLE];\n    bool twoSidedStencil    = stencil && rs[D3DRS_TWOSIDEDSTENCILMODE];\n\n    DxvkDepthStencilState state = { };\n    state.setDepthTest(rs[D3DRS_ZENABLE]);\n    state.setDepthWrite(rs[D3DRS_ZWRITEENABLE]);\n    state.setStencilTest(stencil);\n    state.setDepthCompareOp(DecodeCompareOp(D3DCMPFUNC(rs[D3DRS_ZFUNC])));\n\n    DxvkStencilOp frontOp = { };\n\n    if (stencil) {\n      frontOp.setFailOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_STENCILFAIL])));\n      frontOp.setPassOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_STENCILPASS])));\n      frontOp.setDepthFailOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_STENCILZFAIL])));\n      frontOp.setCompareOp(DecodeCompareOp(D3DCMPFUNC(rs[D3DRS_STENCILFUNC])));\n      frontOp.setCompareMask(rs[D3DRS_STENCILMASK]);\n      frontOp.setWriteMask(rs[D3DRS_STENCILWRITEMASK]);\n    }\n\n    DxvkStencilOp backOp = frontOp;\n\n    if (twoSidedStencil) {\n      backOp.setFailOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_CCW_STENCILFAIL])));\n      backOp.setPassOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_CCW_STENCILPASS])));\n      backOp.setDepthFailOp(DecodeStencilOp(D3DSTENCILOP(rs[D3DRS_CCW_STENCILZFAIL])));\n      backOp.setCompareOp(DecodeCompareOp(D3DCMPFUNC(rs[D3DRS_CCW_STENCILFUNC])));\n      backOp.setCompareMask(rs[D3DRS_STENCILMASK]);\n      backOp.setWriteMask(rs[D3DRS_STENCILWRITEMASK]);\n    }\n\n    state.setStencilOpFront(frontOp);\n    state.setStencilOpBack(backOp);\n\n    EmitCs([\n      cState = state\n    ] (DxvkContext* ctx) mutable {\n      cState.normalize();\n\n      ctx->setDepthStencilState(cState);\n    });\n  }\n\n\n  void D3D9DeviceEx::BindRasterizerState() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::RasterizerState);\n\n    auto& rs = m_state.renderStates;\n\n    DxvkRasterizerState state = { };\n    state.setCullMode(DecodeCullMode(D3DCULL(rs[D3DRS_CULLMODE])));\n    state.setDepthClip(true);\n    state.setFrontFace(VK_FRONT_FACE_CLOCKWISE);\n    state.setPolygonMode(DecodeFillMode(D3DFILLMODE(rs[D3DRS_FILLMODE])));\n    state.setFlatShading(m_state.renderStates[D3DRS_SHADEMODE] == D3DSHADE_FLAT);\n    state.setSampleCount(m_state.renderStates[D3DRS_MULTISAMPLEANTIALIAS]\n      ? VkSampleCountFlags(0u)\n      : VkSampleCountFlags(VK_SAMPLE_COUNT_1_BIT));\n\n    EmitCs([\n      cState  = state\n    ](DxvkContext* ctx) {\n      ctx->setRasterizerState(cState);\n    });\n  }\n\n\n  void D3D9DeviceEx::BindDepthBias() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::DepthBias);\n\n    auto& rs = m_state.renderStates;\n\n    float depthBias            = bit::cast<float>(rs[D3DRS_DEPTHBIAS]) * m_depthBiasScale;\n    float slopeScaledDepthBias = bit::cast<float>(rs[D3DRS_SLOPESCALEDEPTHBIAS]);\n\n    DxvkDepthBias biases;\n    biases.depthBiasConstant = depthBias;\n    biases.depthBiasSlope    = slopeScaledDepthBias;\n    biases.depthBiasClamp    = 0.0f;\n\n    EmitCs([\n      cBiases = biases\n    ](DxvkContext* ctx) {\n      ctx->setDepthBias(cBiases);\n    });\n  }\n\n\n  uint32_t D3D9DeviceEx::GetAlphaTestPrecision() {\n    if (m_state.renderTargets[0] == nullptr)\n      return 0;\n\n    D3D9Format format = m_state.renderTargets[0]->GetCommonTexture()->Desc()->Format;\n\n    switch (format) {\n      case D3D9Format::A2B10G10R10:\n      case D3D9Format::A2R10G10B10:\n      case D3D9Format::A2W10V10U10:\n      case D3D9Format::A2B10G10R10_XR_BIAS:\n        return 0x2; /* 10 bit */\n\n      case D3D9Format::R16F:\n      case D3D9Format::G16R16F:\n      case D3D9Format::A16B16G16R16F:\n        return 0x7; /* 15 bit */\n\n      case D3D9Format::G16R16:\n      case D3D9Format::A16B16G16R16:\n      case D3D9Format::V16U16:\n      case D3D9Format::L16:\n      case D3D9Format::Q16W16V16U16:\n        return 0x8; /* 16 bit */\n\n      case D3D9Format::R32F:\n      case D3D9Format::G32R32F:\n      case D3D9Format::A32B32G32R32F:\n        return 0xF; /* float */\n\n      default:\n        return 0x0; /* 8 bit */\n    }\n  }\n\n\n  void D3D9DeviceEx::BindAlphaTestState() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::AlphaTestState);\n\n    auto& rs = m_state.renderStates;\n\n    VkCompareOp alphaOp = m_alphaTestEnabled\n      ? DecodeCompareOp(D3DCMPFUNC(rs[D3DRS_ALPHAFUNC]))\n      : VK_COMPARE_OP_ALWAYS;\n\n    uint32_t precision = alphaOp != VK_COMPARE_OP_ALWAYS\n      ? GetAlphaTestPrecision()\n      : 0u;\n\n    UpdateAlphaTestSpec(alphaOp, precision);\n  }\n\n\n  void D3D9DeviceEx::BindDepthStencilReference() {\n    auto& rs = m_state.renderStates;\n\n    uint32_t ref = uint32_t(rs[D3DRS_STENCILREF]) & 0xff;\n\n    EmitCs([cRef = ref] (DxvkContext* ctx) {\n      ctx->setStencilReference(cRef);\n    });\n  }\n\n\n  void D3D9DeviceEx::BindSampler(DWORD Sampler) {\n    auto samplerInfo = RemapStateSamplerShader(Sampler);\n\n    const uint32_t slot = computeResourceSlotId(\n      samplerInfo.first, DxsoBindingType::Image,\n      samplerInfo.second);\n\n    m_samplerBindCount++;\n\n    const D3D9CommonTexture* tex = GetCommonTexture(m_state.textures[Sampler]);\n    const bool srgb = m_state.samplerStates[Sampler][D3DSAMP_SRGBTEXTURE] & 0x1;\n\n    Rc<DxvkImageView> imageView;\n\n    if (tex && SamplerUsesBorderColor(Sampler))\n      imageView = tex->GetSampleView(srgb);\n\n    EmitCs([this,\n      cSlot       = slot,\n      cState      = D3D9SamplerInfo(m_state.samplerStates[Sampler]),\n      cIsCube     = tex && tex->IsCube(),\n      cIsMultiMip = tex && (tex->Desc()->MipLevels > 1u),\n      cIsDepth    = bool(m_textureSlotTracking.depth & (1u << Sampler)),\n      cView       = std::move(imageView),\n      cBindId     = m_samplerBindCount\n    ] (DxvkContext* ctx) {\n      DxvkSamplerKey key = { };\n\n      key.setFilter(\n        DecodeFilter(cState.minFilter),\n        DecodeFilter(cState.magFilter),\n        DecodeMipFilter(cState.mipFilter));\n\n      if (cIsCube) {\n        key.setAddressModes(\n          VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n          VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n          VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);\n\n        key.setLegacyCubeFilter(!m_d3d9Options.seamlessCubes);\n      } else {\n        key.setAddressModes(\n          DecodeAddressMode(cState.addressU),\n          DecodeAddressMode(cState.addressV),\n          DecodeAddressMode(cState.addressW));\n      }\n\n      key.setDepthCompare(cIsDepth, VK_COMPARE_OP_LESS_OR_EQUAL);\n\n      if (cState.mipFilter) {\n        uint32_t anisotropy = cState.maxAnisotropy;\n\n        // Anisotropic filtering doesn't make any sense with only one mip\n        if (cState.minFilter != D3DTEXF_ANISOTROPIC || !cIsMultiMip)\n          anisotropy = 0u;\n\n        // Forcing anisotropic filtering doesn't make any sense with only one mip\n        if (m_d3d9Options.samplerAnisotropy != -1 && cIsMultiMip && cState.minFilter > D3DTEXF_POINT)\n          anisotropy = m_d3d9Options.samplerAnisotropy;\n\n        key.setAniso(anisotropy);\n\n        float lodBias = cState.mipLodBias;\n        lodBias += m_d3d9Options.samplerLodBias;\n\n        if (m_d3d9Options.clampNegativeLodBias)\n          lodBias = std::max(lodBias, 0.0f);\n\n        key.setLodRange(float(cState.maxMipLevel), 16.0f, lodBias);\n      }\n\n      if (key.u.p.hasBorder) {\n        DecodeD3DCOLOR(cState.borderColor, key.borderColor.float32);\n\n        if (cView)\n          key.setViewProperties(cView->info().unpackSwizzle(), cView->info().format);\n      }\n\n      VkShaderStageFlags stage = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;\n      ctx->bindResourceSampler(stage, cSlot, m_dxvkDevice->createSampler(key));\n\n      // Let the main thread know about current sampler stats\n      uint64_t liveCount = m_dxvkDevice->getSamplerStats().liveCount;\n      m_lastSamplerStats.store(liveCount | (cBindId << SamplerCountBits), std::memory_order_relaxed);\n    });\n  }\n\n\n  void D3D9DeviceEx::BindTexture(DWORD StateSampler) {\n    auto shaderSampler = RemapStateSamplerShader(StateSampler);\n\n    uint32_t slot = computeResourceSlotId(shaderSampler.first,\n      DxsoBindingType::Image, uint32_t(shaderSampler.second));\n\n    const bool srgb =\n      m_state.samplerStates[StateSampler][D3DSAMP_SRGBTEXTURE] & 0x1;\n\n    D3D9CommonTexture* commonTex =\n      GetCommonTexture(m_state.textures[StateSampler]);\n\n    Rc<DxvkImageView> imageView = commonTex->GetSampleView(srgb);\n\n    EmitCs([\n      cSlot = slot,\n      cImageView = std::move(imageView)\n    ](DxvkContext* ctx) mutable {\n      VkShaderStageFlags stage = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;\n      ctx->bindResourceImageView(stage, cSlot, std::move(cImageView));\n    });\n  }\n\n\n  void D3D9DeviceEx::UnbindTextures(uint32_t mask) {\n    EmitCs([\n      cMask = mask\n    ](DxvkContext* ctx) {\n      for (uint32_t i : bit::BitMask(cMask)) {\n        auto shaderSampler = RemapStateSamplerShader(i);\n\n        uint32_t slot = computeResourceSlotId(shaderSampler.first,\n          DxsoBindingType::Image, uint32_t(shaderSampler.second));\n\n        VkShaderStageFlags stage = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;\n        ctx->bindResourceImageView(stage, slot, nullptr);\n      }\n    });\n  }\n\n\n  void D3D9DeviceEx::UndirtySamplers(uint32_t mask) {\n    EnsureSamplerLimit();\n\n    for (uint32_t i : bit::BitMask(mask))\n      BindSampler(i);\n\n    m_textureSlotTracking.samplerStateDirty &= ~mask;\n  }\n\n\n  void D3D9DeviceEx::UndirtyTextures(uint32_t usedMask) {\n    const uint32_t activeMask   = usedMask &  (m_textureSlotTracking.bound & ~m_textureSlotTracking.mismatchingTextureType);\n    const uint32_t inactiveMask = usedMask & (~m_textureSlotTracking.bound | m_textureSlotTracking.mismatchingTextureType);\n\n    for (uint32_t i : bit::BitMask(activeMask))\n      BindTexture(i);\n\n    if (inactiveMask)\n      UnbindTextures(inactiveMask);\n\n    m_textureSlotTracking.textureDirty &= ~usedMask;\n  }\n\n  void D3D9DeviceEx::MarkTextureBindingDirty(IDirect3DBaseTexture9* texture) {\n    D3D9DeviceLock lock = LockDevice();\n\n    for (uint32_t i : bit::BitMask(m_textureSlotTracking.bound)) {\n      if (m_state.textures[i] == texture)\n        m_textureSlotTracking.textureDirty |= 1u << i;\n    }\n  }\n\n\n  bool D3D9DeviceEx::SamplerUsesBorderColor(DWORD Sampler) const {\n    const auto& sampler = m_state.samplerStates[Sampler];\n\n    return sampler[D3DSAMP_ADDRESSU] == D3DTADDRESS_BORDER\n        || sampler[D3DSAMP_ADDRESSV] == D3DTADDRESS_BORDER\n        || sampler[D3DSAMP_ADDRESSW] == D3DTADDRESS_BORDER;\n  }\n\n\n  D3D9DrawInfo D3D9DeviceEx::GenerateDrawInfo(\n          D3DPRIMITIVETYPE PrimitiveType,\n          UINT             PrimitiveCount,\n          UINT             InstanceCount) {\n    D3D9DrawInfo drawInfo;\n    drawInfo.vertexCount = GetVertexCount(PrimitiveType, PrimitiveCount);\n    drawInfo.instanceCount = (m_iaState.streamsInstanced & m_iaState.streamsUsed)\n      ? InstanceCount\n      : 1u;\n    return drawInfo;\n  }\n\n\n  uint32_t D3D9DeviceEx::GetInstanceCount() const {\n    return std::max(m_state.streamFreq[0] & 0x7FFFFFu, 1u);\n  }\n\n\n  void D3D9DeviceEx::PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadVBOs, bool UploadIBO) {\n    if (unlikely(m_textureSlotTracking.unresolvableHazardRT != 0 || m_textureSlotTracking.unresolvableHazardDS != 0))\n      EmitFeedbackLoopBarriers();\n\n    if (likely(UploadVBOs)) {\n      const uint32_t usedBuffersMask = m_state.vertexDecl != nullptr ? m_state.vertexDecl->GetStreamMask() : ~0u;\n      const uint32_t buffersToUpload = m_vbSlotTracking.needsUpload & usedBuffersMask;\n      for (uint32_t bufferIdx : bit::BitMask(buffersToUpload)) {\n        auto* vbo = GetCommonBuffer(m_state.vertexBuffers[bufferIdx].vertexBuffer);\n        if (likely(vbo != nullptr && vbo->NeedsUpload()))\n          FlushBuffer(vbo);\n      }\n      m_vbSlotTracking.needsUpload &= ~buffersToUpload;\n    }\n\n    const uint32_t usedSamplerMask = PSShaderMasks().samplerMask | VSShaderMasks().samplerMask;\n    const uint32_t usedTextureMask = m_textureSlotTracking.bound & usedSamplerMask;\n\n    const uint32_t texturesToUpload = m_textureSlotTracking.needsUpload & usedTextureMask;\n    if (unlikely(texturesToUpload != 0))\n      UploadManagedTextures(texturesToUpload);\n\n    const uint32_t texturesToGen = m_textureSlotTracking.needsMipGen & usedTextureMask;\n    if (unlikely(texturesToGen != 0))\n      GenerateTextureMips(texturesToGen);\n\n    auto* ibo = GetCommonBuffer(m_state.indices);\n    if (unlikely(UploadIBO && ibo != nullptr && ibo->NeedsUpload()))\n      FlushBuffer(ibo);\n\n    UpdateFog();\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::Framebuffer)))\n      BindFramebuffer();\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::ViewportScissor)))\n      BindViewportAndScissor();\n\n    const uint32_t activeDirtySamplers = m_textureSlotTracking.samplerStateDirty & usedTextureMask;\n    if (unlikely(activeDirtySamplers))\n      UndirtySamplers(activeDirtySamplers);\n\n    const uint32_t usedDirtyTextures = m_textureSlotTracking.textureDirty & usedSamplerMask;\n    if (likely(usedDirtyTextures))\n      UndirtyTextures(usedDirtyTextures);\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::BlendState)))\n      BindBlendState();\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::DepthStencilState)))\n      BindDepthStencilState();\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::RasterizerState)))\n      BindRasterizerState();\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::DepthBias)))\n      BindDepthBias();\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::MultiSampleState)))\n      BindMultiSampleState();\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::AlphaTestState)))\n      BindAlphaTestState();\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::ClipPlanes)))\n      UpdateClipPlanes();\n\n    UpdatePointMode(PrimitiveType == D3DPT_POINTLIST);\n\n    if (likely(UseProgrammableVS())) {\n      UploadConstants<D3D9ShaderType::VertexShader>();\n\n      if (likely(!CanSWVP())) {\n        UpdateVertexBoolSpec(\n          m_state.vsConsts->bConsts[0] &\n          m_consts[uint32_t(D3D9ShaderType::VertexShader)].meta.boolConstantMask);\n      } else\n        UpdateVertexBoolSpec(0);\n    }\n    else {\n      UpdateVertexBoolSpec(0);\n      UpdateFixedFunctionVS();\n    }\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::InputLayout)))\n      BindInputLayout();\n\n    uint32_t projected = m_textureSlotTracking.projected;\n    if (likely(UseProgrammablePS())) {\n      UploadConstants<D3D9ShaderType::PixelShader>();\n\n      const uint32_t psTextureMask = usedTextureMask & ((1u << caps::MaxTexturesPS) - 1u);\n      const uint32_t fetch4        = m_textureSlotTracking.fetch4    & psTextureMask;\n      uint32_t textureTypes        = m_textureSlotTracking.textureType;\n\n      const auto& programInfo = GetCommonShader(m_state.pixelShader)->GetInfo();\n      const bool useProgrammableVS = UseProgrammableVS();\n\n      // Fixed function shaders use the projected spec constant too.\n      if (likely(useProgrammableVS && (programInfo.majorVersion() > 2 || programInfo.minorVersion() > 3))) {\n        projected = 0u;\n      } else if (useProgrammableVS) {\n        // Programmable shaders can only sample textures in SM3 which doesn't use the projected state anymore.\n        // So we can restrict it to the ones that the pixel shader uses.\n        projected &= psTextureMask;\n      }\n\n      if (likely(programInfo.majorVersion() >= 2 && !m_d3d9Options.forceSamplerTypeSpecConstants)) {\n        // SM2 and up need to declare the sampler type in the shader.\n        textureTypes = 0u;\n      }\n\n      UpdatePixelShaderSamplerSpec(textureTypes, fetch4);\n\n      UpdatePixelBoolSpec(\n        m_state.psConsts->bConsts[0] &\n        m_consts[uint32_t(D3D9ShaderType::PixelShader)].meta.boolConstantMask);\n    }\n    else {\n      // Fixed function shaders use the projected spec constant too.\n      if (likely(UseProgrammableVS())) {\n        const uint32_t psTextureMask = usedTextureMask & ((1u << 8u) - 1u);\n        projected &= psTextureMask;\n      }\n\n      UpdatePixelBoolSpec(0);\n      UpdatePixelShaderSamplerSpec(m_textureSlotTracking.textureType, 0u);\n\n      UpdateFixedFunctionPS();\n    }\n\n    const uint32_t nullTextureMask = usedSamplerMask & ~usedTextureMask;\n    const uint32_t depthTextureMask = m_textureSlotTracking.depth & usedTextureMask;\n    const uint32_t drefClampMask = m_textureSlotTracking.drefClamp & depthTextureMask;\n    UpdateCommonSamplerSpec(nullTextureMask, depthTextureMask, drefClampMask, projected);\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::SharedPixelShaderData))) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::SharedPixelShaderData);\n\n      auto mapPtr = m_psShared.AllocSlice();\n      D3D9SharedPS* data = reinterpret_cast<D3D9SharedPS*>(mapPtr);\n\n      for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n        DecodeD3DCOLOR(D3DCOLOR(m_state.textureStages[i][DXVK_TSS_CONSTANT]), data->Stages[i].Constant);\n\n        // Flip major-ness so we can get away with a nice easy\n        // dot in the shader without complex access\n        data->Stages[i].BumpEnvMat[0][0] = bit::cast<float>(m_state.textureStages[i][DXVK_TSS_BUMPENVMAT00]);\n        data->Stages[i].BumpEnvMat[1][0] = bit::cast<float>(m_state.textureStages[i][DXVK_TSS_BUMPENVMAT01]);\n        data->Stages[i].BumpEnvMat[0][1] = bit::cast<float>(m_state.textureStages[i][DXVK_TSS_BUMPENVMAT10]);\n        data->Stages[i].BumpEnvMat[1][1] = bit::cast<float>(m_state.textureStages[i][DXVK_TSS_BUMPENVMAT11]);\n\n        data->Stages[i].BumpEnvLScale    = bit::cast<float>(m_state.textureStages[i][DXVK_TSS_BUMPENVLSCALE]);\n        data->Stages[i].BumpEnvLOffset   = bit::cast<float>(m_state.textureStages[i][DXVK_TSS_BUMPENVLOFFSET]);\n      }\n    }\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::DepthBounds))) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::DepthBounds);\n\n      DxvkDepthBounds db = { };\n      db.minDepthBounds = 0.0f;\n      db.maxDepthBounds = 1.0f;\n\n      if (m_nvdbEnabled) {\n        db.minDepthBounds = std::clamp(bit::cast<float>(m_state.renderStates[D3DRS_ADAPTIVETESS_Z]), 0.0f, 1.0f);\n        db.maxDepthBounds = std::clamp(bit::cast<float>(m_state.renderStates[D3DRS_ADAPTIVETESS_W]), 0.0f, 1.0f);\n\n        if (db.maxDepthBounds < db.minDepthBounds) {\n          db.minDepthBounds = 0.0f;\n          db.maxDepthBounds = 1.0f;\n        }\n      }\n\n      EmitCs([\n        cDepthBounds = db\n      ] (DxvkContext* ctx) {\n        ctx->setDepthBounds(cDepthBounds);\n      });\n    }\n\n    BindSpecConstants();\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::VertexBuffers) && UploadVBOs)) {\n      for (uint32_t i = 0; i < caps::MaxStreams; i++) {\n        const D3D9VBO& vbo = m_state.vertexBuffers[i];\n        BindVertexBuffer(i, vbo.vertexBuffer.ptr(), vbo.offset, vbo.stride);\n      }\n      m_dirty.clr(D3D9DeviceDirtyFlag::VertexBuffers);\n    }\n\n    if (unlikely(m_dirty.test(D3D9DeviceDirtyFlag::IndexBuffer) && UploadIBO)) {\n      BindIndices();\n      m_dirty.clr(D3D9DeviceDirtyFlag::IndexBuffer);\n    }\n  }\n\n\n  void D3D9DeviceEx::EnsureSamplerLimit() {\n    constexpr uint32_t MaxSamplerCount = DxvkSamplerPool::MaxSamplerCount - SamplerCount;\n\n    // Maximum possible number of live samplers we can have\n    // since last reading back from the CS thread.\n    if (likely(m_lastSamplerLiveCount + m_samplerBindCount - m_lastSamplerBindCount <= MaxSamplerCount))\n      return;\n\n    // Update current stats from CS thread and check again. We\n    // don't want to do this every time due to potential cache\n    // thrashing.\n    uint64_t lastStats = m_lastSamplerStats.load(std::memory_order_relaxed);\n    m_lastSamplerLiveCount = lastStats & SamplerCountMask;\n    m_lastSamplerBindCount = lastStats >> SamplerCountBits;\n\n    if (likely(m_lastSamplerLiveCount + m_samplerBindCount - m_lastSamplerBindCount <= MaxSamplerCount))\n      return;\n\n    // If we have a large number of sampler updates in flight, wait for\n    // the CS thread to complete some and re-evaluate. We should not hit\n    // this path under normal gameplay conditions.\n    ConsiderFlush(GpuFlushType::ImplicitSynchronization);\n\n    uint64_t sequenceNumber = m_csThread.lastSequenceNumber();\n\n    while (++sequenceNumber <= GetCurrentSequenceNumber()) {\n      SynchronizeCsThread(sequenceNumber);\n\n      uint64_t lastStats = m_lastSamplerStats.load(std::memory_order_relaxed);\n      m_lastSamplerLiveCount = lastStats & SamplerCountMask;\n      m_lastSamplerBindCount = lastStats >> SamplerCountBits;\n\n      if (m_lastSamplerLiveCount + m_samplerBindCount - m_lastSamplerBindCount <= MaxSamplerCount)\n        return;\n    }\n\n    // If we end up here, the game somehow managed to queue up so\n    // many samplers that we need to wait for the GPU to free some.\n    // We should absolutely never hit this path in the real world.\n    Logger::warn(\"Sampler pool exhausted, synchronizing with GPU.\");\n\n    Flush();\n    SynchronizeCsThread(DxvkCsThread::SynchronizeAll);\n\n    uint64_t submissionId = m_submissionFence->value();\n\n    while (++submissionId <= m_submissionId) {\n      m_submissionFence->wait(submissionId);\n\n      // Need to manually update sampler stats here since we\n      // might otherwise hit this path again the next time\n      auto samplerStats = m_dxvkDevice->getSamplerStats();\n      m_lastSamplerStats = samplerStats.liveCount | (m_samplerBindCount << SamplerCountBits);\n\n      if (samplerStats.liveCount <= MaxSamplerCount)\n        return;\n    }\n\n    // If we end up *here*, good luck.\n    Logger::warn(\"Sampler pool exhausted, cannot create any new samplers.\");\n  }\n\n\n  template <D3D9ShaderType ShaderStage>\n  void D3D9DeviceEx::BindShader(\n  const D3D9CommonShader*                 pShaderModule) {\n    auto shader = pShaderModule->GetShader();\n\n    if (unlikely(shader->needsCompile()))\n      m_dxvkDevice->requestCompileShader(shader);\n\n    EmitCs([\n      cShader = std::move(shader)\n    ] (DxvkContext* ctx) mutable {\n      constexpr VkShaderStageFlagBits stage = GetShaderStage(ShaderStage);\n      ctx->bindShader<stage>(std::move(cShader));\n    });\n  }\n\n\n  template <D3D9ShaderType ShaderStage>\n  void D3D9DeviceEx::BindFFUbershader() {\n    if (ShaderStage == D3D9ShaderType::VertexShader) {\n      EmitCs([\n       &cShaders = m_ffModules\n      ](DxvkContext* ctx) {\n        auto shader = cShaders.GetVSUbershaderModule();\n        ctx->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(shader.GetShader());\n      });\n    } else {\n      EmitCs([\n       &cShaders = m_ffModules\n      ](DxvkContext* ctx) {\n        auto shader = cShaders.GetFSUbershaderModule();\n        ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(shader.GetShader());\n      });\n    }\n  }\n\n\n  void D3D9DeviceEx::BindInputLayout() {\n    m_dirty.clr(D3D9DeviceDirtyFlag::InputLayout);\n\n    if (m_state.vertexDecl == nullptr) {\n      EmitCs([&cIaState = m_iaState] (DxvkContext* ctx) {\n        cIaState.streamsUsed = 0;\n        ctx->setInputLayout(0, nullptr, 0, nullptr);\n      });\n    }\n    else {\n      std::array<uint32_t, caps::MaxStreams> streamFreq;\n\n      for (uint32_t i = 0; i < caps::MaxStreams; i++)\n        streamFreq[i] = m_state.streamFreq[i];\n\n      Com<D3D9VertexDecl,   false> vertexDecl = m_state.vertexDecl;\n      Com<D3D9VertexShader, false> vertexShader;\n\n      if (UseProgrammableVS())\n        vertexShader = m_state.vertexShader;\n\n      EmitCs([\n        &cIaState         = m_iaState,\n        cVertexDecl       = std::move(vertexDecl),\n        cVertexShader     = std::move(vertexShader),\n        cStreamsInstanced = m_vbSlotTracking.instanced,\n        cStreamFreq       = streamFreq\n      ] (DxvkContext* ctx) {\n        cIaState.streamsInstanced = cStreamsInstanced;\n        cIaState.streamsUsed      = 0;\n\n        const auto& elements = cVertexDecl->GetElements();\n\n        std::array<DxvkVertexInput, 2 * caps::InputRegisterCount> attrList = { };\n        std::array<DxvkVertexInput, 2 * caps::InputRegisterCount> bindList = { };\n        std::array<uint32_t, 2 * caps::InputRegisterCount> vertexSizes = { };\n\n        uint32_t attrMask = 0;\n        uint32_t bindMask = 0;\n\n        const auto& isgn = cVertexShader != nullptr\n          ? GetCommonShader(cVertexShader)->GetIsgn()\n          : GetFixedFunctionIsgn();\n\n        for (uint32_t i = 0; i < isgn.elemCount; i++) {\n          const auto& decl = isgn.elems[i];\n\n          DxvkVertexAttribute attrib = { };\n          attrib.location = i;\n          attrib.binding  = NullStreamIdx;\n          attrib.format   = VK_FORMAT_R32G32B32A32_SFLOAT;\n          attrib.offset   = 0;\n\n          for (const auto& element : elements) {\n            DxsoSemantic elementSemantic = { static_cast<DxsoUsage>(element.Usage), element.UsageIndex };\n            if (elementSemantic.usage == DxsoUsage::PositionT)\n              elementSemantic.usage = DxsoUsage::Position;\n\n            if (elementSemantic == decl.semantic) {\n              attrib.binding = uint32_t(element.Stream);\n              attrib.format  = DecodeDecltype(D3DDECLTYPE(element.Type));\n              attrib.offset  = element.Offset;\n\n              cIaState.streamsUsed |= 1u << attrib.binding;\n              break;\n            }\n          }\n\n          attrList[i] = DxvkVertexInput(attrib);\n\n          vertexSizes[attrib.binding] = std::max(vertexSizes[attrib.binding],\n            uint32_t(attrib.offset + lookupFormatInfo(attrib.format)->elementSize));\n\n          DxvkVertexBinding binding = { };\n          binding.binding = attrib.binding;\n          binding.extent = vertexSizes[attrib.binding];\n\n          uint32_t instanceData = cStreamFreq[binding.binding % caps::MaxStreams];\n          if (instanceData & D3DSTREAMSOURCE_INSTANCEDATA) {\n            binding.divisor = instanceData & 0x7FFFFF; // Remove instance packed-in flags in the data.\n            binding.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;\n          }\n          else {\n            binding.divisor = 0u;\n            binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n          }\n\n          bindList[binding.binding] = DxvkVertexInput(binding);\n\n          attrMask |= 1u << i;\n          bindMask |= 1u << binding.binding;\n        }\n\n        // Compact the attribute and binding lists to filter\n        // out attributes and bindings not used by the shader\n        uint32_t attrCount = CompactSparseList(attrList.data(), attrMask);\n        uint32_t bindCount = CompactSparseList(bindList.data(), bindMask);\n\n        ctx->setInputLayout(\n          attrCount, attrList.data(),\n          bindCount, bindList.data());\n      });\n    }\n  }\n\n\n  void D3D9DeviceEx::BindVertexBuffer(\n        UINT                              Slot,\n        D3D9VertexBuffer*                 pBuffer,\n        UINT                              Offset,\n        UINT                              Stride) {\n    EmitCs([\n      cSlotId       = Slot,\n      cBufferSlice  = pBuffer != nullptr ?\n          pBuffer->GetCommonBuffer()->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>(Offset)\n        : DxvkBufferSlice(),\n      cStride       = pBuffer != nullptr ? Stride : 0\n    ] (DxvkContext* ctx) mutable {\n      ctx->bindVertexBuffer(cSlotId, std::move(cBufferSlice), cStride);\n    });\n  }\n\n  void D3D9DeviceEx::BindIndices() {\n    D3D9CommonBuffer* buffer = GetCommonBuffer(m_state.indices);\n\n    D3D9Format format = buffer != nullptr\n                      ? buffer->Desc()->Format\n                      : D3D9Format::INDEX32;\n\n    const VkIndexType indexType = DecodeIndexType(format);\n\n    EmitCs([\n      cBufferSlice = buffer != nullptr ? buffer->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>() : DxvkBufferSlice(),\n      cIndexType   = indexType\n    ](DxvkContext* ctx) mutable {\n      ctx->bindIndexBuffer(std::move(cBufferSlice), cIndexType);\n    });\n  }\n\n\n  void D3D9DeviceEx::Begin(D3D9Query* pQuery) {\n    D3D9DeviceLock lock = LockDevice();\n\n    EmitCs([cQuery = Com<D3D9Query, false>(pQuery)](DxvkContext* ctx) {\n      cQuery->Begin(ctx);\n    });\n  }\n\n\n  void D3D9DeviceEx::End(D3D9Query* pQuery) {\n    D3D9DeviceLock lock = LockDevice();\n\n    EmitCs([cQuery = Com<D3D9Query, false>(pQuery)](DxvkContext* ctx) {\n      cQuery->End(ctx);\n    });\n\n    pQuery->NotifyEnd();\n    if (unlikely(pQuery->IsEvent())) {\n      pQuery->IsStalling()\n        ? Flush()\n        : ConsiderFlush(GpuFlushType::ImplicitStrongHint);\n    } else if (pQuery->IsStalling()) {\n      ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n    }\n  }\n\n\n  void D3D9DeviceEx::SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) {\n    m_state.vsConsts->bConsts[idx] &= ~mask;\n    m_state.vsConsts->bConsts[idx] |= bits & mask;\n\n    m_consts[uint32_t(D3D9ShaderType::VertexShader)].dirty = true;\n  }\n\n\n  void D3D9DeviceEx::SetPixelBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) {\n    m_state.psConsts->bConsts[idx] &= ~mask;\n    m_state.psConsts->bConsts[idx] |= bits & mask;\n\n    m_consts[uint32_t(D3D9ShaderType::PixelShader)].dirty = true;\n  }\n\n\n  HRESULT D3D9DeviceEx::CreateShaderModule(\n        D3D9CommonShader*     pShaderModule,\n        uint32_t*             pLength,\n        VkShaderStageFlagBits ShaderStage,\n  const DWORD*                pShaderBytecode,\n  const DxsoModuleInfo*       pModuleInfo) {\n    try {\n      m_shaderModules->GetShaderModule(this, pShaderModule,\n        pLength, ShaderStage, pModuleInfo, pShaderBytecode);\n\n      return D3D_OK;\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_INVALIDCALL;\n    }\n  }\n\n\n  template <\n    D3D9ShaderType   ShaderType,\n    D3D9ConstantType ConstantType,\n    typename         T>\n    HRESULT D3D9DeviceEx::SetShaderConstants(\n            UINT  StartRegister,\n      const T*    pConstantData,\n            UINT  Count) {\n    const     uint32_t regCountHardware = DetermineHardwareRegCount<ShaderType, ConstantType>();\n    constexpr uint32_t regCountSoftware = DetermineSoftwareRegCount<ShaderType, ConstantType>();\n\n    // Error out in case of StartRegister + Count overflow\n    if (unlikely(StartRegister > std::numeric_limits<uint32_t>::max() - Count))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(StartRegister + Count > regCountSoftware))\n      return D3DERR_INVALIDCALL;\n\n    Count = UINT(\n      std::max<INT>(\n        std::clamp<INT>(Count + StartRegister, 0, regCountHardware) - INT(StartRegister),\n        0));\n\n    if (unlikely(Count == 0))\n      return D3D_OK;\n\n    if (unlikely(pConstantData == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ShouldRecord()))\n      return m_recorder->SetShaderConstants<ShaderType, ConstantType, T>(\n        StartRegister,\n        pConstantData,\n        Count);\n\n    D3D9ConstantSets& constSet = m_consts[uint32_t(ShaderType)];\n\n    if constexpr (ConstantType == D3D9ConstantType::Float) {\n      constSet.maxChangedConstF = std::max(constSet.maxChangedConstF, StartRegister + Count);\n    } else if constexpr (ConstantType == D3D9ConstantType::Int && ShaderType == D3D9ShaderType::VertexShader) {\n      // We only track changed int constants for vertex shaders (and it's only used when the device uses the SWVP UBO layout).\n      // Pixel shaders (and vertex shaders on HWVP devices) always copy all int constants into the same UBO as the float constants\n      constSet.maxChangedConstI = std::max(constSet.maxChangedConstI, StartRegister + Count);\n    } else  if constexpr (ConstantType == D3D9ConstantType::Bool && ShaderType == D3D9ShaderType::VertexShader) {\n      // We only track changed bool constants for vertex shaders (and it's only used when the device uses the SWVP UBO layout).\n      // Pixel shaders (and vertex shaders on HWVP devices) always put all bool constants into a single spec constant.\n      constSet.maxChangedConstB = std::max(constSet.maxChangedConstB, StartRegister + Count);\n    }\n\n    if constexpr (ConstantType != D3D9ConstantType::Bool) {\n      uint32_t maxCount = ConstantType == D3D9ConstantType::Float\n        ? constSet.meta.maxConstIndexF\n        : constSet.meta.maxConstIndexI;\n\n      constSet.dirty |= StartRegister < maxCount;\n    } else if constexpr (ShaderType == D3D9ShaderType::VertexShader) {\n      if (unlikely(CanSWVP())) {\n        constSet.dirty |= StartRegister < constSet.meta.maxConstIndexB;\n      }\n    }\n\n    UpdateStateConstants<ShaderType, ConstantType, T>(\n      &m_state,\n      StartRegister,\n      pConstantData,\n      Count,\n      m_d3d9Options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled);\n\n    return D3D_OK;\n  }\n\n\n  D3D9FFShaderKeyVS D3D9DeviceEx::BuildFFKeyVS(D3D9FF_VertexBlendMode vertexBlendMode, bool indexedVertexBlend) const {\n    D3D9FFShaderKeyVS key;\n    key.Data.Contents.VertexHasPositionT = m_state.vertexDecl != nullptr && m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasPositionT);\n    key.Data.Contents.VertexHasColor0    = m_state.vertexDecl != nullptr && m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasColor0);\n    key.Data.Contents.VertexHasColor1    = m_state.vertexDecl != nullptr && m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasColor1);\n    key.Data.Contents.VertexHasPointSize = m_state.vertexDecl != nullptr && m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasPointSize);\n    key.Data.Contents.VertexHasFog       = m_state.vertexDecl != nullptr && m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasFog);\n\n    bool lighting    = m_state.renderStates[D3DRS_LIGHTING] != 0 && !key.Data.Contents.VertexHasPositionT;\n    bool colorVertex = m_state.renderStates[D3DRS_COLORVERTEX] != 0;\n    uint32_t mask    = (lighting && colorVertex)\n                     ? (key.Data.Contents.VertexHasColor0 ? D3DMCS_COLOR1 : D3DMCS_MATERIAL)\n                     | (key.Data.Contents.VertexHasColor1 ? D3DMCS_COLOR2 : D3DMCS_MATERIAL)\n                     : 0;\n\n    key.Data.Contents.UseLighting      = lighting;\n    key.Data.Contents.NormalizeNormals = m_state.renderStates[D3DRS_NORMALIZENORMALS];\n    key.Data.Contents.LocalViewer      = m_state.renderStates[D3DRS_LOCALVIEWER] && lighting;\n\n    key.Data.Contents.RangeFog         = m_state.renderStates[D3DRS_RANGEFOGENABLE];\n\n    key.Data.Contents.DiffuseSource    = m_state.renderStates[D3DRS_DIFFUSEMATERIALSOURCE]  & mask;\n    key.Data.Contents.AmbientSource    = m_state.renderStates[D3DRS_AMBIENTMATERIALSOURCE]  & mask;\n    key.Data.Contents.SpecularSource   = m_state.renderStates[D3DRS_SPECULARMATERIALSOURCE] & mask;\n    key.Data.Contents.EmissiveSource   = m_state.renderStates[D3DRS_EMISSIVEMATERIALSOURCE] & mask;\n\n    key.Data.Contents.SpecularEnabled  = m_state.renderStates[D3DRS_SPECULARENABLE];\n\n    uint32_t lightCount = 0;\n\n    if (key.Data.Contents.UseLighting) {\n      for (uint32_t i = 0; i < caps::MaxEnabledLights; i++) {\n        if (m_state.enabledLightIndices[i] != std::numeric_limits<uint32_t>::max())\n          lightCount++;\n      }\n    }\n\n    key.Data.Contents.LightCount = lightCount;\n\n    for (uint32_t i = 0; i < caps::MaxTextureBlendStages; i++) {\n      uint32_t transformFlags = m_state.textureStages[i][DXVK_TSS_TEXTURETRANSFORMFLAGS] & ~(D3DTTFF_PROJECTED);\n      uint32_t index          = m_state.textureStages[i][DXVK_TSS_TEXCOORDINDEX];\n      uint32_t indexFlags     = (index & TCIMask) >> TCIOffset;\n\n      transformFlags &= 0b111;\n      index          &= 0b111;\n\n      key.Data.Contents.TransformFlags  |= transformFlags << (i * 3);\n      key.Data.Contents.TexcoordFlags   |= indexFlags     << (i * 3);\n      key.Data.Contents.TexcoordIndices |= index          << (i * 3);\n    }\n\n    key.Data.Contents.VertexTexcoordDeclMask = m_state.vertexDecl != nullptr ? m_state.vertexDecl->GetTexcoordMask() : 0;\n\n    key.Data.Contents.VertexBlendMode  = uint32_t(vertexBlendMode);\n\n    if (vertexBlendMode == D3D9FF_VertexBlendMode_Normal) {\n      key.Data.Contents.VertexBlendIndexed = indexedVertexBlend;\n      key.Data.Contents.VertexBlendCount   = m_state.renderStates[D3DRS_VERTEXBLEND] & 0xff;\n    }\n\n    key.Data.Contents.VertexClipping = m_state.renderStates[D3DRS_CLIPPLANEENABLE] != 0;\n\n    return key;\n  }\n\n\n   void D3D9DeviceEx::UpdateFixedFunctionVS() {\n    bool hasPositionT    = m_state.vertexDecl != nullptr && m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasPositionT);\n    bool hasBlendWeight  = m_state.vertexDecl != nullptr && m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasBlendWeight);\n    bool hasBlendIndices = m_state.vertexDecl != nullptr && m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasBlendIndices);\n\n    bool indexedVertexBlend = hasBlendIndices && m_state.renderStates[D3DRS_INDEXEDVERTEXBLENDENABLE];\n    D3D9FF_VertexBlendMode vertexBlendMode = D3D9FF_VertexBlendMode_Disabled;\n\n    if (m_state.renderStates[D3DRS_VERTEXBLEND] != D3DVBF_DISABLE && !hasPositionT) {\n      vertexBlendMode = m_state.renderStates[D3DRS_VERTEXBLEND] == D3DVBF_TWEENING\n        ? D3D9FF_VertexBlendMode_Tween\n        : D3D9FF_VertexBlendMode_Normal;\n\n      if (m_state.renderStates[D3DRS_VERTEXBLEND] != D3DVBF_0WEIGHTS) {\n        if (!hasBlendWeight)\n          vertexBlendMode = D3D9FF_VertexBlendMode_Disabled;\n      }\n      else if (!indexedVertexBlend)\n        vertexBlendMode = D3D9FF_VertexBlendMode_Disabled;\n    }\n\n    // Shader...\n    const bool useUbershader = m_d3d9Options.ffUbershaderVS;\n\n    if (useUbershader && m_dirty.test(D3D9DeviceDirtyFlag::FFVertexShader)) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::FFVertexShader);\n      m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n    } else if (m_dirty.test(D3D9DeviceDirtyFlag::FFVertexShader)) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::FFVertexShader);\n\n      D3D9FFShaderKeyVS key = BuildFFKeyVS(vertexBlendMode, indexedVertexBlend);\n\n      EmitCs([\n        this,\n        cKey     = key,\n       &cShaders = m_ffModules\n      ](DxvkContext* ctx) {\n        auto shader = cShaders.GetShaderModule(this, cKey);\n        ctx->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(shader.GetShader());\n      });\n    }\n\n    // Viewport...\n    if (hasPositionT && (m_dirty.test(D3D9DeviceDirtyFlag::FFViewport) || m_ffZTest != IsZTestEnabled())) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::FFViewport);\n      m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n\n      const auto& vp = m_state.viewport;\n      // For us to account for the Vulkan viewport rules\n      // when translating Window Coords -> Real Coords:\n      // We need to negate the inverse extent we multiply by,\n      // this follows through to the offset when that gets\n      // timesed by it.\n      // The 1.0f additional offset however does not,\n      // so we account for that there manually.\n\n      m_ffZTest = IsZTestEnabled();\n\n      m_viewportInfo.inverseExtent = Vector4(\n         2.0f / float(vp.Width),\n        -2.0f / float(vp.Height),\n        m_ffZTest ? 1.0f : 0.0f,\n        1.0f);\n\n      m_viewportInfo.inverseOffset = Vector4(\n        -float(vp.X), -float(vp.Y),\n         0.0f,         0.0f);\n\n      m_viewportInfo.inverseOffset = m_viewportInfo.inverseOffset * m_viewportInfo.inverseExtent;\n\n      m_viewportInfo.inverseOffset = m_viewportInfo.inverseOffset + Vector4(-1.0f, 1.0f, 0.0f, 0.0f);\n    }\n\n    // Constants...\n    if (m_dirty.test(D3D9DeviceDirtyFlag::FFVertexData)) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::FFVertexData);\n\n      auto mapPtr = m_vsFixedFunction.AllocSlice();\n\n      auto WorldView    = m_state.transforms[GetTransformIndex(D3DTS_VIEW)] * m_state.transforms[GetTransformIndex(D3DTS_WORLD)];\n      auto NormalMatrix = inverse(WorldView);\n\n      D3D9FixedFunctionVS* data = reinterpret_cast<D3D9FixedFunctionVS*>(mapPtr);\n      data->WorldView    = WorldView;\n      data->NormalMatrix = NormalMatrix;\n      data->InverseView  = transpose(inverse(m_state.transforms[GetTransformIndex(D3DTS_VIEW)]));\n      data->Projection   = m_state.transforms[GetTransformIndex(D3DTS_PROJECTION)];\n\n      for (uint32_t i = 0; i < data->TexcoordMatrices.size(); i++)\n        data->TexcoordMatrices[i] = m_state.transforms[GetTransformIndex(D3DTS_TEXTURE0) + i];\n\n      data->ViewportInfo = m_viewportInfo;\n\n      DecodeD3DCOLOR(m_state.renderStates[D3DRS_AMBIENT], data->GlobalAmbient.data);\n\n      uint32_t lightIdx = 0;\n      for (uint32_t i = 0; i < caps::MaxEnabledLights; i++) {\n        auto idx = m_state.enabledLightIndices[i];\n        if (idx == std::numeric_limits<uint32_t>::max())\n          continue;\n\n        data->Lights[lightIdx++] = D3D9Light(m_state.lights[idx].value(), m_state.transforms[GetTransformIndex(D3DTS_VIEW)]);\n      }\n\n      data->Material = m_state.material;\n      data->TweenFactor = bit::cast<float>(m_state.renderStates[D3DRS_TWEENFACTOR]);\n      if (useUbershader) {\n        data->Key = BuildFFKeyVS(vertexBlendMode, indexedVertexBlend).Data;\n      }\n    }\n\n    if (m_dirty.test(D3D9DeviceDirtyFlag::FFVertexBlend) && vertexBlendMode == D3D9FF_VertexBlendMode_Normal) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::FFVertexBlend);\n\n      auto mapPtr = m_vsVertexBlend.AllocSlice();\n      auto UploadVertexBlendData = [&](auto data) {\n        for (uint32_t i = 0; i < std::size(data->WorldView); i++)\n          data->WorldView[i] = m_state.transforms[GetTransformIndex(D3DTS_VIEW)] * m_state.transforms[GetTransformIndex(D3DTS_WORLDMATRIX(i))];\n      };\n\n      (m_isSWVP && indexedVertexBlend)\n        ? UploadVertexBlendData(reinterpret_cast<D3D9FixedFunctionVertexBlendDataSW*>(mapPtr))\n        : UploadVertexBlendData(reinterpret_cast<D3D9FixedFunctionVertexBlendDataHW*>(mapPtr));\n    }\n  }\n\n\n  D3D9FFShaderKeyFS D3D9DeviceEx::BuildFFKeyFS() const {\n     // Used args for a given operation.\n    auto ArgsMask = [](DWORD Op) {\n      switch (Op) {\n        case D3DTOP_DISABLE:\n          return 0b000u; // No Args\n        case D3DTOP_SELECTARG1:\n        case D3DTOP_PREMODULATE:\n          return 0b010u; // Arg 1\n        case D3DTOP_SELECTARG2:\n          return 0b100u; // Arg 2\n        case D3DTOP_MULTIPLYADD:\n        case D3DTOP_LERP:\n          return 0b111u; // Arg 0, 1, 2\n        default:\n          return 0b110u; // Arg 1, 2\n      }\n    };\n\n    D3D9FFShaderKeyFS key;\n\n    uint32_t activeTextureStageCount = 0;\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n      auto& stage = key.Stages[i].Contents;\n      auto& data  = m_state.textureStages[i];\n\n      // Subsequent stages do not occur if this is true.\n      if (data[DXVK_TSS_COLOROP] == D3DTOP_DISABLE)\n        break;\n\n      // If the stage is invalid (ie. no texture bound),\n      // this and all subsequent stages get disabled.\n      if (m_state.textures[i] == nullptr) {\n        if (((data[DXVK_TSS_COLORARG0] & D3DTA_SELECTMASK) == D3DTA_TEXTURE && (ArgsMask(data[DXVK_TSS_COLOROP]) & (1 << 0u)))\n         || ((data[DXVK_TSS_COLORARG1] & D3DTA_SELECTMASK) == D3DTA_TEXTURE && (ArgsMask(data[DXVK_TSS_COLOROP]) & (1 << 1u)))\n         || ((data[DXVK_TSS_COLORARG2] & D3DTA_SELECTMASK) == D3DTA_TEXTURE && (ArgsMask(data[DXVK_TSS_COLOROP]) & (1 << 2u))))\n          break;\n      }\n\n      stage.ColorOp = data[DXVK_TSS_COLOROP];\n      stage.AlphaOp = data[DXVK_TSS_ALPHAOP];\n\n      stage.ColorArg0 = data[DXVK_TSS_COLORARG0];\n      stage.ColorArg1 = data[DXVK_TSS_COLORARG1];\n      stage.ColorArg2 = data[DXVK_TSS_COLORARG2];\n\n      stage.AlphaArg0 = data[DXVK_TSS_ALPHAARG0];\n      stage.AlphaArg1 = data[DXVK_TSS_ALPHAARG1];\n      stage.AlphaArg2 = data[DXVK_TSS_ALPHAARG2];\n\n      stage.ResultIsTemp = data[DXVK_TSS_RESULTARG] == D3DTA_TEMP;\n\n      activeTextureStageCount = i + 1;\n    }\n\n    auto& stage0 = key.Stages[0].Contents;\n\n    if (stage0.ResultIsTemp &&\n        stage0.ColorOp != D3DTOP_DISABLE &&\n        stage0.AlphaOp == D3DTOP_DISABLE) {\n      stage0.AlphaOp   = D3DTOP_SELECTARG1;\n      stage0.AlphaArg1 = D3DTA_DIFFUSE;\n    }\n\n    stage0.GlobalSpecularEnable = m_state.renderStates[D3DRS_SPECULARENABLE];\n\n    // The last stage *always* writes to current.\n    if (activeTextureStageCount >= 1)\n      key.Stages[activeTextureStageCount - 1].Contents.ResultIsTemp = false;\n\n    return key;\n  }\n\n\n  void D3D9DeviceEx::UpdateFixedFunctionPS() {\n    if (unlikely(!m_dirty.test(D3D9DeviceDirtyFlag::FFPixelShader) && !m_dirty.test(D3D9DeviceDirtyFlag::FFPixelData)))\n      return;\n\n    // Shader...\n    const bool useUbershader = m_d3d9Options.ffUbershaderFS;\n\n    D3D9FFShaderKeyFS key = BuildFFKeyFS();\n    if (useUbershader && m_dirty.test(D3D9DeviceDirtyFlag::FFPixelShader)) {\n      // The flags are set based on the specialized shaders.\n      m_dirty.clr(D3D9DeviceDirtyFlag::FFPixelShader);\n      m_dirty.set(D3D9DeviceDirtyFlag::FFPixelData);\n\n      // Spec constants...\n      uint32_t activeTextureStageCount;\n      for (activeTextureStageCount = 0; activeTextureStageCount < caps::TextureStageCount; activeTextureStageCount++) {\n        auto& stage = key.Stages[activeTextureStageCount].Contents;\n        if (stage.ColorOp == D3DTOP_DISABLE)\n          break;\n      }\n\n      const auto repackArg = [](uint32_t arg) {\n        return (arg & 0b111u) | ((arg & 0b110000u) >> 1u);\n      };\n\n      uint32_t lastActiveTextureStage = std::max(activeTextureStageCount, 1u) - 1u; // Subtract 1 to make it fit 3 bits\n      bool dirty = m_specInfo.set<D3D9SpecConstantId::SpecFFLastActiveTextureStage>(lastActiveTextureStage);\n      dirty |= m_specInfo.set<D3D9SpecConstantId::SpecFFGlobalSpecularEnabled>(m_state.renderStates[D3DRS_SPECULARENABLE]);\n      constexpr uint32_t perTextureStageSpecConsts = static_cast<uint32_t>(D3D9SpecConstantId::SpecFFTextureStage1ColorOp) - static_cast<uint32_t>(D3D9SpecConstantId::SpecFFTextureStage0ColorOp);\n      for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n        if (i <= activeTextureStageCount) {\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorOp + perTextureStageSpecConsts * i), key.Stages[i].Contents.ColorOp);\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorArg1 + perTextureStageSpecConsts * i), repackArg(key.Stages[i].Contents.ColorArg1));\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorArg2 + perTextureStageSpecConsts * i), repackArg(key.Stages[i].Contents.ColorArg2));\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaOp + perTextureStageSpecConsts * i), key.Stages[i].Contents.AlphaOp);\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaArg1 + perTextureStageSpecConsts * i), repackArg(key.Stages[i].Contents.AlphaArg1));\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaArg2 + perTextureStageSpecConsts * i), repackArg(key.Stages[i].Contents.AlphaArg2));\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ResultIsTemp + perTextureStageSpecConsts * i), key.Stages[i].Contents.ResultIsTemp);\n          // Color arg0 and alpha arg0 for all stages are packed after all the other FF spec consts\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorArg0 + i), repackArg(key.Stages[i].Contents.ColorArg0));\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaArg0 + i), repackArg(key.Stages[i].Contents.AlphaArg0));\n        } else {\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorOp + perTextureStageSpecConsts * i), 0);\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorArg1 + perTextureStageSpecConsts * i), 0);\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorArg2 + perTextureStageSpecConsts * i), 0);\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaOp + perTextureStageSpecConsts * i), 0);\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaArg1 + perTextureStageSpecConsts * i), 0);\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaArg2 + perTextureStageSpecConsts * i), 0);\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ResultIsTemp + perTextureStageSpecConsts * i), 0);\n          // Color arg0 and alpha arg0 for all stages are packed after all the other FF spec consts\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0ColorArg0 + i), 0u);\n          dirty |= m_specInfo.set(static_cast<D3D9SpecConstantId>(D3D9SpecConstantId::SpecFFTextureStage0AlphaArg0 + i), 0u);\n        }\n      }\n      if (dirty) {\n        m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n      }\n    } else if (m_dirty.test(D3D9DeviceDirtyFlag::FFPixelShader)) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::FFPixelShader);\n\n      EmitCs([\n        this,\n        cKey     = key,\n       &cShaders = m_ffModules\n      ](DxvkContext* ctx) {\n        auto shader = cShaders.GetShaderModule(this, cKey);\n        ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(shader.GetShader());\n      });\n    }\n\n    // Constants...\n    if (m_dirty.test(D3D9DeviceDirtyFlag::FFPixelData)) {\n      m_dirty.clr(D3D9DeviceDirtyFlag::FFPixelData);\n\n      auto mapPtr = m_psFixedFunction.AllocSlice();\n      auto& rs = m_state.renderStates;\n\n      D3D9FixedFunctionPS* data = reinterpret_cast<D3D9FixedFunctionPS*>(mapPtr);\n      DecodeD3DCOLOR((D3DCOLOR)rs[D3DRS_TEXTUREFACTOR], data->textureFactor.data);\n      if (useUbershader) {\n        data->Key = key;\n      }\n    }\n  }\n\n\n  bool D3D9DeviceEx::UseProgrammableVS() {\n    return m_state.vertexShader != nullptr\n      && m_state.vertexDecl != nullptr\n      && !m_state.vertexDecl->TestFlag(D3D9VertexDeclFlag::HasPositionT);\n  }\n\n\n  bool D3D9DeviceEx::UseProgrammablePS() {\n    return m_state.pixelShader != nullptr;\n  }\n\n\n  void D3D9DeviceEx::ApplyPrimitiveType(\n    DxvkContext*      pContext,\n    D3DPRIMITIVETYPE  PrimType) {\n    if (m_iaState.primitiveType != PrimType) {\n      m_iaState.primitiveType = PrimType;\n\n      auto iaState = DecodeInputAssemblyState(PrimType);\n      pContext->setInputAssemblyState(iaState);\n    }\n  }\n\n\n  void D3D9DeviceEx::ResolveZ() {\n    D3D9Surface*           src = m_state.depthStencil.ptr();\n    IDirect3DBaseTexture9* dst = m_state.textures[0];\n\n    if (unlikely(!src || !dst))\n      return;\n\n    D3D9CommonTexture* srcTextureInfo = GetCommonTexture(src);\n    D3D9CommonTexture* dstTextureInfo = GetCommonTexture(dst);\n\n    const D3D9_COMMON_TEXTURE_DESC* srcDesc = srcTextureInfo->Desc();\n    const D3D9_COMMON_TEXTURE_DESC* dstDesc = dstTextureInfo->Desc();\n\n    VkSampleCountFlagBits dstSampleCount;\n    DecodeMultiSampleType(dstDesc->MultiSample, dstDesc->MultisampleQuality, &dstSampleCount);\n\n    if (unlikely(dstSampleCount != VK_SAMPLE_COUNT_1_BIT)) {\n      Logger::warn(\"D3D9DeviceEx::ResolveZ: dstSampleCount != 1. Discarding.\");\n      return;\n    }\n\n    const D3D9_VK_FORMAT_MAPPING srcFormatInfo = LookupFormat(srcDesc->Format);\n    const D3D9_VK_FORMAT_MAPPING dstFormatInfo = LookupFormat(dstDesc->Format);\n\n    VkImageSubresource dstSubresource =\n      dstTextureInfo->GetSubresourceFromIndex(\n        dstFormatInfo.Aspect, 0);\n\n    VkImageSubresource srcSubresource =\n      srcTextureInfo->GetSubresourceFromIndex(\n        srcFormatInfo.Aspect, src->GetSubresource());\n\n    if ((dstSubresource.aspectMask & srcSubresource.aspectMask) != 0) {\n      // for depthStencil -> depth or depthStencil -> stencil copies, only copy the aspect that both images support\n      dstSubresource.aspectMask = dstSubresource.aspectMask & srcSubresource.aspectMask;\n      srcSubresource.aspectMask = dstSubresource.aspectMask & srcSubresource.aspectMask;\n    } else if (unlikely(dstSubresource.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT && srcSubresource.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT)) {\n      Logger::err(str::format(\"D3D9DeviceEx::ResolveZ: Trying to blit from \",\n        srcFormatInfo.FormatColor, \" (aspect \", srcSubresource.aspectMask, \")\", \" to \",\n        dstFormatInfo.FormatColor, \" (aspect \", dstSubresource.aspectMask, \")\"\n      ));\n      return;\n    }\n\n    const VkImageSubresourceLayers dstSubresourceLayers = {\n      dstSubresource.aspectMask,\n      dstSubresource.mipLevel,\n      dstSubresource.arrayLayer, 1 };\n\n    const VkImageSubresourceLayers srcSubresourceLayers = {\n      srcSubresource.aspectMask,\n      srcSubresource.mipLevel,\n      srcSubresource.arrayLayer, 1 };\n\n    VkSampleCountFlagBits srcSampleCount;\n    DecodeMultiSampleType(srcDesc->MultiSample, srcDesc->MultisampleQuality, &srcSampleCount);\n\n    if (srcSampleCount == VK_SAMPLE_COUNT_1_BIT) {\n      EmitCs([\n        cDstImage  = dstTextureInfo->GetImage(),\n        cSrcImage  = srcTextureInfo->GetImage(),\n        cDstLayers = dstSubresourceLayers,\n        cSrcLayers = srcSubresourceLayers\n      ] (DxvkContext* ctx) {\n        ctx->copyImage(\n          cDstImage, cDstLayers, VkOffset3D { 0, 0, 0 },\n          cSrcImage, cSrcLayers, VkOffset3D { 0, 0, 0 },\n          cDstImage->mipLevelExtent(cDstLayers.mipLevel));\n      });\n    } else {\n      EmitCs([\n        cDstImage  = dstTextureInfo->GetImage(),\n        cSrcImage  = srcTextureInfo->GetImage(),\n        cDstSubres = dstSubresourceLayers,\n        cSrcSubres = srcSubresourceLayers\n      ] (DxvkContext* ctx) {\n        // We should resolve using the first sample according to\n        // http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Advanced-DX9-Capabilities-for-ATI-Radeon-Cards_v2.pdf\n        // \"The resolve operation copies the depth value from the *first sample only* into the resolved depth stencil texture.\"\n        VkImageResolve region;\n        region.srcSubresource = cSrcSubres;\n        region.srcOffset      = VkOffset3D { 0, 0, 0 };\n        region.dstSubresource = cDstSubres;\n        region.dstOffset      = VkOffset3D { 0, 0, 0 };\n        region.extent         = cDstImage->mipLevelExtent(cDstSubres.mipLevel);\n\n        ctx->resolveImage(cDstImage, cSrcImage, region, cSrcImage->info().format,\n          VK_RESOLVE_MODE_SAMPLE_ZERO_BIT, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT);\n      });\n    }\n\n    dstTextureInfo->MarkAllNeedReadback();\n  }\n\n\n  void D3D9DeviceEx::TransformImage(\n          D3D9CommonTexture*       pResource,\n    const VkImageSubresourceRange* pSubresources,\n          VkImageLayout            OldLayout,\n          VkImageLayout            NewLayout) {\n    EmitCs([\n      cImage        = pResource->GetImage(),\n      cSubresources = *pSubresources,\n      cOldLayout    = OldLayout,\n      cNewLayout    = NewLayout\n    ] (DxvkContext* ctx) {\n      ctx->transformImage(\n        cImage, cSubresources,\n        cOldLayout, cNewLayout);\n    });\n  }\n\n\n  void D3D9DeviceEx::ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters) {\n    SetDepthStencilSurface(nullptr);\n\n    for (uint32_t i = 0; i < caps::MaxSimultaneousRenderTargets; i++)\n      SetRenderTargetInternal(i, nullptr);\n\n    auto& rs = m_state.renderStates;\n\n    rs[D3DRS_SEPARATEALPHABLENDENABLE] = FALSE;\n    rs[D3DRS_ALPHABLENDENABLE]         = FALSE;\n    rs[D3DRS_BLENDOP]                  = D3DBLENDOP_ADD;\n    rs[D3DRS_BLENDOPALPHA]             = D3DBLENDOP_ADD;\n    rs[D3DRS_DESTBLEND]                = D3DBLEND_ZERO;\n    rs[D3DRS_DESTBLENDALPHA]           = D3DBLEND_ZERO;\n    rs[D3DRS_COLORWRITEENABLE]         = 0x0000000f;\n    rs[D3DRS_COLORWRITEENABLE1]        = 0x0000000f;\n    rs[D3DRS_COLORWRITEENABLE2]        = 0x0000000f;\n    rs[D3DRS_COLORWRITEENABLE3]        = 0x0000000f;\n    rs[D3DRS_SRCBLEND]                 = D3DBLEND_ONE;\n    rs[D3DRS_SRCBLENDALPHA]            = D3DBLEND_ONE;\n    BindBlendState();\n\n    rs[D3DRS_BLENDFACTOR]              = 0xffffffff;\n    BindBlendFactor();\n\n    rs[D3DRS_ZENABLE]                  = pPresentationParameters->EnableAutoDepthStencil\n                                       ? D3DZB_TRUE\n                                       : D3DZB_FALSE;\n    rs[D3DRS_ZFUNC]                    = D3DCMP_LESSEQUAL;\n    rs[D3DRS_TWOSIDEDSTENCILMODE]      = FALSE;\n    rs[D3DRS_ZWRITEENABLE]             = TRUE;\n    rs[D3DRS_STENCILENABLE]            = FALSE;\n    rs[D3DRS_STENCILFAIL]              = D3DSTENCILOP_KEEP;\n    rs[D3DRS_STENCILZFAIL]             = D3DSTENCILOP_KEEP;\n    rs[D3DRS_STENCILPASS]              = D3DSTENCILOP_KEEP;\n    rs[D3DRS_STENCILFUNC]              = D3DCMP_ALWAYS;\n    rs[D3DRS_CCW_STENCILFAIL]          = D3DSTENCILOP_KEEP;\n    rs[D3DRS_CCW_STENCILZFAIL]         = D3DSTENCILOP_KEEP;\n    rs[D3DRS_CCW_STENCILPASS]          = D3DSTENCILOP_KEEP;\n    rs[D3DRS_CCW_STENCILFUNC]          = D3DCMP_ALWAYS;\n    rs[D3DRS_STENCILMASK]              = 0xFFFFFFFF;\n    rs[D3DRS_STENCILWRITEMASK]         = 0xFFFFFFFF;\n    BindDepthStencilState();\n\n    rs[D3DRS_STENCILREF] = 0;\n    BindDepthStencilReference();\n\n    rs[D3DRS_FILLMODE]            = D3DFILL_SOLID;\n    rs[D3DRS_CULLMODE]            = D3DCULL_CCW;\n    rs[D3DRS_DEPTHBIAS]           = bit::cast<DWORD>(0.0f);\n    rs[D3DRS_SLOPESCALEDEPTHBIAS] = bit::cast<DWORD>(0.0f);\n    BindRasterizerState();\n    BindDepthBias();\n\n    rs[D3DRS_SCISSORTESTENABLE]   = FALSE;\n\n    rs[D3DRS_ALPHATESTENABLE]     = FALSE;\n    rs[D3DRS_ALPHAFUNC]           = D3DCMP_ALWAYS;\n    BindAlphaTestState();\n    rs[D3DRS_ALPHAREF]            = 0;\n    UpdatePushConstant<D3D9RenderStateItem::AlphaRef>();\n\n    rs[D3DRS_MULTISAMPLEMASK]     = 0xffffffff;\n    BindMultiSampleState();\n\n    rs[D3DRS_TEXTUREFACTOR]       = 0xffffffff;\n    m_dirty.set(D3D9DeviceDirtyFlag::FFPixelData);\n\n    rs[D3DRS_DIFFUSEMATERIALSOURCE]  = D3DMCS_COLOR1;\n    rs[D3DRS_SPECULARMATERIALSOURCE] = D3DMCS_COLOR2;\n    rs[D3DRS_AMBIENTMATERIALSOURCE]  = D3DMCS_MATERIAL;\n    rs[D3DRS_EMISSIVEMATERIALSOURCE] = D3DMCS_MATERIAL;\n    rs[D3DRS_LIGHTING]               = TRUE;\n    rs[D3DRS_COLORVERTEX]            = TRUE;\n    rs[D3DRS_LOCALVIEWER]            = TRUE;\n    rs[D3DRS_RANGEFOGENABLE]         = FALSE;\n    rs[D3DRS_NORMALIZENORMALS]       = FALSE;\n    m_dirty.set(D3D9DeviceDirtyFlag::FFVertexShader);\n\n    // PS\n    rs[D3DRS_SPECULARENABLE] = FALSE;\n\n    rs[D3DRS_AMBIENT]                = 0;\n    m_dirty.set(D3D9DeviceDirtyFlag::FFVertexData);\n\n    rs[D3DRS_FOGENABLE]                  = FALSE;\n    rs[D3DRS_FOGCOLOR]                   = 0;\n    rs[D3DRS_FOGTABLEMODE]               = D3DFOG_NONE;\n    rs[D3DRS_FOGSTART]                   = bit::cast<DWORD>(0.0f);\n    rs[D3DRS_FOGEND]                     = bit::cast<DWORD>(1.0f);\n    rs[D3DRS_FOGDENSITY]                 = bit::cast<DWORD>(1.0f);\n    rs[D3DRS_FOGVERTEXMODE]              = D3DFOG_NONE;\n    m_dirty.set(D3D9DeviceDirtyFlag::FogColor);\n    m_dirty.set(D3D9DeviceDirtyFlag::FogDensity);\n    m_dirty.set(D3D9DeviceDirtyFlag::FogEnd);\n    m_dirty.set(D3D9DeviceDirtyFlag::FogScale);\n    m_dirty.set(D3D9DeviceDirtyFlag::FogState);\n\n    rs[D3DRS_CLIPPLANEENABLE] = 0;\n    m_dirty.set(D3D9DeviceDirtyFlag::ClipPlanes);\n\n    const auto& limits = m_dxvkDevice->adapter()->deviceProperties().core.properties.limits;\n\n    rs[D3DRS_POINTSPRITEENABLE]          = FALSE;\n    rs[D3DRS_POINTSCALEENABLE]           = FALSE;\n    rs[D3DRS_POINTSCALE_A]               = bit::cast<DWORD>(1.0f);\n    rs[D3DRS_POINTSCALE_B]               = bit::cast<DWORD>(0.0f);\n    rs[D3DRS_POINTSCALE_C]               = bit::cast<DWORD>(0.0f);\n    rs[D3DRS_POINTSIZE]                  = bit::cast<DWORD>(1.0f);\n    rs[D3DRS_POINTSIZE_MIN]              = m_isD3D8Compatible ? bit::cast<DWORD>(0.0f) : bit::cast<DWORD>(1.0f);\n    rs[D3DRS_POINTSIZE_MAX]              = bit::cast<DWORD>(limits.pointSizeRange[1]);\n    UpdatePushConstant<D3D9RenderStateItem::PointSize>();\n    UpdatePushConstant<D3D9RenderStateItem::PointSizeMin>();\n    UpdatePushConstant<D3D9RenderStateItem::PointSizeMax>();\n    m_dirty.set(D3D9DeviceDirtyFlag::PointScale);\n    UpdatePointMode(false);\n\n    rs[D3DRS_SRGBWRITEENABLE]            = 0;\n\n    rs[D3DRS_SHADEMODE]                  = D3DSHADE_GOURAUD;\n\n    rs[D3DRS_VERTEXBLEND]                = D3DVBF_DISABLE;\n    rs[D3DRS_INDEXEDVERTEXBLENDENABLE]   = FALSE;\n    rs[D3DRS_TWEENFACTOR]                = bit::cast<DWORD>(0.0f);\n    m_dirty.set(D3D9DeviceDirtyFlag::FFVertexBlend);\n\n    // Render States not implemented beyond this point.\n    rs[D3DRS_LASTPIXEL]                  = TRUE;\n    rs[D3DRS_DITHERENABLE]               = FALSE;\n    rs[D3DRS_WRAP0]                      = 0;\n    rs[D3DRS_WRAP1]                      = 0;\n    rs[D3DRS_WRAP2]                      = 0;\n    rs[D3DRS_WRAP3]                      = 0;\n    rs[D3DRS_WRAP4]                      = 0;\n    rs[D3DRS_WRAP5]                      = 0;\n    rs[D3DRS_WRAP6]                      = 0;\n    rs[D3DRS_WRAP7]                      = 0;\n    rs[D3DRS_CLIPPING]                   = TRUE;\n    rs[D3DRS_MULTISAMPLEANTIALIAS]       = TRUE;\n    rs[D3DRS_PATCHEDGESTYLE]             = D3DPATCHEDGE_DISCRETE;\n    rs[D3DRS_DEBUGMONITORTOKEN]          = D3DDMT_ENABLE;\n    rs[D3DRS_POSITIONDEGREE]             = D3DDEGREE_CUBIC;\n    rs[D3DRS_NORMALDEGREE]               = D3DDEGREE_LINEAR;\n    rs[D3DRS_ANTIALIASEDLINEENABLE]      = FALSE;\n    rs[D3DRS_MINTESSELLATIONLEVEL]       = bit::cast<DWORD>(1.0f);\n    rs[D3DRS_MAXTESSELLATIONLEVEL]       = bit::cast<DWORD>(1.0f);\n    rs[D3DRS_ADAPTIVETESS_X]             = bit::cast<DWORD>(0.0f);\n    rs[D3DRS_ADAPTIVETESS_Y]             = bit::cast<DWORD>(0.0f);\n    rs[D3DRS_ADAPTIVETESS_Z]             = bit::cast<DWORD>(1.0f);\n    rs[D3DRS_ADAPTIVETESS_W]             = bit::cast<DWORD>(0.0f);\n    rs[D3DRS_ENABLEADAPTIVETESSELLATION] = FALSE;\n    rs[D3DRS_WRAP8]                      = 0;\n    rs[D3DRS_WRAP9]                      = 0;\n    rs[D3DRS_WRAP10]                     = 0;\n    rs[D3DRS_WRAP11]                     = 0;\n    rs[D3DRS_WRAP12]                     = 0;\n    rs[D3DRS_WRAP13]                     = 0;\n    rs[D3DRS_WRAP14]                     = 0;\n    rs[D3DRS_WRAP15]                     = 0;\n    // End Unimplemented Render States\n\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n      auto& stage = m_state.textureStages[i];\n\n      stage[DXVK_TSS_COLOROP]               = i == 0 ? D3DTOP_MODULATE : D3DTOP_DISABLE;\n      stage[DXVK_TSS_COLORARG1]             = D3DTA_TEXTURE;\n      stage[DXVK_TSS_COLORARG2]             = D3DTA_CURRENT;\n      stage[DXVK_TSS_ALPHAOP]               = i == 0 ? D3DTOP_SELECTARG1 : D3DTOP_DISABLE;\n      stage[DXVK_TSS_ALPHAARG1]             = D3DTA_TEXTURE;\n      stage[DXVK_TSS_ALPHAARG2]             = D3DTA_CURRENT;\n      stage[DXVK_TSS_BUMPENVMAT00]          = bit::cast<DWORD>(0.0f);\n      stage[DXVK_TSS_BUMPENVMAT01]          = bit::cast<DWORD>(0.0f);\n      stage[DXVK_TSS_BUMPENVMAT10]          = bit::cast<DWORD>(0.0f);\n      stage[DXVK_TSS_BUMPENVMAT11]          = bit::cast<DWORD>(0.0f);\n      stage[DXVK_TSS_TEXCOORDINDEX]         = i;\n      stage[DXVK_TSS_BUMPENVLSCALE]         = bit::cast<DWORD>(0.0f);\n      stage[DXVK_TSS_BUMPENVLOFFSET]        = bit::cast<DWORD>(0.0f);\n      stage[DXVK_TSS_TEXTURETRANSFORMFLAGS] = D3DTTFF_DISABLE;\n      stage[DXVK_TSS_COLORARG0]             = D3DTA_CURRENT;\n      stage[DXVK_TSS_ALPHAARG0]             = D3DTA_CURRENT;\n      stage[DXVK_TSS_RESULTARG]             = D3DTA_CURRENT;\n      stage[DXVK_TSS_CONSTANT]              = 0x00000000;\n    }\n\n    // Projected is set based on DXVK_TSS_TEXTURETRANSFORMFLAGS\n    m_textureSlotTracking.projected = 0;\n\n    m_dirty.set(D3D9DeviceDirtyFlag::SharedPixelShaderData);\n    m_dirty.set(D3D9DeviceDirtyFlag::FFPixelShader);\n\n    for (uint32_t i = 0; i < caps::MaxStreams; i++)\n      m_state.streamFreq[i] = 1;\n\n    for (uint32_t i = 0; i < m_state.textures->size(); i++) {\n      SetStateTexture(i, nullptr);\n    }\n\n    EmitCs([\n      cSize = m_state.textures->size()\n    ](DxvkContext* ctx) {\n      VkShaderStageFlags stage = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;\n\n      for (uint32_t i = 0; i < cSize; i++) {\n        auto samplerInfo = RemapStateSamplerShader(DWORD(i));\n        uint32_t slot = computeResourceSlotId(samplerInfo.first, DxsoBindingType::Image, uint32_t(samplerInfo.second));\n        ctx->bindResourceImageView(stage, slot, nullptr);\n      }\n    });\n\n    m_textureSlotTracking.textureDirty = 0;\n    m_textureSlotTracking.depth = 0;\n\n    auto& ss = m_state.samplerStates.get();\n    for (uint32_t i = 0; i < ss.size(); i++) {\n      auto& state = ss[i];\n      state[D3DSAMP_ADDRESSU]      = D3DTADDRESS_WRAP;\n      state[D3DSAMP_ADDRESSV]      = D3DTADDRESS_WRAP;\n      state[D3DSAMP_ADDRESSW]      = D3DTADDRESS_WRAP;\n      state[D3DSAMP_BORDERCOLOR]   = 0x00000000;\n      state[D3DSAMP_MAGFILTER]     = D3DTEXF_POINT;\n      state[D3DSAMP_MINFILTER]     = D3DTEXF_POINT;\n      state[D3DSAMP_MIPFILTER]     = D3DTEXF_NONE;\n      state[D3DSAMP_MIPMAPLODBIAS] = bit::cast<DWORD>(0.0f);\n      state[D3DSAMP_MAXMIPLEVEL]   = 0;\n      state[D3DSAMP_MAXANISOTROPY] = 1;\n      state[D3DSAMP_SRGBTEXTURE]   = 0;\n      state[D3DSAMP_ELEMENTINDEX]  = 0;\n      state[D3DSAMP_DMAPOFFSET]    = 0;\n\n      BindSampler(i);\n    }\n\n    m_textureSlotTracking.samplerStateDirty = 0;\n\n    for (uint32_t i = 0; i < caps::MaxClipPlanes; i++) {\n      float plane[4] = { 0, 0, 0, 0 };\n      SetClipPlane(i, plane);\n    }\n\n    // We should do this...\n    m_dirty.set(D3D9DeviceDirtyFlag::InputLayout);\n\n    UpdatePixelShaderSamplerSpec(0u, 0u);\n    UpdateVertexBoolSpec(0u);\n    UpdatePixelBoolSpec(0u);\n    UpdateCommonSamplerSpec(0u, 0u, 0u, 0u);\n\n    UpdateAnyColorWrites<0>();\n    UpdateAnyColorWrites<1>();\n    UpdateAnyColorWrites<2>();\n    UpdateAnyColorWrites<3>();\n\n    SetIndices(nullptr);\n    for (uint32_t i = 0; i < caps::MaxStreams; i++) {\n      SetStreamSource(i, nullptr, 0, 0);\n    }\n\n    // In D3D8, this represents the value of D3DRS_PATCHSEGMENTS.\n    // It defaults to 1.0f and is reset as any other render state.\n    if (m_isD3D8Compatible)\n      m_state.nPatchSegments = 1.0f;\n\n    m_alphaTestEnabled = false;\n    m_atocEnabled      = false;\n    m_nvdbEnabled      = false;\n  }\n\n\n  HRESULT D3D9DeviceEx::ResetSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode) {\n    D3D9Format backBufferFmt = EnumerateFormat(pPresentationParameters->BackBufferFormat);\n    bool unlockedFormats = m_parent->HasFormatsUnlocked();\n\n    Logger::info(str::format(\n      \"D3D9DeviceEx::ResetSwapChain:\\n\",\n      \"  Requested Presentation Parameters\\n\",\n      \"    - Width:              \", pPresentationParameters->BackBufferWidth, \"\\n\",\n      \"    - Height:             \", pPresentationParameters->BackBufferHeight, \"\\n\",\n      \"    - Format:             \", backBufferFmt, \"\\n\"\n      \"    - Auto Depth Stencil: \", pPresentationParameters->EnableAutoDepthStencil ? \"true\" : \"false\", \"\\n\",\n      \"                ^ Format: \", EnumerateFormat(pPresentationParameters->AutoDepthStencilFormat), \"\\n\",\n      \"    - Windowed:           \", pPresentationParameters->Windowed ? \"true\" : \"false\", \"\\n\",\n      \"    - Swap effect:        \", pPresentationParameters->SwapEffect, \"\\n\"));\n\n    // Black Desert creates a D3DDEVTYPE_NULLREF device and\n    // expects this validation to not prevent a swapchain reset.\n    if (likely(m_deviceType != D3DDEVTYPE_NULLREF) &&\n        unlikely(!pPresentationParameters->Windowed &&\n                 (pPresentationParameters->BackBufferWidth  == 0\n               || pPresentationParameters->BackBufferHeight == 0))) {\n      return D3DERR_INVALIDCALL;\n    }\n\n    if (backBufferFmt != D3D9Format::Unknown && !unlockedFormats) {\n      if (!IsSupportedBackBufferFormat(backBufferFmt)) {\n        Logger::err(str::format(\"D3D9DeviceEx::ResetSwapChain: Unsupported backbuffer format: \",\n          EnumerateFormat(pPresentationParameters->BackBufferFormat)));\n        return D3DERR_INVALIDCALL;\n      }\n    }\n\n    if (m_implicitSwapchain != nullptr) {\n      HRESULT hr = m_implicitSwapchain->Reset(pPresentationParameters, pFullscreenDisplayMode);\n      if (FAILED(hr))\n        return hr;\n    }\n    else {\n      m_implicitSwapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode, true);\n      m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr();\n    }\n\n    if (pPresentationParameters->EnableAutoDepthStencil) {\n      D3D9_COMMON_TEXTURE_DESC desc;\n      desc.Width              = pPresentationParameters->BackBufferWidth;\n      desc.Height             = pPresentationParameters->BackBufferHeight;\n      desc.Depth              = 1;\n      desc.ArraySize          = 1;\n      desc.MipLevels          = 1;\n      desc.Usage              = D3DUSAGE_DEPTHSTENCIL;\n      desc.Format             = EnumerateFormat(pPresentationParameters->AutoDepthStencilFormat);\n      desc.Pool               = D3DPOOL_DEFAULT;\n      desc.Discard            = (pPresentationParameters->Flags & D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL) != 0;\n      desc.MultiSample        = pPresentationParameters->MultiSampleType;\n      desc.MultisampleQuality = pPresentationParameters->MultiSampleQuality;\n      desc.IsBackBuffer       = FALSE;\n      desc.IsAttachmentOnly   = TRUE;\n      desc.IsLockable         = IsLockableDepthStencilFormat(desc.Format);\n\n      if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, D3DRTYPE_SURFACE, &desc)))\n        return D3DERR_NOTAVAILABLE;\n\n      m_autoDepthStencil = new D3D9Surface(this, &desc, IsExtended(), nullptr, nullptr);\n      m_initializer->InitTexture(m_autoDepthStencil->GetCommonTexture());\n      SetDepthStencilSurface(m_autoDepthStencil.ptr());\n      m_losableResourceCounter++;\n    }\n\n    if (!IsExtended()) {\n      SetRenderTarget(0, m_implicitSwapchain->GetBackBuffer(0));\n    } else {\n      // Extended devices will not reset the MinZ/MaxZ viewport values\n      const float MinZ = m_state.viewport.MinZ;\n      const float MaxZ = m_state.viewport.MaxZ;\n\n      SetRenderTarget(0, m_implicitSwapchain->GetBackBuffer(0));\n\n      // Previous MinZ/MaxZ values (saved above) need to be restored\n      m_state.viewport.MinZ = MinZ;\n      m_state.viewport.MaxZ = MaxZ;\n    }\n\n    // Force this if we end up binding the same RT to make scissor change go into effect.\n    BindViewportAndScissor();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9DeviceEx::InitialReset(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode) {\n    ResetState(pPresentationParameters);\n\n    HRESULT hr = ResetSwapChain(pPresentationParameters, pFullscreenDisplayMode);\n    if (FAILED(hr))\n      return hr;\n\n    Flush();\n    SynchronizeCsThread(DxvkCsThread::SynchronizeAll);\n\n    return D3D_OK;\n  }\n\n  void D3D9DeviceEx::TrackBufferMappingBufferSequenceNumber(\n        D3D9CommonBuffer* pResource) {\n    uint64_t sequenceNumber = GetCurrentSequenceNumber();\n    pResource->TrackMappingBufferSequenceNumber(sequenceNumber);\n  }\n\n  void D3D9DeviceEx::TrackTextureMappingBufferSequenceNumber(\n      D3D9CommonTexture* pResource,\n      UINT Subresource) {\n    uint64_t sequenceNumber = GetCurrentSequenceNumber();\n    pResource->TrackMappingBufferSequenceNumber(Subresource, sequenceNumber);\n  }\n\n  uint64_t D3D9DeviceEx::GetCurrentSequenceNumber() {\n    // We do not flush empty chunks, so if we are tracking a resource\n    // immediately after a flush, we need to use the sequence number\n    // of the previously submitted chunk to prevent deadlocks.\n    return m_csChunk->empty() ? m_csSeqNum : m_csSeqNum + 1;\n  }\n\n\n  void* D3D9DeviceEx::MapTexture(D3D9CommonTexture* pTexture, UINT Subresource) {\n    // Will only be called inside the device lock\n    void *ptr = pTexture->GetData(Subresource);\n\n#ifdef D3D9_ALLOW_UNMAPPING\n    if (likely(pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)) {\n      m_mappedTextures.insert(pTexture);\n    }\n#endif\n\n    return ptr;\n  }\n\n  void D3D9DeviceEx::TouchMappedTexture(D3D9CommonTexture* pTexture) {\n#ifdef D3D9_ALLOW_UNMAPPING\n    if (pTexture->GetMapMode() != D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)\n      return;\n\n    D3D9DeviceLock lock = LockDevice();\n    m_mappedTextures.touch(pTexture);\n#endif\n  }\n\n  void D3D9DeviceEx::RemoveMappedTexture(D3D9CommonTexture* pTexture) {\n#ifdef D3D9_ALLOW_UNMAPPING\n    if (pTexture->GetMapMode() != D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)\n      return;\n\n    D3D9DeviceLock lock = LockDevice();\n    m_mappedTextures.remove(pTexture);\n#endif\n  }\n\n  void D3D9DeviceEx::UnmapTextures() {\n    // Will only be called inside the device lock\n\n#ifdef D3D9_ALLOW_UNMAPPING\n    uint32_t mappedMemory = m_memoryAllocator.MappedMemory();\n    if (likely(mappedMemory < uint32_t(m_d3d9Options.textureMemory)))\n      return;\n\n    uint32_t threshold = (m_d3d9Options.textureMemory / 4) * 3;\n\n    auto iter = m_mappedTextures.leastRecentlyUsedIter();\n    while (m_memoryAllocator.MappedMemory() >= threshold && iter != m_mappedTextures.leastRecentlyUsedEndIter()) {\n      if (unlikely((*iter)->IsAnySubresourceLocked() != 0)) {\n        iter++;\n        continue;\n      }\n      (*iter)->UnmapData();\n\n      iter = m_mappedTextures.remove(iter);\n    }\n#endif\n  }\n\n  ////////////////////////////////////\n  // D3D9 Device Lost\n  ////////////////////////////////////\n\n  void D3D9DeviceEx::NotifyFullscreen(HWND window, bool fullscreen) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (fullscreen) {\n      if (unlikely(window != m_fullscreenWindow && m_fullscreenWindow != NULL)) {\n        Logger::warn(\"Multiple fullscreen windows detected.\");\n      }\n      m_fullscreenWindow = window;\n    } else {\n      if (unlikely(m_fullscreenWindow != window)) {\n        Logger::warn(\"Window was not fullscreen in the first place.\");\n      } else {\n        m_fullscreenWindow = 0;\n      }\n    }\n  }\n\n  void D3D9DeviceEx::NotifyWindowActivated(HWND window, bool activated) {\n    D3D9DeviceLock lock = LockDevice();\n\n    if (likely(!m_d3d9Options.deviceLossOnFocusLoss || IsExtended()))\n      return;\n\n    if (activated && m_deviceLostState == D3D9DeviceLostState::Lost) {\n      Logger::info(\"Device not reset\");\n      m_deviceLostState = D3D9DeviceLostState::NotReset;\n    } else if (!activated && m_deviceLostState != D3D9DeviceLostState::Lost && m_fullscreenWindow == window) {\n      Logger::info(\"Device lost\");\n      m_deviceLostState = D3D9DeviceLostState::Lost;\n      m_fullscreenWindow = NULL;\n    }\n  }\n\n  ////////////////////////////////////\n  // D3D9 Device Specialization State\n  ////////////////////////////////////\n\n  void D3D9DeviceEx::UpdateAlphaTestSpec(VkCompareOp alphaOp, uint32_t precision) {\n    bool dirty  = m_specInfo.set<SpecAlphaCompareOp>(uint32_t(alphaOp));\n         dirty |= m_specInfo.set<SpecAlphaPrecisionBits>(precision);\n\n    if (dirty)\n      m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n  }\n\n\n  void D3D9DeviceEx::UpdateVertexBoolSpec(uint32_t value) {\n    if (m_specInfo.set<SpecVertexShaderBools>(value))\n      m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n  }\n\n\n  void D3D9DeviceEx::UpdatePixelBoolSpec(uint32_t value) {\n    if (m_specInfo.set<SpecPixelShaderBools>(value))\n      m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n  }\n\n\n  void D3D9DeviceEx::UpdatePixelShaderSamplerSpec(uint32_t types, uint32_t fetch4) {\n    bool dirty  = m_specInfo.set<SpecSamplerType>(types);\n         dirty |= m_specInfo.set<SpecSamplerFetch4>(fetch4);\n\n    if (dirty)\n      m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n  }\n\n\n  void D3D9DeviceEx::UpdateCommonSamplerSpec(uint32_t nullMask, uint32_t depthMask, uint32_t drefMask, uint32_t projections) {\n    bool dirty  = m_specInfo.set<SpecSamplerDepthMode>(depthMask);\n         dirty |= m_specInfo.set<SpecSamplerNull>(nullMask);\n         dirty |= m_specInfo.set<SpecSamplerDrefClamp>(drefMask);\n         dirty |= m_specInfo.set<SpecSamplerProjected>(projections);\n\n    if (dirty)\n      m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n  }\n\n\n  void D3D9DeviceEx::UpdatePointModeSpec(uint32_t mode) {\n    if (m_specInfo.set<SpecPointMode>(mode))\n      m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n  }\n\n\n  void D3D9DeviceEx::UpdateFogModeSpec(bool fogEnabled, D3DFOGMODE vertexFogMode, D3DFOGMODE pixelFogMode) {\n    bool dirty  = m_specInfo.set<SpecFogEnabled>(fogEnabled);\n         dirty |= m_specInfo.set<SpecVertexFogMode>(vertexFogMode);\n         dirty |= m_specInfo.set<SpecPixelFogMode>(pixelFogMode);\n\n    if (dirty)\n      m_dirty.set(D3D9DeviceDirtyFlag::SpecializationEntries);\n  }\n\n\n  void D3D9DeviceEx::BindSpecConstants() {\n    if (!m_dirty.test(D3D9DeviceDirtyFlag::SpecializationEntries))\n      return;\n\n    EmitCs([cSpecInfo = m_specInfo](DxvkContext* ctx) {\n      for (size_t i = 0; i < cSpecInfo.data.size(); i++)\n        ctx->setSpecConstant(VK_PIPELINE_BIND_POINT_GRAPHICS, i, cSpecInfo.data[i]);\n    });\n\n    // Write spec constants into buffer for fast-linked pipelines to use it.\n    if (m_usingGraphicsPipelines) {\n      // TODO: Make uploading specialization information less naive.\n      auto mapPtr = m_specBuffer.AllocSlice();\n      memcpy(mapPtr, m_specInfo.data.data(), D3D9SpecializationInfo::UBOSize);\n    }\n\n    m_dirty.clr(D3D9DeviceDirtyFlag::SpecializationEntries);\n  }\n\n\n  GpuFlushType D3D9DeviceEx::GetMaxFlushType() const {\n    if (m_d3d9Options.reproducibleCommandStream)\n      return GpuFlushType::ExplicitFlush;\n    else if (m_dxvkDevice->perfHints().preferRenderPassOps)\n      return GpuFlushType::ImplicitStrongHint;\n    else\n      return GpuFlushType::ImplicitWeakHint;\n  }\n\n  bool D3D9DeviceEx::ValidateSharedTexture(\n    HANDLE                          handle,\n    D3DRESOURCETYPE                 type,\n    const D3D9_COMMON_TEXTURE_DESC& textureDesc) const {\n    if (!(reinterpret_cast<uintptr_t>(handle) & 0xc0000000)) {\n      Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: not a D3DKMT handle: \", handle));\n      return false;\n    }\n\n    union d3dkmt_desc desc;\n\n    D3DKMT_QUERYRESOURCEINFO query = { };\n    query.hDevice = m_dxvkDevice->kmtLocal();\n    query.hGlobalShare = reinterpret_cast<uintptr_t>(handle);\n    query.pPrivateRuntimeData = &desc;\n    query.PrivateRuntimeDataSize = sizeof(desc);\n\n    if (D3DKMTQueryResourceInfo(&query)) {\n      Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: Failed to query resource: \", handle));\n    } else if (query.PrivateRuntimeDataSize < sizeof(desc.dxgi) || query.PrivateRuntimeDataSize > sizeof(desc)) {\n      Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: Unexpected size: \", query.PrivateRuntimeDataSize));\n    } else {\n      D3DDDI_OPENALLOCATIONINFO2 alloc = { };\n      D3DKMT_OPENRESOURCE open = { };\n      open.hDevice = m_dxvkDevice->kmtLocal();\n      open.hGlobalShare = reinterpret_cast<uintptr_t>(handle);\n      open.NumAllocations = 1;\n      open.pOpenAllocationInfo2 = &alloc;\n      open.pPrivateRuntimeData = &desc;\n      open.PrivateRuntimeDataSize = query.PrivateRuntimeDataSize;\n\n      if (D3DKMTOpenResource2(&open)) {\n        Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: Failed to open resource: \", handle));\n      } else {\n        D3DKMT_DESTROYALLOCATION destroy = { };\n        destroy.hDevice = m_dxvkDevice->kmtLocal();\n        destroy.hResource = open.hResource;\n        D3DKMTDestroyAllocation(&destroy);\n\n        if (desc.dxgi.size != sizeof(desc.d3d9) || desc.dxgi.version != 1) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: Invalid size: \",\n                                   desc.dxgi.size, \" or version: \", desc.dxgi.version));\n          return false;\n        }\n        if (desc.d3d9.type != type) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: Invalid type: \", desc.d3d9.type));\n          return false;\n        }\n        if (desc.d3d9.dxgi.width != textureDesc.Width || desc.d3d9.dxgi.height != textureDesc.Height) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: Invalid dimensions: \", desc.d3d9.dxgi.width, \"x\", desc.d3d9.dxgi.height));\n          return false;\n        }\n        if (desc.d3d9.format != static_cast<D3DFORMAT>(textureDesc.Format)) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: Invalid format: \", desc.d3d9.format));\n          return false;\n        }\n        if (textureDesc.Usage & ~desc.d3d9.usage) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: Invalid usage: \", desc.d3d9.usage));\n          return false;\n        }\n        if (type == D3DRTYPE_TEXTURE && desc.d3d9.texture.levels != textureDesc.MipLevels) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedTexture: Invalid mip levels: \", desc.d3d9.texture.levels));\n          return false;\n        }\n\n        Logger::debug(str::format(\"Found D3D9 desc: \", desc.d3d9.type));\n        Logger::debug(str::format(\"  dxgi.width: \", desc.d3d9.dxgi.width));\n        Logger::debug(str::format(\"  dxgi.height: \", desc.d3d9.dxgi.height));\n        Logger::debug(str::format(\"  format: \", desc.d3d9.format));\n        Logger::debug(str::format(\"  usage: \", desc.d3d9.usage));\n        if (desc.d3d9.type == D3DRTYPE_TEXTURE) {\n          Logger::debug(str::format(\"  texture.width: \", desc.d3d9.texture.width));\n          Logger::debug(str::format(\"  texture.height: \", desc.d3d9.texture.height));\n          Logger::debug(str::format(\"  texture.depth: \", desc.d3d9.texture.depth));\n          Logger::debug(str::format(\"  texture.levels: \", desc.d3d9.texture.levels));\n        } else if (desc.d3d9.type == D3DRTYPE_SURFACE) {\n          Logger::debug(str::format(\"  surface.width: \", desc.d3d9.surface.width));\n          Logger::debug(str::format(\"  surface.height: \", desc.d3d9.surface.height));\n        }\n        return true;\n      }\n    }\n\n    /* ignore failures for legacy Proton implementation */\n    return true;\n  }\n\n  bool D3D9DeviceEx::ValidateSharedBuffer(\n      HANDLE                        handle,\n      const dxvk::D3D9_BUFFER_DESC& bufferDesc) const {\n    if (!(reinterpret_cast<uintptr_t>(handle) & 0xc0000000)) {\n      Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedBuffer: not a D3DKMT handle: \", handle));\n      return false;\n    }\n\n    union d3dkmt_desc desc;\n\n    D3DKMT_QUERYRESOURCEINFO query = { };\n    query.hDevice = m_dxvkDevice->kmtLocal();\n    query.hGlobalShare = reinterpret_cast<uintptr_t>(handle);\n    query.pPrivateRuntimeData = &desc;\n    query.PrivateRuntimeDataSize = sizeof(desc);\n\n    if (D3DKMTQueryResourceInfo(&query)) {\n      Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedBuffer: Failed to query resource: \", handle));\n    } else if (query.PrivateRuntimeDataSize < sizeof(desc.dxgi) || query.PrivateRuntimeDataSize > sizeof(desc)) {\n      Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedBuffer: Unexpected size: \", query.PrivateRuntimeDataSize));\n    } else {\n      D3DDDI_OPENALLOCATIONINFO2 alloc = { };\n      D3DKMT_OPENRESOURCE open = { };\n      open.hDevice = m_dxvkDevice->kmtLocal();\n      open.hGlobalShare = reinterpret_cast<uintptr_t>(handle);\n      open.NumAllocations = 1;\n      open.pOpenAllocationInfo2 = &alloc;\n      open.pPrivateRuntimeData = &desc;\n      open.PrivateRuntimeDataSize = query.PrivateRuntimeDataSize;\n\n      if (D3DKMTOpenResource2(&open)) {\n        Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedBuffer: Failed to open resource: \", handle));\n      } else {\n        D3DKMT_DESTROYALLOCATION destroy = { };\n        destroy.hDevice = m_dxvkDevice->kmtLocal();\n        destroy.hResource = open.hResource;\n        D3DKMTDestroyAllocation(&destroy);\n\n        if (desc.dxgi.size != sizeof(desc.d3d9) || desc.dxgi.version != 1) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedBuffer: Invalid size: \",\n                                   desc.dxgi.size, \" or version: \", desc.dxgi.version));\n          return false;\n        }\n        if (desc.d3d9.type != bufferDesc.Type) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedBuffer: Invalid type: \", desc.d3d9.type));\n          return false;\n        }\n        if (desc.d3d9.dxgi.width != bufferDesc.Size || desc.d3d9.buffer.width != bufferDesc.Size) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedBuffer: Invalid dimensions: \", desc.d3d9.dxgi.width, \"x\", desc.d3d9.dxgi.height));\n          return false;\n        }\n        if (desc.d3d9.buffer.format != static_cast<UINT>(bufferDesc.Format)) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedBuffer: Invalid format: \", desc.d3d9.format));\n          return false;\n        }\n        if (bufferDesc.Usage & ~desc.d3d9.usage) {\n          Logger::warn(str::format(\"D3D9DeviceEx::ValidateSharedBuffer: Invalid usage: \", desc.d3d9.usage));\n          return false;\n        }\n\n        Logger::debug(str::format(\"Found D3D9 desc: \", desc.d3d9.type));\n        Logger::debug(str::format(\"  dxgi.width: \", desc.d3d9.buffer.width));\n        Logger::debug(str::format(\"  format: \", desc.d3d9.buffer.format));\n        Logger::debug(str::format(\"  usage: \", desc.d3d9.usage));\n        return true;\n      }\n    }\n\n    /* ignore failures for legacy Proton implementation */\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_device.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n#include \"../dxvk/dxvk_cs.h\"\n#include \"../dxvk/dxvk_staging.h\"\n\n#include \"d3d9_include.h\"\n#include \"d3d9_cursor.h\"\n#include \"d3d9_format.h\"\n#include \"d3d9_multithread.h\"\n#include \"d3d9_adapter.h\"\n#include \"d3d9_constant_buffer.h\"\n#include \"d3d9_constant_set.h\"\n#include \"d3d9_mem.h\"\n\n#include \"d3d9_state.h\"\n\n#include \"d3d9_options.h\"\n\n#include \"../dxso/dxso_module.h\"\n#include \"../dxso/dxso_util.h\"\n#include \"../dxso/dxso_options.h\"\n#include \"../dxso/dxso_modinfo.h\"\n\n#include \"d3d9_fixed_function.h\"\n#include \"d3d9_swvp_emu.h\"\n\n#include \"d3d9_spec_constants.h\"\n#include \"d3d9_interop.h\"\n#include \"d3d9_on_12.h\"\n\n#include <cstdint>\n#include <unordered_set>\n#include \"d3d9_bridge.h\"\n\n#include <vector>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"../util/util_flush.h\"\n#include \"../util/util_lru.h\"\n\nnamespace dxvk {\n\n  class D3D9InterfaceEx;\n  class D3D9SwapChainEx;\n  class D3D9CommonTexture;\n  class D3D9CommonBuffer;\n  class D3D9CommonShader;\n  class D3D9ShaderModuleSet;\n  class D3D9Initializer;\n  class D3D9Query;\n  class D3D9StateBlock;\n  class D3D9FormatHelper;\n  class D3D9UserDefinedAnnotation;\n\n  enum class D3D9DeviceDirtyFlag : uint32_t {\n    Framebuffer,\n    ClipPlanes,\n    DepthStencilState,\n    BlendState,\n    RasterizerState,\n    DepthBias,\n    AlphaTestState,\n    InputLayout,\n    ViewportScissor,\n    MultiSampleState,\n    VertexBuffers,\n    IndexBuffer,\n\n    FogState,\n    FogColor,\n    FogDensity,\n    FogScale,\n    FogEnd,\n\n    FFVertexData,\n    FFVertexBlend,\n    FFVertexShader,\n    FFPixelShader,\n    FFViewport,\n    FFPixelData,\n    SharedPixelShaderData,\n    DepthBounds,\n    PointScale,\n\n    SpecializationEntries,\n  };\n\n  using D3D9DeviceDirtyFlags = Flags<D3D9DeviceDirtyFlag>;\n\n  enum class D3D9DeviceLostState {\n    Ok = 0,\n    Lost = 1,\n    NotReset = 2,\n  };\n\n  struct D3D9DrawInfo {\n    uint32_t vertexCount;\n    uint32_t instanceCount;\n  };\n\n  struct D3D9BufferSlice {\n    DxvkBufferSlice slice = {};\n    void*           mapPtr = nullptr;\n  };\n\n  struct D3D9TextureSlotTracking {\n    /* Pixel shaders can access 16 textures/samplers.\n     * Then there's 1 dmap texture/sampler.\n     * Vertex shaders can use 4 textures/samplers.\n     * So unless otherwise noted most bitmasks use 21 bits\n     * and each bit is one texture/sampler slot.\n     * See RemapSamplerState(), IsPSSampler(), IsVSSampler() in d3d9_util.h */\n\n    /** Whether the format of the texture currently bound to each slot is a format that gets fetched in comparison mode. */\n    uint32_t depth = 0;\n\n    /** If a depth texture format isn't supported, we fall back to D32F.\n     * We'll need to clamp the reference value if the original format was an unorm format.\n     * This tracks the texture/sampler slots for which this kind of adjusting needs to be done. */\n    uint32_t drefClamp = 0;\n\n    /** Used to store the type of each bound pixel shader texture.\n     * This is used to generate fixed function shader code\n     * and for PS 1.1 shaders which do not provide this information in the shader bytecode.\n     * SM 1.1 and fixed function doesn't allow sampling textures in the VS, so we only need the 16 PS slots.\n     * There's 3 texture types, so every texture/sampler slot uses 2 bits. */\n    uint32_t textureType = 0;\n\n    /** Whether the type of the texture currently bound to each slot matches the texture type that the shader expects */\n    uint32_t mismatchingTextureType = 0;\n\n    /** Whether projected texture lookup is enabled for each texture/sampler slot. This is only used for generating fixed function shaders. */\n    uint32_t projected = 0;\n\n    /** Whether sampler slots whose state has been changed and bindings in the backend need to be updated */\n    uint32_t samplerStateDirty = 0;\n\n    /** Whether Fetch 4 is enabled for a sampler slot.\n     * This just means the application enabled it using the sampler state.\n     * It does not mean Fetch 4 is actually active, as that depends on other factors\n     * such as the sampling mode and the texture format. */\n    uint32_t fetch4SamplerState = 0;\n\n    /** Whether Fetch 4 is active. */\n    uint32_t fetch4 = 0;\n\n    /** Whether the texture bound to a slot has been changed and bindings in the backend need to be updated */\n    uint32_t textureDirty = 0;\n\n    /** Whether the texture bound to a slot has D3DUSAGE_RENDERTARGET */\n    uint32_t rtUsage = 0;\n\n    /** Whether the texture bound to a slot has D3DUSAGE_DEPTHSTENCIL */\n    uint32_t dsUsage = 0;\n\n    /** Whether the texture bound to a slot is also bound as a render target\n     * and the render target is actually used for writing. */\n    uint32_t unresolvableHazardRT = 0;\n\n    /** Whether the texture bound to a slot is also bound as the depth stencil surface\n     * and depth stencil surface is actually used for depth testing. */\n    uint32_t unresolvableHazardDS = 0;\n\n    /** Whether the texture bound to a slot is also bound as a render target */\n    uint32_t hazardRT = 0;\n\n    /** Whether the texture bound to a slot is also bound as the depth stencil view */\n    uint32_t hazardDS = 0;\n\n    /** Whether there's a texture bound to a slot */\n    uint32_t bound = 0;\n\n    /** Whether there's a texture bound to a slot that needs to be uploaded at draw time */\n    uint32_t needsUpload = 0;\n\n    /** Whether there's a texture bound to a slot that needs to have its mip maps generated */\n    uint32_t needsMipGen = 0;\n  };\n\n  struct D3D9RTSlotTracking {\n    /* D3D9 allows rendering to 4 render targets at the same time.\n     * So all RT bit masks only use the first 4 bits. */\n\n    /** Whether a render target is a D3D9Texture rather than just a D3D9Surface.\n      * Textures can be bound for sampling so there can be a feedback loop if this\n      * RT is also bound for sampling. */\n    uint8_t canBeSampled = 0;\n\n    /** Whether the alpha channel of the format of this RT needs to be manually handled\n      * as part of the blend state. */\n    uint8_t hasAlphaSwizzle = 0;\n  };\n\n  struct D3D9VBSlotTracking {\n    /* D3D9 allows using 16 vertex buffers ('streams'). */\n\n    /** Whether there's a vertex buffer bound to the slot */\n    uint16_t bound = 0;\n\n    /** Whether the vertex buffer at each slot needs to be uploaded at draw time */\n    uint16_t needsUpload = 0;\n\n    /** Whether the vertex buffer for each slot gets copied at draw time to act like DrawUP */\n    uint16_t uploadPerDraw = 0;\n\n    /** Whether instancing is enabled for each slot */\n    uint16_t instanced = 0;\n  };\n\n  class D3D9DeviceEx final : public ComObjectClamp<IDirect3DDevice9Ex> {\n    constexpr static uint32_t DefaultFrameLatency = 3;\n    constexpr static uint32_t MaxFrameLatency     = 20;\n\n    constexpr static uint32_t MinFlushIntervalUs = 750;\n    constexpr static uint32_t IncFlushIntervalUs = 250;\n    constexpr static uint32_t MaxPendingSubmits = 6;\n\n    constexpr static uint32_t NullStreamIdx = caps::MaxStreams;\n\n    constexpr static VkDeviceSize StagingBufferSize = 4ull << 20;\n\n    friend class D3D9SwapChainEx;\n    friend struct D3D9WindowContext;\n    friend class D3D9ConstantBuffer;\n    friend class D3D9UserDefinedAnnotation;\n    friend class DxvkD3D8Bridge;\n    friend D3D9VkInteropDevice;\n  public:\n\n    D3D9DeviceEx(\n            D3D9InterfaceEx*       pParent,\n            D3D9Adapter*           pAdapter,\n            D3DDEVTYPE             DeviceType,\n            HWND                   hFocusWindow,\n            DWORD                  BehaviorFlags,\n            Rc<DxvkDevice>         dxvkDevice);\n\n    ~D3D9DeviceEx();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    HRESULT STDMETHODCALLTYPE TestCooperativeLevel();\n\n    UINT    STDMETHODCALLTYPE GetAvailableTextureMem();\n\n    HRESULT STDMETHODCALLTYPE EvictManagedResources();\n\n    HRESULT STDMETHODCALLTYPE GetDirect3D(IDirect3D9** ppD3D9);\n\n    HRESULT STDMETHODCALLTYPE GetDeviceCaps(D3DCAPS9* pCaps);\n\n    HRESULT STDMETHODCALLTYPE GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode);\n\n    HRESULT STDMETHODCALLTYPE GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS *pParameters);\n\n    HRESULT STDMETHODCALLTYPE SetCursorProperties(\n            UINT               XHotSpot,\n            UINT               YHotSpot,\n            IDirect3DSurface9* pCursorBitmap);\n\n    void    STDMETHODCALLTYPE SetCursorPosition(int X, int Y, DWORD Flags);\n\n    BOOL    STDMETHODCALLTYPE ShowCursor(BOOL bShow);\n\n    HRESULT STDMETHODCALLTYPE CreateAdditionalSwapChain(\n            D3DPRESENT_PARAMETERS* pPresentationParameters,\n            IDirect3DSwapChain9**  ppSwapChain);\n\n    HRESULT STDMETHODCALLTYPE GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain);\n\n    UINT    STDMETHODCALLTYPE GetNumberOfSwapChains();\n\n    HRESULT STDMETHODCALLTYPE Reset(D3DPRESENT_PARAMETERS* pPresentationParameters);\n\n    HRESULT STDMETHODCALLTYPE Present(\n      const RECT* pSourceRect,\n      const RECT* pDestRect, HWND hDestWindowOverride,\n      const RGNDATA* pDirtyRegion);\n\n    HRESULT STDMETHODCALLTYPE GetBackBuffer(\n      UINT iSwapChain,\n      UINT iBackBuffer,\n      D3DBACKBUFFER_TYPE Type,\n      IDirect3DSurface9** ppBackBuffer);\n\n    HRESULT STDMETHODCALLTYPE GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus);\n\n    HRESULT STDMETHODCALLTYPE SetDialogBoxMode(BOOL bEnableDialogs);\n\n    void    STDMETHODCALLTYPE SetGammaRamp(\n      UINT iSwapChain,\n      DWORD Flags,\n      const D3DGAMMARAMP* pRamp);\n\n    void    STDMETHODCALLTYPE GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp);\n\n    HRESULT STDMETHODCALLTYPE CreateTexture(\n            UINT                Width,\n            UINT                Height,\n            UINT                Levels,\n            DWORD               Usage,\n            D3DFORMAT           Format,\n            D3DPOOL             Pool,\n            IDirect3DTexture9** ppTexture,\n            HANDLE*             pSharedHandle);\n\n    HRESULT STDMETHODCALLTYPE CreateVolumeTexture(\n            UINT                      Width,\n            UINT                      Height,\n            UINT                      Depth,\n            UINT                      Levels,\n            DWORD                     Usage,\n            D3DFORMAT                 Format,\n            D3DPOOL                   Pool,\n            IDirect3DVolumeTexture9** ppVolumeTexture,\n            HANDLE*                   pSharedHandle);\n\n    HRESULT STDMETHODCALLTYPE CreateCubeTexture(\n          UINT                      EdgeLength,\n            UINT                    Levels,\n            DWORD                   Usage,\n            D3DFORMAT               Format,\n            D3DPOOL                 Pool,\n            IDirect3DCubeTexture9** ppCubeTexture,\n            HANDLE*                 pSharedHandle);\n\n    HRESULT STDMETHODCALLTYPE CreateVertexBuffer(\n            UINT                     Length,\n            DWORD                    Usage,\n            DWORD                    FVF,\n            D3DPOOL                  Pool,\n            IDirect3DVertexBuffer9** ppVertexBuffer,\n            HANDLE*                  pSharedHandle);\n\n    HRESULT STDMETHODCALLTYPE CreateIndexBuffer(\n            UINT                    Length,\n            DWORD                   Usage,\n            D3DFORMAT               Format,\n            D3DPOOL                 Pool,\n            IDirect3DIndexBuffer9** ppIndexBuffer,\n            HANDLE*                 pSharedHandle);\n\n    HRESULT STDMETHODCALLTYPE CreateRenderTarget(\n            UINT                Width,\n            UINT                Height,\n            D3DFORMAT           Format,\n            D3DMULTISAMPLE_TYPE MultiSample,\n            DWORD               MultisampleQuality,\n            BOOL                Lockable,\n            IDirect3DSurface9** ppSurface,\n            HANDLE*             pSharedHandle);\n\n    HRESULT STDMETHODCALLTYPE CreateDepthStencilSurface(\n            UINT                Width,\n            UINT                Height,\n            D3DFORMAT           Format,\n            D3DMULTISAMPLE_TYPE MultiSample,\n            DWORD               MultisampleQuality,\n            BOOL                Discard,\n            IDirect3DSurface9** ppSurface,\n            HANDLE*             pSharedHandle);\n\n    HRESULT STDMETHODCALLTYPE UpdateSurface(\n            IDirect3DSurface9* pSourceSurface,\n      const RECT*              pSourceRect,\n            IDirect3DSurface9* pDestinationSurface,\n      const POINT*             pDestPoint);\n\n    HRESULT STDMETHODCALLTYPE UpdateTexture(\n            IDirect3DBaseTexture9* pSourceTexture,\n            IDirect3DBaseTexture9* pDestinationTexture);\n\n    HRESULT STDMETHODCALLTYPE GetRenderTargetData(\n            IDirect3DSurface9* pRenderTarget,\n            IDirect3DSurface9* pDestSurface);\n\n    HRESULT STDMETHODCALLTYPE GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface);\n\n    HRESULT STDMETHODCALLTYPE StretchRect(\n            IDirect3DSurface9*   pSourceSurface,\n      const RECT*                pSourceRect,\n            IDirect3DSurface9*   pDestSurface,\n      const RECT*                pDestRect,\n            D3DTEXTUREFILTERTYPE Filter);\n\n    HRESULT STDMETHODCALLTYPE ColorFill(\n            IDirect3DSurface9* pSurface,\n      const RECT*              pRect,\n            D3DCOLOR           Color);\n\n    HRESULT STDMETHODCALLTYPE CreateOffscreenPlainSurface(\n      UINT Width,\n      UINT Height,\n      D3DFORMAT Format,\n      D3DPOOL Pool,\n      IDirect3DSurface9** ppSurface,\n      HANDLE* pSharedHandle);\n\n    HRESULT STDMETHODCALLTYPE SetRenderTarget(\n            DWORD              RenderTargetIndex,\n            IDirect3DSurface9* pRenderTarget);\n\n    HRESULT STDMETHODCALLTYPE GetRenderTarget(\n            DWORD               RenderTargetIndex,\n            IDirect3DSurface9** ppRenderTarget);\n\n    HRESULT STDMETHODCALLTYPE SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil);\n\n    HRESULT STDMETHODCALLTYPE GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface);\n\n    HRESULT STDMETHODCALLTYPE BeginScene();\n\n    HRESULT STDMETHODCALLTYPE EndScene();\n\n    HRESULT STDMETHODCALLTYPE Clear(\n            DWORD    Count,\n      const D3DRECT* pRects,\n            DWORD    Flags,\n            D3DCOLOR Color,\n            float    Z,\n            DWORD    Stencil);\n\n    HRESULT STDMETHODCALLTYPE SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix);\n\n    HRESULT STDMETHODCALLTYPE GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix);\n\n    HRESULT STDMETHODCALLTYPE MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix);\n\n    HRESULT STDMETHODCALLTYPE SetViewport(const D3DVIEWPORT9* pViewport);\n\n    HRESULT STDMETHODCALLTYPE GetViewport(D3DVIEWPORT9* pViewport);\n\n    HRESULT STDMETHODCALLTYPE SetMaterial(const D3DMATERIAL9* pMaterial);\n\n    HRESULT STDMETHODCALLTYPE GetMaterial(D3DMATERIAL9* pMaterial);\n\n    HRESULT STDMETHODCALLTYPE SetLight(DWORD Index, const D3DLIGHT9* pLight);\n\n    HRESULT STDMETHODCALLTYPE GetLight(DWORD Index, D3DLIGHT9* pLight);\n\n    HRESULT STDMETHODCALLTYPE LightEnable(DWORD Index, BOOL Enable);\n\n    HRESULT STDMETHODCALLTYPE GetLightEnable(DWORD Index, BOOL* pEnable);\n\n    HRESULT STDMETHODCALLTYPE SetClipPlane(DWORD Index, const float* pPlane);\n\n    HRESULT STDMETHODCALLTYPE GetClipPlane(DWORD Index, float* pPlane);\n\n    HRESULT STDMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);\n\n    HRESULT STDMETHODCALLTYPE GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue);\n\n    HRESULT STDMETHODCALLTYPE CreateStateBlock(\n            D3DSTATEBLOCKTYPE      Type,\n            IDirect3DStateBlock9** ppSB);\n\n    HRESULT STDMETHODCALLTYPE BeginStateBlock();\n\n    HRESULT STDMETHODCALLTYPE EndStateBlock(IDirect3DStateBlock9** ppSB);\n\n    HRESULT STDMETHODCALLTYPE SetClipStatus(const D3DCLIPSTATUS9* pClipStatus);\n\n    HRESULT STDMETHODCALLTYPE GetClipStatus(D3DCLIPSTATUS9* pClipStatus);\n\n    HRESULT STDMETHODCALLTYPE GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture);\n\n    HRESULT STDMETHODCALLTYPE SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture);\n\n    HRESULT STDMETHODCALLTYPE GetTextureStageState(\n            DWORD                    Stage,\n            D3DTEXTURESTAGESTATETYPE Type,\n            DWORD*                   pValue);\n\n    HRESULT STDMETHODCALLTYPE SetTextureStageState(\n            DWORD                    Stage,\n            D3DTEXTURESTAGESTATETYPE Type,\n            DWORD                    Value);\n\n    HRESULT STDMETHODCALLTYPE GetSamplerState(\n            DWORD               Sampler,\n            D3DSAMPLERSTATETYPE Type,\n            DWORD*              pValue);\n\n    HRESULT STDMETHODCALLTYPE SetSamplerState(\n            DWORD               Sampler,\n            D3DSAMPLERSTATETYPE Type,\n            DWORD               Value);\n\n    HRESULT STDMETHODCALLTYPE ValidateDevice(DWORD* pNumPasses);\n\n    HRESULT STDMETHODCALLTYPE SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries);\n\n    HRESULT STDMETHODCALLTYPE GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries);\n\n    HRESULT STDMETHODCALLTYPE SetCurrentTexturePalette(UINT PaletteNumber);\n\n    HRESULT STDMETHODCALLTYPE GetCurrentTexturePalette(UINT *PaletteNumber);\n\n    HRESULT STDMETHODCALLTYPE SetScissorRect(const RECT* pRect);\n\n    HRESULT STDMETHODCALLTYPE GetScissorRect(RECT* pRect);\n\n    HRESULT STDMETHODCALLTYPE SetSoftwareVertexProcessing(BOOL bSoftware);\n\n    BOOL    STDMETHODCALLTYPE GetSoftwareVertexProcessing();\n\n    HRESULT STDMETHODCALLTYPE SetNPatchMode(float nSegments);\n\n    float   STDMETHODCALLTYPE GetNPatchMode();\n\n    HRESULT STDMETHODCALLTYPE DrawPrimitive(\n            D3DPRIMITIVETYPE PrimitiveType,\n            UINT             StartVertex,\n            UINT             PrimitiveCount);\n\n    HRESULT STDMETHODCALLTYPE DrawIndexedPrimitive(\n            D3DPRIMITIVETYPE PrimitiveType,\n            INT              BaseVertexIndex,\n            UINT             MinVertexIndex,\n            UINT             NumVertices,\n            UINT             StartIndex,\n            UINT             PrimitiveCount);\n\n    HRESULT STDMETHODCALLTYPE DrawPrimitiveUP(\n            D3DPRIMITIVETYPE PrimitiveType,\n            UINT             PrimitiveCount,\n      const void*            pVertexStreamZeroData,\n            UINT             VertexStreamZeroStride);\n\n    HRESULT STDMETHODCALLTYPE DrawIndexedPrimitiveUP(\n            D3DPRIMITIVETYPE PrimitiveType,\n            UINT             MinVertexIndex,\n            UINT             NumVertices,\n            UINT             PrimitiveCount,\n      const void*            pIndexData,\n            D3DFORMAT        IndexDataFormat,\n      const void*            pVertexStreamZeroData,\n            UINT             VertexStreamZeroStride);\n\n    HRESULT STDMETHODCALLTYPE ProcessVertices(\n            UINT                         SrcStartIndex,\n            UINT                         DestIndex,\n            UINT                         VertexCount,\n            IDirect3DVertexBuffer9*      pDestBuffer,\n            IDirect3DVertexDeclaration9* pVertexDecl,\n            DWORD                        Flags);\n\n    HRESULT STDMETHODCALLTYPE CreateVertexDeclaration(\n      const D3DVERTEXELEMENT9*            pVertexElements,\n            IDirect3DVertexDeclaration9** ppDecl);\n\n    HRESULT STDMETHODCALLTYPE SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl);\n\n    HRESULT STDMETHODCALLTYPE GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl);\n\n    HRESULT STDMETHODCALLTYPE SetFVF(DWORD FVF);\n\n    HRESULT STDMETHODCALLTYPE GetFVF(DWORD* pFVF);\n\n    HRESULT STDMETHODCALLTYPE CreateVertexShader(\n      const DWORD*                   pFunction,\n            IDirect3DVertexShader9** ppShader);\n\n    HRESULT STDMETHODCALLTYPE SetVertexShader(IDirect3DVertexShader9* pShader);\n\n    HRESULT STDMETHODCALLTYPE GetVertexShader(IDirect3DVertexShader9** ppShader);\n\n    HRESULT STDMETHODCALLTYPE SetVertexShaderConstantF(\n            UINT   StartRegister,\n      const float* pConstantData,\n            UINT   Vector4fCount);\n\n    HRESULT STDMETHODCALLTYPE GetVertexShaderConstantF(\n            UINT   StartRegister,\n            float* pConstantData,\n            UINT   Vector4fCount);\n\n    HRESULT STDMETHODCALLTYPE SetVertexShaderConstantI(\n            UINT StartRegister,\n      const int* pConstantData,\n            UINT Vector4iCount);\n\n    HRESULT STDMETHODCALLTYPE GetVertexShaderConstantI(\n            UINT StartRegister,\n            int* pConstantData,\n            UINT Vector4iCount);\n\n    HRESULT STDMETHODCALLTYPE SetVertexShaderConstantB(\n            UINT  StartRegister,\n      const BOOL* pConstantData,\n            UINT  BoolCount);\n\n    HRESULT STDMETHODCALLTYPE GetVertexShaderConstantB(\n            UINT  StartRegister,\n            BOOL* pConstantData,\n            UINT  BoolCount);\n\n    HRESULT STDMETHODCALLTYPE SetStreamSource(\n            UINT                    StreamNumber,\n            IDirect3DVertexBuffer9* pStreamData,\n            UINT                    OffsetInBytes,\n            UINT                    Stride);\n\n    HRESULT STDMETHODCALLTYPE GetStreamSource(\n            UINT                     StreamNumber,\n            IDirect3DVertexBuffer9** ppStreamData,\n            UINT*                    pOffsetInBytes,\n            UINT*                    pStride);\n\n    HRESULT STDMETHODCALLTYPE SetStreamSourceFreq(UINT StreamNumber, UINT Setting);\n\n    HRESULT STDMETHODCALLTYPE GetStreamSourceFreq(UINT StreamNumber, UINT* pSetting);\n\n    HRESULT STDMETHODCALLTYPE SetIndices(IDirect3DIndexBuffer9* pIndexData);\n\n    HRESULT STDMETHODCALLTYPE GetIndices(IDirect3DIndexBuffer9** ppIndexData);\n\n    HRESULT STDMETHODCALLTYPE CreatePixelShader(\n      const DWORD*                  pFunction,\n            IDirect3DPixelShader9** ppShader);\n\n    HRESULT STDMETHODCALLTYPE SetPixelShader(IDirect3DPixelShader9* pShader);\n\n    HRESULT STDMETHODCALLTYPE GetPixelShader(IDirect3DPixelShader9** ppShader);\n\n    HRESULT STDMETHODCALLTYPE SetPixelShaderConstantF(\n            UINT   StartRegister,\n      const float* pConstantData,\n            UINT   Vector4fCount);\n\n    HRESULT STDMETHODCALLTYPE GetPixelShaderConstantF(\n            UINT   StartRegister,\n            float* pConstantData,\n            UINT   Vector4fCount);\n\n    HRESULT STDMETHODCALLTYPE SetPixelShaderConstantI(\n            UINT StartRegister,\n      const int* pConstantData,\n            UINT Vector4iCount);\n\n    HRESULT STDMETHODCALLTYPE GetPixelShaderConstantI(\n            UINT StartRegister,\n            int* pConstantData,\n            UINT Vector4iCount);\n\n    HRESULT STDMETHODCALLTYPE SetPixelShaderConstantB(\n            UINT  StartRegister,\n      const BOOL* pConstantData,\n            UINT  BoolCount);\n\n    HRESULT STDMETHODCALLTYPE GetPixelShaderConstantB(\n            UINT  StartRegister,\n            BOOL* pConstantData,\n            UINT  BoolCount);\n\n    HRESULT STDMETHODCALLTYPE DrawRectPatch(\n            UINT               Handle,\n      const float*             pNumSegs,\n      const D3DRECTPATCH_INFO* pRectPatchInfo);\n\n    HRESULT STDMETHODCALLTYPE DrawTriPatch(\n            UINT              Handle,\n      const float*            pNumSegs,\n      const D3DTRIPATCH_INFO* pTriPatchInfo);\n\n    HRESULT STDMETHODCALLTYPE DeletePatch(UINT Handle);\n\n    HRESULT STDMETHODCALLTYPE CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery);\n\n    // Ex Methods\n\n    HRESULT STDMETHODCALLTYPE SetConvolutionMonoKernel(\n            UINT   width,\n            UINT   height,\n            float* rows,\n            float* columns);\n\n    HRESULT STDMETHODCALLTYPE ComposeRects(\n            IDirect3DSurface9*      pSrc,\n            IDirect3DSurface9*      pDst,\n            IDirect3DVertexBuffer9* pSrcRectDescs,\n            UINT                    NumRects,\n            IDirect3DVertexBuffer9* pDstRectDescs,\n            D3DCOMPOSERECTSOP       Operation,\n            int                     Xoffset,\n            int                     Yoffset);\n\n    HRESULT STDMETHODCALLTYPE GetGPUThreadPriority(INT* pPriority);\n\n    HRESULT STDMETHODCALLTYPE SetGPUThreadPriority(INT Priority);\n\n    HRESULT STDMETHODCALLTYPE WaitForVBlank(UINT iSwapChain);\n\n    HRESULT STDMETHODCALLTYPE CheckResourceResidency(IDirect3DResource9** pResourceArray, UINT32 NumResources);\n\n    HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(UINT MaxLatency);\n\n    HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(UINT* pMaxLatency);\n\n    HRESULT STDMETHODCALLTYPE CheckDeviceState(HWND hDestinationWindow);\n\n    HRESULT STDMETHODCALLTYPE PresentEx(\n      const RECT*    pSourceRect,\n      const RECT*    pDestRect,\n            HWND     hDestWindowOverride,\n      const RGNDATA* pDirtyRegion,\n            DWORD    dwFlags);\n\n    HRESULT STDMETHODCALLTYPE CreateRenderTargetEx(\n            UINT                Width,\n            UINT                Height,\n            D3DFORMAT           Format,\n            D3DMULTISAMPLE_TYPE MultiSample,\n            DWORD               MultisampleQuality,\n            BOOL                Lockable,\n            IDirect3DSurface9** ppSurface,\n            HANDLE*             pSharedHandle,\n            DWORD               Usage);\n\n    HRESULT STDMETHODCALLTYPE CreateOffscreenPlainSurfaceEx(\n            UINT                Width,\n            UINT                Height,\n            D3DFORMAT           Format,\n            D3DPOOL             Pool,\n            IDirect3DSurface9** ppSurface,\n            HANDLE*             pSharedHandle,\n            DWORD               Usage);\n\n    HRESULT STDMETHODCALLTYPE CreateDepthStencilSurfaceEx(\n            UINT                Width,\n            UINT                Height,\n            D3DFORMAT           Format,\n            D3DMULTISAMPLE_TYPE MultiSample,\n            DWORD               MultisampleQuality,\n            BOOL                Discard,\n            IDirect3DSurface9** ppSurface,\n            HANDLE*             pSharedHandle,\n            DWORD               Usage);\n\n    HRESULT STDMETHODCALLTYPE ResetEx(\n            D3DPRESENT_PARAMETERS* pPresentationParameters,\n            D3DDISPLAYMODEEX*      pFullscreenDisplayMode);\n\n    HRESULT STDMETHODCALLTYPE GetDisplayModeEx(\n            UINT                iSwapChain,\n            D3DDISPLAYMODEEX*   pMode,\n            D3DDISPLAYROTATION* pRotation);\n\n    HRESULT STDMETHODCALLTYPE CreateAdditionalSwapChainEx(\n            D3DPRESENT_PARAMETERS* pPresentationParameters,\n      const D3DDISPLAYMODEEX*      pFullscreenDisplayMode,\n            IDirect3DSwapChain9**  ppSwapChain);\n\n    /**\n     * @brief Sets the given sampler state\n     *\n     * @param StateSampler Sampler index (according to our internal way of storing samplers)\n     * @param Type Sampler state type to change\n     * @param Value State value\n     */\n    HRESULT SetStateSamplerState(\n        DWORD               StateSampler,\n        D3DSAMPLERSTATETYPE Type,\n        DWORD               Value);\n\n    /**\n     * @brief Sets the given sampler texture\n     *\n     * @param StateSampler Sampler index (according to our internal way of storing samplers)\n     * @param pTexture Texture to use\n     */\n    HRESULT SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture);\n\n    /**\n     * @brief Sets the transform for the given sampler\n     *\n     * @param idx Sampler index (according to our internal way of storing samplers)\n     * @param pMatrix Transform matrix\n     */\n    HRESULT SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix);\n\n    /**\n     * @brief Sets the fixed function texture processing state\n     *\n     * @param Stage Sampler index (according to our internal way of storing samplers)\n     * @param Type Fixed function texture stage type\n     * @param Value Value for the state\n     */\n    HRESULT SetStateTextureStageState(\n            DWORD                      Stage,\n            D3D9TextureStageStateTypes Type,\n            DWORD                      Value);\n\n    VkPipelineStageFlags GetEnabledShaderStages() const {\n      return m_dxvkDevice->getShaderPipelineStages();\n    }\n\n    /**\n     * \\brief Returns whether the Vulkan device supports the required features for ProcessVertices\n     */\n    bool SupportsSWVP();\n\n    bool SupportsVCacheQuery() const;\n\n    bool IsExtended();\n\n    HWND GetWindow();\n\n    const Rc<DxvkDevice>& GetDXVKDevice() {\n      return m_dxvkDevice;\n    }\n\n    D3D9_VK_FORMAT_MAPPING LookupFormat(\n      D3D9Format            Format) const;\n\n    const DxvkFormatInfo* UnsupportedFormatInfo(\n      D3D9Format            Format) const;\n\n    bool WaitForResource(\n      const DxvkPagedResource&      Resource,\n            uint64_t                SequenceNumber,\n            DWORD                   MapFlags);\n\n    /**\n     * \\brief Locks a subresource of an image\n     *\n     * \\param [in] Subresource The subresource of the image to lock\n     * \\param [out] pLockedBox The returned locked box of the image, containing data ptr and strides\n     * \\param [in] pBox The region of the subresource to lock. This offsets the returned data ptr\n     * \\param [in] Flags The D3DLOCK_* flags to lock the image with\n     * \\returns \\c D3D_OK if the parameters are valid or D3DERR_INVALIDCALL if it fails.\n     */\n    HRESULT LockImage(\n            D3D9CommonTexture* pResource,\n            UINT                    Face,\n            UINT                    Mip,\n            D3DLOCKED_BOX*          pLockedBox,\n      const D3DBOX*                 pBox,\n            DWORD                   Flags);\n\n    uint32_t CalcImageLockOffset(\n            uint32_t                SlicePitch,\n            uint32_t                RowPitch,\n      const DxvkFormatInfo*         FormatInfo,\n      const D3DBOX*                 pBox);\n\n    /**\n     * \\brief Unlocks a subresource of an image\n     *\n     * Passthrough to device unlock.\n     * \\param [in] Subresource The subresource of the image to unlock\n     * \\returns \\c D3D_OK if the parameters are valid or D3DERR_INVALIDCALL if it fails.\n     */\n    HRESULT UnlockImage(\n            D3D9CommonTexture*      pResource,\n            UINT                    Face,\n            UINT                    MipLevel);\n\n    /**\n     * \\brief Uploads the given texture subresource from its local system memory copy.\n     */\n    HRESULT FlushImage(\n            D3D9CommonTexture*      pResource,\n            UINT                    Subresource);\n\n    /**\n     * \\brief Copies the given part of a texture from the local system memory copy of the source texture\n     * to the image of the destination texture.\n     */\n    void UpdateTextureFromBuffer(\n            D3D9CommonTexture*      pDestTexture,\n            D3D9CommonTexture*      pSrcTexture,\n            UINT                    DestSubresource,\n            UINT                    SrcSubresource,\n            VkOffset3D              SrcOffset,\n            VkExtent3D              SrcExtent,\n            VkOffset3D              DestOffset);\n\n    void EmitGenerateMips(\n            D3D9CommonTexture* pResource);\n\n    HRESULT LockBuffer(\n            D3D9CommonBuffer*       pResource,\n            UINT                    OffsetToLock,\n            UINT                    SizeToLock,\n            void**                  ppbData,\n            DWORD                   Flags);\n\n    /**\n     * \\brief Uploads the given buffer from its local system memory copy.\n     */\n    HRESULT FlushBuffer(\n            D3D9CommonBuffer*       pResource);\n\n    HRESULT UnlockBuffer(\n            D3D9CommonBuffer*       pResource);\n\n    /**\n     * @brief Uploads data from D3DPOOL_SYSMEM + D3DUSAGE_DYNAMIC buffers and binds the temporary buffers.\n     *\n     * @param FirstVertexIndex The first vertex\n     * @param NumVertices The number of vertices that are accessed. If this is 0, the vertex buffer binding will not be modified.\n     * @param FirstIndex The first index\n     * @param NumIndices The number of indices that will be drawn. If this is 0, the index buffer binding will not be modified.\n     */\n    void UploadPerDrawData(\n            UINT&                   FirstVertexIndex,\n            UINT                    NumVertices,\n            UINT&                   FirstIndex,\n            UINT                    NumIndices,\n            INT&                    BaseVertexIndex,\n            bool*                   pDynamicVBOs,\n            bool*                   pDynamicIBO);\n\n\n    void SetupFPU();\n\n    int64_t DetermineInitialTextureMemory();\n\n    void CreateConstantBuffers();\n\n    void SynchronizeCsThread(uint64_t SequenceNumber);\n\n    void Flush();\n    void FlushAndSync9On12();\n\n    void BeginFrame(Rc<DxvkLatencyTracker> LatencyTracker, uint64_t FrameId);\n    void EndFrame(Rc<DxvkLatencyTracker> LatencyTracker);\n\n    template <uint32_t Index>\n    void UpdateAnyColorWrites();\n\n    void UpdateTextureBitmasks(uint32_t index, DWORD combinedUsage);\n\n    void UpdateActiveHazardsRT(uint32_t texMask);\n\n    void UpdateActiveHazardsDS(uint32_t texMask);\n\n    void EmitFeedbackLoopBarriers();\n\n    void UpdateActiveFetch4(uint32_t stateSampler);\n\n    /**\n     * @brief Sets the mismatching texture type bits for all samplers if necessary.\n     *\n     * This function will check all samplers the shader uses and set the  set the mismatching texture type bit for the given sampler if it does not\n     * match the texture type expected by the respective shader.\n     *\n     * It will *not* unset the bit if the texture type does match.\n     *\n     * @param stateSampler Sampler index (according to our internal way of storing samplers)\n     */\n\n     /**\n      * @brief Sets the mismatching texture type bits for all samplers if necessary.\n      *\n      * This function will check all samplers the shader uses and set the  set the mismatching texture type bit for the given sampler if it does not\n      * match the texture type expected by the shader.\n      *\n      * @param shader The shader\n      * @param shaderSamplerMask Mask of all samplers that the shader uses (according to our internal way of storing samplers)\n      * @param shaderSamplerOffset First index of the shader's samplers according to our internal way of storing samplers.\n      *                            Used to transform the sampler indices that are relative to the entire pipeline to ones relative to the shader.\n      */\n    void UpdateTextureTypeMismatchesForShader(const D3D9CommonShader* shader, uint32_t shaderSamplerMask, uint32_t shaderSamplerOffset);\n\n    /**\n     * @brief Sets the mismatching texture type bit for the given sampler.\n     *\n     * This function will set the mismatching texture type bit for the given sampler if it does not\n     * match the texture type expected by the respective shader.\n     *\n     * It will *not* unset the bit if the texture type does match.\n     *\n     * @param stateSampler Sampler index (according to our internal way of storing samplers)\n     */\n    void UpdateTextureTypeMismatchesForTexture(uint32_t stateSampler);\n\n    void UploadManagedTexture(D3D9CommonTexture* pResource);\n\n    void UploadManagedTextures(uint32_t mask);\n\n    void GenerateTextureMips(uint32_t mask);\n\n    void MarkTextureMipsDirty(D3D9CommonTexture* pResource);\n\n    void MarkTextureMipsUnDirty(D3D9CommonTexture* pResource);\n\n    void MarkTextureUploaded(D3D9CommonTexture* pResource);\n\n    void UpdatePointMode(bool pointList);\n\n    void UpdateFog();\n\n    void BindFramebuffer();\n\n    void BindViewportAndScissor();\n\n    inline bool IsNVDepthBoundsTestEnabled () {\n      // NVDB is not supported by D3D8\n      if (unlikely(m_isD3D8Compatible))\n        return false;\n\n      return m_state.renderStates[D3DRS_ADAPTIVETESS_X] == uint32_t(D3D9Format::NVDB);\n    }\n\n    void UpdateAlphaToCoverangeAndAlphaTest();\n\n    inline bool IsZTestEnabled() {\n      return m_state.renderStates[D3DRS_ZENABLE] && m_state.depthStencil != nullptr;\n    }\n\n    void BindMultiSampleState();\n\n    void BindBlendState();\n\n    void BindBlendFactor();\n\n    void BindDepthStencilState();\n\n    void BindDepthStencilReference();\n\n    void BindRasterizerState();\n\n    void BindDepthBias();\n\n    inline void UploadSoftwareConstantSet(const D3D9ShaderConstantsVSSoftware& Src, const D3D9ConstantLayout& Layout);\n\n    inline void* CopySoftwareConstants(D3D9ConstantBuffer& dstBuffer, const void* src, uint32_t size);\n\n    template <D3D9ShaderType ShaderStage, typename HardwareLayoutType, typename SoftwareLayoutType, typename ShaderType>\n    inline void UploadConstantSet(const SoftwareLayoutType& Src, const D3D9ConstantLayout& Layout, const ShaderType& Shader);\n\n    template <D3D9ShaderType ShaderStage>\n    void UploadConstants();\n\n    void UpdateClipPlanes();\n\n    /**\n     * \\brief Updates the push constant data at the given offset with data from the specified pointer.\n     *\n     * \\param Offset Offset at which the push constant data gets written.\n     * \\param Length Length of the push constant data to write.\n     * \\param pData Push constant data\n     */\n    template <uint32_t Offset, uint32_t Length>\n    void UpdatePushConstant(const void* pData);\n\n    /**\n     * \\brief Updates the specified push constant based on the device state.\n     *\n     * \\param Item Render state push constant to update\n     */\n    template <D3D9RenderStateItem Item>\n    void UpdatePushConstant();\n\n    void BindSampler(DWORD Sampler);\n\n    void BindTexture(DWORD SamplerSampler);\n\n    void UnbindTextures(uint32_t mask);\n\n    void UndirtySamplers(uint32_t mask);\n\n    void UndirtyTextures(uint32_t usedMask);\n\n    void MarkTextureBindingDirty(IDirect3DBaseTexture9* texture);\n\n    bool SamplerUsesBorderColor(DWORD Sampler) const;\n\n    HRESULT STDMETHODCALLTYPE SetRenderTargetInternal(\n            DWORD              RenderTargetIndex,\n            IDirect3DSurface9* pRenderTarget);\n\n    D3D9DrawInfo GenerateDrawInfo(\n      D3DPRIMITIVETYPE PrimitiveType,\n      UINT             PrimitiveCount,\n      UINT             InstanceCount);\n\n    uint32_t GetInstanceCount() const;\n\n    void PrepareDraw(D3DPRIMITIVETYPE PrimitiveType, bool UploadVBOs, bool UploadIBOs);\n\n    void EnsureSamplerLimit();\n\n    template <D3D9ShaderType ShaderStage>\n    void BindShader(\n    const D3D9CommonShader*                 pShaderModule);\n\n    template <D3D9ShaderType ShaderStage>\n    void BindFFUbershader();\n\n    void BindInputLayout();\n\n    void BindVertexBuffer(\n            UINT                              Slot,\n            D3D9VertexBuffer*                 pBuffer,\n            UINT                              Offset,\n            UINT                              Stride);\n\n    void BindIndices();\n\n    D3D9DeviceLock LockDevice() {\n      return m_multithread.AcquireLock();\n    }\n\n    const D3D9Options* GetOptions() const {\n      return &m_d3d9Options;\n    }\n\n    Direct3DState9* GetRawState() {\n      return &m_state;\n    }\n\n    void Begin(D3D9Query* pQuery);\n    void End(D3D9Query* pQuery);\n\n    void SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits);\n    void SetPixelBoolBitfield (uint32_t idx, uint32_t mask, uint32_t bits);\n\n    void ConsiderFlush(GpuFlushType FlushType);\n\n    bool ChangeReportedMemory(int64_t delta) {\n      if (IsExtended())\n        return true;\n\n      int64_t availableMemory = m_availableMemory.fetch_add(delta);\n\n      return !m_d3d9Options.memoryTrackTest || availableMemory >= delta;\n    }\n\n    void ResolveZ();\n\n    void TransformImage(\n            D3D9CommonTexture*       pResource,\n      const VkImageSubresourceRange* pSubresources,\n            VkImageLayout            OldLayout,\n            VkImageLayout            NewLayout);\n\n    const D3D9ConstantLayout& GetVertexConstantLayout() { return m_consts[uint32_t(D3D9ShaderType::VertexShader)].layout; }\n    const D3D9ConstantLayout& GetPixelConstantLayout()  { return m_consts[uint32_t(D3D9ShaderType::PixelShader)].layout; }\n\n    void ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters);\n    HRESULT ResetSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode);\n\n    HRESULT InitialReset(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode);\n\n    /**\n     * \\brief Returns the allocator used for unmappable system memory texture data\n     */\n    D3D9MemoryAllocator* GetAllocator() {\n      return &m_memoryAllocator;\n    }\n\n    /**\n     * \\brief Gets the pointer of the system memory copy of the texture\n     *\n     * Also tracks the texture if it is unmappable.\n     */\n    void* MapTexture(D3D9CommonTexture* pTexture, UINT Subresource);\n\n    /**\n     * \\brief Moves the texture to the front of the LRU list of mapped textures\n     */\n    void TouchMappedTexture(D3D9CommonTexture* pTexture);\n\n    /**\n     * \\brief Removes the texture from the LRU list of mapped textures\n     */\n    void RemoveMappedTexture(D3D9CommonTexture* pTexture);\n\n    /**\n     * \\brief Returns whether the device is currently recording a StateBlock\n     */\n    bool ShouldRecord() const {\n      return m_recorder != nullptr;\n    }\n\n    bool IsD3D8Compatible() const {\n      return m_isD3D8Compatible;\n    }\n\n    // Device Lost\n    bool IsDeviceLost() const {\n      return m_deviceLostState != D3D9DeviceLostState::Ok;\n    }\n\n    void NotifyFullscreen(HWND window, bool fullscreen);\n    void NotifyWindowActivated(HWND window, bool activated);\n\n    /**\n     * \\brief Increases the amount of D3DPOOL_DEFAULT resources that block a device reset\n     */\n    void IncrementLosableCounter() {\n      m_losableResourceCounter++;\n    }\n\n    /**\n     * \\brief Decreases the amount of D3DPOOL_DEFAULT resources that block a device reset\n     */\n    void DecrementLosableCounter() {\n      m_losableResourceCounter--;\n    }\n\n    /**\n     * \\brief Returns whether the device is configured to only support vertex processing.\n     */\n    bool CanOnlySWVP() const {\n      return m_behaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING;\n    }\n\n    /**\n     * \\brief Returns whether the device can be set to do software vertex processing.\n     * It may also be set up to only support software vertex processing.\n     */\n    bool CanSWVP() const {\n      return m_behaviorFlags & (D3DCREATE_MIXED_VERTEXPROCESSING | D3DCREATE_SOFTWARE_VERTEXPROCESSING);\n    }\n\n    /**\n     * \\brief Returns whether or not the device is currently set to do software vertex processing.\n     */\n    bool IsSWVP() const {\n      return m_isSWVP;\n    }\n\n    /**\n     * \\brief Returns the number of vertex shader modules generated for fixed function state.\n     */\n    UINT GetFixedFunctionVSCount() const {\n      return m_ffModules.GetVSCount();\n    }\n\n    /**\n     * \\brief Returns the number of fragment shader modules generated for fixed function state.\n     */\n    UINT GetFixedFunctionFSCount() const {\n      return m_ffModules.GetFSCount();\n    }\n\n    /**\n     * \\brief Returns the number of shader modules generated for ProcessVertices.\n     */\n    UINT GetSWVPShaderCount() const {\n      return m_swvpEmulator.GetShaderCount();\n    }\n\n    void InjectCsChunk(\n            DxvkCsChunkRef&&            Chunk,\n            bool                        Synchronize);\n\n    template<typename Fn>\n    void InjectCs(\n            Fn&&                        Command) {\n      auto chunk = AllocCsChunk();\n      chunk->push(std::move(Command));\n\n      InjectCsChunk(std::move(chunk), false);\n    }\n\n    DxvkCsChunkRef AllocCsChunk() {\n      DxvkCsChunk* chunk = m_csChunkPool.allocChunk(DxvkCsChunkFlag::SingleUse);\n      return DxvkCsChunkRef(chunk, &m_csChunkPool);\n    }\n\n    bool Is9On12Device() const {\n      return m_d3d9On12Args.Enable9On12;\n    }\n\n    D3D9Adapter* GetAdapter() const {\n      return m_adapter;\n    }\n\n  private:\n\n    template<bool AllowFlush = true, typename Cmd>\n    void EmitCs(Cmd&& command) {\n      if (unlikely(!m_csChunk->push(command))) {\n        EmitCsChunk(std::move(m_csChunk));\n        m_csChunk = AllocCsChunk();\n\n        if constexpr (AllowFlush)\n          ConsiderFlush(GpuFlushType::ImplicitWeakHint);\n\n        m_csChunk->push(command);\n      }\n    }\n\n    void EmitCsChunk(DxvkCsChunkRef&& chunk);\n\n    void FlushCsChunk() {\n      if (likely(!m_csChunk->empty())) {\n        EmitCsChunk(std::move(m_csChunk));\n        m_csChunk = AllocCsChunk();\n      }\n    }\n\n    /**\n     * \\brief Queries current reset counter\n     * Used for the deferred surface creation workaround.\n     * (Device Reset detection for D3D9SwapChainEx::Present)\n     */\n    uint32_t GetResetCounter() {\n      return m_resetCtr;\n    }\n\n    template <bool Synchronize9On12>\n    void ExecuteFlush();\n\n    void DetermineConstantLayouts(bool canSWVP);\n\n    /**\n     * \\brief Allocates buffer memory for DrawPrimitiveUp draws\n     */\n    D3D9BufferSlice AllocUPBuffer(VkDeviceSize size);\n\n    /**\n     * \\brief Allocates buffer memory for resource uploads\n     */\n    D3D9BufferSlice AllocStagingBuffer(VkDeviceSize size);\n\n    /**\n     * \\brief Waits until the amount of used staging memory is below a certain threshold.\n     */\n    void ThrottleAllocation();\n\n    DxvkStagingBufferStats GetStagingMemoryStatistics() const;\n\n    HRESULT               CreateShaderModule(\n            D3D9CommonShader*     pShaderModule,\n            uint32_t*             pLength,\n            VkShaderStageFlagBits ShaderStage,\n      const DWORD*                pShaderBytecode,\n      const DxsoModuleInfo*       pModuleInfo);\n\n    inline uint32_t GetUPDataSize(uint32_t vertexCount, uint32_t stride) {\n      return vertexCount * stride;\n    }\n\n    inline uint32_t GetUPBufferSize(uint32_t vertexCount, uint32_t stride) {\n      return (vertexCount - 1) * stride + std::max(m_state.vertexDecl->GetSize(0), stride);\n    }\n\n    /**\n     * \\brief Writes data to the given pointer and zeroes any access buffer space\n     */\n    inline void FillUPVertexBuffer(void* buffer, const void* userData, uint32_t dataSize, uint32_t bufferSize) {\n      uint8_t* data = reinterpret_cast<uint8_t*>(buffer);\n      // Don't copy excess data if we don't end up needing it.\n      dataSize = std::min(dataSize, bufferSize);\n      std::memcpy(data, userData, dataSize);\n      // Pad out with 0 to make buffer range checks happy\n      // Some games have components out of range in the vertex decl\n      // that they don't read from the shader.\n      // My tests show that these are read back as 0 always if out of range of\n      // the dataSize.\n      //\n      // So... make the actual buffer the range that satisfies the range of the vertex\n      // declaration and pad with 0s outside of it.\n      if (dataSize < bufferSize)\n        std::memset(data + dataSize, 0, bufferSize - dataSize);\n    }\n\n    // So we don't do OOB.\n    template <D3D9ShaderType   ShaderType,\n              D3D9ConstantType ConstantType>\n    inline static constexpr uint32_t DetermineSoftwareRegCount() {\n      constexpr bool isVS = ShaderType == D3D9ShaderType::VertexShader;\n\n      switch (ConstantType) {\n        default:\n        case D3D9ConstantType::Float:  return isVS ? caps::MaxFloatConstantsSoftware : caps::MaxSM3FloatConstantsPS;\n        case D3D9ConstantType::Int:    return isVS ? caps::MaxOtherConstantsSoftware : caps::MaxOtherConstants;\n        case D3D9ConstantType::Bool:   return isVS ? caps::MaxOtherConstantsSoftware : caps::MaxOtherConstants;\n      }\n    }\n\n    // So we don't copy more than we need.\n    template <D3D9ShaderType   ShaderType,\n              D3D9ConstantType ConstantType>\n    inline uint32_t DetermineHardwareRegCount() const {\n      const auto& layout = m_consts[uint32_t(ShaderType)].layout;\n\n      switch (ConstantType) {\n        default:\n        case D3D9ConstantType::Float:  return layout.floatCount;\n        case D3D9ConstantType::Int:    return layout.intCount;\n        case D3D9ConstantType::Bool:   return layout.boolCount;\n      }\n    }\n\n    inline uint32_t GetFrameLatency() {\n      return m_frameLatency;\n    }\n\n    template <\n      D3D9ShaderType   ShaderType,\n      D3D9ConstantType ConstantType,\n      typename         T>\n      HRESULT SetShaderConstants(\n              UINT  StartRegister,\n        const T*    pConstantData,\n              UINT  Count);\n\n    template <\n      D3D9ShaderType   ShaderType,\n      D3D9ConstantType ConstantType,\n      typename         T>\n    HRESULT GetShaderConstants(\n            UINT StartRegister,\n            T*   pConstantData,\n            UINT Count) {\n      auto GetHelper = [&] (const auto& set) {\n        const     uint32_t regCountHardware = DetermineHardwareRegCount<ShaderType, ConstantType>();\n        constexpr uint32_t regCountSoftware = DetermineSoftwareRegCount<ShaderType, ConstantType>();\n\n        if (StartRegister + Count > regCountSoftware)\n          return D3DERR_INVALIDCALL;\n\n        Count = UINT(\n          std::max<INT>(\n            std::clamp<INT>(Count + StartRegister, 0, regCountHardware) - INT(StartRegister),\n            0));\n\n        if (Count == 0)\n          return D3D_OK;\n\n        if (pConstantData == nullptr)\n          return D3DERR_INVALIDCALL;\n\n        if constexpr (ConstantType == D3D9ConstantType::Float) {\n          const float* source = set->fConsts[StartRegister].data;\n          const size_t size   = Count * sizeof(Vector4);\n\n          std::memcpy(pConstantData, source, size);\n        }\n        else if constexpr (ConstantType == D3D9ConstantType::Int) {\n          const int*  source = set->iConsts[StartRegister].data;\n          const size_t size  = Count * sizeof(Vector4i);\n\n          std::memcpy(pConstantData, source, size);\n        }\n        else {\n          for (uint32_t i = 0; i < Count; i++) {\n            const uint32_t constantIdx = StartRegister + i;\n            const uint32_t arrayIdx    = constantIdx / 32;\n            const uint32_t bitIdx      = constantIdx % 32;\n\n            const uint32_t bit         = (1u << bitIdx);\n\n            bool constValue = set->bConsts[arrayIdx] & bit;\n            pConstantData[i] = constValue ? TRUE : FALSE;\n          }\n        }\n\n        return D3D_OK;\n      };\n\n      return ShaderType == D3D9ShaderType::VertexShader\n        ? GetHelper(m_state.vsConsts)\n        : GetHelper(m_state.psConsts);\n    }\n\n    void UpdateFixedFunctionVS();\n\n    void UpdateFixedFunctionPS();\n\n    void ApplyPrimitiveType(\n      DxvkContext*      pContext,\n      D3DPRIMITIVETYPE  PrimType);\n\n    bool UseProgrammableVS();\n\n    bool UseProgrammablePS();\n\n    uint32_t GetAlphaTestPrecision();\n\n    void BindAlphaTestState();\n\n    void UpdateAlphaTestSpec(VkCompareOp alphaOp, uint32_t precision);\n    void UpdateVertexBoolSpec(uint32_t value);\n    void UpdatePixelBoolSpec(uint32_t value);\n    void UpdatePixelShaderSamplerSpec(uint32_t types, uint32_t fetch4);\n    void UpdateCommonSamplerSpec(uint32_t boundMask, uint32_t depthMask, uint32_t drefMask, uint32_t projections);\n    void UpdatePointModeSpec(uint32_t mode);\n    void UpdateFogModeSpec(bool fogEnabled, D3DFOGMODE vertexFogMode, D3DFOGMODE pixelFogMode);\n\n    D3D9FFShaderKeyVS BuildFFKeyVS(D3D9FF_VertexBlendMode vertexBlendMode, bool indexedVertexBlend) const;\n    D3D9FFShaderKeyFS BuildFFKeyFS() const;\n\n    void BindSpecConstants();\n\n    void TrackBufferMappingBufferSequenceNumber(\n      D3D9CommonBuffer* pResource);\n\n    void TrackTextureMappingBufferSequenceNumber(\n      D3D9CommonTexture* pResource,\n      UINT Subresource);\n\n    uint64_t GetCurrentSequenceNumber();\n\n    /**\n     * \\brief Will unmap the least recently used textures if the amount of mapped texture memory exceeds a threshold.\n     */\n    void UnmapTextures();\n\n    /**\n     * \\brief Get the swapchain that was used the most recently for presenting\n     * Has to be externally synchronized.\n     */\n    D3D9SwapChainEx* GetMostRecentlyUsedSwapchain() {\n      return m_mostRecentlyUsedSwapchain;\n    }\n\n    /**\n     * \\brief Set the swapchain that was used the most recently for presenting\n     * Has to be externally synchronized.\n     */\n    void SetMostRecentlyUsedSwapchain(D3D9SwapChainEx* swapchain) {\n      m_mostRecentlyUsedSwapchain = swapchain;\n    }\n\n    /**\n     * @brief Reset the most recently swapchain back to the implicit one\n     * Has to be externally synchronized.\n     */\n    void ResetMostRecentlyUsedSwapchain() {\n      m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr();\n    }\n\n    bool IsTextureBoundAsAttachment(const D3D9CommonTexture* pTexture) const {\n      if (unlikely(pTexture->IsRenderTarget())) {\n        for (uint32_t i = 0u; i < m_state.renderTargets.size(); i++) {\n          if (m_state.renderTargets[i] == nullptr)\n            continue;\n\n          auto texInfo = m_state.renderTargets[i]->GetCommonTexture();\n          if (unlikely(texInfo == pTexture)) {\n            return true;\n          }\n        }\n      } else if (unlikely(pTexture->IsDepthStencil() && m_state.depthStencil != nullptr)) {\n        auto texInfo = m_state.depthStencil->GetCommonTexture();\n        if (unlikely(texInfo == pTexture)) {\n          return true;\n        }\n      }\n      return false;\n    }\n\n    inline bool HasRenderTargetBound(uint32_t Index) const {\n      return m_state.renderTargets[Index] != nullptr\n        && !m_state.renderTargets[Index]->IsNull();\n    }\n\n    inline D3D9ShaderMasks VSShaderMasks() const {\n      return m_state.vertexShader != nullptr\n        ? m_state.vertexShader->GetCommonShader()->GetShaderMask()\n        : D3D9ShaderMasks();\n    }\n\n    inline D3D9ShaderMasks PSShaderMasks() const {\n      return m_state.pixelShader != nullptr\n        ? m_state.pixelShader->GetCommonShader()->GetShaderMask()\n        : FixedFunctionMask;\n    }\n\n    GpuFlushType GetMaxFlushType() const;\n\n    bool ValidateSharedTexture(\n      HANDLE                          handle,\n      D3DRESOURCETYPE                 type,\n      const D3D9_COMMON_TEXTURE_DESC& textureDesc) const;\n\n    bool ValidateSharedBuffer(\n      HANDLE                        handle,\n      const dxvk::D3D9_BUFFER_DESC& bufferDesc) const;\n\n    bool HasFormatsUnlocked() const { return m_unlockAdditionalFormats; }\n\n    Com<D3D9InterfaceEx>            m_parent;\n    D3D9Options                     m_d3d9Options;\n    D3DDEVTYPE                      m_deviceType;\n    HWND                            m_window;\n    WORD                            m_behaviorFlags;\n\n    D3D9Adapter*                    m_adapter;\n    Rc<DxvkDevice>                  m_dxvkDevice;\n\n    D3D9MemoryAllocator             m_memoryAllocator;\n\n    // Second memory allocator used for D3D9 shader bytecode.\n    // Most games never access the stored bytecode, so putting that\n    // into the same chunks as texture memory would waste address space.\n    D3D9MemoryAllocator             m_shaderAllocator;\n\n    uint32_t                        m_frameLatency = DefaultFrameLatency;\n\n    D3D9Initializer*                m_initializer = nullptr;\n    D3D9FormatHelper*               m_converter   = nullptr;\n\n    D3D9FFShaderModuleSet           m_ffModules;\n    D3D9SWVPEmulator                m_swvpEmulator;\n\n    Com<D3D9StateBlock, false>      m_recorder;\n\n    Rc<D3D9ShaderModuleSet>         m_shaderModules;\n\n    D3D9ConstantBuffer              m_vsClipPlanes;\n\n    D3D9ConstantBuffer              m_vsFixedFunction;\n    D3D9ConstantBuffer              m_vsVertexBlend;\n    D3D9ConstantBuffer              m_psFixedFunction;\n    D3D9ConstantBuffer              m_psShared;\n    D3D9ConstantBuffer              m_specBuffer;\n\n    Rc<DxvkBuffer>                  m_upBuffer;\n    VkDeviceSize                    m_upBufferOffset  = 0ull;\n    void*                           m_upBufferMapPtr  = nullptr;\n\n    DxvkStagingBuffer               m_stagingBuffer;\n    Rc<sync::Fence>                 m_stagingBufferFence;\n    VkDeviceSize                    m_stagingMemorySignaled = 0ull;\n\n    VkDeviceSize                    m_discardMemoryCounter = 0u;\n    VkDeviceSize                    m_discardMemoryOnFlush = 0u;\n\n    D3D9Cursor                      m_cursor;\n\n    Com<D3D9Surface, false>         m_autoDepthStencil;\n\n    Com<D3D9SwapChainEx, false>     m_implicitSwapchain;\n\n    DxsoOptions                     m_dxsoOptions;\n\n    std::unordered_map<\n      DWORD,\n      Com<D3D9VertexDecl,\n      false>>                       m_fvfTable;\n\n    D3D9Multithread                 m_multithread;\n    D3D9InputAssemblyState          m_iaState;\n\n    D3D9DeviceDirtyFlags            m_dirty;\n\n    D3D9TextureSlotTracking         m_textureSlotTracking;\n\n    D3D9RTSlotTracking              m_rtSlotTracking;\n\n    D3D9VBSlotTracking              m_vbSlotTracking;\n\n    D3D9SpecializationInfo          m_specInfo = D3D9SpecializationInfo();\n\n    bool                            m_isSWVP;\n    bool                            m_isD3D8Compatible;\n    bool                            m_ffZTest          = false;\n\n    // the enablement of below features is tracked independently\n    // of render states both due to complexity and to avoid\n    // incurring overhead on all render state changes\n    bool                            m_alphaTestEnabled = false;\n    // vendor hack state tracking\n    bool                            m_atocEnabled      = false;\n    bool                            m_nvdbEnabled      = false;\n\n    bool                            m_inScene          = false;\n    bool                            m_validSampleMask  = false;\n\n    VkImageLayout                   m_hazardLayout = VK_IMAGE_LAYOUT_GENERAL;\n\n    bool                            m_usingGraphicsPipelines = false;\n    uint32_t                        m_resetCtr = 0u;\n\n    DxvkDepthBiasRepresentation     m_depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false };\n    float                           m_depthBiasScale  = 0.0f;\n\n    uint32_t                        m_robustSSBOAlignment     = 1;\n    uint32_t                        m_robustUBOAlignment      = 1;\n\n    D3D9ConstantSets                m_consts[uint32_t(D3D9ShaderType::PixelShader) + 1];\n\n    D3D9UserDefinedAnnotation*      m_annotation = nullptr;\n\n    D3D9ViewportInfo                m_viewportInfo;\n\n    DxvkCsChunkPool                 m_csChunkPool;\n    DxvkCsThread                    m_csThread;\n    DxvkCsChunkRef                  m_csChunk;\n    uint64_t                        m_csSeqNum = 0ull;\n\n    Rc<sync::Fence>                 m_submissionFence;\n    uint64_t                        m_submissionId = 0ull;\n    DxvkSubmitStatus                m_submitStatus;\n\n    uint64_t                        m_flushSeqNum = 0ull;\n    GpuFlushTracker                 m_flushTracker;\n\n    std::atomic<int64_t>            m_availableMemory = { 0 };\n\n    D3D9DeviceLostState             m_deviceLostState          = D3D9DeviceLostState::Ok;\n    HWND                            m_fullscreenWindow         = NULL;\n    std::atomic<uint32_t>           m_losableResourceCounter   = { 0 };\n\n    D3D9SwapChainEx*                m_mostRecentlyUsedSwapchain = nullptr;\n\n#ifdef D3D9_ALLOW_UNMAPPING\n    lru_list<D3D9CommonTexture*>    m_mappedTextures;\n#endif\n\n    // m_state should be declared last (i.e. freed first), because it\n    // references objects that can call back into the device when freed.\n    Direct3DState9                  m_state;\n\n    D3D9VkInteropDevice             m_d3d9Interop;\n    D3D9ON12_ARGS                   m_d3d9On12Args = { };\n    D3D9On12                        m_d3d9On12;\n    DxvkD3D8Bridge                  m_d3d8Bridge;\n\n    // Sampler statistics\n    constexpr static uint32_t       SamplerCountBits = 12u;\n    constexpr static uint64_t       SamplerCountMask = (1u << SamplerCountBits) - 1u;\n\n    uint64_t                        m_samplerBindCount = 0u;\n\n    uint64_t                        m_lastSamplerLiveCount = 0u;\n    uint64_t                        m_lastSamplerBindCount = 0u;\n\n    // Written by CS thread\n    alignas(CACHE_LINE_SIZE)\n    std::atomic<uint64_t>           m_lastSamplerStats = { 0u };\n\n    bool m_unlockAdditionalFormats = false;\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_device_child.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n\nnamespace dxvk {\n\n  class D3D9DeviceEx;\n\n  template <typename Base>\n  class D3D9DeviceChild : public ComObjectClamp<Base> {\n\n  public:\n\n    D3D9DeviceChild(D3D9DeviceEx* pDevice)\n      : m_parent( pDevice ) { }\n\n    ULONG STDMETHODCALLTYPE AddRef() {\n      uint32_t refCount = this->m_refCount++;\n      if (unlikely(!refCount)) {\n        this->AddRefPrivate();\n        GetDevice()->AddRef();\n      }\n\n      return refCount + 1;\n    }\n    \n    ULONG STDMETHODCALLTYPE Release() {\n      uint32_t oldRefCount, refCount;\n\n      do {\n        oldRefCount = this->m_refCount.load(std::memory_order_acquire);\n\n        // clamp value to 0 to prevent underruns\n        if (unlikely(!oldRefCount))\n          return 0;\n\n        refCount = oldRefCount - 1;\n\n      } while (!this->m_refCount.compare_exchange_weak(oldRefCount,\n                                                       refCount,\n                                                       std::memory_order_release,\n                                                       std::memory_order_acquire));\n\n      if (unlikely(!refCount)) {\n        auto* pDevice = GetDevice();\n        this->ReleasePrivate();\n        pDevice->Release();\n      }\n\n      return refCount;\n    }\n\n    HRESULT STDMETHODCALLTYPE GetDevice(IDirect3DDevice9** ppDevice) {\n      InitReturnPtr(ppDevice);\n\n      if (ppDevice == nullptr)\n        return D3DERR_INVALIDCALL;\n\n      *ppDevice = ref(GetDevice());\n      return D3D_OK;\n    }\n\n    IDirect3DDevice9Ex* GetDevice() {\n      return reinterpret_cast<IDirect3DDevice9Ex*>(m_parent);\n    }\n\n    D3D9DeviceEx* GetParent() {\n      return m_parent;\n    }\n\n  protected:\n\n    D3D9DeviceEx* m_parent;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_fixed_function.cpp",
    "content": "#include \"d3d9_fixed_function.h\"\n\n#include \"d3d9_device.h\"\n#include \"d3d9_util.h\"\n#include \"d3d9_spec_constants.h\"\n\n#include \"../dxvk/dxvk_hash.h\"\n#include \"../dxvk/dxvk_shader_spirv.h\"\n\n#include \"../util/util_small_vector.h\"\n\n#include \"../spirv/spirv_module.h\"\n\n#include <cfloat>\n\n#include <d3d9_fixed_function_vert.h>\n#include <d3d9_fixed_function_frag.h>\n#include <d3d9_fixed_function_frag_sample.h>\n\nnamespace dxvk {\n\n  D3D9FixedFunctionOptions::D3D9FixedFunctionOptions(const D3D9Options* options) {\n    forceSampleRateShading = options->forceSampleRateShading;\n  }\n\n  uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx) {\n    uint32_t floatType  = spvModule.defFloatType(32);\n    uint32_t vec3Type   = spvModule.defVectorType(floatType, 3);\n    uint32_t vec4Type   = spvModule.defVectorType(floatType, 4);\n    uint32_t floatPtr   = spvModule.defPointerType(floatType, spv::StorageClassPushConstant);\n    uint32_t vec3Ptr    = spvModule.defPointerType(vec3Type,  spv::StorageClassPushConstant);\n\n    uint32_t fogColorMember = spvModule.constu32(uint32_t(D3D9RenderStateItem::FogColor));\n    uint32_t fogColor = spvModule.opLoad(vec3Type,\n      spvModule.opAccessChain(vec3Ptr, fogCtx.RenderState, 1, &fogColorMember));\n\n    uint32_t fogScaleMember = spvModule.constu32(uint32_t(D3D9RenderStateItem::FogScale));\n    uint32_t fogScale = spvModule.opLoad(floatType,\n      spvModule.opAccessChain(floatPtr, fogCtx.RenderState, 1, &fogScaleMember));\n\n    uint32_t fogEndMember = spvModule.constu32(uint32_t(D3D9RenderStateItem::FogEnd));\n    uint32_t fogEnd = spvModule.opLoad(floatType,\n      spvModule.opAccessChain(floatPtr, fogCtx.RenderState, 1, &fogEndMember));\n\n    uint32_t fogDensityMember = spvModule.constu32(uint32_t(D3D9RenderStateItem::FogDensity));\n    uint32_t fogDensity = spvModule.opLoad(floatType,\n      spvModule.opAccessChain(floatPtr, fogCtx.RenderState, 1, &fogDensityMember));\n\n    uint32_t fogMode = spec.get(\n      spvModule, fogCtx.SpecUBO,\n      fogCtx.IsPixel ? SpecPixelFogMode : SpecVertexFogMode);\n\n    uint32_t fogEnabled = spec.get(spvModule, fogCtx.SpecUBO, SpecFogEnabled);\n    fogEnabled = spvModule.opINotEqual(spvModule.defBoolType(), fogEnabled, spvModule.constu32(0));\n\n    uint32_t doFog   = spvModule.allocateId();\n    uint32_t skipFog = spvModule.allocateId();\n\n    uint32_t returnType     = fogCtx.IsPixel ? vec4Type : floatType;\n    uint32_t returnTypePtr  = spvModule.defPointerType(returnType, spv::StorageClassPrivate);\n    uint32_t returnValuePtr = spvModule.newVar(returnTypePtr, spv::StorageClassPrivate);\n    spvModule.opStore(returnValuePtr, fogCtx.IsPixel ? fogCtx.oColor : spvModule.constf32(0.0f));\n\n    // Actually do the fog now we have all the vars in-place.\n\n    spvModule.opSelectionMerge(skipFog, spv::SelectionControlMaskNone);\n    spvModule.opBranchConditional(fogEnabled, doFog, skipFog);\n\n    spvModule.opLabel(doFog);\n\n    uint32_t wIndex = 3;\n    uint32_t zIndex = 2;\n\n    uint32_t w = spvModule.opCompositeExtract(floatType, fogCtx.vPos, 1, &wIndex);\n    uint32_t z = spvModule.opCompositeExtract(floatType, fogCtx.vPos, 1, &zIndex);\n\n    uint32_t depth = 0;\n    if (fogCtx.IsPixel)\n      depth = spvModule.opFMul(floatType, z, spvModule.opFDiv(floatType, spvModule.constf32(1.0f), w));\n    else {\n      if (fogCtx.RangeFog) {\n        std::array<uint32_t, 3> indices = { 0, 1, 2 };\n        uint32_t pos3 = spvModule.opVectorShuffle(vec3Type, fogCtx.vPos, fogCtx.vPos, indices.size(), indices.data());\n        depth = spvModule.opLength(floatType, pos3);\n      }\n      else\n        depth = fogCtx.HasFogInput\n          ? fogCtx.vFog\n          : spvModule.opFAbs(floatType, z);\n    }\n    uint32_t fogFactor;\n    if (!fogCtx.IsPixel && fogCtx.IsFixedFunction && fogCtx.IsPositionT) {\n      fogFactor = fogCtx.HasSpecular\n        ? spvModule.opCompositeExtract(floatType, fogCtx.Specular, 1, &wIndex)\n        : spvModule.constf32(1.0f);\n    } else {\n      uint32_t applyFogFactor = spvModule.allocateId();\n\n      std::array<SpirvPhiLabel, 4> fogVariables;\n\n      std::array<SpirvSwitchCaseLabel, 4> fogCaseLabels = { {\n        { uint32_t(D3DFOG_NONE),      spvModule.allocateId() },\n        { uint32_t(D3DFOG_EXP),       spvModule.allocateId() },\n        { uint32_t(D3DFOG_EXP2),      spvModule.allocateId() },\n        { uint32_t(D3DFOG_LINEAR),    spvModule.allocateId() },\n      } };\n\n      spvModule.opSelectionMerge(applyFogFactor, spv::SelectionControlMaskNone);\n      spvModule.opSwitch(fogMode,\n        fogCaseLabels[D3DFOG_NONE].labelId,\n        fogCaseLabels.size(),\n        fogCaseLabels.data());\n\n      for (uint32_t i = 0; i < fogCaseLabels.size(); i++) {\n        spvModule.opLabel(fogCaseLabels[i].labelId);\n\n        fogVariables[i].labelId = fogCaseLabels[i].labelId;\n        fogVariables[i].varId   = [&] {\n          auto mode = D3DFOGMODE(fogCaseLabels[i].literal);\n          switch (mode) {\n            default:\n            // vFog\n            case D3DFOG_NONE: {\n              if (fogCtx.IsPixel)\n                return fogCtx.vFog;\n\n              if (fogCtx.IsFixedFunction && fogCtx.HasSpecular)\n                return spvModule.opCompositeExtract(floatType, fogCtx.Specular, 1, &wIndex);\n\n              return spvModule.constf32(1.0f);\n            }\n\n            // (end - d) / (end - start)\n            case D3DFOG_LINEAR: {\n              uint32_t fogFactor = spvModule.opFSub(floatType, fogEnd, depth);\n              fogFactor = spvModule.opFMul(floatType, fogFactor, fogScale);\n              fogFactor = spvModule.opNClamp(floatType, fogFactor, spvModule.constf32(0.0f), spvModule.constf32(1.0f));\n              return fogFactor;\n            }\n\n            // 1 / (e^[d * density])^2\n            case D3DFOG_EXP2:\n            // 1 / (e^[d * density])\n            case D3DFOG_EXP: {\n              uint32_t fogFactor = spvModule.opFMul(floatType, depth, fogDensity);\n\n              if (mode == D3DFOG_EXP2)\n                fogFactor = spvModule.opFMul(floatType, fogFactor, fogFactor);\n\n              // Provides the rcp.\n              fogFactor = spvModule.opFNegate(floatType, fogFactor);\n              fogFactor = spvModule.opExp(floatType, fogFactor);\n              return fogFactor;\n            }\n          }\n        }();\n\n        spvModule.opBranch(applyFogFactor);\n      }\n\n      spvModule.opLabel(applyFogFactor);\n\n      fogFactor = spvModule.opPhi(floatType,\n        fogVariables.size(),\n        fogVariables.data());\n    }\n\n    uint32_t fogRetValue = 0;\n\n    // Return the new color if we are doing this in PS\n    // or just the fog factor for oFog in VS\n    if (fogCtx.IsPixel) {\n      std::array<uint32_t, 4> indices = { 0, 1, 2, 6 };\n\n      uint32_t color = fogCtx.oColor;\n\n      uint32_t color3 = spvModule.opVectorShuffle(vec3Type, color, color, 3, indices.data());\n\n      std::array<uint32_t, 3> fogFacIndices = { fogFactor, fogFactor, fogFactor };\n      uint32_t fogFact3 = spvModule.opCompositeConstruct(vec3Type, fogFacIndices.size(), fogFacIndices.data());\n\n      uint32_t lerpedFrog = spvModule.opFMix(vec3Type, fogColor, color3, fogFact3);\n\n      fogRetValue = spvModule.opVectorShuffle(vec4Type, lerpedFrog, color, indices.size(), indices.data());\n    }\n    else\n      fogRetValue = fogFactor;\n\n    spvModule.opStore(returnValuePtr, fogRetValue);\n\n    spvModule.opBranch(skipFog);\n\n    spvModule.opLabel(skipFog);\n\n    return spvModule.opLoad(returnType, returnValuePtr);\n  }\n\n\n  void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx) {\n    // Labels for the alpha test\n    std::array<SpirvSwitchCaseLabel, 8> atestCaseLabels = {{\n      { uint32_t(VK_COMPARE_OP_NEVER),            spvModule.allocateId() },\n      { uint32_t(VK_COMPARE_OP_LESS),             spvModule.allocateId() },\n      { uint32_t(VK_COMPARE_OP_EQUAL),            spvModule.allocateId() },\n      { uint32_t(VK_COMPARE_OP_LESS_OR_EQUAL),    spvModule.allocateId() },\n      { uint32_t(VK_COMPARE_OP_GREATER),          spvModule.allocateId() },\n      { uint32_t(VK_COMPARE_OP_NOT_EQUAL),        spvModule.allocateId() },\n      { uint32_t(VK_COMPARE_OP_GREATER_OR_EQUAL), spvModule.allocateId() },\n      { uint32_t(VK_COMPARE_OP_ALWAYS),           spvModule.allocateId() },\n    }};\n\n    uint32_t atestBeginLabel   = spvModule.allocateId();\n    uint32_t atestTestLabel    = spvModule.allocateId();\n    uint32_t atestDiscardLabel = spvModule.allocateId();\n    uint32_t atestKeepLabel    = spvModule.allocateId();\n    uint32_t atestSkipLabel    = spvModule.allocateId();\n\n    // if (alpha_func != ALWAYS) { ... }\n    uint32_t boolType = spvModule.defBoolType();\n    uint32_t isNotAlways = spvModule.opINotEqual(boolType, ctx.alphaFuncId, spvModule.constu32(VK_COMPARE_OP_ALWAYS));\n    spvModule.opSelectionMerge(atestSkipLabel, spv::SelectionControlMaskNone);\n    spvModule.opBranchConditional(isNotAlways, atestBeginLabel, atestSkipLabel);\n    spvModule.opLabel(atestBeginLabel);\n\n    // The lower 8 bits of the alpha ref contain the actual reference value\n    // from the API, the upper bits store the accuracy bit count minus 8.\n    // So if we want 12 bits of accuracy (i.e. 0-4095), that value will be 4.\n    uint32_t uintType = spvModule.defIntType(32, 0);\n\n    // Check if the given bit precision is supported\n    uint32_t precisionIntLabel = spvModule.allocateId();\n    uint32_t precisionFloatLabel = spvModule.allocateId();\n    uint32_t precisionEndLabel = spvModule.allocateId();\n\n    uint32_t useIntPrecision = spvModule.opULessThanEqual(boolType,\n      ctx.alphaPrecisionId, spvModule.constu32(8));\n\n    spvModule.opSelectionMerge(precisionEndLabel, spv::SelectionControlMaskNone);\n    spvModule.opBranchConditional(useIntPrecision, precisionIntLabel, precisionFloatLabel);\n    spvModule.opLabel(precisionIntLabel);\n\n    // Adjust alpha ref to the given range\n    uint32_t alphaRefIdInt = spvModule.opBitwiseOr(uintType,\n      spvModule.opShiftLeftLogical(uintType, ctx.alphaRefId, ctx.alphaPrecisionId),\n      spvModule.opShiftRightLogical(uintType, ctx.alphaRefId,\n        spvModule.opISub(uintType, spvModule.constu32(8), ctx.alphaPrecisionId)));\n\n    // Convert alpha ref to float since we'll do the comparison based on that\n    uint32_t floatType = spvModule.defFloatType(32);\n    alphaRefIdInt = spvModule.opConvertUtoF(floatType, alphaRefIdInt);\n\n    // Adjust alpha to the given range and round\n    uint32_t alphaFactorId = spvModule.opISub(uintType,\n      spvModule.opShiftLeftLogical(uintType, spvModule.constu32(256), ctx.alphaPrecisionId),\n      spvModule.constu32(1));\n    alphaFactorId = spvModule.opConvertUtoF(floatType, alphaFactorId);\n\n    uint32_t alphaIdInt = spvModule.opRoundEven(floatType,\n      spvModule.opFMul(floatType, ctx.alphaId, alphaFactorId));\n\n    spvModule.opBranch(precisionEndLabel);\n    spvModule.opLabel(precisionFloatLabel);\n\n    // If we're not using integer precision, normalize the alpha ref\n    uint32_t alphaRefIdFloat = spvModule.opFDiv(floatType,\n      spvModule.opConvertUtoF(floatType, ctx.alphaRefId),\n      spvModule.constf32(255.0f));\n\n    spvModule.opBranch(precisionEndLabel);\n    spvModule.opLabel(precisionEndLabel);\n\n    std::array<SpirvPhiLabel, 2> alphaRefLabels = {\n      SpirvPhiLabel { alphaRefIdInt,    precisionIntLabel   },\n      SpirvPhiLabel { alphaRefIdFloat,  precisionFloatLabel },\n    };\n\n    uint32_t alphaRefId = spvModule.opPhi(floatType,\n      alphaRefLabels.size(),\n      alphaRefLabels.data());\n\n    std::array<SpirvPhiLabel, 2> alphaIdLabels = {\n      SpirvPhiLabel { alphaIdInt,  precisionIntLabel   },\n      SpirvPhiLabel { ctx.alphaId, precisionFloatLabel },\n    };\n\n    uint32_t alphaId = spvModule.opPhi(floatType,\n      alphaIdLabels.size(),\n      alphaIdLabels.data());\n\n    // switch (alpha_func) { ... }\n    spvModule.opSelectionMerge(atestTestLabel, spv::SelectionControlMaskNone);\n    spvModule.opSwitch(ctx.alphaFuncId,\n      atestCaseLabels[uint32_t(VK_COMPARE_OP_ALWAYS)].labelId,\n      atestCaseLabels.size(),\n      atestCaseLabels.data());\n\n    std::array<SpirvPhiLabel, 8> atestVariables;\n\n    for (uint32_t i = 0; i < atestCaseLabels.size(); i++) {\n      spvModule.opLabel(atestCaseLabels[i].labelId);\n\n      atestVariables[i].labelId = atestCaseLabels[i].labelId;\n      atestVariables[i].varId   = [&] {\n        switch (VkCompareOp(atestCaseLabels[i].literal)) {\n          case VK_COMPARE_OP_NEVER:            return spvModule.constBool(false);\n          case VK_COMPARE_OP_LESS:             return spvModule.opFOrdLessThan        (boolType, alphaId, alphaRefId);\n          case VK_COMPARE_OP_EQUAL:            return spvModule.opFOrdEqual           (boolType, alphaId, alphaRefId);\n          case VK_COMPARE_OP_LESS_OR_EQUAL:    return spvModule.opFOrdLessThanEqual   (boolType, alphaId, alphaRefId);\n          case VK_COMPARE_OP_GREATER:          return spvModule.opFOrdGreaterThan     (boolType, alphaId, alphaRefId);\n          case VK_COMPARE_OP_NOT_EQUAL:        return spvModule.opFUnordNotEqual      (boolType, alphaId, alphaRefId);\n          case VK_COMPARE_OP_GREATER_OR_EQUAL: return spvModule.opFOrdGreaterThanEqual(boolType, alphaId, alphaRefId);\n          default:\n          case VK_COMPARE_OP_ALWAYS:           return spvModule.constBool(true);\n        }\n      }();\n\n      spvModule.opBranch(atestTestLabel);\n    }\n\n    // end switch\n    spvModule.opLabel(atestTestLabel);\n\n    uint32_t atestResult = spvModule.opPhi(boolType,\n      atestVariables.size(),\n      atestVariables.data());\n    uint32_t atestDiscard = spvModule.opLogicalNot(boolType, atestResult);\n\n    // if (do_discard) { ... }\n    spvModule.opSelectionMerge(atestKeepLabel, spv::SelectionControlMaskNone);\n    spvModule.opBranchConditional(atestDiscard, atestDiscardLabel, atestKeepLabel);\n\n    spvModule.opLabel(atestDiscardLabel);\n    spvModule.opDemoteToHelperInvocation();\n    spvModule.opBranch(atestKeepLabel);\n\n    // end if (do_discard)\n    spvModule.opLabel(atestKeepLabel);\n    spvModule.opBranch(atestSkipLabel);\n\n    // end if (alpha_test)\n    spvModule.opLabel(atestSkipLabel);\n  }\n\n\n  std::pair<uint32_t, uint32_t> SetupRenderStateBlock(SpirvModule& spvModule, uint32_t samplerMask) {\n    uint32_t floatType = spvModule.defFloatType(32);\n    uint32_t uintType  = spvModule.defIntType(32, 0);\n    uint32_t vec3Type  = spvModule.defVectorType(floatType, 3);\n\n    small_vector<uint32_t, 32u> rsMembers;\n    rsMembers.push_back(vec3Type);\n    rsMembers.push_back(floatType);\n    rsMembers.push_back(floatType);\n    rsMembers.push_back(floatType);\n\n    rsMembers.push_back(uintType);\n\n    rsMembers.push_back(floatType);\n    rsMembers.push_back(floatType);\n    rsMembers.push_back(floatType);\n    rsMembers.push_back(floatType);\n    rsMembers.push_back(floatType);\n    rsMembers.push_back(floatType);\n\n    // Number of static data members\n    uint32_t rsMemberCount = rsMembers.size();\n\n    // Add one dword for each sampler pair\n    uint32_t samplerCount = bit::popcnt(samplerMask);\n\n    for (uint32_t i = 0u; i < samplerCount; i += 2u)\n      rsMembers.push_back(uintType);\n\n    uint32_t rsStruct = spvModule.defStructTypeUnique(rsMembers.size(), rsMembers.data());\n    uint32_t rsBlock = spvModule.newVar(\n      spvModule.defPointerType(rsStruct, spv::StorageClassPushConstant),\n      spv::StorageClassPushConstant);\n\n    spvModule.setDebugName         (rsBlock, \"render_state\");\n\n    spvModule.setDebugName         (rsStruct, \"render_state_t\");\n    spvModule.decorate             (rsStruct, spv::DecorationBlock);\n\n    uint32_t memberIdx = 0;\n    auto SetMemberName = [&](const char* name, uint32_t offset) {\n      spvModule.setDebugMemberName   (rsStruct, memberIdx, name);\n      spvModule.memberDecorateOffset (rsStruct, memberIdx, offset);\n      memberIdx++;\n    };\n\n    SetMemberName(\"fog_color\",      offsetof(D3D9RenderStateInfo, fogColor));\n    SetMemberName(\"fog_scale\",      offsetof(D3D9RenderStateInfo, fogScale));\n    SetMemberName(\"fog_end\",        offsetof(D3D9RenderStateInfo, fogEnd));\n    SetMemberName(\"fog_density\",    offsetof(D3D9RenderStateInfo, fogDensity));\n    SetMemberName(\"alpha_ref\",      offsetof(D3D9RenderStateInfo, alphaRef));\n    SetMemberName(\"point_size\",     offsetof(D3D9RenderStateInfo, pointSize));\n    SetMemberName(\"point_size_min\", offsetof(D3D9RenderStateInfo, pointSizeMin));\n    SetMemberName(\"point_size_max\", offsetof(D3D9RenderStateInfo, pointSizeMax));\n    SetMemberName(\"point_scale_a\",  offsetof(D3D9RenderStateInfo, pointScaleA));\n    SetMemberName(\"point_scale_b\",  offsetof(D3D9RenderStateInfo, pointScaleB));\n    SetMemberName(\"point_scale_c\",  offsetof(D3D9RenderStateInfo, pointScaleC));\n\n    uint32_t samplerOffset = GetPushSamplerOffset(0u);\n\n    while (samplerMask) {\n      uint32_t s0 = bit::tzcnt(samplerMask); samplerMask &= samplerMask - 1u;\n      uint32_t s1 = bit::tzcnt(samplerMask); samplerMask &= samplerMask - 1u;\n\n      std::string name = s1 < samplerCount\n        ? str::format(\"s\", s0, \"_s\", s1, \"_idx\")\n        : str::format(\"s\", s0, \"_idx\");\n\n      SetMemberName(name.c_str(), samplerOffset);\n      samplerOffset += sizeof(uint32_t);\n    }\n\n    return std::make_pair(rsBlock, rsMemberCount);\n  }\n\n\n  uint32_t SetupSamplerArray(SpirvModule& spvModule) {\n    // Old spir-v, need to enable extension\n    spvModule.enableExtension(\"SPV_EXT_descriptor_indexing\");\n    spvModule.enableCapability(spv::CapabilityRuntimeDescriptorArray);\n\n    uint32_t samplerArray = spvModule.defRuntimeArrayTypeUnique(spvModule.defSamplerType());\n    uint32_t samplerPtr = spvModule.defPointerType(samplerArray, spv::StorageClassUniformConstant);\n\n    uint32_t samplerHeap = spvModule.newVar(samplerPtr, spv::StorageClassUniformConstant);\n    spvModule.setDebugName(samplerHeap, \"sampler_heap\");\n\n    spvModule.decorateBinding(samplerHeap, 0u);\n    spvModule.decorateDescriptorSet(samplerHeap, GetGlobalSamplerSetIndex());\n    return samplerHeap;\n  }\n\n\n  uint32_t LoadSampler(SpirvModule& spvModule, uint32_t descriptorId,\n    uint32_t pushBlockId, uint32_t pushMember, uint32_t samplerIndex) {\n    uint32_t uintType = spvModule.defIntType(32u, 0);\n    uint32_t uintPtr = spvModule.defPointerType(uintType, spv::StorageClassPushConstant);\n\n    uint32_t pushIndexId = spvModule.constu32(pushMember + samplerIndex / 2u);\n\n    uint32_t descriptorIndex = spvModule.opLoad(uintType,\n      spvModule.opAccessChain(uintPtr, pushBlockId, 1u, &pushIndexId));\n\n    descriptorIndex = spvModule.opBitFieldUExtract(uintType, descriptorIndex,\n      spvModule.constu32(16u * (samplerIndex & 1u)), spvModule.constu32(16u));\n\n    uint32_t samplerType = spvModule.defSamplerType();\n    uint32_t samplerPtr = spvModule.defPointerType(samplerType, spv::StorageClassUniformConstant);\n\n    return spvModule.opLoad(samplerType,\n      spvModule.opAccessChain(samplerPtr, descriptorId, 1u, &descriptorIndex));\n  }\n\n\n  uint32_t SetupSpecUBO(SpirvModule& spvModule, std::vector<DxvkBindingInfo>& bindings) {\n    uint32_t uintType = spvModule.defIntType(32, 0);\n\n    std::array<uint32_t, SpecConstantCount> specMembers;\n    for (auto& x : specMembers)\n      x = uintType;\n\n    uint32_t specStruct = spvModule.defStructTypeUnique(uint32_t(specMembers.size()), specMembers.data());\n\n    spvModule.setDebugName         (specStruct, \"spec_state_t\");\n    spvModule.decorate             (specStruct, spv::DecorationBlock);\n\n    for (uint32_t i = 0; i < SpecConstantCount; i++) {\n      std::string name = str::format(\"dword\", i);\n      spvModule.setDebugMemberName   (specStruct, i, name.c_str());\n      spvModule.memberDecorateOffset (specStruct, i, sizeof(uint32_t) * i);\n    }\n\n    uint32_t specBlock = spvModule.newVar(\n      spvModule.defPointerType(specStruct, spv::StorageClassUniform),\n      spv::StorageClassUniform);\n\n    spvModule.setDebugName         (specBlock, \"spec_state\");\n    spvModule.decorateDescriptorSet(specBlock, 0);\n    spvModule.decorateBinding      (specBlock, getSpecConstantBufferSlot());\n\n    auto& binding = bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = getSpecConstantBufferSlot();\n    binding.resourceIndex   = getSpecConstantBufferSlot();\n    binding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n    binding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n    return specBlock;\n  }\n\n\n  D3D9PointSizeInfoVS GetPointSizeInfoVS(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, uint32_t vPos, uint32_t vtx, uint32_t perVertPointSize, uint32_t rsBlock, uint32_t specUbo, bool isFixedFunction) {\n    uint32_t floatType  = spvModule.defFloatType(32);\n    uint32_t floatPtr   = spvModule.defPointerType(floatType, spv::StorageClassPushConstant);\n    uint32_t vec3Type   = spvModule.defVectorType(floatType, 3);\n    uint32_t vec4Type   = spvModule.defVectorType(floatType, 4);\n    uint32_t uint32Type = spvModule.defIntType(32, 0);\n    uint32_t boolType   = spvModule.defBoolType();\n\n    auto LoadFloat = [&](D3D9RenderStateItem item) {\n      uint32_t index = spvModule.constu32(uint32_t(item));\n      return spvModule.opLoad(floatType, spvModule.opAccessChain(floatPtr, rsBlock, 1, &index));\n    };\n\n    uint32_t value = perVertPointSize != 0 ? perVertPointSize : LoadFloat(D3D9RenderStateItem::PointSize);\n\n    if (isFixedFunction) {\n      uint32_t pointMode = spec.get(spvModule, specUbo, SpecPointMode);\n\n      uint32_t scaleBit  = spvModule.opBitFieldUExtract(uint32Type, pointMode, spvModule.consti32(0), spvModule.consti32(1));\n      uint32_t isScale   = spvModule.opIEqual(boolType, scaleBit, spvModule.constu32(1));\n\n      uint32_t scaleC = LoadFloat(D3D9RenderStateItem::PointScaleC);\n      uint32_t scaleB = LoadFloat(D3D9RenderStateItem::PointScaleB);\n      uint32_t scaleA = LoadFloat(D3D9RenderStateItem::PointScaleA);\n\n      std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };\n\n      uint32_t vtx3;\n      if (vPos != 0) {\n        vPos = spvModule.opLoad(vec4Type, vPos);\n\n        uint32_t rhw  = spvModule.opCompositeExtract(floatType, vPos, 1, &indices[3]);\n                 rhw  = spvModule.opFDiv(floatType, spvModule.constf32(1.0f), rhw);\n        uint32_t pos3 = spvModule.opVectorShuffle(vec3Type, vPos, vPos, 3, indices.data());\n                 vtx3 = spvModule.opVectorTimesScalar(vec3Type, pos3, rhw);\n      } else {\n                 vtx3 = spvModule.opVectorShuffle(vec3Type, vtx, vtx, 3, indices.data());\n      }\n\n      uint32_t DeSqr      = spvModule.opDot (floatType, vtx3, vtx3);\n      uint32_t De         = spvModule.opSqrt(floatType, DeSqr);\n      uint32_t scaleValue = spvModule.opFMul(floatType, scaleC, DeSqr);\n               scaleValue = spvModule.opFFma(floatType, scaleB, De, scaleValue);\n               scaleValue = spvModule.opFAdd(floatType, scaleA, scaleValue);\n               scaleValue = spvModule.opSqrt(floatType, scaleValue);\n               scaleValue = spvModule.opFDiv(floatType, value, scaleValue);\n\n      value = spvModule.opSelect(floatType, isScale, scaleValue, value);\n    }\n\n    uint32_t min   = LoadFloat(D3D9RenderStateItem::PointSizeMin);\n    uint32_t max   = LoadFloat(D3D9RenderStateItem::PointSizeMax);\n\n    D3D9PointSizeInfoVS info;\n    info.defaultValue = value;\n    info.min          = min;\n    info.max          = max;\n\n    return info;\n  }\n\n\n  D3D9PointSizeInfoPS GetPointSizeInfoPS(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, uint32_t rsBlock, uint32_t specUbo) {\n    uint32_t uint32Type = spvModule.defIntType(32, 0);\n    uint32_t boolType   = spvModule.defBoolType();\n    uint32_t boolVec4   = spvModule.defVectorType(boolType, 4);\n\n    uint32_t pointMode = spec.get(spvModule, specUbo, SpecPointMode);\n\n    uint32_t spriteBit  = spvModule.opBitFieldUExtract(uint32Type, pointMode, spvModule.consti32(1), spvModule.consti32(1));\n    uint32_t isSprite   = spvModule.opIEqual(boolType, spriteBit, spvModule.constu32(1));\n\n    std::array<uint32_t, 4> isSpriteIndices;\n    for (uint32_t i = 0; i < isSpriteIndices.size(); i++)\n      isSpriteIndices[i] = isSprite;\n\n    isSprite = spvModule.opCompositeConstruct(boolVec4, isSpriteIndices.size(), isSpriteIndices.data());\n\n    D3D9PointSizeInfoPS info;\n    info.isSprite = isSprite;\n\n    return info;\n  }\n\n\n  uint32_t GetPointCoord(SpirvModule& spvModule) {\n    uint32_t floatType  = spvModule.defFloatType(32);\n    uint32_t vec2Type   = spvModule.defVectorType(floatType, 2);\n    uint32_t vec4Type   = spvModule.defVectorType(floatType, 4);\n    uint32_t vec2Ptr    = spvModule.defPointerType(vec2Type, spv::StorageClassInput);\n\n    uint32_t pointCoordPtr = spvModule.newVar(vec2Ptr, spv::StorageClassInput);\n\n    spvModule.decorateBuiltIn(pointCoordPtr, spv::BuiltInPointCoord);\n\n    uint32_t pointCoord    = spvModule.opLoad(vec2Type, pointCoordPtr);\n\n    std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };\n\n    std::array<uint32_t, 4> pointCoordIndices = {\n      spvModule.opCompositeExtract(floatType, pointCoord, 1, &indices[0]),\n      spvModule.opCompositeExtract(floatType, pointCoord, 1, &indices[1]),\n      spvModule.constf32(0.0f),\n      spvModule.constf32(0.0f)\n    };\n\n    return spvModule.opCompositeConstruct(vec4Type, pointCoordIndices.size(), pointCoordIndices.data());\n  }\n\n\n  uint32_t GetSharedConstants(SpirvModule& spvModule) {\n    uint32_t float_t = spvModule.defFloatType(32);\n    uint32_t vec2_t  = spvModule.defVectorType(float_t, 2);\n    uint32_t vec4_t  = spvModule.defVectorType(float_t, 4);\n\n    std::array<uint32_t, D3D9SharedPSStages_Count> stageMembers = {\n      vec4_t,\n\n      vec2_t,\n      vec2_t,\n\n      float_t,\n      float_t,\n    };\n\n    std::array<decltype(stageMembers), caps::TextureStageCount> members;\n\n    for (auto& member : members)\n      member = stageMembers;\n\n    const uint32_t structType =\n      spvModule.defStructType(members.size() * stageMembers.size(), members[0].data());\n\n    spvModule.decorateBlock(structType);\n\n    uint32_t offset = 0;\n    for (uint32_t stage = 0; stage < caps::TextureStageCount; stage++) {\n      spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_Constant, offset);\n      offset += sizeof(float) * 4;\n\n      spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_BumpEnvMat0, offset);\n      offset += sizeof(float) * 2;\n\n      spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_BumpEnvMat1, offset);\n      offset += sizeof(float) * 2;\n\n      spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_BumpEnvLScale, offset);\n      offset += sizeof(float);\n\n      spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_BumpEnvLOffset, offset);\n      offset += sizeof(float);\n\n      // Padding...\n      offset += sizeof(float) * 2;\n    }\n\n    uint32_t sharedState = spvModule.newVar(\n      spvModule.defPointerType(structType, spv::StorageClassUniform),\n      spv::StorageClassUniform);\n\n    spvModule.setDebugName(sharedState, \"D3D9SharedPS\");\n\n    return sharedState;\n  }\n\n\n  enum class D3D9FFVSMembers {\n    WorldViewMatrix,\n    NormalMatrix,\n    InverseViewMatrix,\n    ProjMatrix,\n\n    Texcoord0,\n    Texcoord1,\n    Texcoord2,\n    Texcoord3,\n    Texcoord4,\n    Texcoord5,\n    Texcoord6,\n    Texcoord7,\n\n    InverseOffset,\n    InverseExtent,\n\n    GlobalAmbient,\n\n    Light0,\n    Light1,\n    Light2,\n    Light3,\n    Light4,\n    Light5,\n    Light6,\n    Light7,\n\n    MaterialDiffuse,\n    MaterialAmbient,\n    MaterialSpecular,\n    MaterialEmissive,\n    MaterialPower,\n\n    TweenFactor,\n\n    MemberCount\n  };\n\n  struct D3D9FFVertexData {\n    uint32_t constantBuffer;\n    uint32_t vertexBlendData;\n    uint32_t lightType;\n\n    struct {\n      uint32_t worldview;\n      uint32_t normal;\n      uint32_t inverseView;\n      uint32_t proj;\n\n      uint32_t texcoord[8];\n\n      uint32_t invOffset;\n      uint32_t invExtent;\n\n      uint32_t globalAmbient;\n\n      uint32_t materialDiffuse;\n      uint32_t materialSpecular;\n      uint32_t materialAmbient;\n      uint32_t materialEmissive;\n      uint32_t materialPower;\n      uint32_t tweenFactor;\n    } constants;\n\n    struct {\n      uint32_t POSITION;\n      uint32_t POSITION1;\n      uint32_t POINTSIZE;\n      uint32_t NORMAL;\n      uint32_t NORMAL1;\n      uint32_t TEXCOORD[8];\n      uint32_t COLOR[2];\n      uint32_t FOG;\n\n      uint32_t BLENDWEIGHT;\n      uint32_t BLENDINDICES;\n    } in;\n\n    struct {\n      uint32_t POSITION;\n      uint32_t POINTSIZE;\n      uint32_t NORMAL;\n      uint32_t TEXCOORD[8];\n      uint32_t COLOR[2];\n      uint32_t FOG;\n    } out;\n  };\n\n  enum D3D9FFPSMembers {\n    TextureFactor = 0,\n\n    MemberCount\n  };\n\n  struct D3D9FFSamplerInfo {\n    uint32_t imageTypeId = 0;\n    uint32_t imageVarId = 0;\n    uint32_t sampledTypeId = 0u;\n    uint32_t samplerIndex = 0u;\n  };\n\n  enum D3D9FFSamplerType : uint32_t {\n    SamplerTypeTexture2D = 0,\n    SamplerTypeTexture3D = 1,\n    SamplerTypeTextureCube,\n\n    SamplerTypeCount\n  };\n\n  struct D3D9FFSampler {\n    D3D9FFSamplerInfo color[SamplerTypeCount];\n    D3D9FFSamplerInfo depth[SamplerTypeCount];\n\n    D3D9FFSamplerType type;\n  };\n\n  struct D3D9FFPixelData {\n    uint32_t constantBuffer;\n    uint32_t sharedState;\n\n    struct {\n      uint32_t textureFactor;\n    } constants;\n\n    struct {\n      uint32_t TEXCOORD[8];\n      uint32_t COLOR[2];\n      uint32_t FOG;\n      uint32_t POS;\n    } in;\n\n    D3D9FFSampler samplers[8];\n\n    struct {\n      uint32_t COLOR;\n    } out;\n  };\n\n  class D3D9FFShaderCompiler {\n\n  public:\n\n    D3D9FFShaderCompiler(\n            Rc<DxvkDevice>           Device,\n      const D3D9FFShaderKeyVS&       Key,\n      const std::string&             Name,\n            D3D9FixedFunctionOptions Options);\n\n    D3D9FFShaderCompiler(\n            Rc<DxvkDevice>           Device,\n      const D3D9FFShaderKeyFS&       Key,\n      const std::string&             Name,\n            D3D9FixedFunctionOptions Options);\n\n    Rc<DxvkShader> compile();\n\n    DxsoIsgn isgn() { return m_isgn; }\n\n  private:\n\n    // Returns value for inputs\n    // Returns ptr for outputs\n    uint32_t declareIO(bool input, DxsoSemantic semantic, spv::BuiltIn builtin = spv::BuiltInMax);\n\n    void compileVS();\n\n    void setupRenderStateInfo(VkShaderStageFlagBits stage, uint32_t samplerCount);\n\n    void emitLightTypeDecl();\n\n    void emitBaseBufferDecl();\n\n    void emitVertexBlendDecl();\n\n    void setupVS();\n\n    void compilePS();\n\n    void setupPS();\n\n    void emitPsSharedConstants();\n\n    void emitVsClipping(uint32_t vtx);\n\n    void alphaTestPS();\n\n    uint32_t emitMatrixTimesVector(uint32_t rowCount, uint32_t colCount, uint32_t matrix, uint32_t vector);\n    uint32_t emitVectorTimesMatrix(uint32_t rowCount, uint32_t colCount, uint32_t vector, uint32_t matrix);\n\n    bool isVS() { return m_shaderType == D3D9ShaderType::VertexShader; }\n    bool isPS() { return !isVS(); }\n\n    std::string           m_filename;\n\n    SpirvModule           m_module;\n    std::vector\n      <DxvkBindingInfo>   m_bindings;\n\n    uint32_t              m_inputMask       = 0u;\n    uint32_t              m_outputMask      = 0u;\n    uint32_t              m_flatShadingMask = 0u;\n\n    D3D9ShaderType        m_shaderType;\n    D3D9FFShaderKeyVS     m_vsKey;\n    D3D9FFShaderKeyFS     m_fsKey;\n\n    D3D9FFVertexData      m_vs = { };\n    D3D9FFPixelData       m_ps = { };\n\n    DxsoIsgn              m_isgn;\n    DxsoIsgn              m_osgn;\n\n    uint32_t              m_floatType       = 0u;\n    uint32_t              m_uint32Type      = 0u;\n    uint32_t              m_vec4Type        = 0u;\n    uint32_t              m_vec3Type        = 0u;\n    uint32_t              m_vec2Type        = 0u;\n    uint32_t              m_mat3Type        = 0u;\n    uint32_t              m_mat4Type        = 0u;\n    uint32_t              m_boolType        = 0u;\n\n    uint32_t              m_entryPointId    = 0u;\n\n    uint32_t              m_samplerArray    = 0u;\n\n    uint32_t              m_rsBlock         = 0u;\n    uint32_t              m_rsFirstSampler  = 0u;\n    uint32_t              m_specUbo         = 0u;\n    uint32_t              m_mainFuncLabel   = 0u;\n\n    DxvkPushDataBlock     m_samplerBlock;\n\n    D3D9FixedFunctionOptions m_options;\n\n    D3D9ShaderSpecConstantManager m_spec;\n  };\n\n  D3D9FFShaderCompiler::D3D9FFShaderCompiler(\n          Rc<DxvkDevice>           Device,\n    const D3D9FFShaderKeyVS&       Key,\n    const std::string&             Name,\n          D3D9FixedFunctionOptions Options)\n  : m_filename    ( Name )\n  , m_module      ( spvVersion(1, 3) )\n  , m_shaderType  ( D3D9ShaderType::VertexShader )\n  , m_vsKey       ( Key )\n  , m_options     ( Options ) { }\n\n\n  D3D9FFShaderCompiler::D3D9FFShaderCompiler(\n          Rc<DxvkDevice>           Device,\n    const D3D9FFShaderKeyFS&       Key,\n    const std::string&             Name,\n          D3D9FixedFunctionOptions Options)\n  : m_filename    ( Name )\n  , m_module      ( spvVersion(1, 3) )\n  , m_shaderType  ( D3D9ShaderType::PixelShader )\n  , m_fsKey       ( Key )\n  , m_options     ( Options ) { }\n\n\n  Rc<DxvkShader> D3D9FFShaderCompiler::compile() {\n    m_floatType  = m_module.defFloatType(32);\n    m_uint32Type = m_module.defIntType(32, 0);\n    m_vec4Type   = m_module.defVectorType(m_floatType, 4);\n    m_vec3Type   = m_module.defVectorType(m_floatType, 3);\n    m_vec2Type   = m_module.defVectorType(m_floatType, 2);\n    m_mat3Type   = m_module.defMatrixType(m_vec3Type, 3);\n    m_mat4Type   = m_module.defMatrixType(m_vec4Type, 4);\n    m_boolType   = m_module.defBoolType();\n\n    m_entryPointId = m_module.allocateId();\n\n    // Set the shader name so that we recognize it in renderdoc\n    m_module.setDebugSource(\n      spv::SourceLanguageUnknown, 0,\n      m_module.addDebugString(m_filename.c_str()),\n      nullptr);\n\n    // Set the memory model. This is the same for all shaders.\n    m_module.setMemoryModel(\n      spv::AddressingModelLogical,\n      spv::MemoryModelGLSL450);\n\n    m_module.enableCapability(spv::CapabilityShader);\n    m_module.enableCapability(spv::CapabilityImageQuery);\n\n    m_module.functionBegin(\n      m_module.defVoidType(), m_entryPointId, m_module.defFunctionType(\n        m_module.defVoidType(), 0, nullptr),\n      spv::FunctionControlMaskNone);\n    m_module.setDebugName(m_entryPointId, \"main\");\n\n    m_mainFuncLabel = m_module.allocateId();\n    m_module.opLabel(m_mainFuncLabel);\n\n    if (isVS())\n      compileVS();\n    else\n      compilePS();\n\n    m_module.opReturn();\n    m_module.functionEnd();\n\n    // Declare the entry point, we now have all the\n    // information we need, including the interfaces\n    m_module.addEntryPoint(m_entryPointId,\n      isVS() ? spv::ExecutionModelVertex : spv::ExecutionModelFragment, \"main\");\n\n    // Create the shader module object\n    DxvkSpirvShaderCreateInfo info;\n    info.bindingCount = m_bindings.size();\n    info.bindings = m_bindings.data();\n    info.flatShadingInputs = m_flatShadingMask;\n    info.sharedPushData = DxvkPushDataBlock(0u, sizeof(D3D9RenderStateInfo), 4u, 0u);\n    info.localPushData = m_samplerBlock;\n    info.samplerHeap = DxvkShaderBinding(VK_SHADER_STAGE_ALL, GetGlobalSamplerSetIndex(), 0u);\n\n    return new DxvkSpirvShader(info, m_module.compile());\n  }\n\n\n  uint32_t D3D9FFShaderCompiler::declareIO(bool input, DxsoSemantic semantic, spv::BuiltIn builtin) {\n    // Declare in ISGN and do linkage\n    auto& sgn = input\n      ? m_isgn : m_osgn;\n\n    uint32_t& slots = input\n      ? m_inputMask\n      : m_outputMask;\n\n    uint32_t i = sgn.elemCount++;\n\n    uint32_t slot = i;\n\n    if (builtin == spv::BuiltInMax) {\n      if (input != isVS()) {\n        slot = RegisterLinkerSlot(semantic); // Requires linkage...\n      }\n\n      slots |= 1u << slot;\n    }\n\n    auto& elem = sgn.elems[i];\n    elem.slot = slot;\n    elem.semantic = semantic;\n\n    // Declare variable\n    spv::StorageClass storageClass = input ?\n      spv::StorageClassInput : spv::StorageClassOutput;\n\n    const bool scalar = semantic.usage == DxsoUsage::Fog || semantic.usage == DxsoUsage::PointSize;\n    uint32_t type = scalar ? m_floatType : m_vec4Type;\n\n    uint32_t ptrType = m_module.defPointerType(type, storageClass);\n\n    uint32_t ptr = m_module.newVar(ptrType, storageClass);\n\n    if (builtin == spv::BuiltInMax) {\n      m_module.decorateLocation(ptr, slot);\n\n      if (isPS() && input && m_options.forceSampleRateShading) {\n        m_module.enableCapability(spv::CapabilitySampleRateShading);\n        m_module.decorate(ptr, spv::DecorationSample);\n      }\n    } else {\n      m_module.decorateBuiltIn(ptr, builtin);\n    }\n\n    bool diffuseOrSpec = semantic == DxsoSemantic{ DxsoUsage::Color, 0 }\n                      || semantic == DxsoSemantic{ DxsoUsage::Color, 1 };\n\n    if (diffuseOrSpec && input)\n      m_flatShadingMask |= 1u << slot;\n\n    std::string name = str::format(input ? \"in_\" : \"out_\", semantic.usage, semantic.usageIndex);\n    m_module.setDebugName(ptr, name.c_str());\n\n    if (input)\n      return m_module.opLoad(type, ptr);\n\n    return ptr;\n  }\n\n\n  void D3D9FFShaderCompiler::compileVS() {\n    setupVS();\n\n    std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };\n\n    uint32_t gl_Position = m_vs.in.POSITION;\n    uint32_t vtx         = m_vs.in.POSITION;\n    uint32_t normal      = m_module.opVectorShuffle(m_vec3Type, m_vs.in.NORMAL, m_vs.in.NORMAL, 3, indices.data());\n\n    if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Tween) {\n      uint32_t vtx1    = m_vs.in.POSITION1;\n      uint32_t normal1 = m_module.opVectorShuffle(m_vec3Type, m_vs.in.NORMAL1, m_vs.in.NORMAL1, 3, indices.data());\n\n      vtx    = m_module.opFMix(m_vec3Type, vtx,    vtx1,    m_vs.constants.tweenFactor);\n      normal = m_module.opFMix(m_vec3Type, normal, normal1, m_vs.constants.tweenFactor);\n    }\n\n    const uint32_t wIndex = 3;\n\n    if (!m_vsKey.Data.Contents.VertexHasPositionT) {\n      if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Normal) {\n        uint32_t blendWeightRemaining = m_module.constf32(1);\n        uint32_t vtxSum               = 0;\n        uint32_t nrmSum               = 0;\n\n        for (uint32_t i = 0; i <= m_vsKey.Data.Contents.VertexBlendCount; i++) {\n          std::array<uint32_t, 2> arrayIndices;\n\n          if (m_vsKey.Data.Contents.VertexBlendIndexed) {\n            uint32_t index = m_module.opCompositeExtract(m_floatType, m_vs.in.BLENDINDICES, 1, &i);\n                     index = m_module.opConvertFtoU(m_uint32Type, m_module.opRound(m_floatType, index));\n\n            arrayIndices = { m_module.constu32(0), index };\n          }\n          else\n            arrayIndices = { m_module.constu32(0), m_module.constu32(i) };\n\n          uint32_t worldview = m_module.opLoad(m_mat4Type,\n            m_module.opAccessChain(\n              m_module.defPointerType(m_mat4Type, spv::StorageClassUniform), m_vs.vertexBlendData, arrayIndices.size(), arrayIndices.data()));\n\n          uint32_t nrmMtx = worldview;\n\n          std::array<uint32_t, 3> mtxIndices;\n          for (uint32_t i = 0; i < 3; i++) {\n            mtxIndices[i] = m_module.opCompositeExtract(m_vec4Type, nrmMtx, 1, &i);\n            mtxIndices[i] = m_module.opVectorShuffle(m_vec3Type, mtxIndices[i], mtxIndices[i], 3, indices.data());\n          }\n          nrmMtx = m_module.opCompositeConstruct(m_mat3Type, mtxIndices.size(), mtxIndices.data());\n\n          uint32_t vtxResult = emitVectorTimesMatrix(4, 4, vtx, worldview);\n          uint32_t nrmResult = m_module.opVectorTimesMatrix(m_vec3Type, normal, nrmMtx);\n\n          uint32_t weight;\n          if (i != m_vsKey.Data.Contents.VertexBlendCount) {\n            weight = m_module.opCompositeExtract(m_floatType, m_vs.in.BLENDWEIGHT, 1, &i);\n            blendWeightRemaining = m_module.opFSub(m_floatType, blendWeightRemaining, weight);\n          }\n          else\n            weight = blendWeightRemaining;\n\n          std::array<uint32_t, 4> weightIds = { weight, weight, weight, weight };\n          uint32_t weightVec4 = m_module.opCompositeConstruct(m_vec4Type, 4, weightIds.data());\n          uint32_t weightVec3 = m_module.opCompositeConstruct(m_vec3Type, 3, weightIds.data());\n\n          vtxSum = vtxSum\n            ? m_module.opFFma(m_vec4Type, vtxResult, weightVec4, vtxSum)\n            : m_module.opFMul(m_vec4Type, vtxResult, weightVec4);\n\n          nrmSum = nrmSum\n            ? m_module.opFFma(m_vec3Type, nrmResult, weightVec3, nrmSum)\n            : m_module.opFMul(m_vec3Type, nrmResult, weightVec3);\n\n          m_module.decorate(vtxSum, spv::DecorationNoContraction);\n        }\n\n        vtx    = vtxSum;\n        normal = nrmSum;\n      }\n      else {\n        vtx = emitVectorTimesMatrix(4, 4, vtx, m_vs.constants.worldview);\n\n        uint32_t nrmMtx = m_vs.constants.normal;\n\n        std::array<uint32_t, 3> mtxIndices;\n        for (uint32_t i = 0; i < 3; i++) {\n          mtxIndices[i] = m_module.opCompositeExtract(m_vec4Type, nrmMtx, 1, &i);\n          mtxIndices[i] = m_module.opVectorShuffle(m_vec3Type, mtxIndices[i], mtxIndices[i], 3, indices.data());\n        }\n        nrmMtx = m_module.opCompositeConstruct(m_mat3Type, mtxIndices.size(), mtxIndices.data());\n\n        normal = m_module.opMatrixTimesVector(m_vec3Type, nrmMtx, normal);\n      }\n\n      // Some games rely on normals not being normal.\n      if (m_vsKey.Data.Contents.NormalizeNormals) {\n        uint32_t bool3_t = m_module.defVectorType(m_boolType, 3);\n\n        uint32_t isZeroNormal = m_module.opAll(m_boolType, m_module.opFOrdEqual(bool3_t, normal, m_module.constvec3f32(0.0f, 0.0f, 0.0f)));\n\n        std::array<uint32_t, 3> members = { isZeroNormal, isZeroNormal, isZeroNormal };\n        uint32_t isZeroNormal3 = m_module.opCompositeConstruct(bool3_t, members.size(), members.data());\n\n        normal = m_module.opNormalize(m_vec3Type, normal);\n        normal = m_module.opSelect(m_vec3Type, isZeroNormal3, m_module.constvec3f32(0.0f, 0.0f, 0.0f), normal);\n      }\n\n      gl_Position = emitVectorTimesMatrix(4, 4, vtx, m_vs.constants.proj);\n    } else {\n      gl_Position = m_module.opFMul(m_vec4Type, gl_Position, m_vs.constants.invExtent);\n      gl_Position = m_module.opFAdd(m_vec4Type, gl_Position, m_vs.constants.invOffset);\n\n      // We still need to account for perspective correction here...\n\n      // gl_Position.w    = 1.0f / gl_Position.w\n      // gl_Position.xyz *= gl_Position.w;\n\n      uint32_t w   = m_module.opCompositeExtract (m_floatType, gl_Position, 1, &wIndex);      // w = gl_Position.w\n      uint32_t is0 = m_module.opFOrdEqual        (m_boolType,      w, m_module.constf32(0));      // is0 = w == 0\n      uint32_t rhw = m_module.opFDiv             (m_floatType, m_module.constf32(1.0f), w);   // rhw = 1.0f / w\n               rhw = m_module.opSelect           (m_floatType, is0, m_module.constf32(1.0), rhw); // rhw = w == 0 ? 1.0 : rhw\n      gl_Position  = m_module.opVectorTimesScalar(m_vec4Type,  gl_Position, rhw);             // gl_Position.xyz *= rhw\n      gl_Position  = m_module.opCompositeInsert  (m_vec4Type,  rhw, gl_Position, 1, &wIndex); // gl_Position.w = rhw\n    }\n\n    m_module.opStore(m_vs.out.POSITION, gl_Position);\n\n    std::array<uint32_t, 4> outNrmIndices;\n    for (uint32_t i = 0; i < 3; i++)\n      outNrmIndices[i] = m_module.opCompositeExtract(m_floatType, normal, 1, &i);\n    outNrmIndices[3] = m_module.constf32(1.0f);\n\n    uint32_t outNrm = m_module.opCompositeConstruct(m_vec4Type, outNrmIndices.size(), outNrmIndices.data());\n\n    m_module.opStore(m_vs.out.NORMAL, outNrm);\n\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n      uint32_t inputIndex = (m_vsKey.Data.Contents.TexcoordIndices     >> (i * 3)) & 0b111;\n      uint32_t inputFlags = (m_vsKey.Data.Contents.TexcoordFlags       >> (i * 3)) & 0b111;\n      uint32_t texcoordCount = (m_vsKey.Data.Contents.VertexTexcoordDeclMask >> (inputIndex * 3)) & 0b111;\n\n      uint32_t transformed;\n\n      const uint32_t wIndex = 3;\n\n      uint32_t flags = (m_vsKey.Data.Contents.TransformFlags >> (i * 3)) & 0b111;\n\n      // Passing 0xffffffff results in it getting clamped to the dimensions of the texture coords and getting treated as PROJECTED\n      // but D3D9 does not apply the transformation matrix.\n      bool applyTransform = flags > D3DTTFF_COUNT1 && flags <= D3DTTFF_COUNT4;\n\n      uint32_t count = std::min(flags, 4u);\n\n      // A projection component index of 4 means we won't do projection\n      uint32_t projIndex = count != 0 ? count - 1 : 4;\n\n      switch (inputFlags) {\n        default:\n        case (DXVK_TSS_TCI_PASSTHRU >> TCIOffset):\n          transformed = m_vs.in.TEXCOORD[inputIndex & 0xFF];\n\n          if (texcoordCount < 4) {\n            // Vulkan sets the w component to 1.0 if that's not provided by the vertex buffer, D3D9 expects 0 here\n            transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(0), transformed, 1, &wIndex);\n          }\n\n          if (applyTransform && !m_vsKey.Data.Contents.VertexHasPositionT) {\n            /*This doesn't happen every time and I cannot figure out the difference between when it does and doesn't.\n            Keep it disabled for now, it's more likely that games rely on the zero texcoord than the weird 1 here.\n            if (texcoordCount <= 1) {\n              // y gets padded to 1 for some reason\n              uint32_t idx = 1;\n              transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1), transformed, 1, &idx);\n            }*/\n\n            if (texcoordCount >= 1 && texcoordCount < 4) {\n              // The first component after the last one thats backed by a vertex buffer gets padded to 1 for some reason.\n              uint32_t idx = texcoordCount;\n              transformed = m_module.opCompositeInsert(m_vec4Type, m_module.constf32(1), transformed, 1, &idx);\n            }\n          } else if (texcoordCount != 0 && !applyTransform) {\n            // COUNT0, COUNT1, COUNT > 4 => take count from vertex decl if that's not zero\n            count = texcoordCount;\n          }\n\n          projIndex = count != 0 ? count - 1 : 4;\n          break;\n\n        case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset):\n          transformed = outNrm;\n          if (!applyTransform) {\n            count = 3;\n            projIndex = 4;\n          }\n          break;\n\n        case (DXVK_TSS_TCI_CAMERASPACEPOSITION >> TCIOffset):\n          transformed = vtx;\n          if (!applyTransform) {\n            count = 3;\n            projIndex = 4;\n          }\n          break;\n\n        case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): {\n          uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data());\n          vtx3 = m_module.opNormalize(m_vec3Type, vtx3);\n\n          uint32_t reflection = m_module.opReflect(m_vec3Type, vtx3, normal);\n\n          std::array<uint32_t, 4> transformIndices;\n          for (uint32_t i = 0; i < 3; i++)\n            transformIndices[i] = m_module.opCompositeExtract(m_floatType, reflection, 1, &i);\n          transformIndices[3] = m_module.constf32(1.0f);\n\n          transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data());\n          if (!applyTransform) {\n            count = 3;\n            projIndex = 4;\n          }\n          break;\n        }\n\n        case (DXVK_TSS_TCI_SPHEREMAP >> TCIOffset): {\n          uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data());\n          vtx3 = m_module.opNormalize(m_vec3Type, vtx3);\n\n          uint32_t reflection = m_module.opReflect(m_vec3Type, vtx3, normal);\n          uint32_t m = m_module.opFAdd(m_vec3Type, reflection, m_module.constvec3f32(0, 0, 1));\n          m = m_module.opLength(m_floatType, m);\n          m = m_module.opFMul(m_floatType, m, m_module.constf32(2.0f));\n\n          std::array<uint32_t, 4> transformIndices;\n          for (uint32_t i = 0; i < 2; i++) {\n            transformIndices[i] = m_module.opCompositeExtract(m_floatType, reflection, 1, &i);\n            transformIndices[i] = m_module.opFDiv(m_floatType, transformIndices[i], m);\n            transformIndices[i] = m_module.opFAdd(m_floatType, transformIndices[i], m_module.constf32(0.5f));\n          }\n\n          transformIndices[2] = m_module.constf32(0.0f);\n          transformIndices[3] = m_module.constf32(1.0f);\n\n          transformed = m_module.opCompositeConstruct(m_vec4Type, transformIndices.size(), transformIndices.data());\n          break;\n        }\n      }\n\n      if (applyTransform && !m_vsKey.Data.Contents.VertexHasPositionT) {\n        transformed = m_module.opVectorTimesMatrix(m_vec4Type, transformed, m_vs.constants.texcoord[i]);\n      }\n\n      uint32_t projectedBit = m_spec.get(m_module, m_specUbo, SpecSamplerProjected, i, 1);\n      uint32_t projectedBool = m_module.opINotEqual(m_boolType, projectedBit, m_module.constu32(0));\n      if (projIndex < 4) {\n        // The projection idx is always based on the flags, even when the input mode is not DXVK_TSS_TCI_PASSTHRU.\n        uint32_t projValue = m_module.opCompositeExtract(m_floatType, transformed, 1, &projIndex);\n\n        // The w component is only used for projection or unused, so always insert the component that's supposed to be divided by there.\n        // The fragment shader will then decide whether to project or not.\n        uint32_t originalWComponent = m_module.opCompositeExtract(m_floatType, transformed, 1, &wIndex);\n        uint32_t insert = m_module.opSelect(m_floatType, projectedBool, projValue, originalWComponent);\n        transformed = m_module.opCompositeInsert(m_vec4Type, insert, transformed, 1, &wIndex);\n      }\n\n      for (uint32_t componentI = count; componentI < 4; componentI++) {\n        // Discard the components that exceed the specified D3DTTFF_COUNT\n\n        // (projected && projIndex < 4) ? 3 : 4\n        uint32_t totalComponentCountCondition = m_module.opLogicalAnd(m_boolType, projectedBool, m_module.constBool(projIndex < 4));\n        uint32_t totalComponents = m_module.opSelect(m_uint32Type, totalComponentCountCondition, m_module.constu32(3), m_module.constu32(4));\n\n        uint32_t originalComponent = m_module.opCompositeExtract(m_floatType, transformed, 1, &componentI);\n        uint32_t condition = m_module.opULessThan(m_boolType, m_module.constu32(componentI), totalComponents);\n        uint32_t insert = m_module.opSelect(m_floatType, condition, m_module.constf32(0.0f), originalComponent);\n        transformed = m_module.opCompositeInsert(m_vec4Type, insert, transformed, 1, &componentI);\n      }\n\n      m_module.opStore(m_vs.out.TEXCOORD[i], transformed);\n    }\n\n    if (m_vsKey.Data.Contents.UseLighting) {\n      auto PickSource = [&](uint32_t Source, uint32_t Material) {\n        if (Source == D3DMCS_MATERIAL)\n          return Material;\n        else if (Source == D3DMCS_COLOR1)\n          return m_vs.in.COLOR[0];\n        else\n          return m_vs.in.COLOR[1];\n      };\n\n      uint32_t diffuseValue  = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f);\n      uint32_t specularValue = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f);\n      uint32_t ambientValue  = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f);\n\n      for (uint32_t i = 0; i < m_vsKey.Data.Contents.LightCount; i++) {\n        uint32_t light_ptr_t = m_module.defPointerType(m_vs.lightType, spv::StorageClassUniform);\n\n        uint32_t indexVal = m_module.constu32(uint32_t(D3D9FFVSMembers::Light0) + i);\n        uint32_t lightPtr = m_module.opAccessChain(light_ptr_t, m_vs.constantBuffer, 1, &indexVal);\n\n        auto LoadLightItem = [&](uint32_t type, uint32_t idx) {\n          uint32_t typePtr = m_module.defPointerType(type, spv::StorageClassUniform);\n\n          idx = m_module.constu32(idx);\n\n          return m_module.opLoad(type,\n            m_module.opAccessChain(typePtr, lightPtr, 1, &idx));\n        };\n\n        uint32_t diffuse   = LoadLightItem(m_vec4Type,   0);\n        uint32_t specular  = LoadLightItem(m_vec4Type,   1);\n        uint32_t ambient   = LoadLightItem(m_vec4Type,   2);\n        uint32_t position  = LoadLightItem(m_vec4Type,   3);\n        uint32_t direction = LoadLightItem(m_vec4Type,   4);\n        uint32_t type      = LoadLightItem(m_uint32Type, 5);\n        uint32_t range     = LoadLightItem(m_floatType,  6);\n        uint32_t falloff   = LoadLightItem(m_floatType,  7);\n        uint32_t atten0    = LoadLightItem(m_floatType,  8);\n        uint32_t atten1    = LoadLightItem(m_floatType,  9);\n        uint32_t atten2    = LoadLightItem(m_floatType,  10);\n        uint32_t theta     = LoadLightItem(m_floatType, 11);\n        uint32_t phi       = LoadLightItem(m_floatType, 12);\n\n        uint32_t bool3_t = m_module.defVectorType(m_boolType, 3);\n\n        uint32_t isSpot        = m_module.opIEqual(m_boolType, type, m_module.constu32(D3DLIGHT_SPOT));\n        uint32_t isDirectional = m_module.opIEqual(m_boolType, type, m_module.constu32(D3DLIGHT_DIRECTIONAL));\n\n        std::array<uint32_t, 3> members = { isDirectional, isDirectional, isDirectional };\n\n        uint32_t isDirectional3 = m_module.opCompositeConstruct(bool3_t, members.size(), members.data());\n\n        uint32_t vtx3      = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data());\n                 position  = m_module.opVectorShuffle(m_vec3Type, position, position, 3, indices.data());\n                 direction = m_module.opVectorShuffle(m_vec3Type, direction, direction, 3, indices.data());\n\n        uint32_t delta  = m_module.opFSub(m_vec3Type, position, vtx3);\n        uint32_t d      = m_module.opLength(m_floatType, delta);\n        uint32_t hitDir = m_module.opFNegate(m_vec3Type, direction);\n                 hitDir = m_module.opSelect(m_vec3Type, isDirectional3, hitDir, delta);\n                 hitDir = m_module.opNormalize(m_vec3Type, hitDir);\n\n        uint32_t atten  = m_module.opFFma  (m_floatType, d, atten2, atten1);\n                 atten  = m_module.opFFma  (m_floatType, d, atten,  atten0);\n                 atten  = m_module.opFDiv  (m_floatType, m_module.constf32(1.0f), atten);\n                 atten  = m_module.opNMin  (m_floatType, atten, m_module.constf32(std::numeric_limits<float>::max()));\n\n                 atten  = m_module.opSelect(m_floatType, m_module.opFOrdGreaterThan(m_boolType, d, range), m_module.constf32(0.0f), atten);\n                 atten  = m_module.opSelect(m_floatType, isDirectional, m_module.constf32(1.0f), atten);\n\n        // Spot Lighting\n        {\n          uint32_t rho        = m_module.opDot (m_floatType, m_module.opFNegate(m_vec3Type, hitDir), direction);\n          uint32_t spotAtten  = m_module.opFSub(m_floatType, rho, phi);\n                   spotAtten  = m_module.opFDiv(m_floatType, spotAtten, m_module.opFSub(m_floatType, theta, phi));\n                   spotAtten  = m_module.opPow (m_floatType, spotAtten, falloff);\n\n          uint32_t insideThetaAndPhi = m_module.opFOrdLessThanEqual(m_boolType, rho, theta);\n          uint32_t insidePhi         = m_module.opFOrdGreaterThan(m_boolType, rho, phi);\n                   spotAtten  = m_module.opSelect(m_floatType, insidePhi,         spotAtten, m_module.constf32(0.0f));\n                   spotAtten  = m_module.opSelect(m_floatType, insideThetaAndPhi, spotAtten, m_module.constf32(1.0f));\n                   spotAtten  = m_module.opFClamp(m_floatType, spotAtten, m_module.constf32(0.0f), m_module.constf32(1.0f));\n\n                   spotAtten = m_module.opFMul(m_floatType, atten, spotAtten);\n                   atten     = m_module.opSelect(m_floatType, isSpot, spotAtten, atten);\n        }\n\n\n        uint32_t hitDot = m_module.opDot(m_floatType, normal, hitDir);\n                 hitDot = m_module.opFClamp(m_floatType, hitDot, m_module.constf32(0.0f), m_module.constf32(1.0f));\n\n        uint32_t diffuseness = m_module.opFMul(m_floatType, hitDot, atten);\n\n        uint32_t mid;\n        if (m_vsKey.Data.Contents.LocalViewer) {\n          mid = m_module.opNormalize(m_vec3Type, vtx3);\n          mid = m_module.opFSub(m_vec3Type, hitDir, mid);\n        }\n        else\n          mid = m_module.opFSub(m_vec3Type, hitDir, m_module.constvec3f32(0.0f, 0.0f, 1.0f));\n\n        mid = m_module.opNormalize(m_vec3Type, mid);\n\n        uint32_t midDot = m_module.opDot(m_floatType, normal, mid);\n                 midDot = m_module.opFClamp(m_floatType, midDot, m_module.constf32(0.0f), m_module.constf32(1.0f));\n        uint32_t doSpec = m_module.opFOrdGreaterThan(m_boolType, midDot, m_module.constf32(0.0f));\n                 doSpec = m_module.opLogicalAnd(m_boolType, doSpec, m_module.opFOrdGreaterThan(m_boolType, hitDot, m_module.constf32(0.0f)));\n\n        uint32_t specularness = m_module.opPow(m_floatType, midDot, m_vs.constants.materialPower);\n                 specularness = m_module.opFMul(m_floatType, specularness, atten);\n                 specularness = m_module.opSelect(m_floatType, doSpec, specularness, m_module.constf32(0.0f));\n\n        uint32_t lightAmbient  = m_module.opVectorTimesScalar(m_vec4Type, ambient,  atten);\n        uint32_t lightDiffuse  = m_module.opVectorTimesScalar(m_vec4Type, diffuse,  diffuseness);\n        uint32_t lightSpecular = m_module.opVectorTimesScalar(m_vec4Type, specular, specularness);\n\n        ambientValue  = m_module.opFAdd(m_vec4Type, ambientValue,  lightAmbient);\n        diffuseValue  = m_module.opFAdd(m_vec4Type, diffuseValue,  lightDiffuse);\n        specularValue = m_module.opFAdd(m_vec4Type, specularValue, lightSpecular);\n      }\n\n      uint32_t mat_diffuse  = PickSource(m_vsKey.Data.Contents.DiffuseSource,  m_vs.constants.materialDiffuse);\n      uint32_t mat_ambient  = PickSource(m_vsKey.Data.Contents.AmbientSource,  m_vs.constants.materialAmbient);\n      uint32_t mat_emissive = PickSource(m_vsKey.Data.Contents.EmissiveSource, m_vs.constants.materialEmissive);\n      uint32_t mat_specular = PickSource(m_vsKey.Data.Contents.SpecularSource, m_vs.constants.materialSpecular);\n\n      std::array<uint32_t, 4> alphaSwizzle = {0, 1, 2, 7};\n      uint32_t finalColor0 = m_module.opFFma(m_vec4Type, mat_ambient, m_vs.constants.globalAmbient, mat_emissive);\n               finalColor0 = m_module.opFFma(m_vec4Type, mat_ambient, ambientValue, finalColor0);\n               finalColor0 = m_module.opFFma(m_vec4Type, mat_diffuse, diffuseValue, finalColor0);\n               finalColor0 = m_module.opVectorShuffle(m_vec4Type, finalColor0, mat_diffuse, alphaSwizzle.size(), alphaSwizzle.data());\n\n      uint32_t finalColor1 = m_module.opFMul(m_vec4Type, mat_specular, specularValue);\n\n      // Saturate\n      finalColor0 = m_module.opFClamp(m_vec4Type, finalColor0,\n        m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f),\n        m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f));\n\n      finalColor1 = m_module.opFClamp(m_vec4Type, finalColor1,\n        m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f),\n        m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f));\n\n      m_module.opStore(m_vs.out.COLOR[0], finalColor0);\n      m_module.opStore(m_vs.out.COLOR[1],\n        m_vsKey.Data.Contents.SpecularEnabled\n        ? finalColor1\n        : m_vs.in.COLOR[1]\n      );\n    }\n    else {\n      m_module.opStore(m_vs.out.COLOR[0], m_vs.in.COLOR[0]);\n      m_module.opStore(m_vs.out.COLOR[1], m_vs.in.COLOR[1]);\n    }\n\n    D3D9FogContext fogCtx;\n    fogCtx.IsPixel     = false;\n    fogCtx.RangeFog    = m_vsKey.Data.Contents.RangeFog;\n    fogCtx.RenderState = m_rsBlock;\n    fogCtx.vPos        = vtx;\n    fogCtx.HasFogInput = m_vsKey.Data.Contents.VertexHasFog;\n    fogCtx.vFog        = m_vs.in.FOG;\n    fogCtx.oColor      = 0;\n    fogCtx.IsFixedFunction = true;\n    fogCtx.IsPositionT = m_vsKey.Data.Contents.VertexHasPositionT;\n    fogCtx.HasSpecular = m_vsKey.Data.Contents.VertexHasColor1;\n    fogCtx.Specular    = m_vs.in.COLOR[1];\n    fogCtx.SpecUBO     = m_specUbo;\n    m_module.opStore(m_vs.out.FOG, DoFixedFunctionFog(m_spec, m_module, fogCtx));\n\n    auto pointInfo = GetPointSizeInfoVS(m_spec, m_module, 0, vtx, m_vs.in.POINTSIZE, m_rsBlock, m_specUbo, true);\n\n    uint32_t pointSize = m_module.opFClamp(m_floatType, pointInfo.defaultValue, pointInfo.min, pointInfo.max);\n    m_module.opStore(m_vs.out.POINTSIZE, pointSize);\n\n    if (m_vsKey.Data.Contents.VertexClipping)\n      emitVsClipping(vtx);\n  }\n\n\n  void D3D9FFShaderCompiler::setupRenderStateInfo(VkShaderStageFlagBits stage, uint32_t samplerCount) {\n    auto blockInfo = SetupRenderStateBlock(m_module, (1u << samplerCount) - 1u);\n\n    m_rsBlock = blockInfo.first;\n    m_rsFirstSampler = blockInfo.second;\n\n    if (samplerCount)\n      m_samplerArray = SetupSamplerArray(m_module);\n\n    uint32_t samplerDwordCount = (samplerCount + 1u) / 2u;\n\n    m_samplerBlock = DxvkPushDataBlock(stage, GetPushSamplerOffset(0u),\n      samplerDwordCount * sizeof(uint32_t), sizeof(uint32_t), (1u << samplerDwordCount) - 1u);\n  }\n\n\n  void D3D9FFShaderCompiler::emitLightTypeDecl() {\n    std::array<uint32_t, 13> light_members = {\n      m_vec4Type,   // Diffuse\n      m_vec4Type,   // Specular\n      m_vec4Type,   // Ambient\n      m_vec4Type,   // Position\n      m_vec4Type,   // Direction\n      m_uint32Type, // Type\n      m_floatType,  // Range\n      m_floatType,  // Falloff\n      m_floatType,  // Attenuation0\n      m_floatType,  // Attenuation1\n      m_floatType,  // Attenuation2\n      m_floatType,  // Theta\n      m_floatType,  // Phi\n    };\n\n    m_vs.lightType =\n      m_module.defStructType(light_members.size(), light_members.data());\n\n    m_module.setDebugName(m_vs.lightType, \"light_t\");\n\n    uint32_t offset = 0;\n\n    m_module.memberDecorateOffset(m_vs.lightType, 0, offset);  offset += 4 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 0, \"Diffuse\");\n    m_module.memberDecorateOffset(m_vs.lightType, 1, offset);  offset += 4 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 1, \"Specular\");\n    m_module.memberDecorateOffset(m_vs.lightType, 2, offset);  offset += 4 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 2, \"Ambient\");\n\n    m_module.memberDecorateOffset(m_vs.lightType, 3, offset);  offset += 4 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 3, \"Position\");\n    m_module.memberDecorateOffset(m_vs.lightType, 4, offset);  offset += 4 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 4, \"Direction\");\n\n    m_module.memberDecorateOffset(m_vs.lightType, 5, offset);  offset += 1 * sizeof(uint32_t);\n    m_module.setDebugMemberName  (m_vs.lightType, 5, \"Type\");\n\n    m_module.memberDecorateOffset(m_vs.lightType, 6, offset);  offset += 1 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 6, \"Range\");\n    m_module.memberDecorateOffset(m_vs.lightType, 7, offset);  offset += 1 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 7, \"Falloff\");\n\n    m_module.memberDecorateOffset(m_vs.lightType, 8, offset);  offset += 1 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 8, \"Attenuation0\");\n    m_module.memberDecorateOffset(m_vs.lightType, 9, offset);  offset += 1 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 9, \"Attenuation1\");\n    m_module.memberDecorateOffset(m_vs.lightType, 10, offset); offset += 1 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 10, \"Attenuation2\");\n\n    m_module.memberDecorateOffset(m_vs.lightType, 11, offset); offset += 1 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 11, \"Theta\");\n    m_module.memberDecorateOffset(m_vs.lightType, 12, offset); offset += 1 * sizeof(float);\n    m_module.setDebugMemberName  (m_vs.lightType, 12, \"Phi\");\n  }\n\n\n  void D3D9FFShaderCompiler::emitBaseBufferDecl() {\n    // Constant Buffer for VS.\n    std::array<uint32_t, uint32_t(D3D9FFVSMembers::MemberCount)> members = {\n      m_mat4Type, // World\n      m_mat4Type, // View\n      m_mat4Type, // InverseView\n      m_mat4Type, // Proj\n\n      m_mat4Type, // Texture0\n      m_mat4Type, // Texture1\n      m_mat4Type, // Texture2\n      m_mat4Type, // Texture3\n      m_mat4Type, // Texture4\n      m_mat4Type, // Texture5\n      m_mat4Type, // Texture6\n      m_mat4Type, // Texture7\n\n      m_vec4Type, // Inverse Offset\n      m_vec4Type, // Inverse Extent\n\n      m_vec4Type, // Global Ambient\n\n      m_vs.lightType, // Light0\n      m_vs.lightType, // Light1\n      m_vs.lightType, // Light2\n      m_vs.lightType, // Light3\n      m_vs.lightType, // Light4\n      m_vs.lightType, // Light5\n      m_vs.lightType, // Light6\n      m_vs.lightType, // Light7\n\n      m_vec4Type,  // Material Diffuse\n      m_vec4Type,  // Material Ambient\n      m_vec4Type,  // Material Specular\n      m_vec4Type,  // Material Emissive\n      m_floatType, // Material Power\n\n      m_floatType, // Tween Factor\n    };\n\n    const uint32_t structType =\n      m_module.defStructType(members.size(), members.data());\n\n    m_module.decorateBlock(structType);\n\n    uint32_t offset = 0;\n\n    for (uint32_t i = 0; i < uint32_t(D3D9FFVSMembers::InverseOffset); i++) {\n      m_module.memberDecorateOffset(structType, i, offset);\n      offset += sizeof(Matrix4);\n      m_module.memberDecorateMatrixStride(structType, i, 16);\n      m_module.memberDecorate(structType, i, spv::DecorationRowMajor);\n    }\n\n    for (uint32_t i = uint32_t(D3D9FFVSMembers::InverseOffset); i < uint32_t(D3D9FFVSMembers::Light0); i++) {\n      m_module.memberDecorateOffset(structType, i, offset);\n      offset += sizeof(Vector4);\n    }\n\n    for (uint32_t i = 0; i < caps::MaxEnabledLights; i++) {\n      m_module.memberDecorateOffset(structType, uint32_t(D3D9FFVSMembers::Light0) + i, offset);\n      offset += sizeof(D3D9Light);\n    }\n\n    for (uint32_t i = uint32_t(D3D9FFVSMembers::MaterialDiffuse); i < uint32_t(D3D9FFVSMembers::MaterialPower); i++) {\n      m_module.memberDecorateOffset(structType, i, offset);\n      offset += sizeof(Vector4);\n    }\n\n    m_module.memberDecorateOffset(structType, uint32_t(D3D9FFVSMembers::MaterialPower), offset);\n    offset += sizeof(float);\n\n    m_module.memberDecorateOffset(structType, uint32_t(D3D9FFVSMembers::TweenFactor), offset);\n    offset += sizeof(float);\n\n    m_module.setDebugName(structType, \"D3D9FixedFunctionVS\");\n    uint32_t member = 0;\n    m_module.setDebugMemberName(structType, member++, \"WorldView\");\n    m_module.setDebugMemberName(structType, member++, \"Normal\");\n    m_module.setDebugMemberName(structType, member++, \"InverseView\");\n    m_module.setDebugMemberName(structType, member++, \"Projection\");\n\n    m_module.setDebugMemberName(structType, member++, \"TexcoordTransform0\");\n    m_module.setDebugMemberName(structType, member++, \"TexcoordTransform1\");\n    m_module.setDebugMemberName(structType, member++, \"TexcoordTransform2\");\n    m_module.setDebugMemberName(structType, member++, \"TexcoordTransform3\");\n    m_module.setDebugMemberName(structType, member++, \"TexcoordTransform4\");\n    m_module.setDebugMemberName(structType, member++, \"TexcoordTransform5\");\n    m_module.setDebugMemberName(structType, member++, \"TexcoordTransform6\");\n    m_module.setDebugMemberName(structType, member++, \"TexcoordTransform7\");\n\n    m_module.setDebugMemberName(structType, member++, \"ViewportInfo_InverseOffset\");\n    m_module.setDebugMemberName(structType, member++, \"ViewportInfo_InverseExtent\");\n\n    m_module.setDebugMemberName(structType, member++, \"GlobalAmbient\");\n\n    m_module.setDebugMemberName(structType, member++, \"Light0\");\n    m_module.setDebugMemberName(structType, member++, \"Light1\");\n    m_module.setDebugMemberName(structType, member++, \"Light2\");\n    m_module.setDebugMemberName(structType, member++, \"Light3\");\n    m_module.setDebugMemberName(structType, member++, \"Light4\");\n    m_module.setDebugMemberName(structType, member++, \"Light5\");\n    m_module.setDebugMemberName(structType, member++, \"Light6\");\n    m_module.setDebugMemberName(structType, member++, \"Light7\");\n\n    m_module.setDebugMemberName(structType, member++, \"Material_Diffuse\");\n    m_module.setDebugMemberName(structType, member++, \"Material_Ambient\");\n    m_module.setDebugMemberName(structType, member++, \"Material_Specular\");\n    m_module.setDebugMemberName(structType, member++, \"Material_Emissive\");\n    m_module.setDebugMemberName(structType, member++, \"Material_Power\");\n\n    m_module.setDebugMemberName(structType, member++, \"TweenFactor\");\n\n    m_vs.constantBuffer = m_module.newVar(\n      m_module.defPointerType(structType, spv::StorageClassUniform),\n      spv::StorageClassUniform);\n\n    m_module.setDebugName(m_vs.constantBuffer, \"consts\");\n\n    const uint32_t bindingId = computeResourceSlotId(\n      D3D9ShaderType::VertexShader, DxsoBindingType::ConstantBuffer,\n      DxsoConstantBuffers::VSFixedFunction);\n\n    m_module.decorateDescriptorSet(m_vs.constantBuffer, 0);\n    m_module.decorateBinding(m_vs.constantBuffer, bindingId);\n\n    auto& binding = m_bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = bindingId;\n    binding.resourceIndex   = bindingId;\n    binding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n    binding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n  }\n\n\n  void D3D9FFShaderCompiler::emitVertexBlendDecl() {\n    const uint32_t arrayType = m_module.defRuntimeArrayTypeUnique(m_mat4Type);\n    m_module.decorateArrayStride(arrayType, sizeof(Matrix4));\n\n    const uint32_t structType = m_module.defStructTypeUnique(1, &arrayType);\n\n    m_module.memberDecorateMatrixStride(structType, 0, 16);\n    m_module.memberDecorate(structType, 0, spv::DecorationRowMajor);\n\n    m_module.decorate(structType, spv::DecorationBufferBlock);\n\n    m_module.memberDecorateOffset(structType, 0, 0);\n\n    m_module.setDebugName(structType, \"D3D9FF_VertexBlendData\");\n    m_module.setDebugMemberName(structType, 0, \"WorldViewArray\");\n\n    m_vs.vertexBlendData = m_module.newVar(\n      m_module.defPointerType(structType, spv::StorageClassUniform),\n      spv::StorageClassUniform);\n\n    m_module.setDebugName(m_vs.vertexBlendData, \"VertexBlendData\");\n\n    const uint32_t bindingId = computeResourceSlotId(\n      D3D9ShaderType::VertexShader, DxsoBindingType::ConstantBuffer,\n      DxsoConstantBuffers::VSVertexBlendData);\n\n    m_module.decorateDescriptorSet(m_vs.vertexBlendData, 0);\n    m_module.decorateBinding(m_vs.vertexBlendData, bindingId);\n\n    m_module.decorate(m_vs.vertexBlendData, spv::DecorationNonWritable);\n\n    auto& binding = m_bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = bindingId;\n    binding.resourceIndex   = bindingId;\n    binding.descriptorType  = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n    binding.access          = VK_ACCESS_SHADER_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n  }\n\n\n  void D3D9FFShaderCompiler::setupVS() {\n    setupRenderStateInfo(VK_SHADER_STAGE_VERTEX_BIT, 0u);\n    m_specUbo = SetupSpecUBO(m_module, m_bindings);\n\n    // VS Caps\n    m_module.enableCapability(spv::CapabilityClipDistance);\n\n    emitLightTypeDecl();\n    emitBaseBufferDecl();\n\n    if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Normal)\n      emitVertexBlendDecl();\n\n    // Load constants\n    auto LoadConstant = [&](uint32_t type, uint32_t idx) {\n      uint32_t offset  = m_module.constu32(idx);\n      uint32_t typePtr = m_module.defPointerType(type, spv::StorageClassUniform);\n\n      return m_module.opLoad(type,\n        m_module.opAccessChain(typePtr, m_vs.constantBuffer, 1, &offset));\n    };\n\n    m_vs.constants.worldview = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::WorldViewMatrix));\n    m_vs.constants.normal    = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::NormalMatrix));\n    m_vs.constants.inverseView = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::InverseViewMatrix));\n    m_vs.constants.proj      = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::ProjMatrix));\n\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++)\n      m_vs.constants.texcoord[i] = LoadConstant(m_mat4Type, uint32_t(D3D9FFVSMembers::Texcoord0) + i);\n\n    m_vs.constants.invOffset = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::InverseOffset));\n    m_vs.constants.invExtent = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::InverseExtent));\n\n    m_vs.constants.globalAmbient = LoadConstant(m_vec4Type, uint32_t(D3D9FFVSMembers::GlobalAmbient));\n\n    m_vs.constants.materialDiffuse  = LoadConstant(m_vec4Type,  uint32_t(D3D9FFVSMembers::MaterialDiffuse));\n    m_vs.constants.materialAmbient  = LoadConstant(m_vec4Type,  uint32_t(D3D9FFVSMembers::MaterialAmbient));\n    m_vs.constants.materialSpecular = LoadConstant(m_vec4Type,  uint32_t(D3D9FFVSMembers::MaterialSpecular));\n    m_vs.constants.materialEmissive = LoadConstant(m_vec4Type,  uint32_t(D3D9FFVSMembers::MaterialEmissive));\n    m_vs.constants.materialPower    = LoadConstant(m_floatType, uint32_t(D3D9FFVSMembers::MaterialPower));\n    m_vs.constants.tweenFactor      = LoadConstant(m_floatType, uint32_t(D3D9FFVSMembers::TweenFactor));\n\n    // Do IO\n    m_vs.in.POSITION  = declareIO(true, DxsoSemantic{ DxsoUsage::Position, 0 });\n    m_vs.in.NORMAL    = declareIO(true, DxsoSemantic{ DxsoUsage::Normal, 0 });\n\n    if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Tween) {\n      m_vs.in.POSITION1 = declareIO(true, DxsoSemantic{ DxsoUsage::Position, 1 });\n      m_vs.in.NORMAL1   = declareIO(true, DxsoSemantic{ DxsoUsage::Normal, 1 });\n    }\n    else {\n      m_isgn.elemCount++;\n      m_isgn.elemCount++;\n    }\n\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++)\n      m_vs.in.TEXCOORD[i] = declareIO(true, DxsoSemantic{ DxsoUsage::Texcoord, i });\n\n    if (m_vsKey.Data.Contents.VertexHasColor0)\n      m_vs.in.COLOR[0] = declareIO(true, DxsoSemantic{ DxsoUsage::Color, 0 });\n    else {\n      m_vs.in.COLOR[0] = m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f);\n      m_isgn.elemCount++;\n    }\n\n    if (m_vsKey.Data.Contents.VertexHasColor1)\n      m_vs.in.COLOR[1] = declareIO(true, DxsoSemantic{ DxsoUsage::Color, 1 });\n    else {\n      // TODO: SM3 behavior\n      m_vs.in.COLOR[1] = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 1.0f);\n      m_isgn.elemCount++;\n    }\n\n    if (m_vsKey.Data.Contents.VertexHasFog)\n      m_vs.in.FOG = declareIO(true, DxsoSemantic{ DxsoUsage::Fog,   0 });\n    else\n      m_isgn.elemCount++;\n\n    if (m_vsKey.Data.Contents.VertexHasPointSize)\n      m_vs.in.POINTSIZE = declareIO(true, DxsoSemantic{ DxsoUsage::PointSize, 0 });\n    else\n      m_isgn.elemCount++;\n\n    if (m_vsKey.Data.Contents.VertexBlendMode == D3D9FF_VertexBlendMode_Normal) {\n      m_vs.in.BLENDWEIGHT  = declareIO(true, DxsoSemantic{ DxsoUsage::BlendWeight, 0 });\n      m_vs.in.BLENDINDICES = declareIO(true, DxsoSemantic{ DxsoUsage::BlendIndices, 0 });\n    }\n    else {\n      m_isgn.elemCount++;\n      m_isgn.elemCount++;\n    }\n\n    // Declare Outputs\n    m_vs.out.POSITION = declareIO(false, DxsoSemantic{ DxsoUsage::Position, 0 }, spv::BuiltInPosition);\n    m_module.decorate(m_vs.out.POSITION, spv::DecorationInvariant);\n\n    m_vs.out.POINTSIZE = declareIO(false, DxsoSemantic{ DxsoUsage::PointSize, 0 }, spv::BuiltInPointSize);\n\n    m_vs.out.NORMAL   = declareIO(false, DxsoSemantic{ DxsoUsage::Normal, 0 });\n\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++)\n      m_vs.out.TEXCOORD[i] = declareIO(false, DxsoSemantic{ DxsoUsage::Texcoord, i });\n\n    m_vs.out.COLOR[0] = declareIO(false, DxsoSemantic{ DxsoUsage::Color, 0 });\n    m_vs.out.COLOR[1] = declareIO(false, DxsoSemantic{ DxsoUsage::Color, 1 });\n\n    m_vs.out.FOG      = declareIO(false, DxsoSemantic{ DxsoUsage::Fog,   0 });\n  }\n\n\n  void D3D9FFShaderCompiler::compilePS() {\n    setupPS();\n\n    uint32_t diffuse  = m_ps.in.COLOR[0];\n    uint32_t specular = m_ps.in.COLOR[1];\n\n    // Current starts of as equal to diffuse.\n    uint32_t current = diffuse;\n    // Temp starts off as equal to vec4(0)\n    uint32_t temp  = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f);\n\n    uint32_t texture = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 1.0f);\n\n    uint32_t unboundTextureConstId = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 1.0f);\n\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n      const auto& stage = m_fsKey.Stages[i].Contents;\n\n      bool processedTexture = false;\n\n      auto DoBumpmapCoords = [this](uint32_t textureStage, uint32_t previousTextureValId, uint32_t baseCoords) {\n        uint32_t previousStage = textureStage - 1;\n\n        uint32_t coords = baseCoords;\n        for (uint32_t i = 0; i < 2; i++) {\n          std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };\n\n          uint32_t tc_m_n = m_module.opCompositeExtract(m_floatType, coords, 1, &i);\n\n          uint32_t offset = m_module.constu32(D3D9SharedPSStages_Count * previousStage + D3D9SharedPSStages_BumpEnvMat0 + i);\n          uint32_t bm     = m_module.opAccessChain(m_module.defPointerType(m_vec2Type, spv::StorageClassUniform),\n                                                   m_ps.sharedState, 1, &offset);\n                   bm     = m_module.opLoad(m_vec2Type, bm);\n\n          uint32_t t      = m_module.opVectorShuffle(m_vec2Type, previousTextureValId, previousTextureValId, 2, indices.data());\n\n          uint32_t dot    = m_module.opDot(m_floatType, bm, t);\n\n          uint32_t result = m_module.opFAdd(m_floatType, tc_m_n, dot);\n          coords  = m_module.opCompositeInsert(m_vec4Type, result, coords, 1, &i);\n        }\n\n        return coords;\n      };\n\n      auto ScalarReplicate = [&](uint32_t reg) {\n        std::array<uint32_t, 4> replicant = { reg, reg, reg, reg };\n        return m_module.opCompositeConstruct(m_vec4Type, replicant.size(), replicant.data());\n      };\n\n      auto DoProjection = [&](uint32_t texcoordId, uint32_t samplerIndex) {\n        uint32_t w = 3;\n\n        uint32_t projScalar = m_module.opCompositeExtract(\n          m_module.defFloatType(32), texcoordId, 1, &w);\n\n        projScalar = m_module.opFDiv(m_module.defFloatType(32), m_module.constf32(1.0), projScalar);\n        uint32_t projResult = m_module.opVectorTimesScalar(m_vec4Type, texcoordId, projScalar);\n\n        uint32_t shouldProj = m_spec.get(m_module, m_specUbo, SpecSamplerProjected, samplerIndex, 1);\n        shouldProj = m_module.opINotEqual(m_boolType, shouldProj, m_module.constu32(0));\n\n        uint32_t bvec4_t = m_module.defVectorType(m_boolType, 4);\n        std::array<uint32_t, 4> indices = { shouldProj, shouldProj, shouldProj, shouldProj };\n        shouldProj = m_module.opCompositeConstruct(bvec4_t, indices.size(), indices.data());\n\n        return m_module.opSelect(m_vec4Type, shouldProj, projResult, texcoordId);\n      };\n\n      auto SampleImage = [this, &DoBumpmapCoords, &ScalarReplicate](uint32_t textureStage, D3D9FFSamplerType samplerType, bool depth, uint32_t texcoordId, uint32_t previousTextureValId) {\n        const auto& samplerInfo = !depth ? m_ps.samplers[textureStage].color[samplerType] : m_ps.samplers[textureStage].depth[samplerType];\n\n        SpirvImageOperands imageOperands;\n        uint32_t imageVarId = m_module.opLoad(samplerInfo.imageTypeId, samplerInfo.imageVarId);\n        imageVarId = m_module.opSampledImage(samplerInfo.sampledTypeId, imageVarId,\n          LoadSampler(m_module, m_samplerArray, m_rsBlock, m_rsFirstSampler, samplerInfo.samplerIndex));\n\n        if (textureStage != 0 && (\n          m_fsKey.Stages[textureStage - 1].Contents.ColorOp == D3DTOP_BUMPENVMAP ||\n          m_fsKey.Stages[textureStage - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE)) {\n          texcoordId = DoBumpmapCoords(textureStage, previousTextureValId, texcoordId);\n        }\n\n        if (unlikely(depth)) {\n          uint32_t component = 2;\n          uint32_t reference = m_module.opCompositeExtract(m_floatType, texcoordId, 1, &component);\n\n          // [D3D8] Scale Dref to [0..(2^N - 1)] for D24S8 and D16 if Dref scaling is enabled\n          uint32_t drefScaleShift = m_spec.get(m_module, m_specUbo, SpecDrefScaling);\n          uint32_t drefScale      = m_module.opShiftLeftLogical(m_uint32Type, m_module.constu32(1), drefScaleShift);\n          drefScale               = m_module.opConvertUtoF(m_floatType, drefScale);\n          drefScale               = m_module.opFSub(m_floatType, drefScale, m_module.constf32(1.0f));\n          drefScale               = m_module.opFDiv(m_floatType, m_module.constf32(1.0f), drefScale);\n          reference               = m_module.opSelect(m_floatType,\n            m_module.opINotEqual(m_boolType, drefScaleShift, m_module.constu32(0)),\n            m_module.opFMul(m_floatType, reference, drefScale),\n            reference\n          );\n\n          // Clamp Dref to [0..1] for D32F emulating UNORM textures\n          uint32_t clampDref = m_spec.get(m_module, m_specUbo, SpecSamplerDrefClamp, textureStage, 1);\n          clampDref = m_module.opINotEqual(m_boolType, clampDref, m_module.constu32(0));\n          uint32_t clampedDref = m_module.opFClamp(m_floatType, reference, m_module.constf32(0.0f), m_module.constf32(1.0f));\n          reference = m_module.opSelect(m_floatType, clampDref, clampedDref, reference);\n\n          uint32_t result = m_module.opImageSampleDrefImplicitLod(m_floatType, imageVarId, texcoordId, reference, imageOperands);\n          return ScalarReplicate(result);\n        } else {\n          return m_module.opImageSampleImplicitLod(m_vec4Type, imageVarId, texcoordId, imageOperands);\n        }\n      };\n\n      auto SampleType = [this, &SampleImage, unboundTextureConstId](uint32_t textureStage, D3D9FFSamplerType samplerType, uint32_t texcoordId, uint32_t previousTextureValId) {\n        // Only do the check for depth comp. samplers\n        // if we aren't a 3D texture\n        uint32_t result;\n        if (samplerType != SamplerTypeTexture3D) {\n          uint32_t colorLabel  = m_module.allocateId();\n          uint32_t depthLabel  = m_module.allocateId();\n          uint32_t endLabel    = m_module.allocateId();\n\n          uint32_t isDepth = m_spec.get(m_module, m_specUbo, SpecSamplerDepthMode, textureStage, 1);\n          isDepth = m_module.opINotEqual(m_module.defBoolType(), isDepth, m_module.constu32(0));\n\n          m_module.opSelectionMerge(endLabel, spv::SelectionControlMaskNone);\n          m_module.opBranchConditional(isDepth, depthLabel, colorLabel);\n\n          m_module.opLabel(colorLabel);\n          uint32_t colorResult = SampleImage(textureStage, samplerType, false, texcoordId, previousTextureValId);\n          m_module.opBranch(endLabel);\n\n          m_module.opLabel(depthLabel);\n          // No spec constant as if we are unbound we always fall down the color path.\n          uint32_t depthResult = SampleImage(textureStage, samplerType, true, texcoordId, previousTextureValId);\n          m_module.opBranch(endLabel);\n\n          m_module.opLabel(endLabel);\n\n          std::array<SpirvPhiLabel, 2> resultPhis = {{\n              { colorResult, colorLabel },\n                { depthResult, depthLabel },\n          }};\n          result = m_module.opPhi(m_vec4Type, resultPhis.size(), resultPhis.data());\n        } else {\n          result = SampleImage(textureStage, samplerType, false, texcoordId, previousTextureValId);\n        }\n\n        uint32_t isNull = m_spec.get(m_module, m_specUbo, SpecSamplerNull, textureStage, 1);\n        isNull = m_module.opINotEqual(m_module.defBoolType(), isNull, m_module.constu32(0));\n\n        // If we are sampling depth we've already specc'ed this!\n        // This path is always size 4 because it only hits on color.\n        uint32_t bvec4_t = m_module.defVectorType(m_boolType, 4);\n        std::array<uint32_t, 4> indices = { isNull, isNull, isNull, isNull };\n        isNull = m_module.opCompositeConstruct(bvec4_t, indices.size(), indices.data());\n        return m_module.opSelect(m_vec4Type, isNull, unboundTextureConstId, result);\n      };\n\n      auto GetTexture = [&]() {\n        if (!processedTexture) {\n          uint32_t textureStage = i;\n          uint32_t previousTextureValId = texture;\n          uint32_t texcoordId = DoProjection(m_ps.in.TEXCOORD[i], i);\n\n          std::array<SpirvSwitchCaseLabel, 3> typeCaseLabels = {{\n            { static_cast<uint32_t>(SamplerTypeTexture2D),           m_module.allocateId() },\n            { static_cast<uint32_t>(SamplerTypeTexture3D),           m_module.allocateId() },\n            { static_cast<uint32_t>(SamplerTypeTextureCube),         m_module.allocateId() },\n          }};\n\n          std::array<SpirvPhiLabel, 3> phiLabels;\n\n          uint32_t switchEndLabel = m_module.allocateId();\n          uint32_t type = m_spec.get(m_module, m_specUbo, SpecSamplerType, textureStage * 2, 2);\n\n          m_module.opSelectionMerge(switchEndLabel, spv::SelectionControlMaskNone);\n          m_module.opSwitch(type,\n            typeCaseLabels[static_cast<uint32_t>(SamplerTypeTexture2D)].labelId,\n            typeCaseLabels.size(),\n            typeCaseLabels.data());\n\n          for (uint32_t typeCaseI = 0; typeCaseI < typeCaseLabels.size(); typeCaseI++) {\n            const auto& caseLabel = typeCaseLabels[typeCaseI];\n            auto& phiLabel = phiLabels[typeCaseI];\n            m_module.opLabel(caseLabel.labelId);\n            phiLabel.labelId = caseLabel.labelId;\n            phiLabel.varId = SampleType(textureStage, static_cast<D3D9FFSamplerType>(caseLabel.literal), texcoordId, previousTextureValId);\n            uint32_t extraLabel = m_module.allocateId();\n            m_module.opBranch(extraLabel);\n            m_module.opLabel(extraLabel);\n            phiLabel.labelId = extraLabel;\n            m_module.opBranch(switchEndLabel);\n          }\n\n          m_module.opLabel(switchEndLabel);\n\n          texture = m_module.opPhi(m_vec4Type, phiLabels.size(), phiLabels.data());\n\n          if (i != 0 && m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE) {\n            uint32_t index = m_module.constu32(D3D9SharedPSStages_Count * (i - 1) + D3D9SharedPSStages_BumpEnvLScale);\n            uint32_t lScale = m_module.opAccessChain(m_module.defPointerType(m_floatType, spv::StorageClassUniform),\n                                                     m_ps.sharedState, 1, &index);\n                     lScale = m_module.opLoad(m_floatType, lScale);\n\n                     index = m_module.constu32(D3D9SharedPSStages_Count * (i - 1) + D3D9SharedPSStages_BumpEnvLOffset);\n            uint32_t lOffset = m_module.opAccessChain(m_module.defPointerType(m_floatType, spv::StorageClassUniform),\n                                                     m_ps.sharedState, 1, &index);\n                     lOffset = m_module.opLoad(m_floatType, lOffset);\n\n            uint32_t zIndex = 2;\n            uint32_t scale = m_module.opCompositeExtract(m_floatType, texture, 1, &zIndex);\n                     scale = m_module.opFMul(m_floatType, scale, lScale);\n                     scale = m_module.opFAdd(m_floatType, scale, lOffset);\n                     scale = m_module.opFClamp(m_floatType, scale, m_module.constf32(0.0f), m_module.constf32(1.0));\n\n            texture = m_module.opVectorTimesScalar(m_vec4Type, texture, scale);\n          }\n        }\n\n        processedTexture = true;\n\n        return texture;\n      };\n\n      auto AlphaReplicate = [&](uint32_t reg) {\n        uint32_t alphaComponentId = 3;\n        uint32_t alpha = m_module.opCompositeExtract(m_floatType, reg, 1, &alphaComponentId);\n\n        return ScalarReplicate(alpha);\n      };\n\n      auto Complement = [&](uint32_t reg) {\n        return m_module.opFSub(m_vec4Type,\n          m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f),\n          reg);\n      };\n\n      auto Saturate = [&](uint32_t reg) {\n        return m_module.opFClamp(m_vec4Type, reg,\n          m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f),\n          m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f));\n      };\n\n      auto GetArg = [&] (uint32_t arg) {\n        uint32_t reg = m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f);\n\n        switch (arg & D3DTA_SELECTMASK) {\n          case D3DTA_CONSTANT: {\n            uint32_t offset = m_module.constu32(D3D9SharedPSStages_Count * i + D3D9SharedPSStages_Constant);\n            uint32_t ptr    = m_module.opAccessChain(m_module.defPointerType(m_vec4Type, spv::StorageClassUniform),\n              m_ps.sharedState, 1, &offset);\n\n            reg = m_module.opLoad(m_vec4Type, ptr);\n            break;\n          }\n          case D3DTA_CURRENT:\n            reg = current;\n            break;\n          case D3DTA_DIFFUSE:\n            reg = diffuse;\n            break;\n          case D3DTA_SPECULAR:\n            reg = specular;\n            break;\n          case D3DTA_TEMP:\n            reg = temp;\n            break;\n          case D3DTA_TEXTURE:\n            reg = GetTexture();\n            break;\n          case D3DTA_TFACTOR:\n            reg = m_ps.constants.textureFactor;\n            break;\n          default:\n            break;\n        }\n\n        // reg = 1 - reg\n        if (arg & D3DTA_COMPLEMENT)\n          reg = Complement(reg);\n\n        // reg = reg.wwww\n        if (arg & D3DTA_ALPHAREPLICATE)\n          reg = AlphaReplicate(reg);\n\n        return reg;\n      };\n\n      auto DoOp = [&](D3DTEXTUREOP op, uint32_t dst, std::array<uint32_t, TextureArgCount> arg) {\n        switch (op) {\n          case D3DTOP_SELECTARG1:\n            dst = arg[1];\n            break;\n\n          case D3DTOP_SELECTARG2:\n            dst = arg[2];\n            break;\n\n          case D3DTOP_MODULATE4X:\n            dst = m_module.opFMul(m_vec4Type, arg[1], arg[2]);\n            dst = m_module.opVectorTimesScalar(m_vec4Type, dst, m_module.constf32(4.0f));\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_MODULATE2X:\n            dst = m_module.opFMul(m_vec4Type, arg[1], arg[2]);\n            dst = m_module.opVectorTimesScalar(m_vec4Type, dst, m_module.constf32(2.0f));\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_MODULATE:\n            dst = m_module.opFMul(m_vec4Type, arg[1], arg[2]);\n            break;\n\n          case D3DTOP_ADDSIGNED2X:\n            arg[2] = m_module.opFSub(m_vec4Type, arg[2],\n              m_module.constvec4f32(0.5f, 0.5f, 0.5f, 0.5f));\n\n            dst = m_module.opFAdd(m_vec4Type, arg[1], arg[2]);\n            dst = m_module.opVectorTimesScalar(m_vec4Type, dst, m_module.constf32(2.0f));\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_ADDSIGNED:\n            arg[2] = m_module.opFSub(m_vec4Type, arg[2],\n              m_module.constvec4f32(0.5f, 0.5f, 0.5f, 0.5f));\n\n            dst = m_module.opFAdd(m_vec4Type, arg[1], arg[2]);\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_ADD:\n            dst = m_module.opFAdd(m_vec4Type, arg[1], arg[2]);\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_SUBTRACT:\n            dst = m_module.opFSub(m_vec4Type, arg[1], arg[2]);\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_ADDSMOOTH:\n            dst = m_module.opFFma(m_vec4Type, Complement(arg[1]), arg[2], arg[1]);\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_BLENDDIFFUSEALPHA:\n            dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], AlphaReplicate(diffuse));\n            break;\n\n          case D3DTOP_BLENDTEXTUREALPHA:\n            dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], AlphaReplicate(GetTexture()));\n            break;\n\n          case D3DTOP_BLENDFACTORALPHA:\n            dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], AlphaReplicate(m_ps.constants.textureFactor));\n            break;\n\n          case D3DTOP_BLENDTEXTUREALPHAPM:\n            dst = m_module.opFFma(m_vec4Type, arg[2], Complement(AlphaReplicate(GetTexture())), arg[1]);\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_BLENDCURRENTALPHA:\n            dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], AlphaReplicate(current));\n            break;\n\n          case D3DTOP_PREMODULATE:\n            Logger::warn(\"D3DTOP_PREMODULATE: not implemented\");\n            break;\n\n          case D3DTOP_MODULATEALPHA_ADDCOLOR:\n            dst = m_module.opFFma(m_vec4Type, AlphaReplicate(arg[1]), arg[2], arg[1]);\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_MODULATECOLOR_ADDALPHA:\n            dst = m_module.opFFma(m_vec4Type, arg[1], arg[2], AlphaReplicate(arg[1]));\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_MODULATEINVALPHA_ADDCOLOR:\n            dst = m_module.opFFma(m_vec4Type, Complement(AlphaReplicate(arg[1])), arg[2], arg[1]);\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_MODULATEINVCOLOR_ADDALPHA:\n            dst = m_module.opFFma(m_vec4Type, Complement(arg[1]), arg[2], AlphaReplicate(arg[1]));\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_BUMPENVMAPLUMINANCE:\n          case D3DTOP_BUMPENVMAP:\n            // Load texture for the next stage...\n            texture = GetTexture();\n            break;\n\n          case D3DTOP_DOTPRODUCT3: {\n            // Get vec3 of arg1 & 2\n            uint32_t vec3Type = m_module.defVectorType(m_floatType, 3);\n            std::array<uint32_t, 3> indices = { 0, 1, 2 };\n            arg[1] = m_module.opVectorShuffle(vec3Type, arg[1], arg[1], indices.size(), indices.data());\n            arg[2] = m_module.opVectorShuffle(vec3Type, arg[2], arg[2], indices.size(), indices.data());\n\n            // Bias according to spec.\n            arg[1] = m_module.opFSub(vec3Type, arg[1], m_module.constvec3f32(0.5f, 0.5f, 0.5f));\n            arg[2] = m_module.opFSub(vec3Type, arg[2], m_module.constvec3f32(0.5f, 0.5f, 0.5f));\n\n            // Do the dotting!\n            dst = m_module.opDot(m_floatType, arg[1], arg[2]);\n\n            // Multiply by 4 and replicate -> vec4\n            dst = m_module.opFMul(m_floatType, dst, m_module.constf32(4.0f));\n            dst = ScalarReplicate(dst);\n\n            // Saturate\n            dst = Saturate(dst);\n\n            break;\n          }\n\n          case D3DTOP_MULTIPLYADD:\n            dst = m_module.opFFma(m_vec4Type, arg[1], arg[2], arg[0]);\n            dst = Saturate(dst);\n            break;\n\n          case D3DTOP_LERP:\n            dst = m_module.opFMix(m_vec4Type, arg[2], arg[1], arg[0]);\n            break;\n\n          default:\n            Logger::warn(\"Unhandled texture op!\");\n            break;\n        }\n\n        return dst;\n      };\n\n      uint32_t& dst = stage.ResultIsTemp ? temp : current;\n\n      D3DTEXTUREOP colorOp = (D3DTEXTUREOP)stage.ColorOp;\n\n      // This cancels all subsequent stages.\n      if (colorOp == D3DTOP_DISABLE)\n        break;\n\n      std::array<uint32_t, TextureArgCount> colorArgs = {\n          stage.ColorArg0,\n          stage.ColorArg1,\n          stage.ColorArg2};\n\n      D3DTEXTUREOP alphaOp = (D3DTEXTUREOP)stage.AlphaOp;\n      std::array<uint32_t, TextureArgCount> alphaArgs = {\n          stage.AlphaArg0,\n          stage.AlphaArg1,\n          stage.AlphaArg2};\n\n      auto ProcessArgs = [&](auto op, auto& args) {\n        for (uint32_t& arg : args)\n          arg = GetArg(arg);\n      };\n\n      // Fast path if alpha/color path is identical.\n      // D3DTOP_DOTPRODUCT3 also has special quirky behaviour here.\n      const bool fastPath = colorOp == alphaOp && colorArgs == alphaArgs;\n      if (fastPath || colorOp == D3DTOP_DOTPRODUCT3) {\n        if (colorOp != D3DTOP_DISABLE) {\n          ProcessArgs(colorOp, colorArgs);\n          dst = DoOp(colorOp, dst, colorArgs);\n        }\n      }\n      else {\n        std::array<uint32_t, 4> indices = { 0, 1, 2, 4 + 3 };\n\n        uint32_t colorResult = dst;\n        uint32_t alphaResult = dst;\n        if (colorOp != D3DTOP_DISABLE) {\n          ProcessArgs(colorOp, colorArgs);\n          colorResult = DoOp(colorOp, dst, colorArgs);\n        }\n\n        if (alphaOp != D3DTOP_DISABLE) {\n          ProcessArgs(alphaOp, alphaArgs);\n          alphaResult = DoOp(alphaOp, dst, alphaArgs);\n        }\n\n        // src0.x, src0.y, src0.z src1.w\n        if (colorResult != dst)\n          dst = m_module.opVectorShuffle(m_vec4Type, colorResult, dst, indices.size(), indices.data());\n\n        // src0.x, src0.y, src0.z src1.w\n        // But we flip src0, src1 to be inverse of color.\n        if (alphaResult != dst)\n          dst = m_module.opVectorShuffle(m_vec4Type, dst, alphaResult, indices.size(), indices.data());\n      }\n    }\n\n    if (m_fsKey.Stages[0].Contents.GlobalSpecularEnable) {\n      uint32_t specular = m_module.opFMul(m_vec4Type, m_ps.in.COLOR[1], m_module.constvec4f32(1.0f, 1.0f, 1.0f, 0.0f));\n\n      current = m_module.opFAdd(m_vec4Type, current, specular);\n    }\n\n    D3D9FogContext fogCtx;\n    fogCtx.IsPixel     = true;\n    fogCtx.RangeFog    = false;\n    fogCtx.RenderState = m_rsBlock;\n    fogCtx.vPos        = m_ps.in.POS;\n    fogCtx.vFog        = m_ps.in.FOG;\n    fogCtx.oColor      = current;\n    fogCtx.IsFixedFunction = true;\n    fogCtx.IsPositionT = false;\n    fogCtx.HasSpecular = false;\n    fogCtx.Specular    = 0;\n    fogCtx.SpecUBO     = m_specUbo;\n    current = DoFixedFunctionFog(m_spec, m_module, fogCtx);\n\n    m_module.opStore(m_ps.out.COLOR, current);\n\n    alphaTestPS();\n  }\n\n  void D3D9FFShaderCompiler::setupPS() {\n    setupRenderStateInfo(VK_SHADER_STAGE_FRAGMENT_BIT, 8u);\n    m_specUbo = SetupSpecUBO(m_module, m_bindings);\n\n    // PS Caps\n    m_module.enableExtension(\"SPV_EXT_demote_to_helper_invocation\");\n    m_module.enableCapability(spv::CapabilityDemoteToHelperInvocationEXT);\n    m_module.enableCapability(spv::CapabilityDerivativeControl);\n\n    m_module.setExecutionMode(m_entryPointId,\n      spv::ExecutionModeOriginUpperLeft);\n\n    uint32_t pointCoord = GetPointCoord(m_module);\n    auto pointInfo = GetPointSizeInfoPS(m_spec, m_module, m_rsBlock, m_specUbo);\n\n    // We need to replace TEXCOORD inputs with gl_PointCoord\n    // if D3DRS_POINTSPRITEENABLE is set.\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n      m_ps.in.TEXCOORD[i] = declareIO(true, DxsoSemantic{ DxsoUsage::Texcoord, i });\n      m_ps.in.TEXCOORD[i] = m_module.opSelect(m_vec4Type, pointInfo.isSprite, pointCoord, m_ps.in.TEXCOORD[i]);\n    }\n\n    m_ps.in.COLOR[0] = declareIO(true, DxsoSemantic{ DxsoUsage::Color, 0 });\n    m_ps.in.COLOR[1] = declareIO(true, DxsoSemantic{ DxsoUsage::Color, 1 });\n\n    m_ps.in.FOG      = declareIO(true, DxsoSemantic{ DxsoUsage::Fog, 0 });\n    m_ps.in.POS      = declareIO(true, DxsoSemantic{ DxsoUsage::Position, 0 }, spv::BuiltInFragCoord);\n\n    m_ps.out.COLOR   = declareIO(false, DxsoSemantic{ DxsoUsage::Color, 0 });\n\n    // Constant Buffer for PS.\n    std::array<uint32_t, uint32_t(D3D9FFPSMembers::MemberCount)> members = {\n      m_vec4Type // Texture Factor\n    };\n\n    const uint32_t structType =\n      m_module.defStructType(members.size(), members.data());\n\n    m_module.decorateBlock(structType);\n    uint32_t offset = 0;\n\n    for (uint32_t i = 0; i < uint32_t(D3D9FFPSMembers::MemberCount); i++) {\n      m_module.memberDecorateOffset(structType, i, offset);\n      offset += sizeof(Vector4);\n    }\n\n    m_module.setDebugName(structType, \"D3D9FixedFunctionPS\");\n    m_module.setDebugMemberName(structType, 0, \"textureFactor\");\n\n    m_ps.constantBuffer = m_module.newVar(\n      m_module.defPointerType(structType, spv::StorageClassUniform),\n      spv::StorageClassUniform);\n\n    m_module.setDebugName(m_ps.constantBuffer, \"consts\");\n\n    const uint32_t bindingId = computeResourceSlotId(\n      D3D9ShaderType::PixelShader, DxsoBindingType::ConstantBuffer,\n      DxsoConstantBuffers::PSFixedFunction);\n\n    m_module.decorateDescriptorSet(m_ps.constantBuffer, 0);\n    m_module.decorateBinding(m_ps.constantBuffer, bindingId);\n\n    auto& binding = m_bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = bindingId;\n    binding.resourceIndex   = bindingId;\n    binding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n    binding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n    // Load constants\n    auto LoadConstant = [&](uint32_t type, uint32_t idx) {\n      uint32_t offset  = m_module.constu32(idx);\n      uint32_t typePtr = m_module.defPointerType(type, spv::StorageClassUniform);\n\n      return m_module.opLoad(type,\n        m_module.opAccessChain(typePtr, m_ps.constantBuffer, 1, &offset));\n    };\n\n    m_ps.constants.textureFactor = LoadConstant(m_vec4Type, uint32_t(D3D9FFPSMembers::TextureFactor));\n\n    // Samplers\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n      const uint32_t imageBindingId = computeResourceSlotId(D3D9ShaderType::PixelShader,\n        DxsoBindingType::Image, i);\n\n      auto& imageBinding = m_bindings.emplace_back();\n      imageBinding.set             = 0u;\n      imageBinding.binding         = imageBindingId;\n      imageBinding.resourceIndex   = imageBindingId;\n      imageBinding.descriptorType  = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n      imageBinding.viewType        = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n      imageBinding.access          = VK_ACCESS_SHADER_READ_BIT;\n\n      auto& samplerBinding = m_bindings.emplace_back();\n      samplerBinding.resourceIndex   = imageBindingId;\n      samplerBinding.descriptorType  = VK_DESCRIPTOR_TYPE_SAMPLER;\n      samplerBinding.blockOffset     = GetPushSamplerOffset(i);\n      samplerBinding.flags.set(DxvkDescriptorFlag::PushData);\n\n      for (uint32_t j = 0; j < SamplerTypeCount * 2; j++) {\n        auto samplerType = static_cast<D3D9FFSamplerType>(j % SamplerTypeCount);\n        bool isDepth = j >= SamplerTypeCount;\n\n        if (samplerType == SamplerTypeTexture3D && isDepth) {\n          // This could be done much smarter but let's keep it simple.\n          continue;\n        }\n\n        auto& sampler = !isDepth ? m_ps.samplers[i].color[samplerType] : m_ps.samplers[i].depth[samplerType];\n\n        spv::Dim dimensionality;\n        const char* suffix = \"_2d\";\n\n        switch (samplerType) {\n          default:\n          case SamplerTypeTexture2D:\n            dimensionality = spv::Dim2D;\n            break;\n          case SamplerTypeTextureCube:\n            suffix = \"_cube\";\n            dimensionality = spv::DimCube;\n            break;\n          case SamplerTypeTexture3D:\n            suffix = \"_3d\";\n            dimensionality = spv::Dim3D;\n            break;\n        }\n\n        sampler.imageTypeId = m_module.defImageType(\n          m_module.defFloatType(32),\n          dimensionality, isDepth ? 1 : 0, 0, 0, 1,\n          spv::ImageFormatUnknown);\n        sampler.imageVarId = m_module.newVar(\n          m_module.defPointerType(sampler.imageTypeId, spv::StorageClassUniformConstant),\n          spv::StorageClassUniformConstant);\n\n        sampler.sampledTypeId = m_module.defSampledImageType(sampler.imageTypeId);\n        sampler.samplerIndex = i;\n\n        std::string name = str::format(\"t\", i, suffix, isDepth ? \"_shadow\" : \"\");\n        m_module.setDebugName(sampler.imageVarId, name.c_str());\n\n        m_module.decorateDescriptorSet(sampler.imageVarId, 0);\n        m_module.decorateBinding(sampler.imageVarId, imageBindingId);\n      }\n    }\n\n    emitPsSharedConstants();\n  }\n\n\n  void D3D9FFShaderCompiler::emitPsSharedConstants() {\n    m_ps.sharedState = GetSharedConstants(m_module);\n\n    const uint32_t bindingId = computeResourceSlotId(\n      m_shaderType, DxsoBindingType::ConstantBuffer,\n      PSShared);\n\n    m_module.decorateDescriptorSet(m_ps.sharedState, 0);\n    m_module.decorateBinding(m_ps.sharedState, bindingId);\n\n    auto& binding = m_bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = bindingId;\n    binding.resourceIndex   = bindingId;\n    binding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n    binding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n  }\n\n\n  void D3D9FFShaderCompiler::emitVsClipping(uint32_t vtx) {\n    uint32_t worldPos = emitMatrixTimesVector(4, 4, m_vs.constants.inverseView, vtx);\n\n    uint32_t clipPlaneCountId = m_module.constu32(caps::MaxClipPlanes);\n\n    uint32_t floatType = m_module.defFloatType(32);\n    uint32_t vec4Type  = m_module.defVectorType(floatType, 4);\n    uint32_t boolType  = m_module.defBoolType();\n\n    // Declare uniform buffer containing clip planes\n    uint32_t clipPlaneArray  = m_module.defArrayTypeUnique(vec4Type, clipPlaneCountId);\n    uint32_t clipPlaneStruct = m_module.defStructTypeUnique(1, &clipPlaneArray);\n    uint32_t clipPlaneBlock  = m_module.newVar(\n      m_module.defPointerType(clipPlaneStruct, spv::StorageClassUniform),\n      spv::StorageClassUniform);\n\n    m_module.decorateArrayStride  (clipPlaneArray, 16);\n\n    m_module.setDebugName         (clipPlaneStruct, \"clip_info_t\");\n    m_module.setDebugMemberName   (clipPlaneStruct, 0, \"clip_planes\");\n    m_module.decorate             (clipPlaneStruct, spv::DecorationBlock);\n    m_module.memberDecorateOffset (clipPlaneStruct, 0, 0);\n\n    uint32_t bindingId = computeResourceSlotId(\n      D3D9ShaderType::VertexShader,\n      DxsoBindingType::ConstantBuffer,\n      DxsoConstantBuffers::VSClipPlanes);\n\n    m_module.setDebugName         (clipPlaneBlock, \"clip_info\");\n    m_module.decorateDescriptorSet(clipPlaneBlock, 0);\n    m_module.decorateBinding      (clipPlaneBlock, bindingId);\n\n    auto& binding = m_bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = bindingId;\n    binding.resourceIndex   = bindingId;\n    binding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n    binding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n    // Declare output array for clip distances\n    uint32_t clipDistArray = m_module.newVar(\n      m_module.defPointerType(\n        m_module.defArrayType(floatType, clipPlaneCountId),\n        spv::StorageClassOutput),\n      spv::StorageClassOutput);\n\n    m_module.decorateBuiltIn(clipDistArray, spv::BuiltInClipDistance);\n\n    // Always consider clip planes enabled when doing GPL by forcing 6 for the quick value.\n    uint32_t clipPlaneCount = m_spec.get(m_module, m_specUbo, SpecClipPlaneCount, 0, 32, m_module.constu32(caps::MaxClipPlanes));\n\n    // Compute clip distances\n    for (uint32_t i = 0; i < caps::MaxClipPlanes; i++) {\n      std::array<uint32_t, 2> blockMembers = {{\n        m_module.constu32(0),\n        m_module.constu32(i),\n      }};\n\n      uint32_t planeId = m_module.opLoad(vec4Type,\n        m_module.opAccessChain(\n          m_module.defPointerType(vec4Type, spv::StorageClassUniform),\n          clipPlaneBlock, blockMembers.size(), blockMembers.data()));\n\n      uint32_t distId = m_module.opDot(floatType, worldPos, planeId);\n\n      uint32_t clipPlaneEnabled = m_module.opULessThan(boolType, m_module.constu32(i), clipPlaneCount);\n\n      uint32_t value = m_module.opSelect(floatType, clipPlaneEnabled, distId, m_module.constf32(0.0f));\n\n      m_module.opStore(m_module.opAccessChain(\n        m_module.defPointerType(floatType, spv::StorageClassOutput),\n        clipDistArray, 1, &blockMembers[1]), value);\n    }\n  }\n\n\n  void D3D9FFShaderCompiler::alphaTestPS() {\n    uint32_t uintPtr = m_module.defPointerType(m_uint32Type, spv::StorageClassPushConstant);\n\n    auto oC0 = m_ps.out.COLOR;\n\n    uint32_t alphaComponentId = 3;\n    uint32_t alphaRefMember = m_module.constu32(uint32_t(D3D9RenderStateItem::AlphaRef));\n\n    D3D9AlphaTestContext alphaTestContext;\n    alphaTestContext.alphaFuncId = m_spec.get(m_module, m_specUbo, SpecAlphaCompareOp);\n    alphaTestContext.alphaPrecisionId = m_spec.get(m_module, m_specUbo, SpecAlphaPrecisionBits);\n    alphaTestContext.alphaRefId = m_module.opLoad(m_uint32Type,\n      m_module.opAccessChain(uintPtr, m_rsBlock, 1, &alphaRefMember));\n    alphaTestContext.alphaId = m_module.opCompositeExtract(m_floatType,\n      m_module.opLoad(m_vec4Type, oC0),\n      1, &alphaComponentId);\n\n    DoFixedFunctionAlphaTest(m_module, alphaTestContext);\n  }\n\n\n  uint32_t D3D9FFShaderCompiler::emitMatrixTimesVector(uint32_t rowCount, uint32_t colCount, uint32_t matrix, uint32_t vector) {\n    uint32_t f32Type = m_module.defFloatType(32);\n    uint32_t vecType = m_module.defVectorType(f32Type, rowCount);\n    uint32_t accum = 0;\n\n    for (uint32_t i = 0; i < colCount; i++) {\n      std::array<uint32_t, 4> indices = { i, i, i, i };\n\n      uint32_t a = m_module.opVectorShuffle(vecType, vector, vector, rowCount, indices.data());\n      uint32_t b = m_module.opCompositeExtract(vecType, matrix, 1, &i);\n\n      accum = accum\n        ? m_module.opFFma(vecType, a, b, accum)\n        : m_module.opFMul(vecType, a, b);\n\n      m_module.decorate(accum, spv::DecorationNoContraction);\n    }\n\n    return accum;\n  }\n\n\n  uint32_t D3D9FFShaderCompiler::emitVectorTimesMatrix(uint32_t rowCount, uint32_t colCount, uint32_t vector, uint32_t matrix) {\n    uint32_t f32Type = m_module.defFloatType(32);\n    uint32_t vecType = m_module.defVectorType(f32Type, colCount);\n    uint32_t matType = m_module.defMatrixType(vecType, rowCount);\n\n    matrix = m_module.opTranspose(matType, matrix);\n    return emitMatrixTimesVector(colCount, rowCount, matrix, vector);\n  }\n\n\n  D3D9FFShader::D3D9FFShader(\n          D3D9DeviceEx*         pDevice,\n    const D3D9FFShaderKeyVS&    Key) {\n    Sha1Hash hash = Sha1Hash::compute(&Key, sizeof(Key));\n    DxvkShaderHash shaderKey(VK_SHADER_STAGE_VERTEX_BIT,  0u, hash.digest(), hash.digestLength());\n\n    std::string name = str::format(\"FF_\", shaderKey.toString());\n\n    D3D9FFShaderCompiler compiler(\n      pDevice->GetDXVKDevice(),\n      Key, name,\n      pDevice->GetOptions());\n\n    m_shader = compiler.compile();\n\n    Dump(pDevice, Key, name);\n\n    pDevice->GetDXVKDevice()->registerShader(m_shader);\n  }\n\n\n  D3D9FFShader::D3D9FFShader(\n          D3D9DeviceEx*         pDevice,\n    const D3D9FFShaderKeyFS&    Key) {\n    Sha1Hash hash = Sha1Hash::compute(&Key, sizeof(Key));\n    DxvkShaderHash shaderKey(VK_SHADER_STAGE_FRAGMENT_BIT, 0u, hash.digest(), hash.digestLength());\n\n    std::string name = str::format(\"FF_\", shaderKey.toString());\n\n    D3D9FFShaderCompiler compiler(\n      pDevice->GetDXVKDevice(),\n      Key, name,\n      pDevice->GetOptions());\n\n    m_shader = compiler.compile();\n\n    Dump(pDevice, Key, name);\n\n    pDevice->GetDXVKDevice()->registerShader(m_shader);\n  }\n\n\n  D3D9FFShader::D3D9FFShader(\n          D3D9DeviceEx*         pDevice,\n          D3D9ShaderType        ShaderType) {\n\n    bool isVS = ShaderType == D3D9ShaderType::VertexShader;\n\n    if (isVS) {\n      std::array<DxvkBindingInfo, 4> bindings;\n\n      constexpr uint32_t specConstantBufferBindingId = getSpecConstantBufferSlot();\n      auto& specConstantBufferBinding = bindings[0];\n      specConstantBufferBinding.set = 0u;\n      specConstantBufferBinding.binding        = specConstantBufferBindingId;\n      specConstantBufferBinding.resourceIndex  = specConstantBufferBindingId;\n      specConstantBufferBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n      specConstantBufferBinding.access = VK_ACCESS_UNIFORM_READ_BIT;\n      specConstantBufferBinding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n      constexpr uint32_t fixedFunctionDataBindingId = computeResourceSlotId(\n        D3D9ShaderType::VertexShader, DxsoBindingType::ConstantBuffer,\n        DxsoConstantBuffers::VSFixedFunction);\n      auto& fixedFunctionDataBinding = bindings[1];\n      fixedFunctionDataBinding.set             = 0u;\n      fixedFunctionDataBinding.binding         = fixedFunctionDataBindingId;\n      fixedFunctionDataBinding.resourceIndex   = fixedFunctionDataBindingId;\n      fixedFunctionDataBinding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n      fixedFunctionDataBinding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n      fixedFunctionDataBinding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n      constexpr uint32_t vertexBlendBindingId = computeResourceSlotId(\n        D3D9ShaderType::VertexShader, DxsoBindingType::ConstantBuffer,\n        DxsoConstantBuffers::VSVertexBlendData);\n      auto& vertexBlendBinding = bindings[2];\n      vertexBlendBinding.set             = 0u;\n      vertexBlendBinding.binding         = vertexBlendBindingId;\n      vertexBlendBinding.resourceIndex   = vertexBlendBindingId;\n      vertexBlendBinding.descriptorType  = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n      vertexBlendBinding.access          = VK_ACCESS_SHADER_READ_BIT;\n      vertexBlendBinding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n      constexpr uint32_t clipPlanesBindingId = computeResourceSlotId(\n        D3D9ShaderType::VertexShader, DxsoBindingType::ConstantBuffer,\n        DxsoConstantBuffers::VSClipPlanes);\n      auto& clipPlanesBinding = bindings[3];\n      clipPlanesBinding.set             = 0u;\n      clipPlanesBinding.binding         = clipPlanesBindingId;\n      clipPlanesBinding.resourceIndex   = clipPlanesBindingId;\n      clipPlanesBinding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n      clipPlanesBinding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n      clipPlanesBinding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n      DxvkSpirvShaderCreateInfo info;\n      info.bindingCount = bindings.size();\n      info.bindings = bindings.data();\n      info.flatShadingInputs = 0;\n      info.sharedPushData = DxvkPushDataBlock(0u, sizeof(D3D9RenderStateInfo), 4u, 0u);\n      info.localPushData = DxvkPushDataBlock();\n      info.samplerHeap = DxvkShaderBinding();\n      info.debugName = \"FF VS\";\n\n      m_shader = new DxvkSpirvShader(info, d3d9_fixed_function_vert);\n    } else {\n      std::vector<DxvkBindingInfo> bindings;\n\n      constexpr uint32_t specConstantBufferBindingId = getSpecConstantBufferSlot();\n      auto& specConstantBufferBinding = bindings.emplace_back();\n      specConstantBufferBinding.set = 0u;\n      specConstantBufferBinding.binding        = specConstantBufferBindingId;\n      specConstantBufferBinding.resourceIndex  = specConstantBufferBindingId;\n      specConstantBufferBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n      specConstantBufferBinding.access = VK_ACCESS_UNIFORM_READ_BIT;\n      specConstantBufferBinding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n      constexpr uint32_t fixedFunctionDataBindingId = computeResourceSlotId(\n        D3D9ShaderType::PixelShader, DxsoBindingType::ConstantBuffer,\n        DxsoConstantBuffers::PSFixedFunction);\n      auto& fixedFunctionDataBinding = bindings.emplace_back();\n      fixedFunctionDataBinding.set             = 0u;\n      fixedFunctionDataBinding.binding         = fixedFunctionDataBindingId;\n      fixedFunctionDataBinding.resourceIndex   = fixedFunctionDataBindingId;\n      fixedFunctionDataBinding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n      fixedFunctionDataBinding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n      fixedFunctionDataBinding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n      constexpr uint32_t sharedDataBindingId = computeResourceSlotId(\n        D3D9ShaderType::PixelShader, DxsoBindingType::ConstantBuffer,\n        DxsoConstantBuffers::PSShared);\n      auto& sharedDataBinding = bindings.emplace_back();\n      sharedDataBinding.set             = 0u;\n      sharedDataBinding.binding         = sharedDataBindingId;\n      sharedDataBinding.resourceIndex   = sharedDataBindingId;\n      sharedDataBinding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n      sharedDataBinding.access          = VK_ACCESS_SHADER_READ_BIT;\n      sharedDataBinding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n      constexpr uint32_t textureBindingId = computeResourceSlotId(\n        D3D9ShaderType::PixelShader,\n        DxsoBindingType::Image,\n        0);\n      auto& textureBinding = bindings.emplace_back();\n      textureBinding.set             = 0u;\n      textureBinding.binding         = textureBindingId;\n      textureBinding.resourceIndex   = textureBindingId;\n      textureBinding.descriptorType  = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n      textureBinding.access       = VK_ACCESS_SHADER_READ_BIT;\n      textureBinding.descriptorCount = caps::TextureStageCount;\n\n      for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n        uint32_t samplerBindingId = computeResourceSlotId(\n          D3D9ShaderType::PixelShader,\n          DxsoBindingType::Image,\n          i);\n\n        auto& samplerBinding = bindings.emplace_back();\n        samplerBinding.resourceIndex   = samplerBindingId;\n        samplerBinding.descriptorType  = VK_DESCRIPTOR_TYPE_SAMPLER;\n        samplerBinding.blockOffset     = GetPushSamplerOffset(i);\n        samplerBinding.flags.set(DxvkDescriptorFlag::PushData);\n        bindings.push_back(samplerBinding);\n      }\n\n      uint32_t flatShadingMask = (1u << RegisterLinkerSlot(DxsoSemantic{ DxsoUsage::Color, 0 }))\n        | (1u << RegisterLinkerSlot(DxsoSemantic{ DxsoUsage::Color, 1 }));\n\n      uint32_t samplerCount = caps::TextureStageCount;\n      uint32_t samplerDwordCount = (samplerCount + 1u) / 2u;\n\n      DxvkSpirvShaderCreateInfo info;\n      info.bindingCount = bindings.size();\n      info.bindings = bindings.data();\n      info.flatShadingInputs = flatShadingMask;\n      info.sharedPushData = DxvkPushDataBlock(0u, sizeof(D3D9RenderStateInfo), 4u, 0u);\n      info.localPushData = DxvkPushDataBlock(VK_SHADER_STAGE_FRAGMENT_BIT, GetPushSamplerOffset(0u),\n        samplerDwordCount * sizeof(uint32_t), sizeof(uint32_t), (1u << samplerDwordCount) - 1u);\n      info.samplerHeap = DxvkShaderBinding(VK_SHADER_STAGE_FRAGMENT_BIT, 1u, 0u);\n      info.debugName = \"FF FS\";\n\n      m_shader = pDevice->GetOptions()->forceSampleRateShading\n        ? new DxvkSpirvShader(info, d3d9_fixed_function_frag_sample)\n        : new DxvkSpirvShader(info, d3d9_fixed_function_frag);\n    }\n\n    pDevice->GetDXVKDevice()->registerShader(m_shader);\n  }\n\n\n  template <typename T>\n  void D3D9FFShader::Dump(D3D9DeviceEx* pDevice, const T& Key, const std::string& Name) {\n    const std::string& dumpPath = pDevice->GetOptions()->shaderDumpPath;\n\n    if (dumpPath.size() != 0) {\n      std::ofstream dumpStream(\n        str::topath(str::format(dumpPath, \"/\", Name, \".spv\").c_str()).c_str(),\n        std::ios_base::binary | std::ios_base::trunc);\n\n      m_shader->dump(dumpStream);\n    }\n  }\n\n\n  D3D9FFShaderModuleSet::D3D9FFShaderModuleSet(D3D9DeviceEx* pDevice)\n    : m_vsUbershader(pDevice, D3D9ShaderType::VertexShader)\n    , m_fsUbershader(pDevice, D3D9ShaderType::PixelShader) {}\n\n\n  D3D9FFShader D3D9FFShaderModuleSet::GetShaderModule(\n          D3D9DeviceEx*         pDevice,\n    const D3D9FFShaderKeyVS&    ShaderKey) {\n    // Use the shader's unique key for the lookup\n    auto entry = m_vsModules.find(ShaderKey);\n    if (entry != m_vsModules.end())\n      return entry->second;\n\n    D3D9FFShader shader(\n      pDevice, ShaderKey);\n\n    m_vsModules.insert({ShaderKey, shader});\n\n    return shader;\n  }\n\n\n  D3D9FFShader D3D9FFShaderModuleSet::GetShaderModule(\n          D3D9DeviceEx*         pDevice,\n    const D3D9FFShaderKeyFS&    ShaderKey) {\n    // Use the shader's unique key for the lookup\n    auto entry = m_fsModules.find(ShaderKey);\n    if (entry != m_fsModules.end())\n      return entry->second;\n\n    D3D9FFShader shader(\n      pDevice, ShaderKey);\n\n    m_fsModules.insert({ShaderKey, shader});\n\n    return shader;\n  }\n\n\n  size_t D3D9FFShaderKeyHash::operator () (const D3D9FFShaderKeyVS& key) const {\n    DxvkHashState state;\n\n    std::hash<uint32_t> uint32hash;\n\n    for (uint32_t i = 0; i < std::size(key.Data.Primitive); i++)\n      state.add(uint32hash(key.Data.Primitive[i]));\n\n    return state;\n  }\n\n\n  size_t D3D9FFShaderKeyHash::operator () (const D3D9FFShaderKeyFS& key) const {\n    DxvkHashState state;\n\n    std::hash<uint32_t> uint32hash;\n\n    for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n      for (uint32_t j = 0; j < std::size(key.Stages[i].Primitive); j++)\n        state.add(uint32hash(key.Stages[i].Primitive[j]));\n    }\n\n    return state;\n  }\n\n\n  bool operator == (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b) {\n    return std::memcmp(&a, &b, sizeof(D3D9FFShaderKeyVS)) == 0;\n  }\n\n\n  bool operator == (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b) {\n    return std::memcmp(&a, &b, sizeof(D3D9FFShaderKeyFS)) == 0;\n  }\n\n\n  bool operator != (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b) {\n    return !(a == b);\n  }\n\n\n  bool operator != (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b) {\n    return !(a == b);\n  }\n\n\n  bool D3D9FFShaderKeyEq::operator () (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b) const {\n    return a == b;\n  }\n\n\n  bool D3D9FFShaderKeyEq::operator () (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b) const {\n    return a == b;\n  }\n\n\n  static inline DxsoIsgn CreateFixedFunctionIsgn() {\n    DxsoIsgn ffIsgn;\n\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Position, 0 };\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Normal, 0 };\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Position, 1 };\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Normal, 1 };\n    for (uint32_t i = 0; i < 8; i++)\n      ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Texcoord, i };\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Color, 0 };\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Color, 1 };\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::Fog, 0 };\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::PointSize, 0 };\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::BlendWeight, 0 };\n    ffIsgn.elems[ffIsgn.elemCount++].semantic = DxsoSemantic{ DxsoUsage::BlendIndices, 0 };\n\n    return ffIsgn;\n  }\n\n\n  DxsoIsgn g_ffIsgn = CreateFixedFunctionIsgn();\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_fixed_function.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n\n#include \"d3d9_caps.h\"\n\n#include \"d3d9_state.h\"\n\n#include \"../dxvk/dxvk_shader.h\"\n\n#include \"../dxso/dxso_isgn.h\"\n\n#include <utility>\n#include <unordered_map>\n\nnamespace dxvk {\n\n  class D3D9DeviceEx;\n  class SpirvModule;\n\n  struct D3D9Options;\n  class D3D9ShaderSpecConstantManager;\n\n  struct D3D9FogContext {\n    // General inputs...\n    bool     IsPixel;\n    bool     RangeFog;\n    uint32_t RenderState;\n    uint32_t vPos;\n    uint32_t vFog;\n\n    uint32_t oColor;\n\n    bool     HasFogInput;\n\n    bool     IsFixedFunction;\n    bool     IsPositionT;\n    bool     HasSpecular;\n    uint32_t Specular;\n    uint32_t SpecUBO;\n  };\n\n  struct D3D9AlphaTestContext {\n    uint32_t alphaId;\n    uint32_t alphaPrecisionId;\n    uint32_t alphaFuncId;\n    uint32_t alphaRefId;\n  };\n\n  struct D3D9FixedFunctionOptions {\n    D3D9FixedFunctionOptions(const D3D9Options* options);\n\n    bool    forceSampleRateShading;\n  };\n\n  constexpr uint32_t GetGlobalSamplerSetIndex() {\n    // arbitrary, but must not conflict with bindings\n    return 15u;\n  }\n\n  constexpr uint32_t GetPushSamplerOffset(uint32_t samplerIndex) {\n    // Must not conflict with render state block\n    return MaxSharedPushDataSize + sizeof(uint16_t) * samplerIndex;\n  }\n\n  // Returns new oFog if VS\n  // Returns new oColor if PS\n  uint32_t DoFixedFunctionFog(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, const D3D9FogContext& fogCtx);\n\n  void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx);\n\n  // Returns a render state block, as well as the index of the\n  // first sampler member.\n  std::pair<uint32_t, uint32_t> SetupRenderStateBlock(SpirvModule& spvModule, uint32_t samplerMask);\n\n  // Returns a global sampler descriptor array\n  uint32_t SetupSamplerArray(SpirvModule& spvModule);\n\n  // Common code to load a sampler from the sampler array\n  uint32_t LoadSampler(SpirvModule& spvModule, uint32_t descriptorId,\n    uint32_t pushBlockId, uint32_t pushMember, uint32_t samplerIndex);\n\n  struct D3D9PointSizeInfoVS {\n    uint32_t defaultValue;\n    uint32_t min;\n    uint32_t max;\n  };\n\n  // Default point size and point scale magic!\n  D3D9PointSizeInfoVS GetPointSizeInfoVS(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, uint32_t vPos, uint32_t vtx, uint32_t perVertPointSize, uint32_t rsBlock, uint32_t specUbo, bool isFixedFunction);\n\n  struct D3D9PointSizeInfoPS {\n    uint32_t isSprite;\n  };\n\n  D3D9PointSizeInfoPS GetPointSizeInfoPS(D3D9ShaderSpecConstantManager& spec, SpirvModule& spvModule, uint32_t rsBlock, uint32_t specUbo);\n\n  uint32_t GetPointCoord(SpirvModule& spvModule);\n\n  uint32_t GetSharedConstants(SpirvModule& spvModule);\n\n  uint32_t SetupSpecUBO(SpirvModule& spvModule, std::vector<DxvkBindingInfo>& bindings);\n\n  constexpr uint32_t TCIOffset = 16;\n  constexpr uint32_t TCIMask   = 0b111 << TCIOffset;\n\n  enum D3D9FF_VertexBlendMode {\n    D3D9FF_VertexBlendMode_Disabled,\n    D3D9FF_VertexBlendMode_Normal,\n    D3D9FF_VertexBlendMode_Tween,\n  };\n\n  constexpr uint32_t TextureArgCount = 3;\n\n  struct D3D9FFShaderKeyHash {\n    size_t operator () (const D3D9FFShaderKeyVS& key) const;\n    size_t operator () (const D3D9FFShaderKeyFS& key) const;\n  };\n\n  bool operator == (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b);\n  bool operator != (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b);\n  bool operator == (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b);\n  bool operator != (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b);\n\n  struct D3D9FFShaderKeyEq {\n    bool operator () (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b) const;\n    bool operator () (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b) const;\n  };\n\n  class D3D9FFShader {\n\n  public:\n\n    D3D9FFShader(\n            D3D9DeviceEx*         pDevice,\n      const D3D9FFShaderKeyVS&    Key);\n\n    D3D9FFShader(\n            D3D9DeviceEx*         pDevice,\n      const D3D9FFShaderKeyFS&    Key);\n\n    D3D9FFShader(\n            D3D9DeviceEx*         pDevice,\n            D3D9ShaderType        ShaderType);\n\n    template <typename T>\n    void Dump(D3D9DeviceEx* pDevice, const T& Key, const std::string& Name);\n\n    Rc<DxvkShader> GetShader() const {\n      return m_shader;\n    }\n\n  private:\n\n    Rc<DxvkShader> m_shader;\n\n  };\n\n\n  class D3D9FFShaderModuleSet : public RcObject {\n\n  public:\n\n    D3D9FFShaderModuleSet() = delete;\n\n    explicit D3D9FFShaderModuleSet(D3D9DeviceEx* pDevice);\n\n    D3D9FFShader GetShaderModule(\n            D3D9DeviceEx*         pDevice,\n      const D3D9FFShaderKeyVS&    ShaderKey);\n\n    D3D9FFShader GetShaderModule(\n            D3D9DeviceEx*         pDevice,\n      const D3D9FFShaderKeyFS&    ShaderKey);\n\n    const D3D9FFShader& GetVSUbershaderModule() const {\n      return m_vsUbershader;\n    }\n\n    const D3D9FFShader& GetFSUbershaderModule() const {\n      return m_fsUbershader;\n    }\n\n    UINT GetVSCount() const {\n      return m_vsModules.size();\n    }\n\n    UINT GetFSCount() const {\n      return m_fsModules.size();\n    }\n\n  private:\n\n    std::unordered_map<\n      D3D9FFShaderKeyVS,\n      D3D9FFShader,\n      D3D9FFShaderKeyHash, D3D9FFShaderKeyEq> m_vsModules;\n\n    std::unordered_map<\n      D3D9FFShaderKeyFS,\n      D3D9FFShader,\n      D3D9FFShaderKeyHash, D3D9FFShaderKeyEq> m_fsModules;\n\n    D3D9FFShader m_vsUbershader;\n    D3D9FFShader m_fsUbershader;\n\n  };\n\n\n  inline const DxsoIsgn& GetFixedFunctionIsgn() {\n    extern DxsoIsgn g_ffIsgn;\n\n    return g_ffIsgn;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_format.cpp",
    "content": "#include \"d3d9_format.h\"\n#include \"d3d9_adapter.h\"\n#include \"d3d9_names.h\"\n\nnamespace dxvk {\n\n  // It is also worth noting that the msb/lsb-ness is flipped between VK and D3D9.\n  D3D9_VK_FORMAT_MAPPING ConvertFormatUnfixed(D3D9Format Format) {\n    switch (Format) {\n      case D3D9Format::Unknown: return {};\n\n      case D3D9Format::R8G8B8: return {}; // Unsupported\n\n      case D3D9Format::A8R8G8B8: return {\n        VK_FORMAT_B8G8R8A8_UNORM,\n        VK_FORMAT_B8G8R8A8_SRGB,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::X8R8G8B8: return {\n        VK_FORMAT_B8G8R8A8_UNORM,\n        VK_FORMAT_B8G8R8A8_SRGB,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::R5G6B5: return {\n        VK_FORMAT_R5G6B5_UNORM_PACK16,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT};\n\n      case D3D9Format::X1R5G5B5: return {\n        VK_FORMAT_A1R5G5B5_UNORM_PACK16,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::A1R5G5B5: return {\n        VK_FORMAT_A1R5G5B5_UNORM_PACK16,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::A4R4G4B4: return {\n        VK_FORMAT_A4R4G4B4_UNORM_PACK16,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::R3G3B2: return {}; // Unsupported\n\n      case D3D9Format::A8: return {\n        VK_FORMAT_R8_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO,\n          VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R }};\n\n      case D3D9Format::A8R3G3B2: return {}; // Unsupported\n\n      case D3D9Format::X4R4G4B4: return {\n        VK_FORMAT_A4R4G4B4_UNORM_PACK16,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::A2B10G10R10: return {\n        VK_FORMAT_A2B10G10R10_UNORM_PACK32, // The A2 is out of place here. This should be investigated.\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::A8B8G8R8: return {\n        VK_FORMAT_R8G8B8A8_UNORM,\n        VK_FORMAT_R8G8B8A8_SRGB,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::X8B8G8R8: return {\n        VK_FORMAT_R8G8B8A8_UNORM,\n        VK_FORMAT_R8G8B8A8_SRGB,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::G16R16: return {\n        VK_FORMAT_R16G16_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R,   VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::A2R10G10B10: return {\n        VK_FORMAT_A2R10G10B10_UNORM_PACK32,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::A16B16G16R16: return {\n        VK_FORMAT_R16G16B16A16_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::A8P8: return {}; // Unsupported\n\n      case D3D9Format::P8: return {}; // Unsupported\n\n      case D3D9Format::L8: return {\n        VK_FORMAT_R8_UNORM,\n        VK_FORMAT_R8_SRGB,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,\n          VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::A8L8: return {\n        VK_FORMAT_R8G8_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,\n          VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G }};\n\n      case D3D9Format::A4L4: return {\n        VK_FORMAT_R4G4_UNORM_PACK8,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R }};\n\n      case D3D9Format::V8U8: return {\n        VK_FORMAT_R8G8_SNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R,   VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::L6V5U5: return {\n        // Any PACK16 format will do...\n        VK_FORMAT_B5G6R5_UNORM_PACK16,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A },\n        { D3D9ConversionFormat_L6V5U5,\n        // Convert -> float (this is a mixed snorm and unorm type)\n          VK_FORMAT_R16G16B16A16_SFLOAT } };\n\n      case D3D9Format::X8L8V8U8: return {\n        VK_FORMAT_B8G8R8A8_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE },\n        { D3D9ConversionFormat_X8L8V8U8,\n        // Convert -> float (this is a mixed snorm and unorm type)\n          VK_FORMAT_R16G16B16A16_SFLOAT } };\n\n      case D3D9Format::Q8W8V8U8: return {\n        VK_FORMAT_R8G8B8A8_SNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::V16U16: return {\n        VK_FORMAT_R16G16_SNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R,   VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::A2W10V10U10: return {\n        VK_FORMAT_A2B10G10R10_UNORM_PACK32,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A },\n        { D3D9ConversionFormat_A2W10V10U10,\n        // Convert -> float (this is a mixed snorm and unorm type)\n          VK_FORMAT_R16G16B16A16_SFLOAT } };\n\n      case D3D9Format::W11V11U10: return {\n        VK_FORMAT_B10G11R11_UFLOAT_PACK32,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE },\n        { D3D9ConversionFormat_W11V11U10,\n        // can't use B10G11R11 because this is a snorm type\n          VK_FORMAT_R16G16B16A16_SNORM } };\n\n      case D3D9Format::UYVY: return {\n        VK_FORMAT_G8B8G8R8_422_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,\n          VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY },\n        { D3D9ConversionFormat_UYVY, VK_FORMAT_B8G8R8A8_UNORM }\n      };\n\n      case D3D9Format::R8G8_B8G8: return {\n        VK_FORMAT_G8B8G8R8_422_UNORM, // This format may have been _SCALED in DX9.\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::YUY2: return {\n        VK_FORMAT_G8B8G8R8_422_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,\n          VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY },\n        { D3D9ConversionFormat_YUY2, VK_FORMAT_B8G8R8A8_UNORM }\n      };\n\n      case D3D9Format::G8R8_G8B8: return {\n        VK_FORMAT_B8G8R8G8_422_UNORM, // This format may have been _SCALED in DX9.\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::DXT1: return {\n        VK_FORMAT_BC1_RGBA_UNORM_BLOCK,\n        VK_FORMAT_BC1_RGBA_SRGB_BLOCK,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::DXT2: return {\n        VK_FORMAT_BC2_UNORM_BLOCK,\n        VK_FORMAT_BC2_SRGB_BLOCK,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::DXT3: return {\n        VK_FORMAT_BC2_UNORM_BLOCK,\n        VK_FORMAT_BC2_SRGB_BLOCK,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::DXT4: return {\n        VK_FORMAT_BC3_UNORM_BLOCK,\n        VK_FORMAT_BC3_SRGB_BLOCK,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::DXT5: return {\n        VK_FORMAT_BC3_UNORM_BLOCK,\n        VK_FORMAT_BC3_SRGB_BLOCK,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::D16_LOCKABLE: return {\n        VK_FORMAT_D16_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_DEPTH_BIT };\n\n      case D3D9Format::D32: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::D15S1: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::D24S8: return {\n        VK_FORMAT_D24_UNORM_S8_UINT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT };\n\n      case D3D9Format::D24X8: return {\n        VK_FORMAT_D24_UNORM_S8_UINT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_DEPTH_BIT };\n\n      case D3D9Format::D24X4S4: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::D16: return {\n        VK_FORMAT_D16_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_DEPTH_BIT };\n\n      case D3D9Format::D32F_LOCKABLE: return {\n        VK_FORMAT_D32_SFLOAT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_DEPTH_BIT };\n\n      case D3D9Format::D24FS8: return {\n        VK_FORMAT_D24_UNORM_S8_UINT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT };\n\n      case D3D9Format::D32_LOCKABLE: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::S8_LOCKABLE: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::L16: return {\n        VK_FORMAT_R16_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,\n          VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::VERTEXDATA: return {\n        VK_FORMAT_R8_UINT,\n        VK_FORMAT_UNDEFINED,\n        0 };\n\n      case D3D9Format::INDEX16: return {\n        VK_FORMAT_R16_UINT,\n        VK_FORMAT_UNDEFINED,\n        0 };\n\n      case D3D9Format::INDEX32: return {\n        VK_FORMAT_R32_UINT,\n        VK_FORMAT_UNDEFINED,\n        0 };\n\n      case D3D9Format::Q16W16V16U16: return {\n        VK_FORMAT_R16G16B16A16_SNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::MULTI2_ARGB8: return {}; // Unsupported\n\n      case D3D9Format::R16F: return {\n        VK_FORMAT_R16_SFLOAT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R,   VK_COMPONENT_SWIZZLE_ONE,\n          VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::G16R16F: return {\n        VK_FORMAT_R16G16_SFLOAT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R,   VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::A16B16G16R16F: return {\n        VK_FORMAT_R16G16B16A16_SFLOAT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::R32F: return {\n        VK_FORMAT_R32_SFLOAT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R,   VK_COMPONENT_SWIZZLE_ONE,\n          VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::G32R32F: return {\n        VK_FORMAT_R32G32_SFLOAT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R,   VK_COMPONENT_SWIZZLE_G,\n          VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::A32B32G32R32F: return {\n        VK_FORMAT_R32G32B32A32_SFLOAT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT };\n\n      case D3D9Format::CxV8U8: return {}; // Unsupported\n\n      case D3D9Format::A1: return {}; // Unsupported\n\n      case D3D9Format::A2B10G10R10_XR_BIAS: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::BINARYBUFFER: return {\n        VK_FORMAT_R8_UINT,\n        VK_FORMAT_UNDEFINED,\n        0 };\n\n      case D3D9Format::ATI1: return {\n        VK_FORMAT_BC4_UNORM_BLOCK,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_R,    VK_COMPONENT_SWIZZLE_ZERO,\n          VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::ATI2: return {\n        VK_FORMAT_BC5_UNORM_BLOCK,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_G,   VK_COMPONENT_SWIZZLE_R,\n          VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::INST: return {}; // Driver hack, handled elsewhere\n\n      case D3D9Format::DF24: return {\n        VK_FORMAT_D24_UNORM_S8_UINT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_DEPTH_BIT,\n        { VK_COMPONENT_SWIZZLE_R,    VK_COMPONENT_SWIZZLE_ZERO,\n          VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::DF16: return {\n        VK_FORMAT_D16_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_DEPTH_BIT,\n        { VK_COMPONENT_SWIZZLE_R,    VK_COMPONENT_SWIZZLE_ZERO,\n          VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }};\n\n      case D3D9Format::NULL_FORMAT: return {}; // Driver hack, handled elsewhere\n\n      case D3D9Format::GET4: return {}; // Unsupported\n\n      case D3D9Format::GET1: return {}; // Unsupported\n\n      case D3D9Format::NVDB: return {}; // Driver hack, handled elsewhere\n\n      case D3D9Format::A2M1: return {}; // Driver hack, handled elsewhere\n\n      case D3D9Format::A2M0: return {}; // Driver hack, handled elsewhere\n\n      case D3D9Format::ATOC: return {}; // Driver hack, handled elsewhere\n\n      case D3D9Format::SSAA: return {}; // Driver hack, handled elsewhere\n\n      case D3D9Format::INTZ: return {\n        VK_FORMAT_D24_UNORM_S8_UINT,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,\n        { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,\n          VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R }};\n\n      case D3D9Format::NV12: return {\n        VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,\n          VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY },\n        { D3D9ConversionFormat_NV12, VK_FORMAT_B8G8R8A8_UNORM }\n      };\n\n      case D3D9Format::YV12: return {\n        VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,\n        VK_FORMAT_UNDEFINED,\n        VK_IMAGE_ASPECT_COLOR_BIT,\n        { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,\n          VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY },\n        { D3D9ConversionFormat_YV12, VK_FORMAT_B8G8R8A8_UNORM }\n      };\n\n      case D3D9Format::RAWZ: return {}; // Unsupported\n\n      // EXT1, FXT1, GXT1 and HXT1 are checked for support\n      // by D3D9 SAGE engine games (e.g. Command & Conquer 3)\n\n      case D3D9Format::EXT1: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::FXT1: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::GXT1: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::HXT1: return {}; // Unsupported (everywhere)\n\n      // AL16 and R16 FOURCCs are often checked for support by\n      // various D3D8 and early D3D9 games. AR16 and L16 (FOURCC)\n      // are also checked for support, but to a lesser extent.\n\n      case D3D9Format::AL16: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::AR16: return {}; // Unsupported (everywhere)\n\n      case D3D9Format::R16:  return {}; // Unsupported (everywhere)\n\n      case D3D9Format::L16_FOURCC:  return {}; // Unsupported (everywhere)\n\n      default:\n        Logger::warn(str::format(\"ConvertFormat: Unknown format encountered: \", Format));\n        return {}; // Unsupported\n    }\n  }\n\n  // Block size of formats that require some form of alignment\n  D3D9_FORMAT_BLOCK_SIZE GetFormatAlignedBlockSize(D3D9Format Format) {\n    switch (Format) {\n      case D3D9Format::DXT1:\n      case D3D9Format::DXT2:\n      case D3D9Format::DXT3:\n      case D3D9Format::DXT4:\n      case D3D9Format::DXT5:\n      case D3D9Format::ATI1:\n      case D3D9Format::ATI2:\n        return { 4, 4, 1 };\n\n      case D3D9Format::YUY2:\n      case D3D9Format::UYVY:\n        return { 2, 1, 1 };\n\n      default:\n        return {}; // Irrelevant or unknown block size\n    }\n  }\n\n  D3D9VkFormatTable::D3D9VkFormatTable(\n            D3D9Adapter*     pParent,\n      const Rc<DxvkAdapter>& adapter,\n      const D3D9Options&     options)\n    : m_isExtended (pParent->IsExtended()) {\n\n    const uint32_t vendorId = pParent->GetVendorId();\n    const bool     isNvidia = vendorId == uint32_t(DxvkGpuVendor::Nvidia);\n    const bool     isAmd    = vendorId == uint32_t(DxvkGpuVendor::Amd);\n    const bool     isIntel  = vendorId == uint32_t(DxvkGpuVendor::Intel);\n\n    // NVIDIA does not natively support any DF formats\n    m_dfSupport = !isNvidia ? options.supportDFFormats : false;\n    m_x4r4g4b4Support = options.supportX4R4G4B4;\n    // W11V11U10 is only supported by D3D8\n    m_w11v11u10Support = false;\n    // Only AMD supports D16_LOCKABLE natively\n    m_d16lockableSupport = isAmd;\n\n    // AMD do not support 24-bit depth buffers on Vulkan,\n    // so we have to fall back to a 32-bit depth format.\n    m_d24s8Support = !options.useD32forD24 &&\n                     CheckImageFormatSupport(adapter, VK_FORMAT_D24_UNORM_S8_UINT,\n      VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT |\n      VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT);\n\n    // NVIDIA do not support 16-bit depth buffers with stencil on Vulkan,\n    // so we have to fall back to a 32-bit depth format.\n    m_d16s8Support = CheckImageFormatSupport(adapter, VK_FORMAT_D16_UNORM_S8_UINT,\n      VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT |\n      VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT);\n\n    // AMD and Nvidia support D32F_LOCKABLE natively (Intel does not)\n    m_d32flockableSupport = !isIntel;\n    // Only Intel supports D24FS8 natively\n    m_d24fs8Support = isIntel;\n\n    if (!m_d24s8Support)\n      Logger::info(\"D3D9: VK_FORMAT_D24_UNORM_S8_UINT -> VK_FORMAT_D32_SFLOAT_S8_UINT\");\n\n    if (!m_d16s8Support) {\n      if (m_d24s8Support)\n        Logger::info(\"D3D9: VK_FORMAT_D16_UNORM_S8_UINT -> VK_FORMAT_D24_UNORM_S8_UINT\");\n      else\n        Logger::info(\"D3D9: VK_FORMAT_D16_UNORM_S8_UINT -> VK_FORMAT_D32_SFLOAT_S8_UINT\");\n    }\n  }\n\n  D3D9_VK_FORMAT_MAPPING D3D9VkFormatTable::GetFormatMapping(\n          D3D9Format          Format) const {\n    D3D9_VK_FORMAT_MAPPING mapping = ConvertFormatUnfixed(Format);\n\n    if (Format == D3D9Format::X4R4G4B4 && !m_x4r4g4b4Support)\n      return D3D9_VK_FORMAT_MAPPING();\n\n    if (Format == D3D9Format::W11V11U10 && !m_w11v11u10Support)\n      return D3D9_VK_FORMAT_MAPPING();\n\n    if (Format == D3D9Format::D16_LOCKABLE && !m_d16lockableSupport)\n      return D3D9_VK_FORMAT_MAPPING();\n\n    if (Format == D3D9Format::DF16 && !m_dfSupport)\n      return D3D9_VK_FORMAT_MAPPING();\n\n    if (Format == D3D9Format::DF24 && !m_dfSupport)\n      return D3D9_VK_FORMAT_MAPPING();\n\n    if (Format == D3D9Format::D32F_LOCKABLE && !m_d32flockableSupport)\n      return D3D9_VK_FORMAT_MAPPING();\n\n    if (Format == D3D9Format::D24FS8 && !m_d24fs8Support)\n      return D3D9_VK_FORMAT_MAPPING();\n\n    if (!m_d24s8Support && mapping.FormatColor == VK_FORMAT_D24_UNORM_S8_UINT)\n      mapping.FormatColor = (mapping.Aspect & VK_IMAGE_ASPECT_STENCIL_BIT) ? VK_FORMAT_D32_SFLOAT_S8_UINT : VK_FORMAT_D32_SFLOAT;\n\n    if (!m_d16s8Support && mapping.FormatColor == VK_FORMAT_D16_UNORM_S8_UINT)\n      mapping.FormatColor = m_d24s8Support ? VK_FORMAT_D24_UNORM_S8_UINT : VK_FORMAT_D32_SFLOAT_S8_UINT;\n\n    return mapping;\n  }\n\n\n  const DxvkFormatInfo* D3D9VkFormatTable::GetUnsupportedFormatInfo(\n    D3D9Format            Format) const {\n    static const DxvkFormatInfo r8b8g8        = { 3, VK_IMAGE_ASPECT_COLOR_BIT };\n    static const DxvkFormatInfo r3g3b2        = { 1, VK_IMAGE_ASPECT_COLOR_BIT };\n    static const DxvkFormatInfo x4r4g4b4      = { 2, VK_IMAGE_ASPECT_COLOR_BIT };\n    static const DxvkFormatInfo a8r3g3b2      = { 2, VK_IMAGE_ASPECT_COLOR_BIT };\n    static const DxvkFormatInfo a8p8          = { 2, VK_IMAGE_ASPECT_COLOR_BIT };\n    static const DxvkFormatInfo p8            = { 1, VK_IMAGE_ASPECT_COLOR_BIT };\n    static const DxvkFormatInfo w11v11u10     = { 4, VK_IMAGE_ASPECT_COLOR_BIT };\n    static const DxvkFormatInfo cxv8u8        = { 2, VK_IMAGE_ASPECT_COLOR_BIT };\n    // Unsupported and potentially unsupported lockable depth formats need to be\n    // listed here in order to allow for the creation of offscreen plain surfaces\n    static const DxvkFormatInfo d16_lockable  = { 2, VK_IMAGE_ASPECT_DEPTH_BIT };\n    static const DxvkFormatInfo d32f_lockable = { 4, VK_IMAGE_ASPECT_DEPTH_BIT };\n    static const DxvkFormatInfo d32_lockable  = { 4, VK_IMAGE_ASPECT_DEPTH_BIT };\n    static const DxvkFormatInfo s8_lockable   = { 1, VK_IMAGE_ASPECT_STENCIL_BIT };\n    static const DxvkFormatInfo unknown       = {};\n\n    switch (Format) {\n      case D3D9Format::R8G8B8:\n        return &r8b8g8;\n\n      case D3D9Format::R3G3B2:\n        return &r3g3b2;\n\n      // potentially unsupported through a config option\n      case D3D9Format::X4R4G4B4:\n        return &x4r4g4b4;\n\n      case D3D9Format::A8R3G3B2:\n        return &a8r3g3b2;\n\n      case D3D9Format::A8P8:\n        return &a8p8;\n\n      case D3D9Format::P8:\n        return &p8;\n\n      // only supported by D3D8\n      case D3D9Format::W11V11U10:\n        return &w11v11u10;\n\n      // MULTI2_ARGB8 -> Don't have a clue what this is.\n\n      case D3D9Format::CxV8U8:\n        return &cxv8u8;\n\n      // A1 -> Doesn't map nicely here cause it's not byte aligned.\n      // Gonna just pretend that doesn't exist until something\n      // depends on that.\n\n      // only supported on AMD\n      case D3D9Format::D16_LOCKABLE:\n        return &d16_lockable;\n\n      // unsupported on Intel\n      case D3D9Format::D32F_LOCKABLE:\n        return &d32f_lockable;\n\n      // only considered on d3d9Ex interfaces\n      case D3D9Format::D32_LOCKABLE:\n        if (m_isExtended)\n          return &d32_lockable;\n\n        [[fallthrough]];\n\n      // only considered on d3d9Ex interfaces\n      case D3D9Format::S8_LOCKABLE:\n        if (m_isExtended)\n          return &s8_lockable;\n\n        [[fallthrough]];\n\n      default:\n        return &unknown;\n    }\n  }\n\n\n  bool D3D9VkFormatTable::CheckImageFormatSupport(\n    const Rc<DxvkAdapter>&      Adapter,\n          VkFormat              Format,\n          VkFormatFeatureFlags2 Features) const {\n    DxvkFormatFeatures supported = Adapter->getFormatFeatures(Format);\n\n    return (supported.linear  & Features) == Features\n        || (supported.optimal & Features) == Features;\n  }\n\n\n  void D3D9VkFormatTable::RefreshFormatSupport(\n    const D3D9Adapter*          pParent) {\n    // W11V11U10 is only supported by D3D8\n    m_w11v11u10Support = pParent->IsD3D8Compatible();\n  }\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_format.h",
    "content": "#pragma once\n\n#include <d3d9.h>\n#include \"d3d9_options.h\"\n\n#include \"../dxvk/dxvk_adapter.h\"\n#include \"../dxvk/dxvk_format.h\"\n\n#include <unordered_map>\n\nnamespace dxvk {\n\n  enum class D3D9Format : uint32_t {\n    Unknown = 0,\n\n    R8G8B8 = 20,\n    A8R8G8B8 = 21,\n    X8R8G8B8 = 22,\n    R5G6B5 = 23,\n    X1R5G5B5 = 24,\n    A1R5G5B5 = 25,\n    A4R4G4B4 = 26,\n    R3G3B2 = 27,\n    A8 = 28,\n    A8R3G3B2 = 29,\n    X4R4G4B4 = 30,\n    A2B10G10R10 = 31,\n    A8B8G8R8 = 32,\n    X8B8G8R8 = 33,\n    G16R16 = 34,\n    A2R10G10B10 = 35,\n    A16B16G16R16 = 36,\n    A8P8 = 40,\n    P8 = 41,\n    L8 = 50,\n    A8L8 = 51,\n    A4L4 = 52,\n    V8U8 = 60,\n    L6V5U5 = 61,\n    X8L8V8U8 = 62,\n    Q8W8V8U8 = 63,\n    V16U16 = 64,\n    W11V11U10 = 65,\n    A2W10V10U10 = 67,\n    UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'),\n    R8G8_B8G8 = MAKEFOURCC('R', 'G', 'B', 'G'),\n    YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'),\n    G8R8_G8B8 = MAKEFOURCC('G', 'R', 'G', 'B'),\n    DXT1 = MAKEFOURCC('D', 'X', 'T', '1'),\n    DXT2 = MAKEFOURCC('D', 'X', 'T', '2'),\n    DXT3 = MAKEFOURCC('D', 'X', 'T', '3'),\n    DXT4 = MAKEFOURCC('D', 'X', 'T', '4'),\n    DXT5 = MAKEFOURCC('D', 'X', 'T', '5'),\n    D16_LOCKABLE = 70,\n    D32 = 71,\n    D15S1 = 73,\n    D24S8 = 75,\n    D24X8 = 77,\n    D24X4S4 = 79,\n    D16 = 80,\n    D32F_LOCKABLE = 82,\n    D24FS8 = 83,\n    D32_LOCKABLE = 84,\n    S8_LOCKABLE = 85,\n    L16 = 81,\n    VERTEXDATA = 100,\n    INDEX16 = 101,\n    INDEX32 = 102,\n    Q16W16V16U16 = 110,\n    MULTI2_ARGB8 = MAKEFOURCC('M', 'E', 'T', '1'),\n    R16F = 111,\n    G16R16F = 112,\n    A16B16G16R16F = 113,\n    R32F = 114,\n    G32R32F = 115,\n    A32B32G32R32F = 116,\n    CxV8U8 = 117,\n    A1 = 118,\n    A2B10G10R10_XR_BIAS = 119,\n    BINARYBUFFER = 199,\n\n    // Driver Hacks / Unofficial Formats\n    ATI1 = MAKEFOURCC('A', 'T', 'I', '1'),\n    ATI2 = MAKEFOURCC('A', 'T', 'I', '2'),\n    INST = MAKEFOURCC('I', 'N', 'S', 'T'),\n    DF24 = MAKEFOURCC('D', 'F', '2', '4'),\n    DF16 = MAKEFOURCC('D', 'F', '1', '6'),\n    NULL_FORMAT = MAKEFOURCC('N', 'U', 'L', 'L'),\n    GET4 = MAKEFOURCC('G', 'E', 'T', '4'),\n    GET1 = MAKEFOURCC('G', 'E', 'T', '1'),\n    NVDB = MAKEFOURCC('N', 'V', 'D', 'B'),\n    A2M1 = MAKEFOURCC('A', '2', 'M', '1'),\n    A2M0 = MAKEFOURCC('A', '2', 'M', '0'),\n    ATOC = MAKEFOURCC('A', 'T', 'O', 'C'),\n    INTZ = MAKEFOURCC('I', 'N', 'T', 'Z'),\n    RAWZ = MAKEFOURCC('R', 'A', 'W', 'Z'),\n    RESZ = MAKEFOURCC('R', 'E', 'S', 'Z'),\n\n    NV11 = MAKEFOURCC('N', 'V', '1', '1'),\n    NV12 = MAKEFOURCC('N', 'V', '1', '2'),\n    P010 = MAKEFOURCC('P', '0', '1', '0'), // Same as NV12 but 10 bit\n    P016 = MAKEFOURCC('P', '0', '1', '6'), // Same as NV12 but 16 bit\n    Y210 = MAKEFOURCC('Y', '2', '1', '0'),\n    Y216 = MAKEFOURCC('Y', '2', '1', '6'),\n    Y410 = MAKEFOURCC('Y', '4', '1', '0'),\n    AYUV = MAKEFOURCC('A', 'Y', 'U', 'V'),\n    YV12 = MAKEFOURCC('Y', 'V', '1', '2'),\n    OPAQUE_420 = MAKEFOURCC('4', '2', '0', 'O'),\n\n    // Not supported but exist\n    AI44 = MAKEFOURCC('A', 'I', '4', '4'),\n    IA44 = MAKEFOURCC('I', 'A', '4', '4'),\n    CENT = MAKEFOURCC('C', 'E', 'N', 'T'),\n    R2VB = MAKEFOURCC('R', '2', 'V', 'B'),\n    COPM = MAKEFOURCC('C', 'O', 'P', 'M'),\n    SSAA = MAKEFOURCC('S', 'S', 'A', 'A'),\n    NVCS = MAKEFOURCC('N', 'V', 'C', 'S'),\n    NVHS = MAKEFOURCC('N', 'V', 'H', 'S'),\n    NVHU = MAKEFOURCC('N', 'V', 'H', 'U'),\n\n    EXT1 = MAKEFOURCC('E', 'X', 'T', '1'),\n    FXT1 = MAKEFOURCC('F', 'X', 'T', '1'),\n    GXT1 = MAKEFOURCC('G', 'X', 'T', '1'),\n    HXT1 = MAKEFOURCC('H', 'X', 'T', '1'),\n    AL16 = MAKEFOURCC('A', 'L', '1', '6'),\n    AR16 = MAKEFOURCC('A', 'R', '1', '6'),\n    R16  = MAKEFOURCC(' ', 'R', '1', '6'),\n    L16_FOURCC = MAKEFOURCC(' ', 'L', '1', '6'),\n  };\n\n  inline D3D9Format EnumerateFormat(D3DFORMAT format) {\n    return static_cast<D3D9Format>(format);\n  }\n\n  std::ostream& operator << (std::ostream& os, D3D9Format format);\n\n  enum D3D9ConversionFormat : uint32_t {\n    D3D9ConversionFormat_None = 0,\n    D3D9ConversionFormat_YUY2 = 1,\n    D3D9ConversionFormat_UYVY,\n    D3D9ConversionFormat_L6V5U5,\n    D3D9ConversionFormat_X8L8V8U8,\n    D3D9ConversionFormat_A2W10V10U10,\n    D3D9ConversionFormat_W11V11U10,\n    D3D9ConversionFormat_NV12,\n    D3D9ConversionFormat_YV12,\n    D3D9ConversionFormat_Count\n  };\n\n  struct D3D9_CONVERSION_FORMAT_INFO {\n    D3D9ConversionFormat FormatType     = D3D9ConversionFormat_None;\n    VkFormat             FormatColor    = VK_FORMAT_UNDEFINED;\n    VkFormat             FormatSrgb     = VK_FORMAT_UNDEFINED;\n  };\n\n  struct D3D9_FORMAT_BLOCK_SIZE {\n    uint8_t            Width  = 0;\n    uint8_t            Height = 0;\n    uint8_t            Depth  = 0;\n  };\n\n  /**\n   * \\brief Format mapping\n   *\n   * Maps a D3D9 format to a set of Vulkan formats.\n   */\n  struct D3D9_VK_FORMAT_MAPPING {\n    union {\n      struct {\n        VkFormat          FormatColor;                          ///< Corresponding color format\n        VkFormat          FormatSrgb;                           ///< Corresponding color format\n      };\n      VkFormat            Formats[2];\n    };\n    VkImageAspectFlags    Aspect        = 0;                    ///< Defined aspects for the color format\n    VkComponentMapping    Swizzle       = {                     ///< Color component swizzle\n      VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,\n      VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };\n    D3D9_CONVERSION_FORMAT_INFO ConversionFormatInfo = { };\n\n    bool IsValid() const { return FormatColor != VK_FORMAT_UNDEFINED; }\n  };\n\n  D3D9_VK_FORMAT_MAPPING ConvertFormatUnfixed(D3D9Format Format);\n\n  D3D9_FORMAT_BLOCK_SIZE GetFormatAlignedBlockSize(D3D9Format Format);\n\n  class D3D9Adapter;\n\n  /**\n   * \\brief Format table\n   *\n   * Initializes a format table for a specific\n   * device and provides methods to look up\n   * formats.\n   */\n  class D3D9VkFormatTable {\n\n  public:\n\n    D3D9VkFormatTable(\n            D3D9Adapter*     pParent,\n      const Rc<DxvkAdapter>& adapter,\n      const D3D9Options&     options);\n\n    /**\n     * \\brief Retrieves info for a given D3D9 format\n     *\n     * \\param [in] Format The D3D9 format to look up\n     * \\param [in] Mode the format lookup mode\n     * \\returns Format info\n     */\n    D3D9_VK_FORMAT_MAPPING GetFormatMapping(\n      D3D9Format            Format) const;\n\n    /**\n     * \\brief Retrieves format info for unsupported\n     * formats.\n     *\n     * \\param [in] Format The D3D9 format to look up\n     */\n    const DxvkFormatInfo* GetUnsupportedFormatInfo(\n      D3D9Format            Format) const;\n\n    void RefreshFormatSupport(\n      const D3D9Adapter*          pParent);\n\n  private:\n\n    bool CheckImageFormatSupport(\n      const Rc<DxvkAdapter>&      Adapter,\n            VkFormat              Format,\n            VkFormatFeatureFlags2 Features) const;\n\n    bool m_isExtended;\n\n    bool m_d24s8Support;\n    bool m_d16s8Support;\n\n    bool m_dfSupport;\n    bool m_x4r4g4b4Support;\n    bool m_w11v11u10Support;\n    bool m_d16lockableSupport;\n\n    bool m_d32flockableSupport;\n    bool m_d24fs8Support;\n  };\n\n  inline bool IsFourCCFormat(D3D9Format format) {\n    // BINARYBUFFER is the largest non-fourcc format\n    return format > D3D9Format::BINARYBUFFER;\n  }\n\n  inline bool IsVendorFormat(D3D9Format format) {\n    return IsFourCCFormat(format)\n      && format != D3D9Format::MULTI2_ARGB8\n      && format != D3D9Format::UYVY\n      && format != D3D9Format::R8G8_B8G8\n      && format != D3D9Format::YUY2\n      && format != D3D9Format::G8R8_G8B8\n      && format != D3D9Format::DXT1\n      && format != D3D9Format::DXT2\n      && format != D3D9Format::DXT3\n      && format != D3D9Format::DXT4\n      && format != D3D9Format::DXT5;\n  }\n\n  inline bool IsDXTFormat(D3D9Format format) {\n    return format == D3D9Format::DXT1\n        || format == D3D9Format::DXT2\n        || format == D3D9Format::DXT3\n        || format == D3D9Format::DXT4\n        || format == D3D9Format::DXT5;\n  }\n\n  // D3D9 documentation says: IDirect3DSurface9::GetDC is valid on the following formats only:\n  // D3DFMT_R5G6B5, D3DFMT_X1R5G5B5, D3DFMT_R8G8B8, and D3DFMT_X8R8G8B8. However,\n  // the equivalent formats of D3DFMT_A1R5G5B5 and D3DFMT_A8R8G8B8 are also supported.\n  inline bool IsSurfaceGetDCCompatibleFormat(D3D9Format format) {\n    return format == D3D9Format::R5G6B5\n        || format == D3D9Format::X1R5G5B5\n        || format == D3D9Format::A1R5G5B5\n        || format == D3D9Format::R8G8B8\n        || format == D3D9Format::X8R8G8B8\n        || format == D3D9Format::A8R8G8B8;\n  }\n\n  inline bool IsDepthFormat(D3D9Format Format) {\n    return Format == D3D9Format::D16_LOCKABLE\n        || Format == D3D9Format::D32\n        || Format == D3D9Format::D15S1\n        || Format == D3D9Format::D24S8\n        || Format == D3D9Format::D24X8\n        || Format == D3D9Format::D24X4S4\n        || Format == D3D9Format::D16\n        || Format == D3D9Format::D32F_LOCKABLE\n        || Format == D3D9Format::D24FS8\n        || Format == D3D9Format::D32_LOCKABLE\n        || Format == D3D9Format::DF16\n        || Format == D3D9Format::DF24\n        || Format == D3D9Format::INTZ;\n  }\n\n  inline bool IsDepthStencilFormat(D3D9Format Format) {\n    return IsDepthFormat(Format) || Format == D3D9Format::S8_LOCKABLE;\n  }\n\n  inline bool IsLockableDepthStencilFormat(D3D9Format Format) {\n    return Format == D3D9Format::S8_LOCKABLE\n        || Format == D3D9Format::D16_LOCKABLE\n        || Format == D3D9Format::D32_LOCKABLE\n        || Format == D3D9Format::D32F_LOCKABLE;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_format_helpers.cpp",
    "content": "#include \"d3d9_format_helpers.h\"\n\n#include <d3d9_convert_yuy2_uyvy.h>\n#include <d3d9_convert_l6v5u5.h>\n#include <d3d9_convert_x8l8v8u8.h>\n#include <d3d9_convert_a2w10v10u10.h>\n#include <d3d9_convert_w11v11u10.h>\n#include <d3d9_convert_nv12.h>\n#include <d3d9_convert_yv12.h>\n\nnamespace dxvk {\n\n  D3D9FormatHelper::D3D9FormatHelper(const Rc<DxvkDevice>& device)\n  : m_device          (device)\n  , m_layout          (CreatePipelineLayout()) {\n    InitPipelines();\n  }\n\n\n  D3D9FormatHelper::~D3D9FormatHelper() {\n    auto vk = m_device->vkd();\n\n    for (auto& p : m_pipelines)\n      vk->vkDestroyPipeline(vk->device(), p, nullptr);\n  }\n\n\n  void D3D9FormatHelper::ConvertFormat(\n    const Rc<DxvkCommandList>&          ctx,\n          D3D9_CONVERSION_FORMAT_INFO   conversionFormat,\n    const Rc<DxvkImage>&                dstImage,\n          VkImageSubresourceLayers      dstSubresource,\n    const DxvkBufferSlice&              srcSlice) {\n    switch (conversionFormat.FormatType) {\n      case D3D9ConversionFormat_YUY2:\n      case D3D9ConversionFormat_UYVY: {\n        ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 2u, 1u });\n        break;\n      }\n\n      case D3D9ConversionFormat_NV12:\n        ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R16_UINT, { 2u, 1u });\n        break;\n\n      case D3D9ConversionFormat_YV12:\n        ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R8_UINT, { 1u, 1u });\n        break;\n\n      case D3D9ConversionFormat_L6V5U5:\n        ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R16_UINT, { 1u, 1u });\n        break;\n\n      case D3D9ConversionFormat_X8L8V8U8:\n        ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 1u, 1u });\n        break;\n\n      case D3D9ConversionFormat_A2W10V10U10:\n        ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 1u, 1u });\n        break;\n\n      case D3D9ConversionFormat_W11V11U10:\n        ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 1u, 1u });\n        break;\n\n      default:\n        Logger::warn(\"Unimplemented format conversion\");\n    }\n  }\n\n\n  void D3D9FormatHelper::ConvertGenericFormat(\n    const Rc<DxvkCommandList>&          ctx,\n          D3D9_CONVERSION_FORMAT_INFO   videoFormat,\n    const Rc<DxvkImage>&                dstImage,\n          VkImageSubresourceLayers      dstSubresource,\n    const DxvkBufferSlice&              srcSlice,\n          VkFormat                      bufferFormat,\n          VkExtent2D                    macroPixelRun) {\n    DxvkImageViewKey imageViewInfo;\n    imageViewInfo.viewType  = VK_IMAGE_VIEW_TYPE_2D;\n    imageViewInfo.format    = dstImage->info().format;\n    imageViewInfo.usage     = VK_IMAGE_USAGE_STORAGE_BIT;\n    imageViewInfo.layout    = VK_IMAGE_LAYOUT_GENERAL;\n    imageViewInfo.aspects   = dstSubresource.aspectMask;\n    imageViewInfo.mipIndex  = dstSubresource.mipLevel;\n    imageViewInfo.mipCount  = 1;\n    imageViewInfo.layerIndex = dstSubresource.baseArrayLayer;\n    imageViewInfo.layerCount = dstSubresource.layerCount;\n    auto tmpImageView = dstImage->createView(imageViewInfo);\n\n    VkExtent3D imageExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel);\n    imageExtent = VkExtent3D{ imageExtent.width  / macroPixelRun.width,\n                              imageExtent.height / macroPixelRun.height,\n                              1 };\n\n    DxvkBufferViewKey bufferViewInfo;\n    bufferViewInfo.format = bufferFormat;\n    bufferViewInfo.offset = srcSlice.offset();\n    bufferViewInfo.size = srcSlice.length();\n    bufferViewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n    auto tmpBufferView = srcSlice.buffer()->createView(bufferViewInfo);\n\n    std::array<DxvkDescriptorWrite, 2> descriptors = { };\n\n    auto& imageDescriptor = descriptors[0u];\n    imageDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n    imageDescriptor.descriptor = tmpImageView->getDescriptor();\n\n    auto& bufferDescriptor = descriptors[1u];\n    bufferDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;\n    bufferDescriptor.descriptor = tmpBufferView->getDescriptor(false);\n\n    ctx->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_COMPUTE, m_pipelines[videoFormat.FormatType]);\n\n    ctx->bindResources(DxvkCmdBuffer::ExecBuffer,\n      m_layout, descriptors.size(), descriptors.data(),\n      sizeof(imageExtent), &imageExtent);\n\n    ctx->cmdDispatch(DxvkCmdBuffer::ExecBuffer,\n      ((imageExtent.width + 7u) / 8u),\n      ((imageExtent.height + 7u) / 8u),\n      1u);\n\n    // We can reasonably assume that the image is in GENERAL layout anyway\n    VkMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    memoryBarrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;\n    memoryBarrier.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT;\n    memoryBarrier.dstStageMask = dstImage->info().stages | srcSlice.buffer()->info().stages;\n    memoryBarrier.dstAccessMask = dstImage->info().access | srcSlice.buffer()->info().access;\n\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.memoryBarrierCount = 1u;\n    depInfo.pMemoryBarriers = &memoryBarrier;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n\n    ctx->track(tmpImageView->image(), DxvkAccess::Write);\n    ctx->track(tmpBufferView->buffer(), DxvkAccess::Read);\n  }\n\n\n  void D3D9FormatHelper::InitPipelines() {\n    m_pipelines[D3D9ConversionFormat_YUY2] = CreatePipeline(sizeof(d3d9_convert_yuy2_uyvy), d3d9_convert_yuy2_uyvy, 0);\n    m_pipelines[D3D9ConversionFormat_UYVY] = CreatePipeline(sizeof(d3d9_convert_yuy2_uyvy), d3d9_convert_yuy2_uyvy, 1);\n    m_pipelines[D3D9ConversionFormat_L6V5U5] = CreatePipeline(sizeof(d3d9_convert_l6v5u5), d3d9_convert_l6v5u5, 0);\n    m_pipelines[D3D9ConversionFormat_X8L8V8U8] = CreatePipeline(sizeof(d3d9_convert_x8l8v8u8), d3d9_convert_x8l8v8u8, 0);\n    m_pipelines[D3D9ConversionFormat_A2W10V10U10] = CreatePipeline(sizeof(d3d9_convert_a2w10v10u10), d3d9_convert_a2w10v10u10, 0);\n    m_pipelines[D3D9ConversionFormat_W11V11U10] = CreatePipeline(sizeof(d3d9_convert_w11v11u10), d3d9_convert_w11v11u10, 0);\n    m_pipelines[D3D9ConversionFormat_NV12] = CreatePipeline(sizeof(d3d9_convert_nv12), d3d9_convert_nv12, 0);\n    m_pipelines[D3D9ConversionFormat_YV12] = CreatePipeline(sizeof(d3d9_convert_yv12), d3d9_convert_yv12, 0);\n  }\n\n\n  const DxvkPipelineLayout* D3D9FormatHelper::CreatePipelineLayout() {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 2> bindings = {{\n      { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,        1, VK_SHADER_STAGE_COMPUTE_BIT },\n      { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },\n    }};\n\n    return m_device->createBuiltInPipelineLayout(0u, VK_SHADER_STAGE_COMPUTE_BIT,\n      sizeof(VkExtent2D), bindings.size(), bindings.data());\n  }\n\n\n  VkPipeline D3D9FormatHelper::CreatePipeline(size_t size, const uint32_t* code, uint32_t specConstant) {\n    auto vk = m_device->vkd();\n\n    VkSpecializationMapEntry specEntry = { };\n    specEntry.size = sizeof(specConstant);\n\n    VkSpecializationInfo specInfo = { };\n    specInfo.mapEntryCount = 1;\n    specInfo.pMapEntries = &specEntry;\n    specInfo.dataSize = sizeof(specConstant);\n    specInfo.pData = &specConstant;\n\n    util::DxvkBuiltInShaderStage stage;\n    stage.size = size;\n    stage.code = code;\n    stage.spec = &specInfo;\n\n    return m_device->createBuiltInComputePipeline(m_layout, stage);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_format_helpers.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n#include \"d3d9_format.h\"\n#include \"../dxvk/dxvk_device.h\"\n#include \"../dxvk/dxvk_context.h\"\n\nnamespace dxvk {\n\n  class D3D9FormatHelper {\n\n  public:\n\n    D3D9FormatHelper(const Rc<DxvkDevice>& device);\n\n    ~D3D9FormatHelper();\n\n    void Flush();\n\n    void ConvertFormat(\n      const Rc<DxvkCommandList>&          ctx,\n            D3D9_CONVERSION_FORMAT_INFO   conversionFormat,\n      const Rc<DxvkImage>&                dstImage,\n            VkImageSubresourceLayers      dstSubresource,\n      const DxvkBufferSlice&              srcSlice);\n\n  private:\n\n    void ConvertGenericFormat(\n      const Rc<DxvkCommandList>&          ctx,\n            D3D9_CONVERSION_FORMAT_INFO   videoFormat,\n      const Rc<DxvkImage>&                dstImage,\n            VkImageSubresourceLayers      dstSubresource,\n      const DxvkBufferSlice&              srcSlice,\n            VkFormat                      bufferFormat,\n            VkExtent2D                    macroPixelRun);\n\n    enum BindingIds : uint32_t {\n      Image  = 0,\n      Buffer = 1,\n    };\n\n    void InitPipelines();\n\n    const DxvkPipelineLayout* CreatePipelineLayout();\n\n    VkPipeline CreatePipeline(size_t size, const uint32_t* code, uint32_t specConstant);\n\n    Rc<DxvkDevice>            m_device;\n\n    const DxvkPipelineLayout* m_layout = nullptr;\n\n    std::array<VkPipeline, D3D9ConversionFormat_Count> m_pipelines = { };\n\n  };\n  \n}\n"
  },
  {
    "path": "src/d3d9/d3d9_hud.cpp",
    "content": "#include \"d3d9_hud.h\"\n\nnamespace dxvk::hud {\n\n  HudTextureMemory::HudTextureMemory(D3D9DeviceEx* device)\n  : m_device          (device)\n  , m_allocatedString (\"\")\n  , m_mappedString    (\"\") { }\n\n\n  void HudTextureMemory::update(dxvk::high_resolution_clock::time_point time) {\n    D3D9MemoryAllocator* allocator = m_device->GetAllocator();\n\n    m_maxAllocated = std::max(m_maxAllocated, allocator->AllocatedMemory());\n    m_maxUsed = std::max(m_maxUsed, allocator->UsedMemory());\n    m_maxMapped = std::max(m_maxMapped, allocator->MappedMemory());\n\n    auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);\n\n    if (elapsed.count() < UpdateInterval)\n      return;\n\n    m_allocatedString = str::format(m_maxAllocated >> 20, \" MB (Used: \", m_maxUsed >> 20, \" MB)\");\n    m_mappedString = str::format(m_maxMapped >> 20, \" MB\");\n    m_maxAllocated = 0;\n    m_maxUsed = 0;\n    m_maxMapped = 0;\n    m_lastUpdate = time;\n  }\n\n\n  HudPos HudTextureMemory::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n    renderer.drawText(16, position, 0xffc0ff00u, \"Mappable:\");\n    renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_allocatedString);\n\n    position.y += 20;\n    renderer.drawText(16, position, 0xffc0ff00u, \"Mapped:\");\n    renderer.drawText(16, { position.x + 120, position.y }, 0xffffffffu, m_mappedString);\n\n    position.y += 8;\n    return position;\n  }\n\n  HudFixedFunctionShaders::HudFixedFunctionShaders(D3D9DeviceEx* device)\n  : m_device        (device)\n  , m_ffShaderCount (\"\") {}\n\n\n  void HudFixedFunctionShaders::update(dxvk::high_resolution_clock::time_point time) {\n    m_ffShaderCount = str::format(\n      \"VS: \", m_device->GetOptions()->ffUbershaderVS ? \"1*\" : str::format(m_device->GetFixedFunctionVSCount()),\n      \", FS: \", m_device->GetOptions()->ffUbershaderFS ? \"1*\" : str::format(m_device->GetFixedFunctionFSCount()),\n      \", SWVP: \", m_device->GetSWVPShaderCount()\n    );\n  }\n\n\n  HudPos HudFixedFunctionShaders::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n    renderer.drawText(16, position, 0xffc0ff00u, \"FF Shaders:\");\n    renderer.drawText(16, { position.x + 155, position.y }, 0xffffffffu, m_ffShaderCount);\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudSWVPState::HudSWVPState(D3D9DeviceEx* device)\n          : m_device          (device)\n          , m_isSWVPText (\"\") {}\n\n\n\n  void HudSWVPState::update(dxvk::high_resolution_clock::time_point time) {\n    if (m_device->IsSWVP()) {\n      if (m_device->CanOnlySWVP()) {\n        m_isSWVPText = \"SWVP\";\n      } else {\n        m_isSWVPText = \"SWVP (Mixed)\";\n      }\n    } else {\n      if (m_device->CanSWVP()) {\n        m_isSWVPText = \"HWVP (Mixed)\";\n      } else {\n        m_isSWVPText = \"HWVP\";\n      }\n    }\n  }\n\n\n  HudPos HudSWVPState::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n    renderer.drawText(16, position, 0xffc0ff00u, \"Vertex Processing:\");\n    renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, m_isSWVPText);\n\n    position.y += 8;\n    return position;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_hud.h",
    "content": "#pragma once\n\n#include \"d3d9_device.h\"\n#include \"../dxvk/hud/dxvk_hud_item.h\"\n\nnamespace dxvk::hud {\n\n  /**\n   * \\brief HUD item to display unmappable memory\n   */\n  class HudTextureMemory : public HudItem {\n    constexpr static int64_t UpdateInterval = 500'000;\n  public:\n\n    HudTextureMemory(D3D9DeviceEx* device);\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    D3D9DeviceEx* m_device;\n\n    uint32_t m_maxAllocated = 0;\n    uint32_t m_maxUsed      = 0;\n    uint32_t m_maxMapped    = 0;\n\n    dxvk::high_resolution_clock::time_point m_lastUpdate\n      = dxvk::high_resolution_clock::now();\n\n    std::string m_allocatedString;\n    std::string m_mappedString;\n\n  };\n\n\n  /**\n   * \\brief HUD item to display amount of generated fixed function shaders\n   */\n  class HudFixedFunctionShaders : public HudItem {\n\n  public:\n\n    HudFixedFunctionShaders(D3D9DeviceEx* device);\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    D3D9DeviceEx* m_device;\n\n    std::string m_ffShaderCount;\n\n  };\n\n\n  /**\n   * \\brief HUD item to whether or not we're in SWVP mode\n   */\n  class HudSWVPState : public HudItem {\n\n  public:\n\n    HudSWVPState(D3D9DeviceEx* device);\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    D3D9DeviceEx* m_device;\n\n    std::string m_isSWVPText;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_include.h",
    "content": "#pragma once\n\n#ifndef _MSC_VER\n#ifdef _WIN32_WINNT\n#undef _WIN32_WINNT\n#endif\n#define _WIN32_WINNT 0x0A00\n#endif\n\n#include <stdint.h>\n#include <d3d9.h>\n\n//for some reason we need to specify __declspec(dllexport) for MinGW\n#if defined(__WINE__) || !defined(_WIN32)\n  #define DLLEXPORT __attribute__((visibility(\"default\")))\n#else\n  #define DLLEXPORT\n#endif\n\n\n#include \"../util/com/com_guid.h\"\n#include \"../util/com/com_object.h\"\n#include \"../util/com/com_pointer.h\"\n\n#include \"../util/log/log.h\"\n#include \"../util/log/log_debug.h\"\n\n#include \"../util/rc/util_rc.h\"\n#include \"../util/rc/util_rc_ptr.h\"\n\n#include \"../util/sync/sync_recursive.h\"\n\n#include \"../util/util_env.h\"\n#include \"../util/util_enum.h\"\n#include \"../util/util_error.h\"\n#include \"../util/util_flags.h\"\n#include \"../util/util_likely.h\"\n#include \"../util/util_math.h\"\n#include \"../util/util_string.h\"\n\n// Missed definitions in Wine/MinGW.\n\n#ifndef D3DPRESENT_FORCEIMMEDIATE\n#define D3DPRESENT_FORCEIMMEDIATE              0x00000100L\n#endif\n\n// Missing in some versions of mingw headers\n#ifndef _MSC_VER\nnamespace dxvk {\n  typedef struct _D3DDEVINFO_RESOURCEMANAGER\n  {\n    char dummy;\n  } D3DDEVINFO_RESOURCEMANAGER, * LPD3DDEVINFO_RESOURCEMANAGER;\n}\n#endif\n\n// This is the managed pool on D3D9Ex, it's just hidden!\n#define D3DPOOL_MANAGED_EX D3DPOOL(6)\n\nusing D3D9VertexElements = std::vector<D3DVERTEXELEMENT9>;\n\n/////////////////////\n// D3D9On12 content\n/////////////////////\n\n#include <d3d12.h>\n\n#define MAX_D3D9ON12_QUEUES 2\n\nstruct D3D9ON12_ARGS {\n  BOOL Enable9On12;\n  IUnknown* pD3D12Device;\n  IUnknown* ppD3D12Queues[MAX_D3D9ON12_QUEUES];\n  UINT NumQueues;\n  UINT NodeMask;\n};\n\nextern \"C\" {\n\n  // Ordinal 20\n  typedef IDirect3D9* (WINAPI* PFN_Direct3DCreate9On12)(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count);\n  IDirect3D9* WINAPI Direct3DCreate9On12(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count);\n\n  // Ordinal 21\n  typedef HRESULT(WINAPI* PFN_Direct3DCreate9On12Ex)(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count, IDirect3D9Ex** output);\n  HRESULT WINAPI Direct3DCreate9On12Ex(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count, IDirect3D9Ex** output);\n\n}\n\nMIDL_INTERFACE(\"e7fda234-b589-4049-940d-8878977531c8\")\nIDirect3DDevice9On12 : public IUnknown {\n\n  virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** object) = 0;\n  virtual ULONG STDMETHODCALLTYPE AddRef() = 0;\n  virtual ULONG STDMETHODCALLTYPE Release() = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetD3D12Device(REFIID riid, void** object) = 0;\n  virtual HRESULT STDMETHODCALLTYPE UnwrapUnderlyingResource(IDirect3DResource9* resource, ID3D12CommandQueue* command_queue, REFIID riid, void** object) = 0;\n  virtual HRESULT STDMETHODCALLTYPE ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) = 0;\n};\n\n#ifndef _MSC_VER\n__CRT_UUID_DECL(IDirect3DDevice9On12,      0xe7fda234,0xb589,0x4049,0x94,0x0d,0x88,0x78,0x97,0x75,0x31,0xc8);\n#endif\n\nenum class D3D9ShaderType : uint16_t {\n  VertexShader = 0,\n  PixelShader  = 1,\n};\n\n"
  },
  {
    "path": "src/d3d9/d3d9_initializer.cpp",
    "content": "#include <cstring>\n\n#include \"d3d9_initializer.h\"\n#include \"d3d9_device.h\"\n\nnamespace dxvk {\n\n  D3D9Initializer::D3D9Initializer(\n    D3D9DeviceEx*             pParent)\n  : m_parent(pParent),\n    m_device(pParent->GetDXVKDevice()),\n    m_csChunk(m_parent->AllocCsChunk()) {\n\n  }\n\n\n  D3D9Initializer::~D3D9Initializer() {\n\n  }\n\n\n  void D3D9Initializer::NotifyContextFlush() {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    m_transferCommands = 0;\n  }\n\n\n  void D3D9Initializer::InitBuffer(\n          D3D9CommonBuffer*  pBuffer) {\n    VkMemoryPropertyFlags memFlags = pBuffer->GetBuffer<D3D9_COMMON_BUFFER_TYPE_REAL>()->memFlags();\n\n    (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)\n      ? InitHostVisibleBuffer(pBuffer->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>())\n      : InitDeviceLocalBuffer(pBuffer->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>());\n\n    if (pBuffer->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER)\n      InitHostVisibleBuffer(pBuffer->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_STAGING>());\n  }\n\n\n  void D3D9Initializer::InitTexture(\n          D3D9CommonTexture* pTexture,\n          void*              pInitialData) {\n    if (pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_NONE)\n      return;\n\n    void* mapPtr = nullptr;\n\n    if (pTexture->Desc()->Pool != D3DPOOL_DEFAULT) {\n      mapPtr = pTexture->GetData(0);\n      if (mapPtr == nullptr)\n        throw DxvkError(\"D3D9: InitTexture: map failed\");\n    }\n\n    if (pTexture->GetImage() != nullptr)\n      InitDeviceLocalTexture(pTexture);\n\n    if (mapPtr != nullptr) {\n      InitHostVisibleTexture(pTexture, pInitialData, mapPtr);\n      pTexture->UnmapData();\n    }\n\n    SyncSharedTexture(pTexture);\n  }\n\n\n  void D3D9Initializer::InitDeviceLocalBuffer(\n          DxvkBufferSlice    Slice) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    m_transferCommands += 1;\n\n    EmitCs([\n      cBuffer = Slice.buffer()\n    ] (DxvkContext* ctx) {\n      ctx->initBuffer(\n        cBuffer);\n    });\n\n    ThrottleAllocationLocked();\n  }\n\n\n  void D3D9Initializer::InitHostVisibleBuffer(\n          DxvkBufferSlice    Slice) {\n    // If the buffer is mapped, we can write data directly\n    // to the mapped memory region instead of doing it on\n    // the GPU. Same goes for zero-initialization.\n    std::memset(\n      Slice.mapPtr(0), 0,\n      Slice.length());\n  }\n\n\n  void D3D9Initializer::InitDeviceLocalTexture(\n          D3D9CommonTexture* pTexture) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    Rc<DxvkImage> image = pTexture->GetImage();\n\n    EmitCs([\n      cImage = std::move(image)\n    ] (DxvkContext* ctx) {\n      ctx->initImage(cImage, VK_IMAGE_LAYOUT_UNDEFINED);\n    });\n\n    ThrottleAllocationLocked();\n  }\n\n\n  void D3D9Initializer::InitHostVisibleTexture(\n          D3D9CommonTexture* pTexture,\n          void*              pInitialData,\n          void*              mapPtr) {\n    // If the buffer is mapped, we can write data directly\n    // to the mapped memory region instead of doing it on\n    // the GPU. Same goes for zero-initialization.\n    if (pInitialData) {\n      // Initial data is only supported for textures with 1 subresource\n      VkExtent3D mipExtent = pTexture->GetExtentMip(0);\n      const DxvkFormatInfo* formatInfo = lookupFormatInfo(pTexture->GetFormatMapping().FormatColor);\n      VkExtent3D blockCount = util::computeBlockCount(mipExtent, formatInfo->blockSize);\n      uint32_t pitch = blockCount.width * formatInfo->elementSize;\n      uint32_t alignedPitch = align(pitch, 4);\n\n      util::packImageData(\n        mapPtr,\n        pInitialData,\n        pitch,\n        pitch * blockCount.height,\n        alignedPitch,\n        alignedPitch * blockCount.height,\n        D3D9CommonTexture::GetImageTypeFromResourceType(pTexture->GetType()),\n        mipExtent,\n        pTexture->Desc()->ArraySize,\n        formatInfo,\n        VK_IMAGE_ASPECT_COLOR_BIT);\n    } else {\n      // All subresources are allocated in one chunk of memory.\n      // So we can just get the pointer for subresource 0 and memset all of them at once.\n      std::memset(\n        mapPtr, 0,\n        pTexture->GetTotalSize());\n    }\n  }\n\n\n  void D3D9Initializer::ThrottleAllocationLocked() {\n    if (m_transferCommands > MaxTransferCommands)\n      ExecuteFlushLocked();\n  }\n\n\n  void D3D9Initializer::ExecuteFlush() {\n    std::lock_guard lock(m_mutex);\n\n    ExecuteFlushLocked();\n  }\n\n\n  void D3D9Initializer::ExecuteFlushLocked() {\n    EmitCs([] (DxvkContext* ctx) {\n      ctx->flushCommandList(nullptr, nullptr);\n    });\n\n    FlushCsChunk();\n\n    m_transferCommands = 0;\n  }\n\n\n  void D3D9Initializer::SyncSharedTexture(D3D9CommonTexture* pResource) {\n    if (pResource->GetImage() == nullptr || pResource->GetImage()->info().sharing.mode == DxvkSharedHandleMode::None)\n      return;\n\n    // Ensure that initialization commands are submitted and waited on before\n    // returning control to the application in order to avoid race conditions\n    // in case the texture is used immediately on a secondary device.\n    ExecuteFlush();\n\n    m_device->waitForResource(*pResource->GetImage(), DxvkAccess::Write);\n  }\n\n\n  void D3D9Initializer::FlushCsChunkLocked() {\n    m_parent->InjectCsChunk(std::move(m_csChunk), false);\n    m_csChunk = m_parent->AllocCsChunk();\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_initializer.h",
    "content": "#pragma once\n\n#include \"d3d9_common_buffer.h\"\n#include \"d3d9_common_texture.h\"\n\nnamespace dxvk {\n\n    class D3D9DeviceEx;\n\n  /**\n   * \\brief Resource initialization context\n   * \n   * Manages a context which is used for resource\n   * initialization. This includes \n   * zero-initialization for buffers and images.\n   */\n  class D3D9Initializer {\n    constexpr static size_t MaxTransferMemory    = 32 * 1024 * 1024;\n    constexpr static size_t MaxTransferCommands  = 512;\n  public:\n\n    D3D9Initializer(\n      D3D9DeviceEx*           pParent);\n\n    ~D3D9Initializer();\n\n    void FlushCsChunk() {\n      std::lock_guard<dxvk::mutex> lock(m_csMutex);\n\n    if (!m_csChunk->empty())\n        FlushCsChunkLocked();\n    }\n\n    void NotifyContextFlush();\n\n    void InitBuffer(\n            D3D9CommonBuffer*  pBuffer);\n\n    void InitTexture(\n            D3D9CommonTexture* pTexture,\n            void*              pInitialData = nullptr);\n\n  private:\n\n    dxvk::mutex       m_mutex;\n\n    D3D9DeviceEx*     m_parent;\n    Rc<DxvkDevice>    m_device;\n\n    size_t            m_transferCommands  = 0;\n\n    dxvk::mutex       m_csMutex;\n    DxvkCsChunkRef    m_csChunk;\n\n    void InitDeviceLocalBuffer(\n            DxvkBufferSlice    Slice);\n\n    void InitHostVisibleBuffer(\n            DxvkBufferSlice    Slice);\n\n    void InitDeviceLocalTexture(\n            D3D9CommonTexture* pTexture);\n\n    void InitHostVisibleTexture(\n            D3D9CommonTexture* pTexture,\n            void*              pInitialData,\n            void*              mapPtr);\n\n    void ThrottleAllocationLocked();\n\n    void ExecuteFlush();\n\n    void ExecuteFlushLocked();\n\n    void SyncSharedTexture(\n            D3D9CommonTexture* pResource);\n\n    void FlushCsChunkLocked();\n\n    void NotifyContextFlushLocked();\n\n    template<typename Cmd>\n    void EmitCs(Cmd&& command) {\n      std::lock_guard<dxvk::mutex> lock(m_csMutex);\n\n      if (unlikely(!m_csChunk->push(command))) {\n        FlushCsChunkLocked();\n\n        m_csChunk->push(command);\n      }\n    }\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_interface.cpp",
    "content": "#include \"d3d9_interface.h\"\n\n#include \"d3d9_monitor.h\"\n#include \"d3d9_caps.h\"\n#include \"d3d9_device.h\"\n#include \"d3d9_bridge.h\"\n#include \"d3d9_window.h\"\n\n#include \"../util/util_singleton.h\"\n\n#include <algorithm>\n\nnamespace dxvk {\n\n  Singleton<DxvkInstance> g_dxvkInstance;\n\n  D3D9InterfaceEx::D3D9InterfaceEx(bool bExtended, const D3D9ON12_ARGS* pOverrideList, uint32_t OverrideCount)\n    : m_instance    ( g_dxvkInstance.acquire(DxvkInstanceFlag::ClientApiIsD3D9) )\n    , m_d3d8Bridge  ( this )\n    , m_extended    ( bExtended ) \n    , m_d3d9Options ( nullptr, m_instance->config() )\n    , m_d3d9Interop ( this )\n    , m_d3d9ExtInterface( this ) {\n    // D3D9 doesn't enumerate adapters like physical adapters...\n    // only as connected displays.\n\n    // Let's create some \"adapters\" for the amount of displays we have.\n    // We'll go through and match up displays -> our adapters in order.\n    // If we run out of adapters, then we'll just make repeats of the first one.\n    // We can't match up by names on Linux/Wine as they don't match at all\n    // like on Windows, so this is our best option.\n#ifdef _WIN32\n    if (m_d3d9Options.enumerateByDisplays) {\n      DISPLAY_DEVICEA device = { };\n      device.cb = sizeof(device);\n\n      uint32_t adapterOrdinal = 0;\n      uint32_t i = 0;\n      while (::EnumDisplayDevicesA(nullptr, i++, &device, 0)) {\n        // If we aren't attached, skip over.\n        if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP))\n          continue;\n\n        // If we are a mirror, skip over this device.\n        if (device.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)\n          continue;\n\n        Rc<DxvkAdapter> adapter = adapterOrdinal >= m_instance->adapterCount()\n          ? m_instance->enumAdapters(0)\n          : m_instance->enumAdapters(adapterOrdinal);\n\n        if (adapter != nullptr) {\n          const auto* d3d9On12Args = Find9On12Args(adapter, pOverrideList, OverrideCount);\n          m_adapters.emplace_back(this, d3d9On12Args, adapter, adapterOrdinal++, i - 1);\n        }\n      }\n    }\n    else\n#endif\n    {\n      const uint32_t adapterCount = m_instance->adapterCount();\n      m_adapters.reserve(adapterCount);\n\n      for (uint32_t i = 0; i < adapterCount; i++) {\n        const auto* d3d9On12Args = Find9On12Args(m_instance->enumAdapters(i), pOverrideList, OverrideCount);\n        m_adapters.emplace_back(this, d3d9On12Args, m_instance->enumAdapters(i), i, 0);\n      }\n    }\n\n#ifdef _WIN32\n    if (m_d3d9Options.dpiAware) {\n      Logger::info(\"Process set as DPI aware\");\n      SetProcessDPIAware();\n    }\n#endif\n\n    if (unlikely(m_d3d9Options.shaderModel == 0))\n      Logger::warn(\"D3D9InterfaceEx: WARNING! Fixed-function exclusive mode is enabled.\");\n  }\n\n\n  D3D9InterfaceEx::~D3D9InterfaceEx() {\n    g_dxvkInstance.release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3D9)\n     || (m_extended && riid == __uuidof(IDirect3D9Ex))) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(IDxvkD3D8InterfaceBridge)) {\n      *ppvObject = ref(&m_d3d8Bridge);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D9VkInteropInterface)\n     || riid == __uuidof(ID3D9VkInteropInterface1)) {\n      *ppvObject = ref(&m_d3d9Interop);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D9VkExtInterface)) {\n      *ppvObject = ref(&m_d3d9ExtInterface);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3D9), riid)) {\n      Logger::warn(\"D3D9InterfaceEx::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::RegisterSoftwareDevice(void* pInitializeFunction) {\n    Logger::warn(\"D3D9InterfaceEx::RegisterSoftwareDevice: Stub\");\n    return D3D_OK;\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterCount() {\n    return UINT(m_adapters.size());\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterIdentifier(\n          UINT                    Adapter,\n          DWORD                   Flags,\n          D3DADAPTER_IDENTIFIER9* pIdentifier) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->GetAdapterIdentifier(Flags, pIdentifier);\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  UINT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterModeCount(UINT Adapter, D3DFORMAT Format) {\n    D3DDISPLAYMODEFILTER filter;\n    filter.Size             = sizeof(D3DDISPLAYMODEFILTER);\n    filter.Format           = Format;\n    filter.ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE;\n    \n    return this->GetAdapterModeCountEx(Adapter, &filter);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) {\n    if (auto* adapter = GetAdapter(Adapter)) {\n      D3DDISPLAYMODEEX modeEx = { };\n      modeEx.Size = sizeof(D3DDISPLAYMODEEX);\n      HRESULT hr = adapter->GetAdapterDisplayModeEx(&modeEx, nullptr);\n\n      if (FAILED(hr))\n        return hr;\n\n      pMode->Width       = modeEx.Width;\n      pMode->Height      = modeEx.Height;\n      pMode->RefreshRate = modeEx.RefreshRate;\n      pMode->Format      = modeEx.Format;\n\n      return D3D_OK;\n    }\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceType(\n          UINT       Adapter,\n          D3DDEVTYPE DevType,\n          D3DFORMAT  AdapterFormat,\n          D3DFORMAT  BackBufferFormat,\n          BOOL       bWindowed) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->CheckDeviceType(\n        DevType, EnumerateFormat(AdapterFormat),\n        EnumerateFormat(BackBufferFormat), bWindowed);\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceFormat(\n          UINT            Adapter,\n          D3DDEVTYPE      DeviceType,\n          D3DFORMAT       AdapterFormat,\n          DWORD           Usage,\n          D3DRESOURCETYPE RType,\n          D3DFORMAT       CheckFormat) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->CheckDeviceFormat(\n        DeviceType, EnumerateFormat(AdapterFormat),\n        Usage, RType,\n        EnumerateFormat(CheckFormat));\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceMultiSampleType(\n          UINT                Adapter,\n          D3DDEVTYPE          DeviceType,\n          D3DFORMAT           SurfaceFormat,\n          BOOL                Windowed,\n          D3DMULTISAMPLE_TYPE MultiSampleType,\n          DWORD*              pQualityLevels) { \n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->CheckDeviceMultiSampleType(\n        DeviceType, EnumerateFormat(SurfaceFormat),\n        Windowed, MultiSampleType,\n        pQualityLevels);\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDepthStencilMatch(\n          UINT       Adapter,\n          D3DDEVTYPE DeviceType,\n          D3DFORMAT  AdapterFormat,\n          D3DFORMAT  RenderTargetFormat,\n          D3DFORMAT  DepthStencilFormat) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->CheckDepthStencilMatch(\n        DeviceType, EnumerateFormat(AdapterFormat),\n        EnumerateFormat(RenderTargetFormat),\n        EnumerateFormat(DepthStencilFormat));\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceFormatConversion(\n          UINT       Adapter,\n          D3DDEVTYPE DeviceType,\n          D3DFORMAT  SourceFormat,\n          D3DFORMAT  TargetFormat) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->CheckDeviceFormatConversion(\n        DeviceType, EnumerateFormat(SourceFormat),\n        EnumerateFormat(TargetFormat));\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetDeviceCaps(\n          UINT       Adapter,\n          D3DDEVTYPE DeviceType,\n          D3DCAPS9*  pCaps) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->GetDeviceCaps(\n        DeviceType, pCaps);\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HMONITOR STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterMonitor(UINT Adapter) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->GetMonitor();\n\n    return nullptr;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CreateDevice(\n          UINT                   Adapter,\n          D3DDEVTYPE             DeviceType,\n          HWND                   hFocusWindow,\n          DWORD                  BehaviorFlags,\n          D3DPRESENT_PARAMETERS* pPresentationParameters,\n          IDirect3DDevice9**     ppReturnedDeviceInterface) {\n    return this->CreateDeviceEx(\n      Adapter,\n      DeviceType,\n      hFocusWindow,\n      BehaviorFlags,\n      pPresentationParameters,\n      nullptr, // <-- pFullscreenDisplayMode\n      reinterpret_cast<IDirect3DDevice9Ex**>(ppReturnedDeviceInterface));\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::EnumAdapterModes(\n          UINT            Adapter,\n          D3DFORMAT       Format,\n          UINT            Mode,\n          D3DDISPLAYMODE* pMode) {\n    if (pMode == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    D3DDISPLAYMODEFILTER filter;\n    filter.Format           = Format;\n    filter.ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE;\n    filter.Size             = sizeof(D3DDISPLAYMODEFILTER);\n\n    D3DDISPLAYMODEEX modeEx = { };\n    modeEx.Size = sizeof(D3DDISPLAYMODEEX);\n    HRESULT hr = this->EnumAdapterModesEx(Adapter, &filter, Mode, &modeEx);\n\n    if (FAILED(hr))\n      return hr;\n\n    pMode->Width       = modeEx.Width;\n    pMode->Height      = modeEx.Height;\n    pMode->RefreshRate = modeEx.RefreshRate;\n    pMode->Format      = modeEx.Format;\n\n    return D3D_OK;\n  }\n\n\n  // Ex Methods\n\n\n  UINT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterModeCountEx(UINT Adapter, CONST D3DDISPLAYMODEFILTER* pFilter) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->GetAdapterModeCountEx(pFilter);\n\n    return 0;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::EnumAdapterModesEx(\n          UINT                  Adapter,\n    const D3DDISPLAYMODEFILTER* pFilter,\n          UINT                  Mode,\n          D3DDISPLAYMODEEX*     pMode) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->EnumAdapterModesEx(pFilter, Mode, pMode);\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterDisplayModeEx(\n          UINT                Adapter,\n          D3DDISPLAYMODEEX*   pMode,\n          D3DDISPLAYROTATION* pRotation) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->GetAdapterDisplayModeEx(pMode, pRotation);\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CreateDeviceEx(\n          UINT                   Adapter,\n          D3DDEVTYPE             DeviceType,\n          HWND                   hFocusWindow,\n          DWORD                  BehaviorFlags,\n          D3DPRESENT_PARAMETERS* pPresentationParameters,\n          D3DDISPLAYMODEEX*      pFullscreenDisplayMode,\n          IDirect3DDevice9Ex**   ppReturnedDeviceInterface) {\n    InitReturnPtr(ppReturnedDeviceInterface);\n\n    if (unlikely(ppReturnedDeviceInterface  == nullptr\n              || pPresentationParameters    == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(DeviceType == D3DDEVTYPE_SW))\n      return D3DERR_INVALIDCALL;\n\n    // Creating a device with D3DCREATE_PUREDEVICE only works in conjunction\n    // with D3DCREATE_HARDWARE_VERTEXPROCESSING on native drivers.\n    if (unlikely(BehaviorFlags & D3DCREATE_PUREDEVICE &&\n               !(BehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING)))\n      return D3DERR_INVALIDCALL;\n\n    // Neither D3DDEVTYPE_REF nor D3DDEVTYPE_NULLREF support HWVP, although they\n    // will accept these flags as any regular D3DDEVTYPE_HAL device would.\n    if (unlikely(DeviceType == D3DDEVTYPE_REF || DeviceType == D3DDEVTYPE_NULLREF)) {\n      BehaviorFlags &= ~D3DCREATE_MIXED_VERTEXPROCESSING\n                     & ~D3DCREATE_PUREDEVICE\n                     & ~D3DCREATE_HARDWARE_VERTEXPROCESSING;\n      BehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;\n    }\n\n    HRESULT hr;\n    // Black Desert creates a D3DDEVTYPE_NULLREF device and\n    // expects it be created despite passing invalid parameters.\n    if (likely(DeviceType != D3DDEVTYPE_NULLREF)) {\n      hr = ValidatePresentationParameters(pPresentationParameters);\n\n      if (unlikely(FAILED(hr)))\n        return hr;\n    }\n\n    auto* adapter = GetAdapter(Adapter);\n\n    if (adapter == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    auto dxvkAdapter = adapter->GetDXVKAdapter();\n\n    try {\n      auto dxvkDevice = dxvkAdapter->createDevice();\n\n      auto* device = new D3D9DeviceEx(\n        this,\n        adapter,\n        DeviceType,\n        hFocusWindow,\n        BehaviorFlags,\n        dxvkDevice);\n\n      if (!pPresentationParameters->Windowed)\n        ActivateFocusWindow(hFocusWindow ? hFocusWindow : pPresentationParameters->hDeviceWindow);\n\n      hr = device->InitialReset(pPresentationParameters, pFullscreenDisplayMode);\n\n      if (unlikely(FAILED(hr)))\n        return hr;\n\n      *ppReturnedDeviceInterface = ref(device);\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_NOTAVAILABLE;\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterLUID(UINT Adapter, LUID* pLUID) {\n    if (auto* adapter = GetAdapter(Adapter))\n      return adapter->GetAdapterLUID(pLUID);\n\n    return D3DERR_INVALIDCALL;\n  }\n\n\n  HRESULT D3D9InterfaceEx::ValidatePresentationParametersEx(\n    const D3DPRESENT_PARAMETERS* pPresentationParameters,\n    const D3DDISPLAYMODEEX*      pFullscreenDisplayMode) {\n    if (unlikely(pPresentationParameters == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // pFullscreenDisplayMode must not be NULL in full screen mode.\n    if (unlikely(!pPresentationParameters->Windowed && pFullscreenDisplayMode == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // pFullscreenDisplayMode must be NULL in windowed mode.\n    if (unlikely(pPresentationParameters->Windowed && pFullscreenDisplayMode != nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // On extended devices, the backbuffer dimensions\n    // must match the display mode when in full screen mode.\n    if (unlikely(!pPresentationParameters->Windowed &&\n                  (pPresentationParameters->BackBufferWidth  != pFullscreenDisplayMode->Width\n                || pPresentationParameters->BackBufferHeight != pFullscreenDisplayMode->Height)))\n      return D3DERR_INVALIDCALL;\n\n    return ValidatePresentationParameters(pPresentationParameters);\n  }\n\n\n  HRESULT D3D9InterfaceEx::ValidatePresentationParameters(\n    const D3DPRESENT_PARAMETERS* pPresentationParameters) {\n    if (unlikely(pPresentationParameters == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (m_extended) {\n      // The swap effect value on a D3D9Ex device\n      // can not be higher than D3DSWAPEFFECT_FLIPEX.\n      if (unlikely(pPresentationParameters->SwapEffect > D3DSWAPEFFECT_FLIPEX))\n        return D3DERR_INVALIDCALL;\n\n      // 30 is the highest supported back buffer count for Ex devices.\n      if (unlikely(pPresentationParameters->BackBufferCount > D3DPRESENT_BACK_BUFFERS_MAX_EX))\n        return D3DERR_INVALIDCALL;\n    } else {\n      // The swap effect value on a non-Ex D3D9 device\n      // can not be higher than D3DSWAPEFFECT_COPY.\n      if (unlikely(pPresentationParameters->SwapEffect > D3DSWAPEFFECT_COPY))\n        return D3DERR_INVALIDCALL;\n\n      // 3 is the highest supported back buffer count for non-Ex devices.\n      if (unlikely(pPresentationParameters->BackBufferCount > D3DPRESENT_BACK_BUFFERS_MAX))\n        return D3DERR_INVALIDCALL;\n    }\n\n    // The swap effect value can not be 0.\n    if (unlikely(!pPresentationParameters->SwapEffect))\n      return D3DERR_INVALIDCALL;\n\n    // D3DSWAPEFFECT_COPY can not be used with more than one back buffer.\n    // Allow D3DSWAPEFFECT_COPY to bypass this restriction in D3D8 compatibility\n    // mode, since it may be a remapping of D3DSWAPEFFECT_COPY_VSYNC and RC Cars\n    // depends on it not being validated.\n    if (unlikely(!IsD3D8Compatible()\n              && pPresentationParameters->SwapEffect == D3DSWAPEFFECT_COPY\n              && pPresentationParameters->BackBufferCount > 1))\n      return D3DERR_INVALIDCALL;\n\n    // Valid fullscreen presentation intervals must be known values.\n    if (unlikely(!pPresentationParameters->Windowed\n            && !(pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_DEFAULT\n              || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_ONE\n              || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_TWO\n              || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_THREE\n              || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_FOUR\n              || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE)))\n      return D3DERR_INVALIDCALL;\n\n    // In windowed mode, only a subset of the presentation interval flags can be used.\n    if (unlikely(pPresentationParameters->Windowed\n            && !(pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_DEFAULT\n              || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_ONE\n              || pPresentationParameters->PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE)))\n      return D3DERR_INVALIDCALL;\n\n    return D3D_OK;\n  }\n\n\n  const D3D9ON12_ARGS* D3D9InterfaceEx::Find9On12Args(\n    const Rc<DxvkAdapter>& Adapter,\n    const D3D9ON12_ARGS*   pOverrides,\n          uint32_t         OverrideCount) {\n    const D3D9ON12_ARGS* arg = nullptr;\n\n#ifdef _WIN32\n    for (uint32_t i = 0u; i < OverrideCount; i++) {\n      if (pOverrides[i].pD3D12Device) {\n        const auto& vk11 = Adapter->deviceProperties().vk11;\n\n        if (vk11.deviceLUIDValid) {\n          Com<ID3D12Device> device = nullptr;\n\n          if (SUCCEEDED(pOverrides[i].pD3D12Device->QueryInterface(__uuidof(ID3D12Device), reinterpret_cast<void**>(&device)))) {\n            LUID luid = device->GetAdapterLuid();\n\n            if (!std::memcmp(&luid, vk11.deviceLUID, sizeof(vk11.deviceLUID)))\n              arg = &pOverrides[i];\n          }\n        }\n      } else if (!arg) {\n        arg = &pOverrides[i];\n      }\n    }\n#endif\n\n    return arg;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_interface.h",
    "content": "#pragma once\n\n#include \"d3d9_adapter.h\"\n#include \"d3d9_bridge.h\"\n#include \"d3d9_interop.h\"\n\n#include \"../dxvk/dxvk_instance.h\"\n\nnamespace dxvk {\n\n  /**\n  * \\brief D3D9 interface implementation\n  *\n  * Implements the IDirect3DDevice9Ex interfaces\n  * which provides the way to get adapters and create other objects such as \\ref IDirect3DDevice9Ex.\n  * similar to \\ref DxgiFactory but for D3D9.\n  */\n  class D3D9InterfaceEx final : public ComObjectClamp<IDirect3D9Ex> {\n\n  public:\n\n    D3D9InterfaceEx(\n              bool           bExtended,\n        const D3D9ON12_ARGS* pOverrideList,\n              uint32_t       OverrideCount);\n\n    ~D3D9InterfaceEx();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction);\n\n    UINT STDMETHODCALLTYPE GetAdapterCount();\n\n    HRESULT STDMETHODCALLTYPE GetAdapterIdentifier(\n            UINT                    Adapter,\n            DWORD                   Flags,\n            D3DADAPTER_IDENTIFIER9* pIdentifier);\n\n    UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter, D3DFORMAT Format);\n\n    HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode);\n\n    HRESULT STDMETHODCALLTYPE CheckDeviceType(\n            UINT       Adapter,\n            D3DDEVTYPE DevType,\n            D3DFORMAT  AdapterFormat,\n            D3DFORMAT  BackBufferFormat,\n            BOOL       bWindowed);\n\n    HRESULT STDMETHODCALLTYPE CheckDeviceFormat(\n            UINT            Adapter,\n            D3DDEVTYPE      DeviceType,\n            D3DFORMAT       AdapterFormat,\n            DWORD           Usage,\n            D3DRESOURCETYPE RType,\n            D3DFORMAT       CheckFormat);\n\n    HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType(\n            UINT                Adapter,\n            D3DDEVTYPE          DeviceType,\n            D3DFORMAT           SurfaceFormat,\n            BOOL                Windowed,\n            D3DMULTISAMPLE_TYPE MultiSampleType,\n            DWORD*              pQualityLevels);\n\n    HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch(\n            UINT       Adapter,\n            D3DDEVTYPE DeviceType,\n            D3DFORMAT  AdapterFormat,\n            D3DFORMAT  RenderTargetFormat,\n            D3DFORMAT  DepthStencilFormat);\n\n    HRESULT STDMETHODCALLTYPE CheckDeviceFormatConversion(\n            UINT       Adapter,\n            D3DDEVTYPE DeviceType,\n            D3DFORMAT  SourceFormat,\n            D3DFORMAT  TargetFormat);\n\n    HRESULT STDMETHODCALLTYPE GetDeviceCaps(\n            UINT       Adapter,\n            D3DDEVTYPE DeviceType,\n            D3DCAPS9*  pCaps);\n\n    HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter);\n\n    HRESULT STDMETHODCALLTYPE CreateDevice(\n            UINT                   Adapter,\n            D3DDEVTYPE             DeviceType,\n            HWND                   hFocusWindow,\n            DWORD                  BehaviorFlags,\n            D3DPRESENT_PARAMETERS* pPresentationParameters,\n            IDirect3DDevice9**     ppReturnedDeviceInterface);\n\n    HRESULT STDMETHODCALLTYPE EnumAdapterModes(\n            UINT            Adapter,\n            D3DFORMAT       Format,\n            UINT            Mode,\n            D3DDISPLAYMODE* pMode);\n\n    // Ex Methods\n\n    UINT STDMETHODCALLTYPE GetAdapterModeCountEx(UINT Adapter, CONST D3DDISPLAYMODEFILTER* pFilter);\n\n    HRESULT STDMETHODCALLTYPE EnumAdapterModesEx(\n            UINT                  Adapter,\n      const D3DDISPLAYMODEFILTER* pFilter,\n            UINT                  Mode,\n            D3DDISPLAYMODEEX*     pMode);\n\n    HRESULT STDMETHODCALLTYPE GetAdapterDisplayModeEx(\n            UINT                Adapter,\n            D3DDISPLAYMODEEX*   pMode,\n            D3DDISPLAYROTATION* pRotation);\n\n    HRESULT STDMETHODCALLTYPE CreateDeviceEx(\n            UINT                   Adapter,\n            D3DDEVTYPE             DeviceType,\n            HWND                   hFocusWindow,\n            DWORD                  BehaviorFlags,\n            D3DPRESENT_PARAMETERS* pPresentationParameters,\n            D3DDISPLAYMODEEX*      pFullscreenDisplayMode,\n            IDirect3DDevice9Ex**   ppReturnedDeviceInterface);\n\n    HRESULT STDMETHODCALLTYPE GetAdapterLUID(UINT Adapter, LUID* pLUID);\n\n    HRESULT ValidatePresentationParametersEx(\n        const D3DPRESENT_PARAMETERS* pPresentationParameters,\n        const D3DDISPLAYMODEEX*      pFullscreenDisplayMode);\n\n    HRESULT ValidatePresentationParameters(\n        const D3DPRESENT_PARAMETERS* pPresentationParameters);\n\n    const D3D9Options& GetOptions() { return m_d3d9Options; }\n\n    D3D9Adapter* GetAdapter(UINT Ordinal) {\n      return Ordinal < m_adapters.size()\n        ? &m_adapters[Ordinal]\n        : nullptr;\n    }\n\n    bool IsExtended() { return m_extended; }\n\n    bool IsD3D8Compatible() const {\n      return m_isD3D8Compatible;\n    }\n\n    void EnableD3D8CompatibilityMode() {\n      m_isD3D8Compatible = true;\n      RefreshAdapterFormatTables();\n      Logger::info(\"The D3D9 interface is now operating in D3D8 compatibility mode.\");\n    }\n\n    Rc<DxvkInstance> GetInstance() { return m_instance; }\n\n    bool HasFormatsUnlocked() const { return m_unlockAdditionalFormats; }\n\n    void EnableAdditionalFormats() {\n      m_unlockAdditionalFormats = true;\n    }\n\n  private:\n\n    inline void RefreshAdapterFormatTables() {\n      for (auto& adapter : m_adapters)\n        adapter.RefreshFormatsTable();\n    }\n\n    Rc<DxvkInstance>              m_instance;\n\n    DxvkD3D8InterfaceBridge       m_d3d8Bridge;\n\n    bool                          m_extended;\n\n    bool                          m_isD3D8Compatible = false;\n\n    D3D9Options                   m_d3d9Options;\n\n    std::vector<D3D9Adapter>      m_adapters;\n\n    D3D9VkInteropInterface        m_d3d9Interop;\n\n    bool m_unlockAdditionalFormats = false;\n\n    D3D9VkExtInterface            m_d3d9ExtInterface;\n\n    static const D3D9ON12_ARGS* Find9On12Args(\n      const Rc<DxvkAdapter>& Adapter,\n      const D3D9ON12_ARGS*   pOverrides,\n            uint32_t         OverrideCount);\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_interfaces.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n#include \"../vulkan/vulkan_loader.h\"\n\n/**\n * \\brief D3D9 interface for Vulkan interop\n *\n * Provides access to the instance and physical device\n * handles for the given D3D9 interface and adapter ordinals.\n */\nMIDL_INTERFACE(\"3461a81b-ce41-485b-b6b5-fcf08ba6a6bd\")\nID3D9VkInteropInterface : public IUnknown {\n  /**\n   * \\brief Queries Vulkan handles used by DXVK\n   * \n   * \\param [out] pInstance The Vulkan instance\n   */\n  virtual void STDMETHODCALLTYPE GetInstanceHandle(\n          VkInstance*           pInstance) = 0;\n\n  /**\n   * \\brief Queries Vulkan handles used by DXVK\n   * \n   * \\param [in] Adapter Adapter ordinal\n   * \\param [out] pInstance The Vulkan instance\n   */\n  virtual void STDMETHODCALLTYPE GetPhysicalDeviceHandle(\n          UINT                  Adapter,\n          VkPhysicalDevice*     pPhysicalDevice) = 0;\n};\n\n/**\n * \\brief D3D9 interface for Vulkan interop - extended\n *\n * Provides access to the instance extension lists\n * and everything provided by ID3D9VkInteropInterface\n */\nMIDL_INTERFACE(\"d6589ed4-7a37-4096-bac2-223b25ae31d2\")\nID3D9VkInteropInterface1 : public ID3D9VkInteropInterface {\n  /**\n   * \\brief Gets a list of enabled instance extensions\n   * \n   * \\param [out] pExtensionCount Number of extensions\n   * \\param [out] ppExtensions List of extension names\n   * \\returns D3DERR_MOREDATA if the list was truncated\n   */\n  virtual HRESULT STDMETHODCALLTYPE GetInstanceExtensions(\n          UINT*                       pExtensionCount,\n    const char**                      ppExtensions) = 0;\n};\n\n/**\n * \\brief D3D9 texture interface for Vulkan interop\n * \n * Provides access to the backing image of a\n * D3D9 texture, surface, or volume.\n */\nMIDL_INTERFACE(\"d56344f5-8d35-46fd-806d-94c351b472c1\")\nID3D9VkInteropTexture : public IUnknown {\n  /**\n   * \\brief Retrieves Vulkan image info\n   * \n   * Retrieves both the image handle as well as the image's\n   * properties. Any of the given pointers may be \\c nullptr.\n   * \n   * If \\c pInfo is not \\c nullptr, the following rules apply:\n   * - \\c pInfo->sType \\e must be \\c VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO\n   * - \\c pInfo->pNext \\e must be \\c nullptr or point to a supported\n   *   extension-specific structure (currently none)\n   * - \\c pInfo->queueFamilyIndexCount must be the length of the\n   *   \\c pInfo->pQueueFamilyIndices array, in \\c uint32_t units.\n   * - \\c pInfo->pQueueFamilyIndices must point to a pre-allocated\n   *   array of \\c uint32_t of size \\c pInfo->pQueueFamilyIndices.\n   * \n   * \\note As of now, the sharing mode will always be\n   *       \\c VK_SHARING_MODE_EXCLUSIVE and no queue\n   *       family indices will be written to the array.\n   * \n   * After the call, the structure pointed to by \\c pInfo can\n   * be used to create an image with identical properties.\n   * \n   * If \\c pLayout is not \\c nullptr, it will receive the\n   * layout that the image will be in after flushing any\n   * outstanding commands on the device.\n   * \\param [out] pHandle The image handle\n   * \\param [out] pLayout Image layout\n   * \\param [out] pInfo Image properties\n   * \\returns \\c S_OK on success\n   * \\returns \\c D3DERR_INVALIDCALL on failure\n   * \\returns \\c D3DERR_NOTFOUND if this texture does not\n   *          have a device handle, such as when the image\n   *          has \\c NULL format or the image's pool is\n   *          \\c D3DPOOL_SYSMEM or \\c D3DPOOL_SCRATCH.\n   */\n  virtual HRESULT STDMETHODCALLTYPE GetVulkanImageInfo(\n          VkImage*              pHandle,\n          VkImageLayout*        pLayout,\n          VkImageCreateInfo*    pInfo) = 0;\n};\n\n\n/**\n * \\brief D3D9 image description\n */\nstruct D3D9VkExtImageDesc {\n  D3DRESOURCETYPE     Type;               // Can be SURFACE, TEXTURE, CUBETEXTURE, VOLUMETEXTURE\n  UINT                Width;\n  UINT                Height;\n  UINT                Depth;              // Can be > 1 for VOLUMETEXTURE\n  UINT                MipLevels;          // Can be > 1 for TEXTURE, CUBETEXTURE, VOLUMETEXTURE\n  DWORD               Usage;\n  D3DFORMAT           Format;\n  D3DPOOL             Pool;\n  D3DMULTISAMPLE_TYPE MultiSample;        // Must be NONE unless Type is SURFACE\n  DWORD               MultiSampleQuality;\n  bool                Discard;            // Depth stencils only\n  bool                IsAttachmentOnly;   // If false, then VK_IMAGE_USAGE_SAMPLED_BIT will be added\n  bool                IsLockable;\n  VkImageUsageFlags   ImageUsage;         // Additional image usage flags\n};\n\n/**\n * \\brief D3D9 device interface for Vulkan interop\n *\n * Provides access to the device and instance handles\n * as well as the queue that is used for rendering.\n */\nMIDL_INTERFACE(\"2eaa4b89-0107-4bdb-87f7-0f541c493ce0\")\nID3D9VkInteropDevice : public IUnknown {\n  /**\n   * \\brief Queries Vulkan handles used by DXVK\n   * \n   * \\param [out] pInstance The Vulkan instance\n   * \\param [out] pPhysDev The physical device\n   * \\param [out] pDevide The device handle\n   */\n  virtual void STDMETHODCALLTYPE GetVulkanHandles(\n          VkInstance*           pInstance,\n          VkPhysicalDevice*     pPhysDev,\n          VkDevice*             pDevice) = 0;\n\n  /**\n   * \\brief Queries the rendering queue used by DXVK\n   * \n   * \\param [out] pQueue The Vulkan queue handle\n   * \\param [out] pQueueIndex Queue index\n   * \\param [out] pQueueFamilyIndex Queue family index\n   */\n  virtual void STDMETHODCALLTYPE GetSubmissionQueue(\n          VkQueue*              pQueue,\n          uint32_t*             pQueueIndex,\n          uint32_t*             pQueueFamilyIndex) = 0;\n\n  /**\n   * \\brief Transitions a Texture to a given layout\n   * \n   * Executes an explicit image layout transition on the\n   * D3D device. Note that the image subresources \\e must\n   * be transitioned back to its original layout before\n   * using it again from D3D9.\n   * Synchronization is left up to the caller.\n   * This function merely emits a call to transition the\n   * texture on the DXVK internal command stream.\n   * \\param [in] pTexture The image to transform\n   * \\param [in] pSubresources Subresources to transform\n   * \\param [in] OldLayout Current image layout\n   * \\param [in] NewLayout Desired image layout\n   */\n  virtual void STDMETHODCALLTYPE TransitionTextureLayout(\n          ID3D9VkInteropTexture*    pTexture,\n    const VkImageSubresourceRange*  pSubresources,\n          VkImageLayout             OldLayout,\n          VkImageLayout             NewLayout) = 0;\n\n  /**\n   * \\brief Flushes outstanding D3D rendering commands\n   * \n   * Must be called before submitting Vulkan commands\n   * to the rendering queue if those commands use the\n   * backing resource of a D3D9 object.\n   */\n  virtual void STDMETHODCALLTYPE FlushRenderingCommands() = 0;\n  \n  /**\n   * \\brief Locks submission queue\n   * \n   * Should be called immediately before submitting\n   * Vulkan commands to the rendering queue in order\n   * to prevent DXVK from using the queue.\n   * \n   * While the submission queue is locked, no D3D9\n   * methods must be called from the locking thread,\n   * or otherwise a deadlock might occur.\n   */\n  virtual void STDMETHODCALLTYPE LockSubmissionQueue() = 0;\n  \n  /**\n   * \\brief Releases submission queue\n   * \n   * Should be called immediately after submitting\n   * Vulkan commands to the rendering queue in order\n   * to allow DXVK to submit new commands.\n   */\n  virtual void STDMETHODCALLTYPE ReleaseSubmissionQueue() = 0;\n\n  /**\n   * \\brief Locks the device\n   * \n   * Can be called to ensure no D3D9 device methods\n   * can be executed until UnlockDevice has been called.\n   * \n   * This will do nothing if the D3DCREATE_MULTITHREADED\n   * is not set.\n   */\n  virtual void STDMETHODCALLTYPE LockDevice() = 0;\n  \n  /**\n   * \\brief Unlocks the device\n   * \n   * Must only be called after a call to LockDevice.\n   */\n  virtual void STDMETHODCALLTYPE UnlockDevice() = 0;\n\n  /**\n   * \\brief Wait for a resource to finish being used\n   * \n   * Waits for the GPU resource associated with the\n   * resource to finish being used by the GPU.\n   * \n   * Valid D3DLOCK flags:\n   *  - D3DLOCK_READONLY:  Only waits for writes\n   *  - D3DLOCK_DONOTWAIT: Does not wait for the resource (may flush)\n   * \n   * \\param [in] pResource Resource to be waited upon\n   * \\param [in] MapFlags D3DLOCK flags\n   * \\returns true if the resource is ready to use,\n   *          false if the resource is till in use\n   */\n  virtual bool STDMETHODCALLTYPE WaitForResource(\n          IDirect3DResource9*  pResource,\n          DWORD                MapFlags) = 0;\n\n  /**\n   * \\brief Creates a custom image/surface/texture\n   * \n   * \\param [in] desc Image description\n   * \\param [out, retval] ppResult Pointer to a resource of the D3DRESOURCETYPE given by desc.Type\n   * \\returns D3D_OK, D3DERR_INVALIDCALL, or D3DERR_OUTOFVIDEOMEMORY\n   */\n  virtual HRESULT STDMETHODCALLTYPE CreateImage(\n          const D3D9VkExtImageDesc* desc,\n          IDirect3DResource9**      ppResult) = 0;\n};\n\n/**\n * \\brief D3D9 current output metadata\n */\nstruct D3D9VkExtOutputMetadata {\n  float RedPrimary[2];\n  float GreenPrimary[2];\n  float BluePrimary[2];\n  float WhitePoint[2];\n  float MinLuminance;\n  float MaxLuminance;\n  float MaxFullFrameLuminance;\n};\n\n/**\n * \\brief D3D9 extended swapchain\n */\nMIDL_INTERFACE(\"13776e93-4aa9-430a-a4ec-fe9e281181d5\")\nID3D9VkExtSwapchain : public IUnknown {\n  virtual BOOL STDMETHODCALLTYPE CheckColorSpaceSupport(\n          VkColorSpaceKHR           ColorSpace) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetColorSpace(\n          VkColorSpaceKHR           ColorSpace) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetHDRMetaData(\n    const VkHdrMetadataEXT          *pHDRMetadata) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetCurrentOutputDesc(\n          D3D9VkExtOutputMetadata   *pOutputDesc) = 0;\n\n  virtual void STDMETHODCALLTYPE UnlockAdditionalFormats() = 0;\n};\n\n/**\n * \\brief D3D9 extended interface\n */\nMIDL_INTERFACE(\"65b55086-e3e3-4c3e-b3a0-86815cce2c4c\")\nID3D9VkExtInterface: public IUnknown {\n  virtual void STDMETHODCALLTYPE UnlockAdditionalFormats() = 0;\n};\n\n#ifndef _MSC_VER\n__CRT_UUID_DECL(ID3D9VkInteropInterface,   0x3461a81b,0xce41,0x485b,0xb6,0xb5,0xfc,0xf0,0x8b,0xa6,0xa6,0xbd);\n__CRT_UUID_DECL(ID3D9VkInteropInterface1,  0xd6589ed4,0x7a37,0x4096,0xba,0xc2,0x22,0x3b,0x25,0xae,0x31,0xd2);\n__CRT_UUID_DECL(ID3D9VkInteropTexture,     0xd56344f5,0x8d35,0x46fd,0x80,0x6d,0x94,0xc3,0x51,0xb4,0x72,0xc1);\n__CRT_UUID_DECL(ID3D9VkInteropDevice,      0x2eaa4b89,0x0107,0x4bdb,0x87,0xf7,0x0f,0x54,0x1c,0x49,0x3c,0xe0);\n__CRT_UUID_DECL(ID3D9VkExtSwapchain,       0x13776e93,0x4aa9,0x430a,0xa4,0xec,0xfe,0x9e,0x28,0x11,0x81,0xd5);\n__CRT_UUID_DECL(ID3D9VkExtInterface,       0x65b55086,0xe3e3,0x4c3e,0xb3,0xa0,0x86,0x81,0x5c,0xce,0x2c,0x4c);\n#endif\n"
  },
  {
    "path": "src/d3d9/d3d9_interop.cpp",
    "content": "#include \"d3d9_interop.h\"\n#include \"d3d9_interface.h\"\n#include \"d3d9_common_texture.h\"\n#include \"d3d9_device.h\"\n#include \"d3d9_texture.h\"\n#include \"d3d9_buffer.h\"\n#include \"d3d9_initializer.h\"\n\nnamespace dxvk {\n\n  ////////////////////////////////\n  // Interface Interop\n  ///////////////////////////////\n\n  D3D9VkInteropInterface::D3D9VkInteropInterface(\n          D3D9InterfaceEx*      pInterface)\n  : m_interface(pInterface), m_extensions(pInterface->GetInstance()->getExtensionList()) {\n\n  }\n\n  D3D9VkInteropInterface::~D3D9VkInteropInterface() {\n\n  }\n\n  ULONG STDMETHODCALLTYPE D3D9VkInteropInterface::AddRef() {\n    return m_interface->AddRef();\n  }\n  \n  ULONG STDMETHODCALLTYPE D3D9VkInteropInterface::Release() {\n    return m_interface->Release();\n  }\n  \n  HRESULT STDMETHODCALLTYPE D3D9VkInteropInterface::QueryInterface(\n          REFIID                riid,\n          void**                ppvObject) {\n    return m_interface->QueryInterface(riid, ppvObject);\n  }\n\n  void STDMETHODCALLTYPE D3D9VkInteropInterface::GetInstanceHandle(\n          VkInstance*           pInstance) {\n    if (pInstance != nullptr)\n      *pInstance = m_interface->GetInstance()->handle();\n  }\n\n  void STDMETHODCALLTYPE D3D9VkInteropInterface::GetPhysicalDeviceHandle(\n          UINT                  Adapter,\n          VkPhysicalDevice*     pPhysicalDevice) {\n    if (pPhysicalDevice != nullptr) {\n      D3D9Adapter* adapter = m_interface->GetAdapter(Adapter);\n      *pPhysicalDevice = adapter ? adapter->GetDXVKAdapter()->handle() : nullptr;\n    }\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9VkInteropInterface::GetInstanceExtensions(\n          UINT* pExtensionCount, const char** ppExtensions) {\n    if (pExtensionCount == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    if (!ppExtensions) {\n      *pExtensionCount = m_extensions.size();\n      return D3D_OK;\n    }\n\n    UINT count = 0;\n    UINT maxCount = *pExtensionCount;\n\n    for (uint32_t i = 0; i < m_extensions.size() && i < maxCount; i++) {\n      ppExtensions[i] = m_extensions[i].extensionName;\n      count++;\n    }\n\n    *pExtensionCount = count;\n    return (count < maxCount) ? D3DERR_MOREDATA : D3D_OK;\n  }\n\n  ////////////////////////////////\n  // Texture Interop\n  ///////////////////////////////\n\n  D3D9VkInteropTexture::D3D9VkInteropTexture(\n          IUnknown*             pInterface,\n          D3D9CommonTexture*    pTexture)\n    : m_interface(pInterface)\n    , m_texture  (pTexture) {\n\n  }\n\n  D3D9VkInteropTexture::~D3D9VkInteropTexture() {\n\n  }\n\n  ULONG STDMETHODCALLTYPE D3D9VkInteropTexture::AddRef() {\n    return m_interface->AddRef();\n  }\n  \n  ULONG STDMETHODCALLTYPE D3D9VkInteropTexture::Release() {\n    return m_interface->Release();\n  }\n  \n  HRESULT STDMETHODCALLTYPE D3D9VkInteropTexture::QueryInterface(\n          REFIID                riid,\n          void**                ppvObject) {\n    return m_interface->QueryInterface(riid, ppvObject);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9VkInteropTexture::GetVulkanImageInfo(\n          VkImage*              pHandle,\n          VkImageLayout*        pLayout,\n          VkImageCreateInfo*    pInfo) {\n    const Rc<DxvkImage> image = m_texture->GetImage();\n    \n    if (unlikely(!image)) {\n      if (pHandle != nullptr)\n        *pHandle = VK_NULL_HANDLE;\n\n      if (pLayout != nullptr)\n        *pLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n      return D3DERR_NOTFOUND;\n    }\n\n    const DxvkImageCreateInfo& info = image->info();\n    \n    if (pHandle != nullptr)\n      *pHandle = image->handle();\n    \n    if (pLayout != nullptr)\n      *pLayout = info.layout;\n    \n    if (pInfo != nullptr) {\n      // We currently don't support any extended structures\n      if (pInfo->sType != VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO\n       || pInfo->pNext != nullptr)\n        return D3DERR_INVALIDCALL;\n      \n      pInfo->flags          = 0;\n      pInfo->imageType      = info.type;\n      pInfo->format         = info.format;\n      pInfo->extent         = info.extent;\n      pInfo->mipLevels      = info.mipLevels;\n      pInfo->arrayLayers    = info.numLayers;\n      pInfo->samples        = info.sampleCount;\n      pInfo->tiling         = info.tiling;\n      pInfo->usage          = info.usage;\n      pInfo->sharingMode    = VK_SHARING_MODE_EXCLUSIVE;\n      pInfo->queueFamilyIndexCount = 0;\n      pInfo->initialLayout  = VK_IMAGE_LAYOUT_UNDEFINED;\n    }\n    \n    return S_OK;\n  }\n\n  ////////////////////////////////\n  // Device Interop\n  ///////////////////////////////\n\n  D3D9VkInteropDevice::D3D9VkInteropDevice(\n          D3D9DeviceEx*         pInterface)\n    : m_device(pInterface) {\n\n  }\n\n  D3D9VkInteropDevice::~D3D9VkInteropDevice() {\n\n  }\n\n  ULONG STDMETHODCALLTYPE D3D9VkInteropDevice::AddRef() {\n    return m_device->AddRef();\n  }\n  \n  ULONG STDMETHODCALLTYPE D3D9VkInteropDevice::Release() {\n    return m_device->Release();\n  }\n  \n  HRESULT STDMETHODCALLTYPE D3D9VkInteropDevice::QueryInterface(\n          REFIID                riid,\n          void**                ppvObject) {\n    return m_device->QueryInterface(riid, ppvObject);\n  }\n\n  void STDMETHODCALLTYPE D3D9VkInteropDevice::GetVulkanHandles(\n          VkInstance*           pInstance,\n          VkPhysicalDevice*     pPhysDev,\n          VkDevice*             pDevice) {\n    auto device   = m_device->GetDXVKDevice();\n    auto adapter  = device->adapter();\n    auto instance = device->instance();\n    \n    if (pDevice != nullptr)\n      *pDevice = device->handle();\n    \n    if (pPhysDev != nullptr)\n      *pPhysDev = adapter->handle();\n    \n    if (pInstance != nullptr)\n      *pInstance = instance->handle();\n  }\n\n  void STDMETHODCALLTYPE D3D9VkInteropDevice::GetSubmissionQueue(\n          VkQueue*              pQueue,\n          uint32_t*             pQueueIndex,\n          uint32_t*             pQueueFamilyIndex) {\n    auto device = m_device->GetDXVKDevice();\n    DxvkDeviceQueue queue = device->queues().graphics;\n    \n    if (pQueue != nullptr)\n      *pQueue = queue.queueHandle;\n    \n    if (pQueueIndex != nullptr)\n      *pQueueIndex = queue.queueIndex;\n    \n    if (pQueueFamilyIndex != nullptr)\n      *pQueueFamilyIndex = queue.queueFamily;\n  }\n\n  void STDMETHODCALLTYPE D3D9VkInteropDevice::TransitionTextureLayout(\n          ID3D9VkInteropTexture*    pTexture,\n    const VkImageSubresourceRange*  pSubresources,\n          VkImageLayout             OldLayout,\n          VkImageLayout             NewLayout) {\n    auto texture = static_cast<D3D9VkInteropTexture *>(pTexture)->GetCommonTexture();\n\n    m_device->EmitCs([\n      cImage        = texture->GetImage(),\n      cSubresources = *pSubresources,\n      cOldLayout    = OldLayout,\n      cNewLayout    = NewLayout\n    ] (DxvkContext* ctx) {\n      ctx->transformImage(\n        cImage, cSubresources,\n        cOldLayout, cNewLayout);\n    });\n  }\n\n  void STDMETHODCALLTYPE D3D9VkInteropDevice::FlushRenderingCommands() {\n    m_device->Flush();\n    m_device->SynchronizeCsThread(DxvkCsThread::SynchronizeAll);\n  }\n\n  void STDMETHODCALLTYPE D3D9VkInteropDevice::LockSubmissionQueue() {\n    m_device->GetDXVKDevice()->lockSubmission();\n  }\n\n  void STDMETHODCALLTYPE D3D9VkInteropDevice::ReleaseSubmissionQueue() {\n    m_device->GetDXVKDevice()->unlockSubmission();\n  }\n\n  void STDMETHODCALLTYPE D3D9VkInteropDevice::LockDevice() {\n    m_lock = m_device->LockDevice();\n  }\n  \n  void STDMETHODCALLTYPE D3D9VkInteropDevice::UnlockDevice() {\n    m_lock = D3D9DeviceLock();\n  }\n\n  static Rc<DxvkPagedResource> GetDxvkResource(IDirect3DResource9 *pResource) {\n    switch (pResource->GetType()) {\n      case D3DRTYPE_SURFACE:       return static_cast<D3D9Surface*>     (pResource)->GetCommonTexture()->GetImage();\n      // Does not inherit from IDirect3DResource9... lol.\n      //case D3DRTYPE_VOLUME:        return static_cast<D3D9Volume*>      (pResource)->GetCommonTexture()->GetImage();\n      case D3DRTYPE_TEXTURE:       return static_cast<D3D9Texture2D*>   (pResource)->GetCommonTexture()->GetImage();\n      case D3DRTYPE_VOLUMETEXTURE: return static_cast<D3D9Texture3D*>   (pResource)->GetCommonTexture()->GetImage();\n      case D3DRTYPE_CUBETEXTURE:   return static_cast<D3D9TextureCube*> (pResource)->GetCommonTexture()->GetImage();\n      case D3DRTYPE_VERTEXBUFFER:  return static_cast<D3D9VertexBuffer*>(pResource)->GetCommonBuffer()->GetBuffer<D3D9_COMMON_BUFFER_TYPE_REAL>();\n      case D3DRTYPE_INDEXBUFFER:   return static_cast<D3D9IndexBuffer*> (pResource)->GetCommonBuffer()->GetBuffer<D3D9_COMMON_BUFFER_TYPE_REAL>();\n      default:                     return nullptr;\n    }\n  }\n\n  bool STDMETHODCALLTYPE D3D9VkInteropDevice::WaitForResource(\n          IDirect3DResource9*  pResource,\n          DWORD                MapFlags) {\n    return m_device->WaitForResource(*GetDxvkResource(pResource), DxvkCsThread::SynchronizeAll, MapFlags);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9VkInteropDevice::CreateImage(\n          const D3D9VkExtImageDesc* params,\n          IDirect3DResource9**      ppResult) {\n    InitReturnPtr(ppResult);\n\n    if (unlikely(ppResult == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(params == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    /////////////////////////////\n    // Image desc validation\n\n    // Cannot create a volume by itself, use D3DRTYPE_VOLUMETEXTURE\n    if (unlikely(params->Type == D3DRTYPE_VOLUME))\n      return D3DERR_INVALIDCALL;\n\n    // Only allowed: SURFACE, TEXTURE, CUBETEXTURE, VOLUMETEXTURE\n    if (unlikely(params->Type < D3DRTYPE_SURFACE || params->Type > D3DRTYPE_CUBETEXTURE))\n      return D3DERR_INVALIDCALL;\n\n    // Only volume textures can have depth > 1\n    if (unlikely(params->Type != D3DRTYPE_VOLUMETEXTURE && params->Depth > 1))\n      return D3DERR_INVALIDCALL;\n\n    if (params->Type == D3DRTYPE_SURFACE) {\n      // Surfaces can only have 1 mip level\n      if (unlikely(params->MipLevels > 1))\n        return D3DERR_INVALIDCALL;\n\n      if (unlikely(params->MultiSample > D3DMULTISAMPLE_16_SAMPLES))\n        return D3DERR_INVALIDCALL;\n    } else {\n      // Textures can't be multisampled\n      if (unlikely(params->MultiSample != D3DMULTISAMPLE_NONE))\n        return D3DERR_INVALIDCALL;\n    }\n\n    D3D9_COMMON_TEXTURE_DESC desc;\n    desc.Width              = params->Width;\n    desc.Height             = params->Height;\n    desc.Depth              = params->Depth;\n    desc.ArraySize          = params->Type == D3DRTYPE_CUBETEXTURE ? 6 : 1;\n    desc.MipLevels          = params->MipLevels;\n    desc.Usage              = params->Usage;\n    desc.Format             = EnumerateFormat(params->Format);\n    desc.Pool               = params->Pool;\n    desc.Discard            = params->Discard;\n    desc.MultiSample        = params->MultiSample;\n    desc.MultisampleQuality = params->MultiSampleQuality;\n    desc.IsBackBuffer       = FALSE;\n    desc.IsAttachmentOnly   = params->IsAttachmentOnly;\n    desc.IsLockable         = params->IsLockable;\n    desc.ImageUsage         = params->ImageUsage;\n    \n    D3DRESOURCETYPE textureType = params->Type == D3DRTYPE_SURFACE ? D3DRTYPE_TEXTURE : params->Type;\n\n    HRESULT hr = D3D9CommonTexture::NormalizeTextureProperties(m_device, textureType, &desc);\n    if (FAILED(hr))\n      return hr;\n\n    switch (params->Type) {\n      case D3DRTYPE_SURFACE:\n        return CreateTextureResource<D3D9Surface>(desc, ppResult);\n\n      case D3DRTYPE_TEXTURE:\n        return CreateTextureResource<D3D9Texture2D>(desc, ppResult);\n\n      case D3DRTYPE_VOLUMETEXTURE:\n        return CreateTextureResource<D3D9Texture3D>(desc, ppResult);\n\n      case D3DRTYPE_CUBETEXTURE:\n        return CreateTextureResource<D3D9TextureCube>(desc, ppResult);\n\n      default:\n        return D3DERR_INVALIDCALL;\n    }\n  }\n\n  template <typename ResourceType>\n  HRESULT D3D9VkInteropDevice::CreateTextureResource(\n          const D3D9_COMMON_TEXTURE_DESC& desc,\n          IDirect3DResource9**            ppResult) {\n    try {\n      const Com<ResourceType> texture = new ResourceType(m_device, &desc, m_device->IsExtended());\n      m_device->m_initializer->InitTexture(texture->GetCommonTexture());\n      *ppResult = texture.ref();\n\n      if (desc.Pool == D3DPOOL_DEFAULT)\n        m_device->m_losableResourceCounter++;\n\n      return D3D_OK;\n    }\n    catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return D3DERR_OUTOFVIDEOMEMORY;\n    }\n  }\n\n  D3D9VkExtInterface::D3D9VkExtInterface(D3D9InterfaceEx *pInterface)\n    : m_interface(pInterface) {\n\n  }\n  \n  ULONG STDMETHODCALLTYPE D3D9VkExtInterface::AddRef() {\n    return m_interface->AddRef();\n  }\n  \n  ULONG STDMETHODCALLTYPE D3D9VkExtInterface::Release() {\n    return m_interface->Release();\n  }\n  \n  HRESULT STDMETHODCALLTYPE D3D9VkExtInterface::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_interface->QueryInterface(riid, ppvObject);\n  }\n\n  void STDMETHODCALLTYPE D3D9VkExtInterface::UnlockAdditionalFormats() {\n    m_interface->EnableAdditionalFormats();\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_interop.h",
    "content": "#pragma once\n\n#include \"d3d9_interfaces.h\"\n#include \"d3d9_multithread.h\"\n\n#include \"../dxvk/dxvk_extension_provider.h\"\n\nnamespace dxvk {\n\n  class D3D9InterfaceEx;\n  class D3D9CommonTexture;\n  class D3D9DeviceEx;\n  struct D3D9_COMMON_TEXTURE_DESC;\n\n  class D3D9VkInteropInterface final : public ID3D9VkInteropInterface1 {\n\n  public:\n\n    D3D9VkInteropInterface(\n            D3D9InterfaceEx*      pInterface);\n\n    ~D3D9VkInteropInterface();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject);\n\n    void STDMETHODCALLTYPE GetInstanceHandle(\n            VkInstance*           pInstance);\n\n    void STDMETHODCALLTYPE GetPhysicalDeviceHandle(\n            UINT                  Adapter,\n            VkPhysicalDevice*     pPhysicalDevice);\n\n    HRESULT STDMETHODCALLTYPE GetInstanceExtensions(\n            UINT*                 pExtensionCount,\n      const char**                ppExtensions);\n\n  private:\n\n    D3D9InterfaceEx* m_interface;\n    DxvkExtensionList m_extensions = { };\n\n  };\n\n  class D3D9VkInteropTexture final : public ID3D9VkInteropTexture {\n\n  public:\n\n    D3D9VkInteropTexture(\n            IUnknown*             pInterface,\n            D3D9CommonTexture*    pTexture);\n\n    ~D3D9VkInteropTexture();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetVulkanImageInfo(\n            VkImage*              pHandle,\n            VkImageLayout*        pLayout,\n            VkImageCreateInfo*    pInfo);\n\n    D3D9CommonTexture* GetCommonTexture() { return m_texture; }\n\n  private:\n\n    IUnknown*          m_interface;\n    D3D9CommonTexture* m_texture;\n\n  };\n\n  class D3D9VkInteropDevice final : public ID3D9VkInteropDevice {\n\n  public:\n\n    D3D9VkInteropDevice(\n            D3D9DeviceEx*         pInterface);\n\n    ~D3D9VkInteropDevice();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject);\n\n    void STDMETHODCALLTYPE GetVulkanHandles(\n            VkInstance*           pInstance,\n            VkPhysicalDevice*     pPhysDev,\n            VkDevice*             pDevice);\n\n    void STDMETHODCALLTYPE GetSubmissionQueue(\n            VkQueue*              pQueue,\n            uint32_t*             pQueueIndex,\n            uint32_t*             pQueueFamilyIndex);\n\n    void STDMETHODCALLTYPE TransitionTextureLayout(\n            ID3D9VkInteropTexture*    pTexture,\n      const VkImageSubresourceRange*  pSubresources,\n            VkImageLayout             OldLayout,\n            VkImageLayout             NewLayout);\n\n    void STDMETHODCALLTYPE FlushRenderingCommands();\n\n    void STDMETHODCALLTYPE LockSubmissionQueue();\n\n    void STDMETHODCALLTYPE ReleaseSubmissionQueue();\n\n    void STDMETHODCALLTYPE LockDevice();\n    \n    void STDMETHODCALLTYPE UnlockDevice();\n\n    bool STDMETHODCALLTYPE WaitForResource(\n            IDirect3DResource9*  pResource,\n            DWORD                MapFlags);\n\n    HRESULT STDMETHODCALLTYPE CreateImage(\n            const D3D9VkExtImageDesc* desc,\n            IDirect3DResource9**      ppResult);\n\n  private:\n\n    template <typename ResourceType>\n    HRESULT CreateTextureResource(\n            const D3D9_COMMON_TEXTURE_DESC& desc,\n            IDirect3DResource9**            ppResult);\n\n    D3D9DeviceEx*  m_device;\n    D3D9DeviceLock m_lock;\n\n  };\n\n  class D3D9VkExtInterface final : public ID3D9VkExtInterface {\n\n  public:\n\n    D3D9VkExtInterface(D3D9InterfaceEx *pInterface);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);\n\n    void STDMETHODCALLTYPE UnlockAdditionalFormats();\n\n  private:\n    D3D9InterfaceEx *m_interface;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_main.cpp",
    "content": "#include \"../dxvk/dxvk_instance.h\"\n\n#include \"d3d9_interface.h\"\n#include \"d3d9_shader_validator.h\"\n\n#include \"d3d9_annotation.h\"\n\nclass D3DFE_PROCESSVERTICES;\nusing PSGPERRORID = UINT;\n\nnamespace dxvk {\n  Logger Logger::s_instance(\"d3d9.log\");\n  D3D9GlobalAnnotationList D3D9GlobalAnnotationList::s_instance;\n\n  HRESULT CreateD3D9(\n          bool           Extended,\n          IDirect3D9Ex** ppDirect3D9Ex,\n    const D3D9ON12_ARGS* pOverrideList,\n          uint32_t       OverrideCount) {\n    if (!ppDirect3D9Ex)\n      return D3DERR_INVALIDCALL;\n\n    *ppDirect3D9Ex = ref(new D3D9InterfaceEx(Extended, pOverrideList, OverrideCount));\n    return D3D_OK;\n  }\n}\n\n\nextern \"C\" {\n\n  DLLEXPORT IDirect3D9* __stdcall Direct3DCreate9(UINT nSDKVersion) {\n    IDirect3D9Ex* pDirect3D = nullptr;\n    dxvk::CreateD3D9(false, &pDirect3D, nullptr, 0);\n\n    return pDirect3D;\n  }\n\n  DLLEXPORT HRESULT __stdcall Direct3DCreate9Ex(UINT nSDKVersion, IDirect3D9Ex** ppDirect3D9Ex) {\n    return dxvk::CreateD3D9(true, ppDirect3D9Ex, nullptr, 0);\n  }\n\n  DLLEXPORT int __stdcall D3DPERF_BeginEvent(D3DCOLOR col, LPCWSTR wszName) {\n    return dxvk::D3D9GlobalAnnotationList::Instance().BeginEvent(col, wszName);\n  }\n\n  DLLEXPORT int __stdcall D3DPERF_EndEvent(void) {\n    return dxvk::D3D9GlobalAnnotationList::Instance().EndEvent();\n  }\n\n  DLLEXPORT void __stdcall D3DPERF_SetMarker(D3DCOLOR col, LPCWSTR wszName) {\n    dxvk::D3D9GlobalAnnotationList::Instance().SetMarker(col, wszName);\n  }\n\n  DLLEXPORT void __stdcall D3DPERF_SetRegion(D3DCOLOR col, LPCWSTR wszName) {\n    dxvk::D3D9GlobalAnnotationList::Instance().SetRegion(col, wszName);\n  }\n\n  DLLEXPORT BOOL __stdcall D3DPERF_QueryRepeatFrame(void) {\n    return dxvk::D3D9GlobalAnnotationList::Instance().QueryRepeatFrame();\n  }\n\n  DLLEXPORT void __stdcall D3DPERF_SetOptions(DWORD dwOptions) {\n    dxvk::D3D9GlobalAnnotationList::Instance().SetOptions(dwOptions);\n  }\n\n  DLLEXPORT DWORD __stdcall D3DPERF_GetStatus(void) {\n    return dxvk::D3D9GlobalAnnotationList::Instance().GetStatus();\n  }\n\n\n  DLLEXPORT void __stdcall DebugSetMute(void) {\n  }\n\n  DLLEXPORT int __stdcall DebugSetLevel(void) {\n    return 0;\n  }\n\n  // Processor Specific Geometry Pipeline\n  // for P3 SIMD/AMD 3DNow.\n\n  DLLEXPORT void __stdcall PSGPError(D3DFE_PROCESSVERTICES* a, PSGPERRORID b, UINT c) {\n  }\n\n  DLLEXPORT void __stdcall PSGPSampleTexture(D3DFE_PROCESSVERTICES* a, UINT b, float(*const c)[4], UINT d, float(*const e)[4]) {\n  }\n\n  DLLEXPORT dxvk::D3D9ShaderValidator* __stdcall Direct3DShaderValidatorCreate9(void) {\n    return ref(new dxvk::D3D9ShaderValidator());\n  }\n\n  DLLEXPORT int __stdcall Direct3D9EnableMaximizedWindowedModeShim(UINT a) {\n    return 0;\n  }\n\n  DLLEXPORT void __stdcall DXVK_RegisterAnnotation(IDXVKUserDefinedAnnotation* annotation) {\n    dxvk::D3D9GlobalAnnotationList::Instance().RegisterAnnotator(annotation);\n  }\n\n  DLLEXPORT void __stdcall DXVK_UnRegisterAnnotation(IDXVKUserDefinedAnnotation* annotation) {\n    dxvk::D3D9GlobalAnnotationList::Instance().UnregisterAnnotator(annotation);\n  }\n\n  DLLEXPORT void __stdcall Direct3D9ForceHybridEnumeration(UINT uHybrid) {\n  }\n\n  DLLEXPORT IDirect3D9* __stdcall Direct3DCreate9On12(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count) {\n    dxvk::Logger::warn(\"Direct3DCreate9On12: 9On12 functionality is unimplemented.\");\n\n    IDirect3D9Ex* pDirect3D = nullptr;\n    dxvk::CreateD3D9(false, &pDirect3D, override_list, override_entry_count);\n\n    return pDirect3D;\n  }\n\n  DLLEXPORT HRESULT __stdcall Direct3DCreate9On12Ex(UINT sdk_version, D3D9ON12_ARGS* override_list, UINT override_entry_count, IDirect3D9Ex** output) {\n    dxvk::Logger::warn(\"Direct3DCreate9On12Ex: 9On12 functionality is unimplemented.\");\n    return dxvk::CreateD3D9(true, output, override_list, override_entry_count);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_mem.cpp",
    "content": "#include \"d3d9_mem.h\"\n#include \"../util/util_string.h\"\n#include \"../util/util_math.h\"\n#include \"../util/log/log.h\"\n#include \"../util/util_likely.h\"\n#include <utility>\n#include <algorithm>\n\n#ifdef D3D9_ALLOW_UNMAPPING\n#include <sysinfoapi.h>\n#else\n#include <stdlib.h>\n#endif\n\nnamespace dxvk {\n\n#ifdef D3D9_ALLOW_UNMAPPING\n  D3D9MemoryAllocator::D3D9MemoryAllocator() {\n    SYSTEM_INFO sysInfo;\n    GetSystemInfo(&sysInfo);\n    m_allocationGranularity = sysInfo.dwAllocationGranularity;\n    m_mappingGranularity = m_allocationGranularity * 16;\n  }\n\n  D3D9Memory D3D9MemoryAllocator::Alloc(uint32_t Size) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    uint32_t alignedSize = align(Size, CACHE_LINE_SIZE);\n    for (auto& chunk : m_chunks) {\n      D3D9Memory memory = chunk->AllocLocked(alignedSize);\n      if (memory) {\n        m_usedMemory += memory.GetSize();\n        return memory;\n      }\n    }\n\n    uint32_t chunkSize = std::max(D3D9ChunkSize, alignedSize);\n    m_allocatedMemory += chunkSize;\n\n    D3D9MemoryChunk* chunk = new D3D9MemoryChunk(this, chunkSize);\n    std::unique_ptr<D3D9MemoryChunk> uniqueChunk(chunk);\n    D3D9Memory memory = uniqueChunk->AllocLocked(alignedSize);\n    m_usedMemory += memory.GetSize();\n\n    m_chunks.push_back(std::move(uniqueChunk));\n    return memory;\n  }\n\n  void D3D9MemoryAllocator::Free(D3D9Memory *Memory) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    D3D9MemoryChunk* chunk = Memory->GetChunk();\n    chunk->FreeLocked(Memory);\n    m_usedMemory -= Memory->GetSize();\n    if (chunk->IsEmpty())\n      FreeChunk(chunk);\n  }\n\n  void D3D9MemoryAllocator::FreeChunk(D3D9MemoryChunk *Chunk) {\n    // Has to be called in the lock\n\n    m_allocatedMemory -= Chunk->Size();\n\n    m_chunks.erase(std::remove_if(m_chunks.begin(), m_chunks.end(), [&](auto& item) {\n        return item.get() == Chunk;\n    }), m_chunks.end());\n  }\n\n  void* D3D9MemoryAllocator::Map(D3D9Memory* Memory) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    D3D9MemoryChunk* chunk = Memory->GetChunk();\n    uint32_t memoryMapped;\n    void* ptr = chunk->MapLocked(Memory, memoryMapped);\n    m_mappedMemory += memoryMapped;\n    return ptr;\n  }\n\n  void D3D9MemoryAllocator::Unmap(D3D9Memory* Memory) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    D3D9MemoryChunk* chunk = Memory->GetChunk();\n    m_mappedMemory -= chunk->UnmapLocked(Memory);\n  }\n\n  uint32_t D3D9MemoryAllocator::MappedMemory() const {\n    return m_mappedMemory.load();\n  }\n\n  uint32_t D3D9MemoryAllocator::UsedMemory() const {\n    return m_usedMemory.load();\n  }\n\n  uint32_t D3D9MemoryAllocator::AllocatedMemory() const {\n    return m_allocatedMemory.load();\n  }\n\n  D3D9MemoryChunk::D3D9MemoryChunk(D3D9MemoryAllocator* Allocator, uint32_t Size)\n    : m_allocator ( Allocator )\n    , m_size ( Size )\n    , m_mapping ( CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE | SEC_COMMIT, 0, Size, nullptr) ) {\n    m_freeRanges.push_back({ 0, Size });\n    uint32_t mappingGranularity = Allocator->MappingGranularity();\n    m_mappingRanges.resize(((Size + mappingGranularity - 1) / mappingGranularity));\n  }\n\n  D3D9MemoryChunk::~D3D9MemoryChunk() {\n    // Has to be protected by the allocator lock\n\n    CloseHandle(m_mapping);\n  }\n\n  void* D3D9MemoryChunk::MapLocked(D3D9Memory* Memory, uint32_t& mappedSize) {\n    // Has to be protected by the allocator lock\n\n    mappedSize = 0;\n    uint32_t mappingGranularity = m_allocator->MappingGranularity();\n\n    uint32_t alignedOffset = alignDown(Memory->GetOffset(), mappingGranularity);\n    uint32_t alignmentDelta = Memory->GetOffset() - alignedOffset;\n    uint32_t alignedSize = Memory->GetSize() + alignmentDelta;\n    if (alignedSize > mappingGranularity) {\n      // The allocation crosses the boundary of the internal mapping page it's a part of\n      // so we map it on it's own.\n      alignedOffset = alignDown(Memory->GetOffset(), m_allocator->AllocationGranularity());\n      alignmentDelta = Memory->GetOffset() - alignedOffset;\n      alignedSize = Memory->GetSize() + alignmentDelta;\n\n      mappedSize = alignedSize;\n      uint8_t* basePtr = static_cast<uint8_t*>(MapViewOfFile(m_mapping, FILE_MAP_ALL_ACCESS, 0, alignedOffset, alignedSize));\n      if (unlikely(basePtr == nullptr)) {\n        DWORD error = GetLastError();\n        Logger::err(str::format(\"Mapping non-persisted file failed: \", error, \", Mapped memory: \", m_allocator->MappedMemory()));\n        return nullptr;\n      }\n      return basePtr + alignmentDelta;\n    }\n\n    // For small allocations we map the entire mapping page to minimize the overhead from having the align the offset to 65k bytes.\n    // This should hopefully also reduce the amount of MapViewOfFile calls we do for tiny allocations.\n    auto& mappingRange = m_mappingRanges[Memory->GetOffset() / mappingGranularity];\n    if (unlikely(mappingRange.refCount == 0)) {\n      mappedSize = mappingGranularity;\n      mappingRange.ptr = static_cast<uint8_t*>(MapViewOfFile(m_mapping, FILE_MAP_ALL_ACCESS, 0, alignedOffset, m_allocator->MappingGranularity()));\n      if (unlikely(mappingRange.ptr == nullptr)) {\n        DWORD error = GetLastError();\n        LPTSTR buffer = nullptr;\n        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPTSTR)&buffer, 0, nullptr);\n        Logger::err(str::format(\"Mapping non-persisted file failed: \", error, \", Mapped memory: \", m_allocator->MappedMemory(), \", Msg: \", buffer));\n        if (buffer) {\n          LocalFree(buffer);\n        }\n      }\n    }\n    mappingRange.refCount++;\n    uint8_t* basePtr = static_cast<uint8_t*>(mappingRange.ptr);\n    return basePtr + alignmentDelta;\n  }\n\n  uint32_t D3D9MemoryChunk::UnmapLocked(D3D9Memory* Memory) {\n    // Has to be protected by the allocator lock\n\n    uint32_t mappingGranularity = m_allocator->MappingGranularity();\n\n    uint32_t alignedOffset = alignDown(Memory->GetOffset(), mappingGranularity);\n    uint32_t alignmentDelta = Memory->GetOffset() - alignedOffset;\n    uint32_t alignedSize = Memory->GetSize() + alignmentDelta;\n    if (alignedSize > mappingGranularity) {\n      // Single use mapping\n      alignedOffset = alignDown(Memory->GetOffset(), m_allocator->AllocationGranularity());\n      alignmentDelta = Memory->GetOffset() - alignedOffset;\n      alignedSize = Memory->GetSize() + alignmentDelta;\n\n      uint8_t* basePtr = static_cast<uint8_t*>(Memory->Ptr()) - alignmentDelta;\n      UnmapViewOfFile(basePtr);\n      return alignedSize;\n    }\n    auto& mappingRange = m_mappingRanges[Memory->GetOffset() / mappingGranularity];\n    mappingRange.refCount--;\n    if (unlikely(mappingRange.refCount == 0)) {\n      UnmapViewOfFile(mappingRange.ptr);\n      mappingRange.ptr = nullptr;\n      return mappingGranularity;\n    }\n    return 0;\n  }\n\n  D3D9Memory D3D9MemoryChunk::AllocLocked(uint32_t Size) {\n    // Has to be protected by the allocator lock\n\n    uint32_t offset = 0;\n    uint32_t size = 0;\n\n    for (auto range = m_freeRanges.begin(); range != m_freeRanges.end(); range++) {\n      if (range->length >= Size) {\n        offset = range->offset;\n        size = Size;\n        range->offset += Size;\n        range->length -= Size;\n        if (range->length < (4 << 10)) {\n          size += range->length;\n          m_freeRanges.erase(range);\n        }\n        break;\n      }\n    }\n\n    if (size != 0)\n      return D3D9Memory(this, offset, Size);\n\n    return {};\n  }\n\n  void D3D9MemoryChunk::FreeLocked(D3D9Memory *Memory) {\n    // Has to be protected by the allocator lock\n\n    uint32_t offset = Memory->GetOffset();\n    uint32_t size = Memory->GetSize();\n\n    auto curr = m_freeRanges.begin();\n\n    // shamelessly stolen from dxvk_memory.cpp\n    while (curr != m_freeRanges.end()) {\n      if (curr->offset == offset + size) {\n        size += curr->length;\n        curr = m_freeRanges.erase(curr);\n      } else if (curr->offset + curr->length == offset) {\n        offset -= curr->length;\n        size += curr->length;\n        curr = m_freeRanges.erase(curr);\n      } else {\n        curr++;\n      }\n    }\n\n    m_freeRanges.push_back({ offset, size });\n  }\n\n  bool D3D9MemoryChunk::IsEmpty() const {\n    // Has to be protected by the allocator lock\n\n    return m_freeRanges.size() == 1\n        && m_freeRanges[0].length == m_size;\n  }\n\n  D3D9MemoryAllocator* D3D9MemoryChunk::Allocator() const {\n    return m_allocator;\n  }\n\n\n  D3D9Memory::D3D9Memory(D3D9MemoryChunk* Chunk, size_t Offset, size_t Size)\n    : m_chunk(Chunk), m_offset(Offset), m_size(Size) {}\n\n  D3D9Memory::D3D9Memory(D3D9Memory&& other)\n    : m_chunk(std::exchange(other.m_chunk, nullptr)),\n      m_ptr(std::exchange(other.m_ptr, nullptr)),\n      m_offset(std::exchange(other.m_offset, 0)),\n      m_size(std::exchange(other.m_size, 0)) {}\n\n  D3D9Memory::~D3D9Memory() {\n    this->Free();\n  }\n\n  D3D9Memory& D3D9Memory::operator = (D3D9Memory&& other) {\n    this->Free();\n\n    m_chunk = std::exchange(other.m_chunk, nullptr);\n    m_ptr = std::exchange(other.m_ptr, nullptr);\n    m_offset = std::exchange(other.m_offset, 0);\n    m_size = std::exchange(other.m_size, 0);\n    return *this;\n  }\n\n  void D3D9Memory::Free() {\n    if (unlikely(m_chunk == nullptr))\n      return;\n\n    if (m_ptr != nullptr)\n      Unmap();\n\n    m_chunk->Allocator()->Free(this);\n    m_chunk = nullptr;\n  }\n\n  void D3D9Memory::Map() {\n    if (unlikely(m_ptr != nullptr))\n      return;\n\n    if (unlikely(m_chunk == nullptr))\n      return;\n\n    m_ptr = m_chunk->Allocator()->Map(this);\n  }\n\n  void D3D9Memory::Unmap() {\n    if (unlikely(m_ptr == nullptr))\n      return;\n\n    m_chunk->Allocator()->Unmap(this);\n    m_ptr = nullptr;\n  }\n\n  void* D3D9Memory::Ptr() {\n    return m_ptr;\n  }\n\n#else\n\n  D3D9Memory D3D9MemoryAllocator::Alloc(uint32_t Size) {\n    D3D9Memory memory(this, Size);\n    m_allocatedMemory += Size;\n    return memory;\n  }\n\n  uint32_t D3D9MemoryAllocator::MappedMemory() const {\n    return m_allocatedMemory.load();\n  }\n\n  uint32_t D3D9MemoryAllocator::UsedMemory() const {\n    return m_allocatedMemory.load();\n  }\n\n  uint32_t D3D9MemoryAllocator::AllocatedMemory() const {\n    return m_allocatedMemory.load();\n  }\n\n  D3D9Memory::D3D9Memory(D3D9MemoryAllocator* pAllocator, size_t Size)\n    : m_allocator (pAllocator),\n      m_ptr       (malloc(Size)),\n      m_size      (Size) {}\n\n  D3D9Memory::D3D9Memory(D3D9Memory&& other)\n    : m_allocator(std::exchange(other.m_allocator, nullptr)),\n      m_ptr(std::exchange(other.m_ptr, nullptr)),\n      m_size(std::exchange(other.m_size, 0)) {}\n\n  D3D9Memory::~D3D9Memory() {\n    this->Free();\n  }\n\n  D3D9Memory& D3D9Memory::operator = (D3D9Memory&& other) {\n    this->Free();\n\n    m_allocator = std::exchange(other.m_allocator, nullptr);\n    m_ptr = std::exchange(other.m_ptr, nullptr);\n    m_size = std::exchange(other.m_size, 0);\n    return *this;\n  }\n\n  void D3D9Memory::Free() {\n    if (m_ptr == nullptr)\n      return;\n\n    free(m_ptr);\n    m_ptr = nullptr;\n    m_allocator->NotifyFreed(m_size);\n  }\n\n\n#endif\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_mem.h",
    "content": "\n#pragma once\n\n#include \"../util/thread.h\"\n\n#if defined(_WIN32) && !defined(_WIN64)\n  #define D3D9_ALLOW_UNMAPPING\n#endif\n\n#ifdef D3D9_ALLOW_UNMAPPING\n  #define WIN32_LEAN_AND_MEAN\n  #include <winbase.h>\n#endif\n\n#include <vector>\n\nnamespace dxvk {\n\n  class D3D9MemoryAllocator;\n  class D3D9Memory;\n\n#ifdef D3D9_ALLOW_UNMAPPING\n\n  class D3D9MemoryChunk;\n\n  constexpr uint32_t D3D9ChunkSize = 64 << 20;\n\n  struct D3D9MemoryRange {\n    uint32_t offset;\n    uint32_t length;\n  };\n\n  struct D3D9MappingRange {\n    uint32_t refCount = 0;\n    void* ptr = nullptr;\n  };\n\n  class D3D9MemoryChunk {\n    friend D3D9MemoryAllocator;\n\n    public:\n      D3D9MemoryChunk(D3D9MemoryAllocator* Allocator, uint32_t Size);\n      ~D3D9MemoryChunk();\n\n      D3D9MemoryChunk             (const D3D9MemoryChunk&) = delete;\n      D3D9MemoryChunk& operator = (const D3D9MemoryChunk&) = delete;\n\n      D3D9MemoryChunk             (D3D9MemoryChunk&& other) = delete;\n      D3D9MemoryChunk& operator = (D3D9MemoryChunk&& other) = delete;\n\n      D3D9MemoryAllocator* Allocator() const;\n\n    private:\n      bool IsEmpty() const;\n      uint32_t Size() const { return m_size; }\n\n      D3D9Memory AllocLocked(uint32_t Size);\n      void FreeLocked(D3D9Memory* Memory);\n      void* MapLocked(D3D9Memory* memory, uint32_t& mappedSize);\n      uint32_t UnmapLocked(D3D9Memory* memory);\n\n      D3D9MemoryAllocator* m_allocator;\n      uint32_t m_size;\n      HANDLE m_mapping;\n      std::vector<D3D9MemoryRange> m_freeRanges;\n      std::vector<D3D9MappingRange> m_mappingRanges;\n  };\n\n  class D3D9Memory {\n    friend D3D9MemoryChunk;\n    friend D3D9MemoryAllocator;\n\n    public:\n      D3D9Memory() {}\n      ~D3D9Memory();\n\n      D3D9Memory             (const D3D9Memory&) = delete;\n      D3D9Memory& operator = (const D3D9Memory&) = delete;\n\n      D3D9Memory             (D3D9Memory&& other);\n      D3D9Memory& operator = (D3D9Memory&& other);\n\n      explicit operator bool() const { return m_chunk != nullptr; }\n\n      void Map();\n      void Unmap();\n      void* Ptr();\n\n    private:\n      D3D9Memory(D3D9MemoryChunk* Chunk, size_t Offset, size_t Size);\n      void Free();\n      D3D9MemoryChunk* GetChunk() const { return m_chunk; }\n      size_t GetOffset() const { return m_offset; }\n      size_t GetSize() const { return m_size; }\n\n      D3D9MemoryChunk* m_chunk = nullptr;\n      void* m_ptr              = nullptr;\n      size_t m_offset          = 0;\n      size_t m_size            = 0;\n  };\n\n  class D3D9MemoryAllocator {\n    friend D3D9MemoryChunk;\n\n    public:\n      D3D9MemoryAllocator();\n      ~D3D9MemoryAllocator() = default;\n      D3D9Memory Alloc(uint32_t Size);\n      D3D9Memory AllocFromChunk(D3D9MemoryChunk* Chunk, uint32_t Size);\n      void Free(D3D9Memory* Memory);\n      void* Map(D3D9Memory* Memory);\n      void Unmap(D3D9Memory* Memory);\n      uint32_t MappedMemory() const;\n      uint32_t UsedMemory() const;\n      uint32_t AllocatedMemory() const;\n      uint32_t AllocationGranularity() const { return m_allocationGranularity; }\n      uint32_t MappingGranularity() const { return m_mappingGranularity; }\n\n    private:\n      void FreeChunk(D3D9MemoryChunk* Chunk);\n\n      dxvk::mutex m_mutex;\n      std::vector<std::unique_ptr<D3D9MemoryChunk>> m_chunks;\n      std::atomic<size_t> m_mappedMemory = 0;\n      std::atomic<size_t> m_allocatedMemory = 0;\n      std::atomic<size_t> m_usedMemory = 0;\n      uint32_t m_allocationGranularity;\n      uint32_t m_mappingGranularity;\n  };\n\n#else\n  class D3D9Memory {\n    friend D3D9MemoryAllocator;\n\n    public:\n      D3D9Memory() {}\n      ~D3D9Memory();\n\n      D3D9Memory             (const D3D9Memory&) = delete;\n      D3D9Memory& operator = (const D3D9Memory&) = delete;\n\n      D3D9Memory             (D3D9Memory&& other);\n      D3D9Memory& operator = (D3D9Memory&& other);\n\n      explicit operator bool() const { return m_ptr != nullptr; }\n\n      void Map() {}\n      void Unmap() {}\n      void* Ptr() { return m_ptr; }\n\n    private:\n      D3D9Memory(D3D9MemoryAllocator* pAllocator, size_t Size);\n      void Free();\n\n      D3D9MemoryAllocator* m_allocator = nullptr;\n      void* m_ptr                      = nullptr;\n      size_t m_size                    = 0;\n    };\n\n    class D3D9MemoryAllocator {\n\n    public:\n      D3D9Memory Alloc(uint32_t Size);\n      uint32_t MappedMemory() const;\n      uint32_t UsedMemory() const;\n      uint32_t AllocatedMemory() const;\n      void NotifyFreed(uint32_t Size) {\n        m_allocatedMemory -= Size;\n      }\n\n    private:\n      std::atomic<size_t> m_allocatedMemory = 0;\n\n    };\n\n#endif\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_monitor.cpp",
    "content": "#include \"d3d9_monitor.h\"\n\n#include \"d3d9_format.h\"\n\nnamespace dxvk {\n\n  uint32_t GetMonitorFormatBpp(D3D9Format Format) {\n    switch (Format) {\n    case D3D9Format::A8R8G8B8:\n    case D3D9Format::X8R8G8B8: // This is still 32 bit even though the alpha is unspecified.\n    case D3D9Format::A2R10G10B10:\n      return 32;\n\n    case D3D9Format::A1R5G5B5:\n    case D3D9Format::X1R5G5B5:\n    case D3D9Format::R5G6B5:\n      return 16;\n\n    default:\n      Logger::warn(str::format(\n        \"GetMonitorFormatBpp: Unknown format: \",\n        Format));\n      return 32;\n    }\n  }\n\n\n  bool IsSupportedAdapterFormat(\n          D3D9Format Format) {\n    return Format == D3D9Format::A2R10G10B10\n        || Format == D3D9Format::X8R8G8B8\n        || Format == D3D9Format::X1R5G5B5\n        || Format == D3D9Format::R5G6B5;\n  }\n\n\n  bool IsSupportedModeFormat(\n          D3D9Format Format) {\n    // Native drivers list no modes for D3D9Format::X1R5G5B5, and some apps,\n    // such as the BGE SettingsApplication, rely on it not being advertised.\n    return Format == D3D9Format::A2R10G10B10\n        || Format == D3D9Format::X8R8G8B8\n        || Format == D3D9Format::R5G6B5;\n  }\n\n\n  bool IsSupportedBackBufferFormat(\n          D3D9Format AdapterFormat,\n          D3D9Format BackBufferFormat,\n          BOOL       Windowed) {\n    if (!Windowed) {\n      // D3D9Format::X1R5G5B5 is not advertised by native\n      // drivers as a full screen adapter format.\n      return (AdapterFormat == D3D9Format::A2R10G10B10 && BackBufferFormat == D3D9Format::A2R10G10B10)\n          || (AdapterFormat == D3D9Format::X8R8G8B8    && BackBufferFormat == D3D9Format::X8R8G8B8)\n          || (AdapterFormat == D3D9Format::X8R8G8B8    && BackBufferFormat == D3D9Format::A8R8G8B8)\n          || (AdapterFormat == D3D9Format::R5G6B5      && BackBufferFormat == D3D9Format::R5G6B5);\n    }\n\n    // D3D9Format::A2R10G10B10 is not advertised by native\n    // drivers as a windowed backbuffer format.\n    return BackBufferFormat == D3D9Format::A8R8G8B8\n        || BackBufferFormat == D3D9Format::X8R8G8B8\n        || BackBufferFormat == D3D9Format::A1R5G5B5\n        || BackBufferFormat == D3D9Format::X1R5G5B5\n        || BackBufferFormat == D3D9Format::R5G6B5\n        || BackBufferFormat == D3D9Format::Unknown;\n  }\n\n  bool IsSupportedBackBufferFormat(\n        D3D9Format BackBufferFormat) {\n    return BackBufferFormat == D3D9Format::A2R10G10B10\n        || BackBufferFormat == D3D9Format::A8R8G8B8\n        || BackBufferFormat == D3D9Format::X8R8G8B8\n        || BackBufferFormat == D3D9Format::A1R5G5B5\n        || BackBufferFormat == D3D9Format::X1R5G5B5\n        || BackBufferFormat == D3D9Format::R5G6B5\n        || BackBufferFormat == D3D9Format::Unknown;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_monitor.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n\n#include \"d3d9_format.h\"\n\n#include \"../wsi/wsi_window.h\"\n#include \"../wsi/wsi_monitor.h\"\n\nnamespace dxvk {\n\n  /**\n  * \\brief Queries bits per pixel for a format\n  *\n  * The format must be a valid swap chain format.\n  * \\param [in] Format The D3D9 format to query\n  * \\returns Bits per pixel for this format\n  */\n  uint32_t GetMonitorFormatBpp(\n    D3D9Format             Format);\n\n  /**\n  * \\brief Returns if a format is supported for a backbuffer/swapchain.\n  *\n  * \\param [in] Format The D3D9 format to query\n  * \\returns If it is supported as a swapchain/backbuffer format.\n  */\n  bool IsSupportedAdapterFormat(\n          D3D9Format Format);\n\n  /**\n  * \\brief Returns if a format is considered for mode enumeration.\n  *\n  * \\param [in] Format The D3D9 format to query\n  * \\returns If it is supported for mode enumeration.\n  */\n  bool IsSupportedModeFormat(\n          D3D9Format Format);\n\n  bool IsSupportedBackBufferFormat(\n          D3D9Format AdapterFormat,\n          D3D9Format BackBufferFormat,\n          BOOL       Windowed);\n\n  bool IsSupportedBackBufferFormat(\n          D3D9Format BackBufferFormat);\n\n  inline wsi::WsiMode ConvertDisplayMode(const D3DDISPLAYMODEEX& mode) {\n    wsi::WsiMode wsiMode  = { };\n    wsiMode.width        = mode.Width;\n    wsiMode.height       = mode.Height;\n    wsiMode.refreshRate  = wsi::WsiRational{ mode.RefreshRate, 1 };\n    wsiMode.bitsPerPixel = GetMonitorFormatBpp(EnumerateFormat(mode.Format));\n    wsiMode.interlaced   = mode.ScanLineOrdering == D3DSCANLINEORDERING_INTERLACED;\n    return wsiMode;\n  }\n\n\n  inline D3DDISPLAYMODEEX ConvertDisplayMode(const wsi::WsiMode& wsiMode) {\n      D3DDISPLAYMODEEX d3d9Mode = { };\n      d3d9Mode.Size             = sizeof(D3DDISPLAYMODEEX);\n      d3d9Mode.Width            = wsiMode.width;\n      d3d9Mode.Height           = wsiMode.height;\n      d3d9Mode.RefreshRate      = wsiMode.refreshRate.numerator / wsiMode.refreshRate.denominator;\n      d3d9Mode.Format           = D3DFMT_X8R8G8B8;\n      d3d9Mode.ScanLineOrdering = wsiMode.interlaced ? D3DSCANLINEORDERING_INTERLACED : D3DSCANLINEORDERING_PROGRESSIVE;\n      return d3d9Mode;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_multithread.cpp",
    "content": "#include \"d3d9_device.h\"\n\nnamespace dxvk {\n\n  D3D9Multithread::D3D9Multithread(\n          BOOL                  Protected)\n    : m_protected( Protected ) { }\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_multithread.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Device lock\n   *\n   * Lightweight RAII wrapper that implements\n   * a subset of the functionality provided by\n   * \\c std::unique_lock, with the goal of being\n   * cheaper to construct and destroy.\n   */\n  class D3D9DeviceLock {\n\n  public:\n\n    D3D9DeviceLock()\n      : m_mutex(nullptr) { }\n\n    D3D9DeviceLock(sync::RecursiveSpinlock& mutex)\n      : m_mutex(&mutex) {\n      mutex.lock();\n    }\n\n    D3D9DeviceLock(D3D9DeviceLock&& other)\n      : m_mutex(other.m_mutex) {\n      other.m_mutex = nullptr;\n    }\n\n    D3D9DeviceLock& operator = (D3D9DeviceLock&& other) {\n      if (m_mutex)\n        m_mutex->unlock();\n\n      m_mutex = other.m_mutex;\n      other.m_mutex = nullptr;\n      return *this;\n    }\n\n    ~D3D9DeviceLock() {\n      if (m_mutex != nullptr)\n        m_mutex->unlock();\n    }\n\n  private:\n\n    sync::RecursiveSpinlock* m_mutex;\n\n  };\n\n\n  /**\n   * \\brief D3D9 context lock\n   */\n  class D3D9Multithread {\n\n  public:\n\n    D3D9Multithread(\n      BOOL                  Protected);\n\n    D3D9DeviceLock AcquireLock() {\n      return m_protected\n        ? D3D9DeviceLock(m_mutex)\n        : D3D9DeviceLock();\n    }\n\n  private:\n\n    BOOL            m_protected;\n\n    sync::RecursiveSpinlock m_mutex;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_names.cpp",
    "content": "#include \"d3d9_format.h\"\n\nnamespace dxvk {\n\n  std::ostream& operator << (std::ostream& os, D3D9Format e) {\n    switch (e) {\n      ENUM_NAME(D3D9Format::Unknown);\n\n      ENUM_NAME(D3D9Format::R8G8B8);\n      ENUM_NAME(D3D9Format::A8R8G8B8);\n      ENUM_NAME(D3D9Format::X8R8G8B8);\n      ENUM_NAME(D3D9Format::R5G6B5);\n      ENUM_NAME(D3D9Format::X1R5G5B5);\n      ENUM_NAME(D3D9Format::A1R5G5B5);\n      ENUM_NAME(D3D9Format::A4R4G4B4);\n      ENUM_NAME(D3D9Format::R3G3B2);\n      ENUM_NAME(D3D9Format::A8);\n      ENUM_NAME(D3D9Format::A8R3G3B2);\n      ENUM_NAME(D3D9Format::X4R4G4B4);\n      ENUM_NAME(D3D9Format::A2B10G10R10);\n      ENUM_NAME(D3D9Format::A8B8G8R8);\n      ENUM_NAME(D3D9Format::X8B8G8R8);\n      ENUM_NAME(D3D9Format::G16R16);\n      ENUM_NAME(D3D9Format::A2R10G10B10);\n      ENUM_NAME(D3D9Format::A16B16G16R16);\n      ENUM_NAME(D3D9Format::A8P8);\n      ENUM_NAME(D3D9Format::P8);\n      ENUM_NAME(D3D9Format::L8);\n      ENUM_NAME(D3D9Format::A8L8);\n      ENUM_NAME(D3D9Format::A4L4);\n      ENUM_NAME(D3D9Format::V8U8);\n      ENUM_NAME(D3D9Format::L6V5U5);\n      ENUM_NAME(D3D9Format::X8L8V8U8);\n      ENUM_NAME(D3D9Format::Q8W8V8U8);\n      ENUM_NAME(D3D9Format::V16U16);\n      ENUM_NAME(D3D9Format::W11V11U10);\n      ENUM_NAME(D3D9Format::A2W10V10U10);\n      ENUM_NAME(D3D9Format::UYVY);\n      ENUM_NAME(D3D9Format::R8G8_B8G8);\n      ENUM_NAME(D3D9Format::YUY2);\n      ENUM_NAME(D3D9Format::G8R8_G8B8);\n      ENUM_NAME(D3D9Format::DXT1);\n      ENUM_NAME(D3D9Format::DXT2);\n      ENUM_NAME(D3D9Format::DXT3);\n      ENUM_NAME(D3D9Format::DXT4);\n      ENUM_NAME(D3D9Format::DXT5);\n      ENUM_NAME(D3D9Format::D16_LOCKABLE);\n      ENUM_NAME(D3D9Format::D32);\n      ENUM_NAME(D3D9Format::D15S1);\n      ENUM_NAME(D3D9Format::D24S8);\n      ENUM_NAME(D3D9Format::D24X8);\n      ENUM_NAME(D3D9Format::D24X4S4);\n      ENUM_NAME(D3D9Format::D16);\n      ENUM_NAME(D3D9Format::D32F_LOCKABLE);\n      ENUM_NAME(D3D9Format::D24FS8);\n      ENUM_NAME(D3D9Format::D32_LOCKABLE);\n      ENUM_NAME(D3D9Format::S8_LOCKABLE);\n      ENUM_NAME(D3D9Format::L16);\n      ENUM_NAME(D3D9Format::VERTEXDATA);\n      ENUM_NAME(D3D9Format::INDEX16);\n      ENUM_NAME(D3D9Format::INDEX32);\n      ENUM_NAME(D3D9Format::Q16W16V16U16);\n      ENUM_NAME(D3D9Format::MULTI2_ARGB8);\n      ENUM_NAME(D3D9Format::R16F);\n      ENUM_NAME(D3D9Format::G16R16F);\n      ENUM_NAME(D3D9Format::A16B16G16R16F);\n      ENUM_NAME(D3D9Format::R32F);\n      ENUM_NAME(D3D9Format::G32R32F);\n      ENUM_NAME(D3D9Format::A32B32G32R32F);\n      ENUM_NAME(D3D9Format::CxV8U8);\n      ENUM_NAME(D3D9Format::A1);\n      ENUM_NAME(D3D9Format::A2B10G10R10_XR_BIAS);\n      ENUM_NAME(D3D9Format::BINARYBUFFER);\n\n      // Driver Hacks / Unofficial Formats\n      ENUM_NAME(D3D9Format::ATI1);\n      ENUM_NAME(D3D9Format::ATI2);\n      ENUM_NAME(D3D9Format::INST);\n      ENUM_NAME(D3D9Format::DF24);\n      ENUM_NAME(D3D9Format::DF16);\n      ENUM_NAME(D3D9Format::NULL_FORMAT);\n      ENUM_NAME(D3D9Format::GET4);\n      ENUM_NAME(D3D9Format::GET1);\n      ENUM_NAME(D3D9Format::NVDB);\n      ENUM_NAME(D3D9Format::A2M1);\n      ENUM_NAME(D3D9Format::A2M0);\n      ENUM_NAME(D3D9Format::ATOC);\n      ENUM_NAME(D3D9Format::INTZ);\n      ENUM_NAME(D3D9Format::RAWZ);\n      ENUM_NAME(D3D9Format::RESZ);\n\n      ENUM_NAME(D3D9Format::NV11);\n      ENUM_NAME(D3D9Format::NV12);\n      ENUM_NAME(D3D9Format::P010);\n      ENUM_NAME(D3D9Format::P016);\n      ENUM_NAME(D3D9Format::Y210);\n      ENUM_NAME(D3D9Format::Y216);\n      ENUM_NAME(D3D9Format::Y410);\n      ENUM_NAME(D3D9Format::AYUV);\n      ENUM_NAME(D3D9Format::YV12);\n      ENUM_NAME(D3D9Format::OPAQUE_420);\n\n      // Not supported but exist\n      ENUM_NAME(D3D9Format::AI44);\n      ENUM_NAME(D3D9Format::IA44);\n      ENUM_NAME(D3D9Format::CENT);\n      ENUM_NAME(D3D9Format::R2VB);\n      ENUM_NAME(D3D9Format::COPM);\n      ENUM_NAME(D3D9Format::SSAA);\n      ENUM_NAME(D3D9Format::NVCS);\n      ENUM_NAME(D3D9Format::NVHS);\n      ENUM_NAME(D3D9Format::NVHU);\n\n      ENUM_NAME(D3D9Format::EXT1);\n      ENUM_NAME(D3D9Format::FXT1);\n      ENUM_NAME(D3D9Format::GXT1);\n      ENUM_NAME(D3D9Format::HXT1);\n      ENUM_NAME(D3D9Format::AL16);\n      ENUM_NAME(D3D9Format::AR16);\n      ENUM_NAME(D3D9Format::R16);\n      ENUM_NAME(D3D9Format::L16_FOURCC);\n\n      ENUM_DEFAULT(e);\n    }\n  }\n\n\n  std::ostream& operator << (std::ostream& os, D3DRENDERSTATETYPE e) {\n    switch (e) {\n      ENUM_NAME(D3DRS_ZENABLE);\n      ENUM_NAME(D3DRS_FILLMODE);\n      ENUM_NAME(D3DRS_SHADEMODE);\n      ENUM_NAME(D3DRS_ZWRITEENABLE);\n      ENUM_NAME(D3DRS_ALPHATESTENABLE);\n      ENUM_NAME(D3DRS_LASTPIXEL);\n      ENUM_NAME(D3DRS_SRCBLEND);\n      ENUM_NAME(D3DRS_DESTBLEND);\n      ENUM_NAME(D3DRS_CULLMODE);\n      ENUM_NAME(D3DRS_ZFUNC);\n      ENUM_NAME(D3DRS_ALPHAREF);\n      ENUM_NAME(D3DRS_ALPHAFUNC);\n      ENUM_NAME(D3DRS_DITHERENABLE);\n      ENUM_NAME(D3DRS_ALPHABLENDENABLE);\n      ENUM_NAME(D3DRS_FOGENABLE);\n      ENUM_NAME(D3DRS_SPECULARENABLE);\n      ENUM_NAME(D3DRS_FOGCOLOR);\n      ENUM_NAME(D3DRS_FOGTABLEMODE);\n      ENUM_NAME(D3DRS_FOGSTART);\n      ENUM_NAME(D3DRS_FOGEND);\n      ENUM_NAME(D3DRS_FOGDENSITY);\n      ENUM_NAME(D3DRS_RANGEFOGENABLE);\n      ENUM_NAME(D3DRS_STENCILENABLE);\n      ENUM_NAME(D3DRS_STENCILFAIL);\n      ENUM_NAME(D3DRS_STENCILZFAIL);\n      ENUM_NAME(D3DRS_STENCILPASS);\n      ENUM_NAME(D3DRS_STENCILFUNC);\n      ENUM_NAME(D3DRS_STENCILREF);\n      ENUM_NAME(D3DRS_STENCILMASK);\n      ENUM_NAME(D3DRS_STENCILWRITEMASK);\n      ENUM_NAME(D3DRS_TEXTUREFACTOR);\n      ENUM_NAME(D3DRS_WRAP0);\n      ENUM_NAME(D3DRS_WRAP1);\n      ENUM_NAME(D3DRS_WRAP2);\n      ENUM_NAME(D3DRS_WRAP3);\n      ENUM_NAME(D3DRS_WRAP4);\n      ENUM_NAME(D3DRS_WRAP5);\n      ENUM_NAME(D3DRS_WRAP6);\n      ENUM_NAME(D3DRS_WRAP7);\n      ENUM_NAME(D3DRS_CLIPPING);\n      ENUM_NAME(D3DRS_LIGHTING);\n      ENUM_NAME(D3DRS_AMBIENT);\n      ENUM_NAME(D3DRS_FOGVERTEXMODE);\n      ENUM_NAME(D3DRS_COLORVERTEX);\n      ENUM_NAME(D3DRS_LOCALVIEWER);\n      ENUM_NAME(D3DRS_NORMALIZENORMALS);\n      ENUM_NAME(D3DRS_DIFFUSEMATERIALSOURCE);\n      ENUM_NAME(D3DRS_SPECULARMATERIALSOURCE);\n      ENUM_NAME(D3DRS_AMBIENTMATERIALSOURCE);\n      ENUM_NAME(D3DRS_EMISSIVEMATERIALSOURCE);\n      ENUM_NAME(D3DRS_VERTEXBLEND);\n      ENUM_NAME(D3DRS_CLIPPLANEENABLE);\n      ENUM_NAME(D3DRS_POINTSIZE);\n      ENUM_NAME(D3DRS_POINTSIZE_MIN);\n      ENUM_NAME(D3DRS_POINTSPRITEENABLE);\n      ENUM_NAME(D3DRS_POINTSCALEENABLE);\n      ENUM_NAME(D3DRS_POINTSCALE_A);\n      ENUM_NAME(D3DRS_POINTSCALE_B);\n      ENUM_NAME(D3DRS_POINTSCALE_C);\n      ENUM_NAME(D3DRS_MULTISAMPLEANTIALIAS);\n      ENUM_NAME(D3DRS_MULTISAMPLEMASK);\n      ENUM_NAME(D3DRS_PATCHEDGESTYLE);\n      ENUM_NAME(D3DRS_DEBUGMONITORTOKEN);\n      ENUM_NAME(D3DRS_POINTSIZE_MAX);\n      ENUM_NAME(D3DRS_INDEXEDVERTEXBLENDENABLE);\n      ENUM_NAME(D3DRS_COLORWRITEENABLE);\n      ENUM_NAME(D3DRS_TWEENFACTOR);\n      ENUM_NAME(D3DRS_BLENDOP);\n      ENUM_NAME(D3DRS_POSITIONDEGREE);\n      ENUM_NAME(D3DRS_NORMALDEGREE);\n      ENUM_NAME(D3DRS_SCISSORTESTENABLE);\n      ENUM_NAME(D3DRS_SLOPESCALEDEPTHBIAS);\n      ENUM_NAME(D3DRS_ANTIALIASEDLINEENABLE);\n      ENUM_NAME(D3DRS_MINTESSELLATIONLEVEL);\n      ENUM_NAME(D3DRS_MAXTESSELLATIONLEVEL);\n      ENUM_NAME(D3DRS_ADAPTIVETESS_X);\n      ENUM_NAME(D3DRS_ADAPTIVETESS_Y);\n      ENUM_NAME(D3DRS_ADAPTIVETESS_Z);\n      ENUM_NAME(D3DRS_ADAPTIVETESS_W);\n      ENUM_NAME(D3DRS_ENABLEADAPTIVETESSELLATION);\n      ENUM_NAME(D3DRS_TWOSIDEDSTENCILMODE);\n      ENUM_NAME(D3DRS_CCW_STENCILFAIL);\n      ENUM_NAME(D3DRS_CCW_STENCILZFAIL);\n      ENUM_NAME(D3DRS_CCW_STENCILPASS);\n      ENUM_NAME(D3DRS_CCW_STENCILFUNC);\n      ENUM_NAME(D3DRS_COLORWRITEENABLE1);\n      ENUM_NAME(D3DRS_COLORWRITEENABLE2);\n      ENUM_NAME(D3DRS_COLORWRITEENABLE3);\n      ENUM_NAME(D3DRS_BLENDFACTOR);\n      ENUM_NAME(D3DRS_SRGBWRITEENABLE);\n      ENUM_NAME(D3DRS_DEPTHBIAS);\n      ENUM_NAME(D3DRS_WRAP8);\n      ENUM_NAME(D3DRS_WRAP9);\n      ENUM_NAME(D3DRS_WRAP10);\n      ENUM_NAME(D3DRS_WRAP11);\n      ENUM_NAME(D3DRS_WRAP12);\n      ENUM_NAME(D3DRS_WRAP13);\n      ENUM_NAME(D3DRS_WRAP14);\n      ENUM_NAME(D3DRS_WRAP15);\n      ENUM_NAME(D3DRS_SEPARATEALPHABLENDENABLE);\n      ENUM_NAME(D3DRS_SRCBLENDALPHA);\n      ENUM_NAME(D3DRS_DESTBLENDALPHA);\n      ENUM_NAME(D3DRS_BLENDOPALPHA);\n\n      ENUM_DEFAULT(e);\n    }\n  }\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_names.h",
    "content": "#include \"d3d9_include.h\"\n\nnamespace dxvk {\n\n  std::ostream& operator << (std::ostream& os, D3D9Format e);\n\n  std::ostream& operator << (std::ostream& os, D3DRENDERSTATETYPE e);\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_on_12.cpp",
    "content": "#include \"d3d9_on_12.h\"\n\n#include \"d3d9_device.h\"\n\nnamespace dxvk {\n\n  D3D9On12::D3D9On12(D3D9DeviceEx* device)\n    : m_device(device) {\n\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9On12::QueryInterface(REFIID riid, void** object) {\n    return m_device->QueryInterface(riid, object);\n  }\n  ULONG STDMETHODCALLTYPE D3D9On12::AddRef() {\n    return m_device->AddRef();\n  }\n  ULONG STDMETHODCALLTYPE D3D9On12::Release() {\n    return m_device->Release();\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9On12::GetD3D12Device(REFIID riid, void** object) {\n    InitReturnPtr(object);\n\n    Logger::err(\"D3D9On12::GetD3D12Device: Stub\");\n    return E_NOINTERFACE;\n  }\n  HRESULT STDMETHODCALLTYPE D3D9On12::UnwrapUnderlyingResource(IDirect3DResource9* resource, ID3D12CommandQueue* command_queue, REFIID riid, void** object) {\n    Logger::err(\"D3D9On12::GetD3D12Device: UnwrapUnderlyingResource: Stub\");\n    return E_NOINTERFACE;\n  }\n  HRESULT STDMETHODCALLTYPE D3D9On12::ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences) {\n    if (num_sync)\n      Logger::err(\"D3D9On12::GetD3D12Device: ReturnUnderlyingResource: Stub\");\n\n    m_device->FlushAndSync9On12();\n    return S_OK;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_on_12.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n\nnamespace dxvk {\n\n  class D3D9DeviceEx;\n\n  class D3D9On12 final : public IDirect3DDevice9On12 {\n\n  public:\n\n    D3D9On12(D3D9DeviceEx* device);\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** object);\n    ULONG STDMETHODCALLTYPE AddRef();\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE GetD3D12Device(REFIID riid, void** object);\n    HRESULT STDMETHODCALLTYPE UnwrapUnderlyingResource(IDirect3DResource9* resource, ID3D12CommandQueue* command_queue, REFIID riid, void** object);\n    HRESULT STDMETHODCALLTYPE ReturnUnderlyingResource(IDirect3DResource9* resource, UINT num_sync, UINT64* signal_values, ID3D12Fence** fences);\n\n  private:\n    \n    D3D9DeviceEx* m_device;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_options.cpp",
    "content": "#include \"../util/util_math.h\"\n\n#include \"d3d9_options.h\"\n\n#include \"d3d9_caps.h\"\n\nnamespace dxvk {\n\n  static int32_t parsePciId(const std::string& str) {\n    if (str.size() != 4)\n      return -1;\n    \n    int32_t id = 0;\n\n    for (size_t i = 0; i < str.size(); i++) {\n      id *= 16;\n\n      if (str[i] >= '0' && str[i] <= '9')\n        id += str[i] - '0';\n      else if (str[i] >= 'A' && str[i] <= 'F')\n        id += str[i] - 'A' + 10;\n      else if (str[i] >= 'a' && str[i] <= 'f')\n        id += str[i] - 'a' + 10;\n      else\n        return -1;\n    }\n\n    return id;\n  }\n\n\n  D3D9Options::D3D9Options(const Rc<DxvkDevice>& device, const Config& config) {\n    const Rc<DxvkAdapter> adapter = device != nullptr ? device->adapter() : nullptr;\n\n    // Fetch these as a string representing a hexadecimal number and parse it.\n    this->customVendorId                = parsePciId(config.getOption<std::string>(\"d3d9.customVendorId\"));\n    this->customDeviceId                = parsePciId(config.getOption<std::string>(\"d3d9.customDeviceId\"));\n    this->customDeviceDesc              = config.getOption<std::string> (\"d3d9.customDeviceDesc\");\n\n    this->hideNvidiaGpu                 = config.getOption<Tristate>    (\"d3d9.hideNvidiaGpu\",                 Tristate::Auto) == Tristate::True;\n    this->hideNvkGpu                    = config.getOption<Tristate>    (\"d3d9.hideNvkGpu\",                    Tristate::Auto) == Tristate::True;\n    this->hideAmdGpu                    = config.getOption<Tristate>    (\"d3d9.hideAmdGpu\",                    Tristate::Auto) == Tristate::True;\n    this->hideIntelGpu                  = config.getOption<Tristate>    (\"d3d9.hideIntelGpu\",                  Tristate::True) == Tristate::True;\n    this->maxFrameLatency               = config.getOption<int32_t>     (\"d3d9.maxFrameLatency\",               0);\n    this->maxFrameRate                  = config.getOption<int32_t>     (\"d3d9.maxFrameRate\",                  0);\n    this->presentInterval               = config.getOption<int32_t>     (\"d3d9.presentInterval\",               -1);\n    this->shaderModel                   = config.getOption<int32_t>     (\"d3d9.shaderModel\",                   3u);\n    this->dpiAware                      = config.getOption<bool>        (\"d3d9.dpiAware\",                      true);\n    this->lenientClear                  = config.getOption<bool>        (\"d3d9.lenientClear\",                  false);\n    this->deferSurfaceCreation          = config.getOption<bool>        (\"d3d9.deferSurfaceCreation\",          false);\n    this->samplerAnisotropy             = config.getOption<int32_t>     (\"d3d9.samplerAnisotropy\",             -1);\n    this->maxAvailableMemory            = config.getOption<int32_t>     (\"d3d9.maxAvailableMemory\",            4096);\n    this->supportDFFormats              = config.getOption<bool>        (\"d3d9.supportDFFormats\",              true);\n    this->supportX4R4G4B4               = config.getOption<bool>        (\"d3d9.supportX4R4G4B4\",               true);\n    this->useD32forD24                  = config.getOption<bool>        (\"d3d9.useD32forD24\",                  false);\n    this->disableA8RT                   = config.getOption<bool>        (\"d3d9.disableA8RT\",                   false);\n    this->memoryTrackTest               = config.getOption<bool>        (\"d3d9.memoryTrackTest\",               false);\n    this->forceSamplerTypeSpecConstants = config.getOption<bool>        (\"d3d9.forceSamplerTypeSpecConstants\", false);\n    this->forceSampleRateShading        = config.getOption<bool>        (\"d3d9.forceSampleRateShading\",        false);\n    this->forceAspectRatio              = config.getOption<std::string> (\"d3d9.forceAspectRatio\",              \"\");\n    this->forceRefreshRate              = config.getOption<int32_t>     (\"d3d9.forceRefreshRate\",              0u);\n    this->modeCountCompatibility        = config.getOption<bool>        (\"d3d9.modeCountCompatibility\",        false);\n    this->allowDiscard                  = config.getOption<bool>        (\"d3d9.allowDiscard\",                  true);\n    this->enumerateByDisplays           = config.getOption<bool>        (\"d3d9.enumerateByDisplays\",           true);\n    this->cachedWriteOnlyBuffers        = config.getOption<bool>        (\"d3d9.cachedWriteOnlyBuffers\",          false);\n    this->deviceLocalConstantBuffers    = config.getOption<bool>        (\"d3d9.deviceLocalConstantBuffers\",    false);\n    this->allowDirectBufferMapping      = config.getOption<bool>        (\"d3d9.allowDirectBufferMapping\",      true);\n    this->seamlessCubes                 = config.getOption<bool>        (\"d3d9.seamlessCubes\",                 false);\n    this->textureMemory                 = config.getOption<int32_t>     (\"d3d9.textureMemory\",                 100) << 20;\n    this->deviceLossOnFocusLoss         = config.getOption<bool>        (\"d3d9.deviceLossOnFocusLoss\",         false);\n    this->samplerLodBias                = config.getOption<float>       (\"d3d9.samplerLodBias\",                0.0f);\n    this->clampNegativeLodBias          = config.getOption<bool>        (\"d3d9.clampNegativeLodBias\",          false);\n    this->countLosableResources         = config.getOption<bool>        (\"d3d9.countLosableResources\",         true);\n    this->reproducibleCommandStream     = config.getOption<bool>        (\"d3d9.reproducibleCommandStream\",     false);\n    this->extraFrontbuffer              = config.getOption<bool>        (\"d3d9.extraFrontbuffer\",              false);\n    this->ffUbershaderVS                = config.getOption<bool>        (\"d3d9.ffUbershaderVS\",                true);\n    this->ffUbershaderFS                = config.getOption<bool>        (\"d3d9.ffUbershaderFS\",                true);\n\n    // D3D8 options\n    this->drefScaling                   = config.getOption<int32_t>     (\"d3d8.scaleDref\",                     0);\n\n    // Clamp the shader model value between 0 and 3\n    this->shaderModel    = dxvk::clamp(this->shaderModel, 0u, 3u);\n    // Clamp LOD bias so that people don't abuse this in unintended ways\n    this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f);\n\n    std::string floatEmulation = Config::toLower(config.getOption<std::string>(\"d3d9.floatEmulation\", \"auto\"));\n    if (floatEmulation == \"strict\") {\n      this->d3d9FloatEmulation = D3D9FloatEmulation::Strict;\n    } else if (floatEmulation == \"false\") {\n      this->d3d9FloatEmulation = D3D9FloatEmulation::Disabled;\n    } else if (floatEmulation == \"true\") {\n      this->d3d9FloatEmulation = D3D9FloatEmulation::Enabled;\n    } else {\n      bool hasMulz = adapter != nullptr\n                  && (adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV)\n                   || adapter->matchesDriver(VK_DRIVER_ID_MESA_NVK)\n                   || adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE, Version(2, 0, 316), Version())\n                   || adapter->matchesDriver(VK_DRIVER_ID_NVIDIA_PROPRIETARY, Version(565, 57, 1), Version()));\n      this->d3d9FloatEmulation = hasMulz ? D3D9FloatEmulation::Strict : D3D9FloatEmulation::Enabled;\n    }\n\n    this->shaderDumpPath = env::getEnvVar(\"DXVK_SHADER_DUMP_PATH\");\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_options.h",
    "content": "#pragma once\n\n#include \"../util/config/config.h\"\n#include \"../dxvk/dxvk_device.h\"\n\nnamespace dxvk {\n\n  enum class D3D9FloatEmulation {\n    Disabled,\n    Enabled,\n    Strict\n  };\n\n  struct D3D9Options {\n\n    D3D9Options(const Rc<DxvkDevice>& device, const Config& config);\n\n    /// Override PCI vendor and device IDs reported to the\n    /// application. This may make apps think they are running\n    /// on a different GPU than they do and behave differently.\n    int32_t customVendorId;\n    int32_t customDeviceId;\n    std::string customDeviceDesc;\n\n    /// Reports Nvidia GPUs running on the proprietary driver as a different\n    /// vendor (usually AMD)\n    bool hideNvidiaGpu;\n\n    /// Reports Nvidia GPUs running on NVK as a different vendor (usually AMD)\n    bool hideNvkGpu;\n\n    /// Reports AMD GPUs as a different vendor (usually Nvidia)\n    bool hideAmdGpu;\n\n    /// Reports Intel GPUs as a different vendor (usually AMD)\n    bool hideIntelGpu;\n\n    /// Present interval. Overrides the value\n    /// in D3DPRESENT_PARAMS used in swapchain present.\n    int32_t presentInterval;\n\n    /// Override maximum frame latency if the app specifies\n    /// a higher value. May help with frame timing issues.\n    int32_t maxFrameLatency;\n\n    /// Limit frame rate\n    int32_t maxFrameRate;\n\n    /// Set the max shader model the device can support in the caps.\n    uint32_t shaderModel;\n\n    /// Whether or not to set the process as DPI aware in Windows when the API interface is created.\n    bool dpiAware;\n\n    /// Whether or not to do a fast path clear if we're close enough to the whole render target.\n    bool lenientClear;\n\n    /// Defer surface creation\n    bool deferSurfaceCreation;\n\n    /// Anisotropic filter override\n    ///\n    /// Enforces anisotropic filtering with the\n    /// given anisotropy value for all samplers.\n    int32_t samplerAnisotropy;\n\n    /// Max available memory override\n    ///\n    /// Changes the max initial value used in\n    /// tracking and GetAvailableTextureMem\n    uint32_t maxAvailableMemory;\n\n    /// D3D9 Floating Point Emulation (anything * 0 = 0)\n    D3D9FloatEmulation d3d9FloatEmulation;\n\n    /// Support the DF16 & DF24 texture format\n    bool supportDFFormats;\n\n    /// Support X4R4G4B4\n    bool supportX4R4G4B4;\n\n    /// Use D32f for D24\n    bool useD32forD24;\n\n    /// Disable D3DFMT_A8 for render targets.\n    /// Specifically to work around a game\n    /// bug in The Sims 2 that happens on native too!\n    bool disableA8RT;\n\n    /// Whether or not to respect memory tracking for\n    /// failing resource allocation.\n    bool memoryTrackTest;\n\n    /// Forced aspect ratio, disable other modes\n    std::string forceAspectRatio;\n\n    /// Forced refresh rate, disable other modes\n    uint32_t forceRefreshRate;\n\n    /// Restrict the mode count to ensure a maximum total count of 24\n    bool modeCountCompatibility;\n\n    /// Always use a spec constant to determine sampler type (instead of just in PS 1.x)\n    /// Works around a game bug in Halo CE where it gives cube textures to 2d/volume samplers\n    bool forceSamplerTypeSpecConstants;\n\n    /// Forces sample rate shading\n    bool forceSampleRateShading;\n\n    /// Allow D3DLOCK_DISCARD\n    bool allowDiscard;\n\n    /// Enumerate adapters by displays\n    bool enumerateByDisplays;\n\n    /// Cached dynamic buffers: Maps all buffers in cached memory.\n    bool cachedWriteOnlyBuffers;\n\n    /// Use device local memory for constant buffers.\n    bool deviceLocalConstantBuffers;\n\n    /// Disable direct buffer mapping\n    bool allowDirectBufferMapping;\n\n    /// Don't use non seamless cube maps\n    bool seamlessCubes;\n\n    /// Mipmap LOD bias\n    ///\n    /// Enforces the given LOD bias for all samplers.\n    float samplerLodBias;\n\n    /// Clamps negative LOD bias\n    bool clampNegativeLodBias;\n\n    /// How much virtual memory will be used for textures (in MB).\n    int32_t textureMemory;\n\n    /// Shader dump path\n    std::string shaderDumpPath;\n\n    /// Enable emulation of device loss when a fullscreen app loses focus\n    bool deviceLossOnFocusLoss;\n\n    /// Disable counting losable resources and rejecting calls to Reset() if any are still alive\n    bool countLosableResources;\n\n    /// Ensure that for the same D3D commands the output VK commands\n    /// don't change between runs. Useful for comparative benchmarking,\n    /// can negatively affect performance.\n    bool reproducibleCommandStream;\n\n    /// Enable depth texcoord Z (Dref) scaling (D3D8 quirk)\n    int32_t drefScaling;\n\n    /// Add an extra front buffer to make GetFrontBufferData() work correctly when the swapchain only has a single buffer\n    bool extraFrontbuffer;\n\n    /// Use the uber shader for fixed function vertex shaders.\n    bool ffUbershaderVS;\n\n    /// Use the uber shader for fixed function fragment shaders.\n    bool ffUbershaderFS;\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_query.cpp",
    "content": "#include \"d3d9_query.h\"\n\n#include \"d3d9_device.h\"\n\nnamespace dxvk {\n\n  D3D9Query::D3D9Query(\n        D3D9DeviceEx*      pDevice,\n        D3DQUERYTYPE       QueryType)\n    : D3D9DeviceChild<IDirect3DQuery9>(pDevice)\n    , m_queryType                          (QueryType)\n    , m_state                              (D3D9_VK_QUERY_INITIAL) {\n    Rc<DxvkDevice> dxvkDevice = m_parent->GetDXVKDevice();\n\n    switch (m_queryType) {\n      case D3DQUERYTYPE_VCACHE:\n        break;\n\n      case D3DQUERYTYPE_EVENT:\n        m_event[0] = dxvkDevice->createGpuEvent();\n        break;\n\n      case D3DQUERYTYPE_OCCLUSION:\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_OCCLUSION,\n          VK_QUERY_CONTROL_PRECISE_BIT, 0);\n        break;\n\n      case D3DQUERYTYPE_TIMESTAMP:\n        m_query[0] = dxvkDevice->createGpuQuery(\n          VK_QUERY_TYPE_TIMESTAMP, 0, 0);\n        break;\n\n      case D3DQUERYTYPE_TIMESTAMPDISJOINT:\n        for (uint32_t i = 0; i < 2; i++) {\n          m_query[i] = dxvkDevice->createGpuQuery(\n            VK_QUERY_TYPE_TIMESTAMP, 0, 0);\n        }\n        break;\n\n      case D3DQUERYTYPE_TIMESTAMPFREQ:\n        break;\n\n      default:\n        throw DxvkError(str::format(\"D3D9Query: Unsupported query type \", m_queryType));\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Query::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DQuery9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DQuery9), riid)) {\n      Logger::warn(\"D3D9Query::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  D3DQUERYTYPE STDMETHODCALLTYPE D3D9Query::GetType() {\n    return m_queryType;\n  }\n\n\n  DWORD STDMETHODCALLTYPE D3D9Query::GetDataSize() {\n    switch (m_queryType) {\n      case D3DQUERYTYPE_VCACHE:               return sizeof(D3DDEVINFO_VCACHE);\n      case D3DQUERYTYPE_RESOURCEMANAGER:      return sizeof(D3DDEVINFO_RESOURCEMANAGER);\n      case D3DQUERYTYPE_VERTEXSTATS:          return sizeof(D3DDEVINFO_D3DVERTEXSTATS);\n      case D3DQUERYTYPE_EVENT:                return sizeof(BOOL);\n      case D3DQUERYTYPE_OCCLUSION:            return sizeof(DWORD);\n      case D3DQUERYTYPE_TIMESTAMP:            return sizeof(UINT64);\n      case D3DQUERYTYPE_TIMESTAMPDISJOINT:    return sizeof(BOOL);\n      case D3DQUERYTYPE_TIMESTAMPFREQ:        return sizeof(UINT64);\n      case D3DQUERYTYPE_PIPELINETIMINGS:      return sizeof(D3DDEVINFO_D3D9PIPELINETIMINGS);\n      case D3DQUERYTYPE_INTERFACETIMINGS:     return sizeof(D3DDEVINFO_D3D9INTERFACETIMINGS);\n      case D3DQUERYTYPE_VERTEXTIMINGS:        return sizeof(D3DDEVINFO_D3D9STAGETIMINGS);\n      case D3DQUERYTYPE_PIXELTIMINGS:         return sizeof(D3DDEVINFO_D3D9PIPELINETIMINGS);\n      case D3DQUERYTYPE_BANDWIDTHTIMINGS:     return sizeof(D3DDEVINFO_D3D9BANDWIDTHTIMINGS);\n      case D3DQUERYTYPE_CACHEUTILIZATION:     return sizeof(D3DDEVINFO_D3D9CACHEUTILIZATION);\n      default:                                return 0;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Query::Issue(DWORD dwIssueFlags) {\n    // Note: No need to submit to CS if we don't do anything!\n\n    if (dwIssueFlags == D3DISSUE_BEGIN) {\n      if (QueryBeginnable(m_queryType)) {\n        if (m_state == D3D9_VK_QUERY_BEGUN && QueryEndable(m_queryType)) {\n          m_resetCtr.fetch_add(1, std::memory_order_acquire);\n          m_parent->End(this);\n        }\n\n        m_parent->Begin(this);\n\n        m_state = D3D9_VK_QUERY_BEGUN;\n      }\n    }\n    else {\n      if (QueryEndable(m_queryType)) {\n        if (m_state != D3D9_VK_QUERY_BEGUN && QueryBeginnable(m_queryType))\n          m_parent->Begin(this);\n\n        m_resetCtr.fetch_add(1, std::memory_order_acquire);\n\n        m_parent->End(this);\n\n      }\n      m_state = D3D9_VK_QUERY_ENDED;\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Query::GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) {\n    D3D9DeviceLock lock = m_parent->LockDevice();\n\n    bool flush = dwGetDataFlags & D3DGETDATA_FLUSH;\n\n    if (unlikely(m_parent->IsDeviceLost())) {\n      return flush ? D3DERR_DEVICELOST : S_FALSE;\n    }\n\n    if (m_state == D3D9_VK_QUERY_CACHED) {\n      // Query data was already retrieved once.\n      // Use cached query data to prevent having to check the VK event\n      // and having to iterate over the VK queries again\n      if (likely(pData && dwSize)) {\n        if (m_queryType != D3DQUERYTYPE_EVENT) {\n          memcpy(pData, &m_dataCache, dwSize);\n        } else {\n          *static_cast<bool*>(pData) = true;\n        }\n      }\n      return D3D_OK;\n    }\n\n    HRESULT hr = this->GetQueryData(pData, dwSize);\n\n    // If we get S_FALSE and it's not from the fact\n    // they didn't call end, do some flushy stuff...\n    if (flush && hr == S_FALSE && m_state != D3D9_VK_QUERY_BEGUN) {\n      this->NotifyStall();\n      m_parent->ConsiderFlush(GpuFlushType::ImplicitSynchronization);\n    }\n\n    return hr;\n  }\n\n\n  HRESULT D3D9Query::GetQueryData(void* pData, DWORD dwSize) {\n    // Let the game know that calling end might be a good idea...\n    if (m_state == D3D9_VK_QUERY_BEGUN)\n      return S_FALSE;\n\n    if (unlikely(!pData && dwSize))\n      return D3DERR_INVALIDCALL;\n\n    // The game forgot to even issue the query!\n    // Let's do it for them...\n    // This will issue both the begin, and the end.\n    if (m_state == D3D9_VK_QUERY_INITIAL)\n      this->Issue(D3DISSUE_END);\n\n    if (m_resetCtr != 0u)\n      return S_FALSE;\n\n    if (m_queryType == D3DQUERYTYPE_EVENT) {\n      DxvkGpuEventStatus status = m_event[0]->test();\n\n      if (status == DxvkGpuEventStatus::Invalid)\n        return D3DERR_INVALIDCALL;\n\n      bool signaled = status == DxvkGpuEventStatus::Signaled;\n\n      if (pData != nullptr)\n        *static_cast<BOOL*>(pData) = signaled;\n\n      if (signaled) {\n        m_state = D3D9_VK_QUERY_CACHED;\n        return D3D_OK;\n      } else {\n        return S_FALSE;\n      }\n    }\n    else {\n      std::array<DxvkQueryData, MaxGpuQueries> queryData = { };\n\n      for (uint32_t i = 0; i < MaxGpuQueries && m_query[i] != nullptr; i++) {\n        DxvkGpuQueryStatus status = m_query[i]->getData(queryData[i]);\n\n        if (status == DxvkGpuQueryStatus::Invalid\n         || status == DxvkGpuQueryStatus::Failed)\n          return D3DERR_INVALIDCALL;\n\n        if (status == DxvkGpuQueryStatus::Pending)\n          return S_FALSE;\n      }\n\n      if (pData == nullptr)\n        return D3D_OK;\n\n\n      switch (m_queryType) {\n        case D3DQUERYTYPE_VCACHE:\n          m_dataCache.VCache.Pattern     = MAKEFOURCC('C', 'A', 'C', 'H');\n          m_dataCache.VCache.OptMethod   = 1;\n          m_dataCache.VCache.CacheSize   = 16;\n          m_dataCache.VCache.MagicNumber = 7;\n          break;\n\n        case D3DQUERYTYPE_OCCLUSION:\n          m_dataCache.Occlusion = DWORD(queryData[0].occlusion.samplesPassed);\n          break;\n\n        case D3DQUERYTYPE_TIMESTAMP:\n          m_dataCache.Timestamp = queryData[0].timestamp.time;\n          break;\n\n        case D3DQUERYTYPE_TIMESTAMPDISJOINT:\n          m_dataCache.TimestampDisjoint = queryData[0].timestamp.time < queryData[1].timestamp.time;\n          break;\n\n        case D3DQUERYTYPE_TIMESTAMPFREQ:\n          m_dataCache.TimestampFreq = GetTimestampQueryFrequency();\n          break;\n\n        default:\n          break;\n      }\n\n      if (likely(pData && dwSize))\n        memcpy(pData, &m_dataCache, dwSize);\n\n      m_state = D3D9_VK_QUERY_CACHED;\n      return D3D_OK;\n    }\n  }\n\n\n  UINT64 D3D9Query::GetTimestampQueryFrequency() const {\n    Rc<DxvkDevice>  device  = m_parent->GetDXVKDevice();\n    Rc<DxvkAdapter> adapter = device->adapter();\n\n    const auto& limits = adapter->deviceProperties().core.properties.limits;\n    return uint64_t(1'000'000'000.0f / limits.timestampPeriod);\n  }\n\n\n  void D3D9Query::Begin(DxvkContext* ctx) {\n    switch (m_queryType) {\n      case D3DQUERYTYPE_OCCLUSION:\n        ctx->beginQuery(m_query[0]);\n        break;\n\n      case D3DQUERYTYPE_TIMESTAMPDISJOINT:\n        ctx->writeTimestamp(m_query[1]);\n        break;\n\n      default: break;\n    }\n  }\n\n\n  void D3D9Query::End(DxvkContext* ctx) {\n    switch (m_queryType) {\n      case D3DQUERYTYPE_TIMESTAMP:\n      case D3DQUERYTYPE_TIMESTAMPDISJOINT:\n        ctx->writeTimestamp(m_query[0]);\n        break;\n\n      case D3DQUERYTYPE_OCCLUSION:\n        ctx->endQuery(m_query[0]);\n        break;\n\n      case D3DQUERYTYPE_EVENT:\n        ctx->signalGpuEvent(m_event[0]);\n        break;\n\n      default: break;\n    }\n\n    m_resetCtr.fetch_sub(1, std::memory_order_release);\n  }\n\n\n  bool D3D9Query::QueryBeginnable(D3DQUERYTYPE QueryType) {\n    return QueryType == D3DQUERYTYPE_OCCLUSION\n        || QueryType == D3DQUERYTYPE_TIMESTAMPDISJOINT;\n  }\n\n\n  bool D3D9Query::QueryEndable(D3DQUERYTYPE QueryType) {\n    return QueryBeginnable(QueryType)\n        || QueryType == D3DQUERYTYPE_TIMESTAMP\n        || QueryType == D3DQUERYTYPE_EVENT;\n  }\n\n\n  HRESULT D3D9Query::QuerySupported(D3D9DeviceEx* pDevice, D3DQUERYTYPE QueryType) {\n    switch (QueryType) {\n      case D3DQUERYTYPE_VCACHE:\n        if (!pDevice->SupportsVCacheQuery())\n          return D3DERR_NOTAVAILABLE;\n\n        return D3D_OK;\n      case D3DQUERYTYPE_EVENT:\n      case D3DQUERYTYPE_OCCLUSION:\n      case D3DQUERYTYPE_TIMESTAMP:\n      case D3DQUERYTYPE_TIMESTAMPDISJOINT:\n      case D3DQUERYTYPE_TIMESTAMPFREQ:\n        return D3D_OK;\n\n      default:\n        return D3DERR_NOTAVAILABLE;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_query.h",
    "content": "#pragma once\n\n#include \"d3d9_device_child.h\"\n\n#include \"../dxvk/dxvk_context.h\"\n\nnamespace dxvk {\n\n  enum D3D9_VK_QUERY_STATE : uint32_t {\n    D3D9_VK_QUERY_INITIAL,\n    D3D9_VK_QUERY_BEGUN,\n    D3D9_VK_QUERY_ENDED,\n    D3D9_VK_QUERY_CACHED\n  };\n\n  union D3D9_QUERY_DATA {\n    D3DDEVINFO_VCACHE         VCache;\n    DWORD                     Occlusion;\n    UINT64                    Timestamp;\n    BOOL                      TimestampDisjoint;\n    UINT64                    TimestampFreq;\n    D3DDEVINFO_D3DVERTEXSTATS VertexStats;\n  };\n\n  class D3D9Query : public D3D9DeviceChild<IDirect3DQuery9> {\n    constexpr static uint32_t MaxGpuQueries = 2;\n    constexpr static uint32_t MaxGpuEvents  = 1;\n  public:\n\n    D3D9Query(\n            D3D9DeviceEx*      pDevice,\n            D3DQUERYTYPE       QueryType);\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    D3DQUERYTYPE STDMETHODCALLTYPE GetType() final;\n\n    DWORD STDMETHODCALLTYPE GetDataSize() final;\n\n    HRESULT STDMETHODCALLTYPE Issue(DWORD dwIssueFlags) final;\n\n    HRESULT STDMETHODCALLTYPE GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) final;\n\n    HRESULT GetQueryData(void* pData, DWORD dwSize);\n\n    void Begin(DxvkContext* ctx);\n    void End(DxvkContext* ctx);\n\n    static bool QueryBeginnable(D3DQUERYTYPE QueryType);\n    static bool QueryEndable(D3DQUERYTYPE QueryType);\n\n    static HRESULT QuerySupported(D3D9DeviceEx* pDevice, D3DQUERYTYPE QueryType);\n\n    bool IsEvent() const {\n      return m_queryType == D3DQUERYTYPE_EVENT;\n    }\n\n    bool IsStalling() const {\n      return m_stallFlag;\n    }\n\n    void NotifyEnd() {\n      m_stallMask <<= 1;\n    }\n\n    void NotifyStall() {\n      m_stallMask |= 1;\n      m_stallFlag |= bit::popcnt(m_stallMask) >= 16;\n    }\n\n  private:\n\n    D3DQUERYTYPE      m_queryType;\n\n    D3D9_VK_QUERY_STATE m_state;\n\n    std::array<Rc<DxvkQuery>, MaxGpuQueries> m_query;\n    std::array<Rc<DxvkEvent>, MaxGpuEvents>  m_event;\n\n    uint32_t m_stallMask = 0;\n    bool     m_stallFlag = false;\n\n    std::atomic<uint32_t> m_resetCtr = { 0u };\n\n    D3D9_QUERY_DATA m_dataCache;\n\n    UINT64 GetTimestampQueryFrequency() const;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_resource.h",
    "content": "#pragma once\n\n#include \"d3d9_device_child.h\"\n\n#include \"../util/com/com_private_data.h\"\n\nnamespace dxvk {\n\n  template <typename... Type>\n  class D3D9Resource : public D3D9DeviceChild<Type...> {\n\n  public:\n\n    D3D9Resource(D3D9DeviceEx* pDevice, D3DPOOL Pool, bool Extended)\n      : D3D9DeviceChild<Type...>(pDevice)\n      , m_pool                  ( Pool )\n      , m_priority              ( 0 )\n      , m_isExtended            ( Extended ) { }\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID     refguid,\n      const void*       pData,\n            DWORD       SizeOfData,\n            DWORD       Flags) final {\n      HRESULT hr;\n      if (Flags & D3DSPD_IUNKNOWN) {\n        if(unlikely(SizeOfData != sizeof(IUnknown*)))\n          return D3DERR_INVALIDCALL;\n        IUnknown* unknown =\n          const_cast<IUnknown*>(\n            reinterpret_cast<const IUnknown*>(pData));\n        hr = m_privateData.setInterface(\n          refguid, unknown);\n      }\n      else\n        hr = m_privateData.setData(\n          refguid, SizeOfData, pData);\n\n      if (unlikely(FAILED(hr)))\n        return D3DERR_INVALIDCALL;\n\n      return D3D_OK;\n    }\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID     refguid,\n            void*       pData,\n            DWORD*      pSizeOfData) final {\n      if (unlikely(pData == nullptr && pSizeOfData == nullptr))\n        return D3DERR_NOTFOUND;\n\n      HRESULT hr = m_privateData.getData(\n        refguid, reinterpret_cast<UINT*>(pSizeOfData), pData);\n\n      if (unlikely(FAILED(hr))) {\n        if(hr == DXGI_ERROR_MORE_DATA)\n          return D3DERR_MOREDATA;\n        else if (hr == DXGI_ERROR_NOT_FOUND)\n          return D3DERR_NOTFOUND;\n        else\n          return D3DERR_INVALIDCALL;\n      }\n\n      return D3D_OK;\n    }\n\n    HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final {\n      HRESULT hr = m_privateData.setData(refguid, 0, nullptr);\n\n      if (unlikely(FAILED(hr)))\n        return D3DERR_INVALIDCALL;\n\n      return D3D_OK;\n    }\n\n    DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) {\n      // Priority can only be set for D3DPOOL_MANAGED resources on\n      // D3D9 interfaces, and for D3DPOOL_DEFAULT on D3D9Ex interfaces\n      if (likely((m_pool == D3DPOOL_MANAGED && !m_isExtended)\n              || (m_pool == D3DPOOL_DEFAULT &&  m_isExtended))) {\n        DWORD oldPriority = m_priority;\n        m_priority = PriorityNew;\n        return oldPriority;\n      }\n\n      return m_priority;\n    }\n\n    DWORD STDMETHODCALLTYPE GetPriority() {\n      return m_priority;\n    }\n\n\n  protected:\n\n    const D3DPOOL        m_pool;\n          DWORD          m_priority;\n\n  private:\n\n    const bool           m_isExtended;\n          ComPrivateData m_privateData;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_shader.cpp",
    "content": "#include \"d3d9_shader.h\"\n\n#include \"d3d9_caps.h\"\n#include \"d3d9_device.h\"\n#include \"d3d9_util.h\"\n\nnamespace dxvk {\n\n  D3D9CommonShader::D3D9CommonShader() {}\n\n  D3D9CommonShader::D3D9CommonShader(\n            D3D9DeviceEx*         pDevice,\n            VkShaderStageFlagBits ShaderStage,\n      const DxvkShaderHash&       Key,\n      const DxsoModuleInfo*       pDxsoModuleInfo,\n      const void*                 pShaderBytecode,\n      const DxsoAnalysisInfo&     AnalysisInfo,\n            DxsoModule*           pModule) {\n    const uint32_t bytecodeLength = AnalysisInfo.bytecodeByteLength;\n\n    const std::string name = Key.toString();\n    Logger::debug(str::format(\"Compiling shader \", name));\n    \n    // If requested by the user, dump both the raw DXBC\n    // shader and the compiled SPIR-V module to a file.\n    const std::string& dumpPath = pDevice->GetOptions()->shaderDumpPath;\n    \n    if (dumpPath.size() != 0) {\n      DxsoReader reader(\n        reinterpret_cast<const char*>(pShaderBytecode));\n\n      reader.store(std::ofstream(str::topath(str::format(dumpPath, \"/\", name, \".dxso\").c_str()).c_str(),\n        std::ios_base::binary | std::ios_base::trunc), bytecodeLength);\n\n      char comment[2048];\n      Com<ID3DBlob> blob;\n      HRESULT hr = DisassembleShader(\n        pShaderBytecode,\n        TRUE,\n        comment, \n        &blob);\n      \n      if (SUCCEEDED(hr)) {\n        std::ofstream disassembledOut(str::topath(str::format(dumpPath, \"/\", name, \".dxso.dis\").c_str()).c_str(), std::ios_base::binary | std::ios_base::trunc);\n        disassembledOut.write(\n          reinterpret_cast<const char*>(blob->GetBufferPointer()),\n          blob->GetBufferSize());\n      }\n    }\n    \n    // Decide whether we need to create a pass-through\n    // geometry shader for vertex shader stream output\n\n    const D3D9ConstantLayout& constantLayout = ShaderStage == VK_SHADER_STAGE_VERTEX_BIT\n      ? pDevice->GetVertexConstantLayout()\n      : pDevice->GetPixelConstantLayout();\n    m_shader       = pModule->compile(*pDxsoModuleInfo, name, AnalysisInfo, constantLayout);\n    m_isgn         = pModule->isgn();\n    m_usedSamplers = pModule->usedSamplers();\n    m_textureTypes = pModule->textureTypes();\n\n    // Shift up these sampler bits so we can just\n    // do an or per-draw in the device.\n    // We shift by 17 because 16 ps samplers + 1 dmap (tess)\n    if (ShaderStage == VK_SHADER_STAGE_VERTEX_BIT)\n      m_usedSamplers <<= FirstVSSamplerSlot;\n\n    m_usedRTs              = pModule->usedRTs();\n\n    m_info                 = pModule->info();\n    m_meta                 = pModule->meta();\n    m_constants            = pModule->constants();\n    m_maxDefinedFloatConst = pModule->maxDefinedFloatConstant();\n    m_maxDefinedIntConst   = pModule->maxDefinedIntConstant();\n    m_maxDefinedBoolConst  = pModule->maxDefinedBoolConstant();\n\n    if (dumpPath.size() != 0) {\n      std::ofstream dumpStream(\n        str::topath(str::format(dumpPath, \"/\", name, \".spv\").c_str()).c_str(),\n        std::ios_base::binary | std::ios_base::trunc);\n      \n      m_shader->dump(dumpStream);\n    }\n\n    pDevice->GetDXVKDevice()->registerShader(m_shader);\n  }\n\n\n  void D3D9ShaderModuleSet::GetShaderModule(\n            D3D9DeviceEx*         pDevice,\n            D3D9CommonShader*     pShaderModule,\n            uint32_t*             pLength,\n            VkShaderStageFlagBits ShaderStage,\n      const DxsoModuleInfo*       pDxbcModuleInfo,\n      const void*                 pShaderBytecode) {\n    DxsoReader reader(\n      reinterpret_cast<const char*>(pShaderBytecode));\n\n    DxsoModule module(reader);\n\n    if (unlikely(module.info().shaderStage() != ShaderStage))\n      throw DxvkError(\"GetShaderModule: Bytecode does not match shader stage\");\n\n    auto* options = pDevice->GetOptions();\n\n    const uint32_t majorVersion = module.info().majorVersion();\n    const uint32_t minorVersion = module.info().minorVersion();\n\n    // Vertex shader version checks\n    if (ShaderStage == VK_SHADER_STAGE_VERTEX_BIT) {\n      // Late fixed-function capable hardware exposed support for VS 1.1\n      const uint32_t shaderModelVS = pDevice->IsD3D8Compatible() ? 1u : std::max(1u, options->shaderModel);\n\n      if (unlikely(majorVersion > shaderModelVS\n               || (majorVersion == 1 && minorVersion > 1)\n               // Skip checking the SM2 minor version, as it has a 2_x mode apparently\n               || (majorVersion == 3 && minorVersion != 0))) {\n        throw DxvkError(str::format(\"GetShaderModule: Unsupported VS version \", majorVersion, \".\", minorVersion));\n      }\n    // Pixel shader version checks\n    } else if (ShaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) {\n      const uint32_t shaderModelPS = pDevice->IsD3D8Compatible() ? std::min(1u, options->shaderModel) : options->shaderModel;\n\n      if (unlikely(majorVersion > shaderModelPS\n               || (majorVersion == 1 && minorVersion > 4)\n               // Skip checking the SM2 minor version, as it has a 2_x mode apparently\n               || (majorVersion == 3 && minorVersion != 0))) {\n        throw DxvkError(str::format(\"GetShaderModule: Unsupported PS version \", majorVersion, \".\", minorVersion));\n      }\n    } else {\n      throw DxvkError(\"GetShaderModule: Unsupported shader stage\");\n    }\n\n    DxsoAnalysisInfo info = module.analyze();\n    *pLength = info.bytecodeByteLength;\n\n    Sha1Hash hash = Sha1Hash::compute(pShaderBytecode, info.bytecodeByteLength);\n    DxvkShaderHash lookupKey = DxvkShaderHash(\n      ShaderStage,\n      info.bytecodeByteLength,\n      hash.digest(),\n      hash.digestLength());\n\n    // Use the shader's unique key for the lookup\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      \n      auto entry = m_modules.find(lookupKey);\n      if (entry != m_modules.end()) {\n        *pShaderModule = entry->second;\n        return;\n      }\n    }\n    \n    // This shader has not been compiled yet, so we have to create a\n    // new module. This takes a while, so we won't lock the structure.\n    *pShaderModule = D3D9CommonShader(\n      pDevice, ShaderStage, lookupKey,\n      pDxbcModuleInfo, pShaderBytecode,\n      info, &module);\n\n    const int32_t maxFloatConstantIndex = pShaderModule->GetMaxDefinedFloatConstant();\n    const int32_t maxIntConstantIndex = pShaderModule->GetMaxDefinedIntConstant();\n    const int32_t maxBoolConstantIndex = pShaderModule->GetMaxDefinedBoolConstant();\n\n    // Vertex shader specific validations. These validations are not\n    // performed on SWVP devices or on MIXED devices, even if\n    // SetSoftwareVertexProcessing(FALSE) is used to disable SWVP mode.\n    if (!pDevice->CanSWVP() && ShaderStage == VK_SHADER_STAGE_VERTEX_BIT) {\n      // Validate the float constant value advertised in pCaps->MaxFloatConstantsVS for HWVP.\n      if (unlikely(maxFloatConstantIndex > static_cast<int32_t>(caps::MaxFloatConstantsVS - 1)))\n        throw DxvkError(str::format(\"GetShaderModule: Invalid VS float constant index \", maxFloatConstantIndex));\n      // Validate the integer constant value advertised in pCaps->MaxOtherConstants for HWVP.\n      if (unlikely(maxIntConstantIndex > static_cast<int32_t>(caps::MaxOtherConstants - 1)))\n        throw DxvkError(str::format(\"GetShaderModule: Invalid VS int constant index \", maxIntConstantIndex));\n      // Validate the bool constant value advertised in pCaps->MaxOtherConstants for HWVP.\n      if (unlikely(maxBoolConstantIndex > static_cast<int32_t>(caps::MaxOtherConstants - 1)))\n        throw DxvkError(str::format(\"GetShaderModule: Invalid VS bool constant index \", maxBoolConstantIndex));\n    // Pixel shader specific validations.\n    } else if (ShaderStage == VK_SHADER_STAGE_FRAGMENT_BIT) {\n      const bool isSM2XOrNewer = majorVersion == 3 || (majorVersion == 2 && minorVersion != 0);\n      // Pixel shader model version 2_x has the same limits here as version 2_0\n      const uint32_t maxFloatConstantsPS = majorVersion == 3 ? caps::MaxSM3FloatConstantsPS :\n                                           majorVersion == 2 ? caps::MaxSM2FloatConstantsPS :\n                                           caps::MaxSM1FloatConstantsPS;\n      // Validate the float constant value coresponding to the supported shader model version.\n      if (unlikely(!pDevice->CanSWVP() && maxFloatConstantIndex > static_cast<int32_t>(maxFloatConstantsPS - 1)))\n        throw DxvkError(str::format(\"GetShaderModule: Invalid PS float constant index \", maxFloatConstantIndex));\n      // Pixel shaders below version 2_x can not use integer constants, not even in SWVP/MIXED mode\n      if (unlikely(!isSM2XOrNewer && maxIntConstantIndex != -1))\n        throw DxvkError(\"GetShaderModule: Invalid use of PS int constant\");\n      // Validate the integer constant value advertised in pCaps->MaxOtherConstants for HWVP.\n      else if (unlikely(isSM2XOrNewer && !pDevice->CanSWVP() &&\n                        maxIntConstantIndex > static_cast<int32_t>(caps::MaxOtherConstants - 1)))\n        throw DxvkError(str::format(\"GetShaderModule: Invalid PS int constant index \", maxIntConstantIndex));\n      // Pixel shaders below version 2_x can not use bool constants, not even in SWVP/MIXED mode\n      if (unlikely(!isSM2XOrNewer && maxBoolConstantIndex != -1))\n        throw DxvkError(\"GetShaderModule: Invalid use of PS bool constant\");\n      // Validate the bool constant value advertised in pCaps->MaxOtherConstants for HWVP.\n      else if (unlikely(isSM2XOrNewer && !pDevice->CanSWVP() &&\n                        maxBoolConstantIndex > static_cast<int32_t>(caps::MaxOtherConstants - 1)))\n        throw DxvkError(str::format(\"GetShaderModule: Invalid PS bool constant index \", maxBoolConstantIndex));\n    }\n    \n    // Insert the new module into the lookup table. If another thread\n    // has compiled the same shader in the meantime, we should return\n    // that object instead and discard the newly created module.\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      \n      auto status = m_modules.insert({ lookupKey, *pShaderModule });\n      if (!status.second) {\n        *pShaderModule = status.first->second;\n        return;\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_shader.h",
    "content": "#pragma once\n\n#include \"../dxso/dxso_module.h\"\n\n#include \"../dxvk/dxvk_shader.h\"\n#include \"../dxvk/dxvk_shader_key.h\"\n\n#include \"d3d9_resource.h\"\n#include \"d3d9_util.h\"\n#include \"d3d9_mem.h\"\n\n#include <array>\n\nnamespace dxvk {\n\n\n  /**\n   * \\brief Common shader object\n   * \n   * Stores the compiled SPIR-V shader and the SHA-1\n   * hash of the original DXBC shader, which can be\n   * used to identify the shader.\n   */\n  class D3D9CommonShader {\n\n  public:\n\n    D3D9CommonShader();\n\n    D3D9CommonShader(\n            D3D9DeviceEx*         pDevice,\n            VkShaderStageFlagBits ShaderStage,\n      const DxvkShaderHash&       Key,\n      const DxsoModuleInfo*       pDxbcModuleInfo,\n      const void*                 pShaderBytecode,\n      const DxsoAnalysisInfo&     AnalysisInfo,\n            DxsoModule*           pModule);\n\n\n    Rc<DxvkShader> GetShader() const {\n      return m_shader;\n    }\n\n    std::string GetName() const {\n      return m_shader->debugName();\n    }\n\n    const DxsoIsgn& GetIsgn() const {\n      return m_isgn;\n    }\n\n    const DxsoShaderMetaInfo& GetMeta() const { return m_meta; }\n    const DxsoDefinedConstants& GetConstants() const { return m_constants; }\n\n    D3D9ShaderMasks GetShaderMask() const { return D3D9ShaderMasks{ m_usedSamplers, m_usedRTs }; }\n\n    const DxsoProgramInfo& GetInfo() const { return m_info; }\n\n    int32_t GetMaxDefinedFloatConstant() const { return m_maxDefinedFloatConst; }\n\n    int32_t GetMaxDefinedIntConstant() const { return m_maxDefinedIntConst; }\n\n    int32_t GetMaxDefinedBoolConstant() const { return m_maxDefinedBoolConst; }\n\n    VkImageViewType GetImageViewType(uint32_t samplerSlot) const {\n      const uint32_t offset = samplerSlot * 2;\n      const uint32_t mask = 0b11;\n      return static_cast<VkImageViewType>((m_textureTypes >> offset) & mask);\n    }\n\n  private:\n\n    DxsoIsgn              m_isgn;\n    uint32_t              m_usedSamplers;\n    uint32_t              m_usedRTs;\n    uint32_t              m_textureTypes;\n\n    DxsoProgramInfo       m_info;\n    DxsoShaderMetaInfo    m_meta;\n    DxsoDefinedConstants  m_constants;\n    int32_t               m_maxDefinedFloatConst = -1;\n    int32_t               m_maxDefinedIntConst = -1;\n    int32_t               m_maxDefinedBoolConst = -1;\n\n    Rc<DxvkShader>        m_shader;\n\n  };\n\n  /**\n   * \\brief Common shader interface\n   * \n   * Implements methods for all D3D11*Shader\n   * interfaces and stores the actual shader\n   * module object.\n   */\n  template <typename Base>\n  class D3D9Shader : public D3D9DeviceChild<Base> {\n\n  public:\n\n    D3D9Shader(\n            D3D9DeviceEx*        pDevice,\n            D3D9MemoryAllocator* pAllocator,\n      const D3D9CommonShader&    CommonShader,\n      const void*                pShaderBytecode,\n            uint32_t             BytecodeLength)\n      : D3D9DeviceChild<Base>( pDevice )\n      , m_shader             ( CommonShader )\n      , m_bytecode           ( pAllocator->Alloc(BytecodeLength) )\n      , m_bytecodeLength     ( BytecodeLength ) {\n      m_bytecode.Map();\n      std::memcpy(m_bytecode.Ptr(), pShaderBytecode, BytecodeLength);\n      m_bytecode.Unmap();\n    }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) {\n      if (ppvObject == nullptr)\n        return E_POINTER;\n\n      *ppvObject = nullptr;\n\n      if (riid == __uuidof(IUnknown)\n       || riid == __uuidof(Base)) {\n        *ppvObject = ref(this);\n        return S_OK;\n      }\n\n      if (logQueryInterfaceError(__uuidof(Base), riid)) {\n        Logger::warn(\"D3D9Shader::QueryInterface: Unknown interface query\");\n        Logger::warn(str::format(riid));\n      }\n\n      return E_NOINTERFACE;\n    }\n\n    HRESULT STDMETHODCALLTYPE GetFunction(void* pOut, UINT* pSizeOfData) {\n      if (pSizeOfData == nullptr)\n        return D3DERR_INVALIDCALL;\n\n      if (pOut == nullptr) {\n        *pSizeOfData = m_bytecodeLength;\n        return D3D_OK;\n      }\n\n      m_bytecode.Map();\n      uint32_t copyAmount = std::min(*pSizeOfData, m_bytecodeLength);\n      std::memcpy(pOut, m_bytecode.Ptr(), copyAmount);\n      m_bytecode.Unmap();\n\n      return D3D_OK;\n    }\n\n    const D3D9CommonShader* GetCommonShader() const {\n      return &m_shader;\n    }\n\n  private:\n\n    D3D9CommonShader m_shader;\n\n    D3D9Memory       m_bytecode;\n    uint32_t         m_bytecodeLength;\n\n  };\n\n  // Needs their own classes and not usings for forward decl.\n\n  class D3D9VertexShader final : public D3D9Shader<IDirect3DVertexShader9> {\n\n  public:\n\n    D3D9VertexShader(\n            D3D9DeviceEx*        pDevice,\n            D3D9MemoryAllocator* pAllocator,\n      const D3D9CommonShader&    CommonShader,\n      const void*                pShaderBytecode,\n            uint32_t             BytecodeLength)\n      : D3D9Shader<IDirect3DVertexShader9>( pDevice, pAllocator, CommonShader, pShaderBytecode, BytecodeLength ) { }\n\n  };\n\n  class D3D9PixelShader final : public D3D9Shader<IDirect3DPixelShader9> {\n\n  public:\n\n    D3D9PixelShader(\n            D3D9DeviceEx*        pDevice,\n            D3D9MemoryAllocator* pAllocator,\n      const D3D9CommonShader&    CommonShader,\n      const void*                pShaderBytecode,\n            uint32_t             BytecodeLength)\n      : D3D9Shader<IDirect3DPixelShader9>( pDevice, pAllocator, CommonShader, pShaderBytecode, BytecodeLength ) { }\n\n  };\n\n  /**\n   * \\brief Shader module set\n   * \n   * Some applications may compile the same shader multiple\n   * times, so we should cache the resulting shader modules\n   * and reuse them rather than creating new ones. This\n   * class is thread-safe.\n   */\n  class D3D9ShaderModuleSet : public RcObject {\n    \n  public:\n    \n    void GetShaderModule(\n            D3D9DeviceEx*         pDevice,\n            D3D9CommonShader*     pShaderModule,\n            uint32_t*             pLength,\n            VkShaderStageFlagBits ShaderStage,\n      const DxsoModuleInfo*       pDxbcModuleInfo,\n      const void*                 pShaderBytecode);\n    \n  private:\n    \n    dxvk::mutex m_mutex;\n    \n    std::unordered_map<\n      DxvkShaderHash,\n      D3D9CommonShader,\n      DxvkHash, DxvkEq> m_modules;\n    \n  };\n\n  template<typename T>\n  const D3D9CommonShader* GetCommonShader(const T& pShader) {\n    return pShader != nullptr ? pShader->GetCommonShader() : nullptr;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_shader_validator.cpp",
    "content": "#include \"d3d9_shader_validator.h\"\n\nnamespace dxvk {\n\n  HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = ref(this);\n    return S_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::Begin(\n      D3D9ShaderValidatorCallback pCallback,\n      void*                       pUserData,\n      DWORD                       Unknown) {\n    if (unlikely(m_state != D3D9ShaderValidatorState::Begin)) {\n      return ErrorCallback(nullptr, -1, 0, nullptr, 0,\n        D3D9ShaderValidatorMessage::BeginOutOfOrder,\n        \"IDirect3DShaderValidator9::Begin called out of order. ::End must be called first.\");\n    }\n\n    m_callback = pCallback;\n    m_userData = pUserData;\n    m_state    = D3D9ShaderValidatorState::ValidatingHeader;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::Instruction(\n      const char*  pFile,\n            UINT   Line,\n      const DWORD* pdwInst,\n            DWORD  cdw) {\n    if (unlikely(pdwInst == nullptr || !cdw)) {\n      return ErrorCallback(pFile, Line, 0, pdwInst, cdw,\n        D3D9ShaderValidatorMessage::InstructionNullArgs,\n        \"IDirect3DShaderValidator9::Instruction called with NULL == pdwInst or 0 == cdw.\");\n    }\n\n    if (unlikely(m_state == D3D9ShaderValidatorState::Begin)) {\n      return ErrorCallback(pFile, Line, 0, pdwInst, cdw,\n        D3D9ShaderValidatorMessage::InstructionOutOfOrder,\n        \"IDirect3DShaderValidator9::Instruction called out of order. ::Begin must be called first.\");\n    } else if (unlikely(m_state == D3D9ShaderValidatorState::EndOfShader)) {\n      return ErrorCallback(pFile, Line, 0, pdwInst, cdw,\n        D3D9ShaderValidatorMessage::InstructionEndOfShader,\n        \"IDirect3DShaderValidator9::Instruction called out of order. After end token there should be no more instructions. Call ::End next.\");\n    } else if (unlikely(m_state == D3D9ShaderValidatorState::Error)) {\n      return E_FAIL;\n    }\n\n    if (m_state == D3D9ShaderValidatorState::ValidatingHeader)\n      return ValidateHeader(pFile, Line, pdwInst, cdw);\n\n    DxsoCodeIter pdwInstIter{ reinterpret_cast<const uint32_t*>(pdwInst) };\n    bool isEndToken = !m_ctx->decodeInstruction(pdwInstIter);\n    const DxsoInstructionContext instContext = m_ctx->getInstructionContext();\n\n    if (isEndToken)\n      return ValidateEndToken(pFile, Line, pdwInst, cdw);\n\n    // TODO: DxsoDecodeContext::decodeInstructionLength() does not currently appear\n    // to return the correct token length in many cases, and as such dwordLength\n    // will not be equal to cdw in many situations that are expected to pass validation\n    //\n    /*Logger::debug(str::format(\"IDirect3DShaderValidator9::Instruction: opcode \", instContext.instruction.opcode));\n    // + 1 to account for the opcode...\n    uint32_t dwordLength = instContext.instruction.tokenLength + 1;\n    Logger::debug(str::format(\"IDirect3DShaderValidator9::Instruction: cdw \", cdw));\n    Logger::debug(str::format(\"IDirect3DShaderValidator9::Instruction: dwordLength \", dwordLength));\n    if (unlikely(cdw != dwordLength)) {\n      return ErrorCallback(pFile, Line, 0x2,\n        D3D9ShaderValidatorMessage::BadInstructionLength,\n        str::format(\"Instruction length specified for instruction (\", cdw, \") does not match the token count encountered (\", dwordLength, \"). Aborting validation.\"));\n    }*/\n\n    // a maximum of 10 inputs are supported with PS 3.0 (validation required by The Void)\n    if (m_isPixelShader && m_majorVersion == 3) {\n      switch (instContext.instruction.opcode) {\n        case DxsoOpcode::Comment:\n        case DxsoOpcode::Def:\n        case DxsoOpcode::DefB:\n        case DxsoOpcode::DefI:\n          break;\n\n        default:\n          // Iterate over register tokens. Bit 31 of register tokens is always 1.\n          for (uint32_t instNum = 1; instNum < cdw && (pdwInst[instNum] >> 31); instNum++) {\n            DWORD regType  = ((pdwInst[instNum] & D3DSP_REGTYPE_MASK)  >> D3DSP_REGTYPE_SHIFT)\n                            | ((pdwInst[instNum] & D3DSP_REGTYPE_MASK2) >> D3DSP_REGTYPE_SHIFT2);\n            DWORD regIndex = pdwInst[instNum] & D3DSP_REGNUM_MASK;\n\n            if (unlikely(regType == static_cast<DWORD>(DxsoRegisterType::Input) && regIndex >= 10)) {\n              return ErrorCallback(pFile, Line, 0x2, pdwInst, cdw,\n                instContext.instruction.opcode == DxsoOpcode::Dcl ?\n                  D3D9ShaderValidatorMessage::BadInputRegisterDeclaration :\n                  D3D9ShaderValidatorMessage::BadInputRegister,\n                str::format(\"IDirect3DShaderValidator9::Instruction: PS input registers index #\", regIndex, \" not valid for operand \", instNum, \".\"));\n            }\n          }\n      }\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9ShaderValidator::End() {\n    if (unlikely(m_state == D3D9ShaderValidatorState::Error)) {\n      return E_FAIL;\n    } else if (unlikely(m_state == D3D9ShaderValidatorState::Begin)) {\n      return ErrorCallback(nullptr, 0, 0, nullptr, 0,\n        D3D9ShaderValidatorMessage::EndOutOfOrder,\n        \"IDirect3DShaderValidator9::End called out of order. Call to ::Begin, followed by calls to ::Instruction must occur first.\");\n    } else if (unlikely(m_state != D3D9ShaderValidatorState::EndOfShader)) {\n      return ErrorCallback(nullptr, 0, 0, nullptr, 0,\n        D3D9ShaderValidatorMessage::MissingEndToken,\n        \"IDirect3DShaderValidator9::End: Shader missing end token.\");\n    }\n\n    m_state    = D3D9ShaderValidatorState::Begin;\n    m_isPixelShader = false;\n    m_majorVersion  = 0;\n    m_minorVersion  = 0;\n    m_callback = nullptr;\n    m_userData = nullptr;\n    m_ctx      = nullptr;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9ShaderValidator::ValidateHeader(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) {\n    if (unlikely(cdw != 1)) {\n      return ErrorCallback(pFile, Line, 0x6, pdwInst, cdw,\n        D3D9ShaderValidatorMessage::BadVersionTokenLength,\n        \"IDirect3DShaderValidator9::Instruction: Bad version token. DWORD count > 1 given. Expected DWORD count to be 1 for version token.\");\n    }\n\n    DxsoReader reader = { reinterpret_cast<const char*>(pdwInst) };\n    uint32_t headerToken = reader.readu32();\n    uint32_t shaderTypeDword  = headerToken & 0xffff0000;\n    D3D9ShaderType shaderType;\n\n    if (shaderTypeDword == 0xffff0000) { // Pixel Shader\n      shaderType = D3D9ShaderType::PixelShader;\n      m_isPixelShader = true;\n    } else if (shaderTypeDword == 0xfffe0000) { // Vertex Shader\n      shaderType = D3D9ShaderType::VertexShader;\n      m_isPixelShader = false;\n    } else {\n      return ErrorCallback(pFile, Line, 0x6, pdwInst, cdw,\n        D3D9ShaderValidatorMessage::BadVersionTokenType,\n        \"IDirect3DShaderValidator9::Instruction: Bad version token. It indicates neither a pixel shader nor a vertex shader.\");\n    }\n\n    m_majorVersion = D3DSHADER_VERSION_MAJOR(headerToken);\n    m_minorVersion = D3DSHADER_VERSION_MINOR(headerToken);\n    m_ctx   = std::make_unique<DxsoDecodeContext>(DxsoProgramInfo{ shaderType, m_minorVersion, m_majorVersion });\n    m_state = D3D9ShaderValidatorState::ValidatingInstructions;\n\n    const char* shaderTypeOutput = m_isPixelShader ? \"PS\" : \"VS\";\n    Logger::debug(str::format(\"IDirect3DShaderValidator9::Instruction: Validating \",\n                              shaderTypeOutput, \" version \", m_majorVersion, \".\", m_minorVersion));\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9ShaderValidator::ValidateEndToken(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw) {\n    // Reached the end token.\n    if (unlikely(cdw != 1)) {\n      return ErrorCallback(pFile, Line, 0x6, pdwInst, cdw,\n        D3D9ShaderValidatorMessage::BadEndToken,\n        \"IDirect3DShaderValidator9::Instruction: Bad end token. DWORD count > 1 given. Expected DWORD count to be 1 for end token.\");\n    }\n\n    m_state = D3D9ShaderValidatorState::EndOfShader;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9ShaderValidator::ErrorCallback(\n      const char*                      pFile,\n            UINT                       Line,\n            DWORD                      Unknown,\n      const DWORD*                     pInstr,\n            DWORD                      InstrLength,\n            D3D9ShaderValidatorMessage MessageID,\n      const std::string&               Message) {\n    if (m_callback)\n      m_callback(pFile, Line, Unknown, MessageID, Message.c_str(), m_userData);\n\n    // TODO: Consider switching this to debug, once we're\n    // confident the implementation doesn't cause any issues\n    Logger::warn(Message);\n\n    // Log instruction that caused the error as raw bytecode\n    if (Logger::logLevel() <= LogLevel::Debug && pInstr && InstrLength) {\n      std::stringstream instMsg;\n\n      for (uint32_t i = 0; i < InstrLength; i++) {\n        instMsg << (i ? \",\" : \" [\");\n        instMsg << std::hex << std::setfill('0') << std::setw(8) << pInstr[i];\n        instMsg << (i + 1 == InstrLength ? \"]\" : \"\");\n      }\n\n      Logger::debug(instMsg.str());\n    }\n\n    m_state = D3D9ShaderValidatorState::Error;\n    return E_FAIL;\n  }\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_shader_validator.h",
    "content": "#pragma once\n\n#include \"d3d9_include.h\"\n\n#include \"../dxso/dxso_header.h\"\n#include \"../dxso/dxso_decoder.h\"\n\nnamespace dxvk {\n\n  enum class D3D9ShaderValidatorMessage : uint32_t {\n    BeginOutOfOrder = 0xeb,\n    InstructionOutOfOrder = 0xec,\n    InstructionEndOfShader = 0xed,\n    InstructionNullArgs = 0xee,\n    BadVersionTokenLength = 0xef,\n    BadVersionTokenType = 0xf0,\n    BadEndToken = 0xf1,\n    EndOutOfOrder = 0xf2,\n    MissingEndToken = 0xf3,\n    BadInputRegisterDeclaration = 0x12c,\n    BadInputRegister = 0x167,\n    BadInstructionLength = 0x21e,\n  };\n\n  enum class D3D9ShaderValidatorState {\n    Begin,\n    ValidatingHeader,\n    ValidatingInstructions,\n    EndOfShader,\n    Error,\n  };\n\n  using D3D9ShaderValidatorCallback = HRESULT(STDMETHODCALLTYPE *)(\n    const char*                      pFile,\n          UINT                       Line,\n          DWORD                      Unknown,\n          D3D9ShaderValidatorMessage MessageID,\n    const char*                      pMessage,\n          void*                      pUserData);\n\n  class IDirect3DShaderValidator9 : public IUnknown {\n\n  public:\n\n    virtual HRESULT STDMETHODCALLTYPE Begin(\n            D3D9ShaderValidatorCallback pCallback,\n            void*                       pUserParam,\n            DWORD                       Unknown) = 0;\n\n    virtual HRESULT STDMETHODCALLTYPE Instruction(\n      const char*  pFile,\n            UINT   Line,\n      const DWORD* pdwInst,\n            DWORD  cdw) = 0;\n\n    virtual HRESULT STDMETHODCALLTYPE End() = 0;\n\n  };\n\n  class D3D9ShaderValidator final : public ComObjectClamp<IDirect3DShaderValidator9> {\n\n  public:\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    HRESULT STDMETHODCALLTYPE Begin(\n        D3D9ShaderValidatorCallback pCallback,\n        void*                       pUserData,\n        DWORD                       Unknown);\n\n    HRESULT STDMETHODCALLTYPE Instruction(\n        const char*  pFile,\n              UINT   Line,\n        const DWORD* pdwInst,\n              DWORD  cdw);\n\n    HRESULT STDMETHODCALLTYPE End();\n\n  private:\n\n    HRESULT ValidateHeader(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw);\n\n    HRESULT ValidateEndToken(const char* pFile, UINT Line, const DWORD* pdwInst, DWORD cdw);\n\n    HRESULT ErrorCallback(\n        const char*                      pFile,\n              UINT                       Line,\n              DWORD                      Unknown,\n        const DWORD*                     pInstr,\n              DWORD                      InstrLength,\n              D3D9ShaderValidatorMessage MessageID,\n        const std::string&               Message);\n\n    bool                        m_isPixelShader = false;\n    uint32_t                    m_majorVersion  = 0;\n    uint32_t                    m_minorVersion  = 0;\n\n    D3D9ShaderValidatorState    m_state         = D3D9ShaderValidatorState::Begin;\n    D3D9ShaderValidatorCallback m_callback      = nullptr;\n    void*                       m_userData      = nullptr;\n\n    std::unique_ptr<DxsoDecodeContext> m_ctx;\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_spec_constants.h",
    "content": "#pragma once\n\n#include <array>\n\n#include <cstdint>\n\n#include \"../spirv/spirv_module.h\"\n\nclass D3D9DeviceEx;\n\nnamespace dxvk {\n\n  enum D3D9SpecConstantId : uint32_t {\n    SpecSamplerType,        // 2 bits for 16 PS samplers      | Bits: 32\n\n    SpecSamplerDepthMode,   // 1 bit for 21 VS + PS samplers  | Bits: 21\n    SpecAlphaCompareOp,     // Range: 0 -> 7                  | Bits: 3\n    SpecSamplerProjected,   // 1 bit for 6 PS 1.x samplers    | Bits: 8\n                            // (1 bit for 8 FF texture stages)\n\n    SpecSamplerNull,        // 1 bit for 21 samplers          | Bits: 21\n    SpecAlphaPrecisionBits, // Range: 0 -> 8 or 0xF           | Bits: 4\n    SpecFogEnabled,         // Range: 0 -> 1                  | Bits: 1\n    SpecVertexFogMode,      // Range: 0 -> 3                  | Bits: 2\n    SpecPixelFogMode,       // Range: 0 -> 3                  | Bits: 2\n\n    SpecVertexShaderBools,  // 16 bools                       | Bits: 16\n    SpecPixelShaderBools,   // 16 bools                       | Bits: 16\n\n    SpecSamplerFetch4,      // 1 bit for 16 PS samplers       | Bits: 16\n    SpecFFLastActiveTextureStage, // Range: 1 -> 8            | Bits: 3\n\n    SpecSamplerDrefClamp,   // 1 bit for 21 VS + PS samplers  | Bits: 21\n    SpecClipPlaneCount,     // 3 bits for 6 clip planes       | Bits : 3\n    SpecPointMode,          // Range: 0 -> 3                  | Bits: 2\n    SpecDrefScaling,        // Range: 0-31 | Bits: 5\n\n    SpecFFGlobalSpecularEnabled,  // Bool                     | Bits: 1\n\n    SpecFFTextureStage0ColorOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage0ColorArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage0ColorArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage0AlphaOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage0AlphaArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage0AlphaArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage0ResultIsTemp, // Bool                  | Bits: 1\n\n    SpecFFTextureStage1ColorOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage1ColorArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage1ColorArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage1AlphaOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage1AlphaArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage1AlphaArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage1ResultIsTemp, // Bool                  | Bits: 1\n\n    SpecFFTextureStage2ColorOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage2ColorArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage2ColorArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage2AlphaOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage2AlphaArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage2AlphaArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage2ResultIsTemp, // Bool                  | Bits: 1\n\n    SpecFFTextureStage3ColorOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage3ColorArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage3ColorArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage3AlphaOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage3AlphaArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage3AlphaArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage3ResultIsTemp, // Bool                  | Bits: 1\n\n    SpecFFTextureStage4ColorOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage4ColorArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage4ColorArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage4AlphaOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage4AlphaArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage4AlphaArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage4ResultIsTemp, // Bool                  | Bits: 1\n\n    SpecFFTextureStage5ColorOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage5ColorArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage5ColorArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage5AlphaOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage5AlphaArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage5AlphaArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage5ResultIsTemp, // Bool                  | Bits: 1\n\n    SpecFFTextureStage6ColorOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage6ColorArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage6ColorArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage6AlphaOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage6AlphaArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage6AlphaArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage6ResultIsTemp, // Bool                  | Bits: 1\n\n    SpecFFTextureStage7ColorOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage7ColorArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage7ColorArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage7AlphaOp,   // Range: 1 -> 26           | Bits: 5\n    SpecFFTextureStage7AlphaArg1, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage7AlphaArg2, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage7ResultIsTemp, // Bool                  | Bits: 1\n\n    SpecFFTextureStage0ColorArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage1ColorArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage2ColorArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage3ColorArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage4ColorArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage5ColorArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n\n    SpecFFTextureStage6ColorArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage7ColorArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage0AlphaArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage1AlphaArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage2AlphaArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage3AlphaArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n\n    SpecFFTextureStage4AlphaArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage5AlphaArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage6AlphaArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n    SpecFFTextureStage7AlphaArg0, // Range: 0 -> 6 + 2 flags  | Bits: 5\n\n    SpecConstantCount,\n  };\n\n  struct BitfieldPosition {\n    constexpr uint32_t mask() const {\n      return uint32_t((1ull << sizeInBits) - 1) << bitOffset;\n    }\n\n    uint32_t dwordOffset;\n    uint32_t bitOffset;\n    uint32_t sizeInBits;\n  };\n\n  struct D3D9SpecializationInfo {\n    // Spec const word 0 determines whether the other spec constants are used rather than the spec const UBO\n    static constexpr uint32_t MaxSpecDwords = 17;\n\n    static constexpr size_t UBOSize = MaxSpecDwords * sizeof(uint32_t);\n\n    static constexpr std::array<BitfieldPosition, SpecConstantCount> Layout{{\n      { 0, 0, 32 },  // SamplerType\n\n      { 1, 0,  21 }, // SamplerDepthMode\n      { 1, 21, 3 },  // AlphaCompareOp\n      { 1, 24, 8 },  // SamplerProjected\n\n      { 2, 0,  21 }, // SamplerNull\n      { 2, 21, 4 },  // AlphaPrecisionBits\n      { 2, 25, 1 },  // FogEnabled\n      { 2, 26, 2 },  // VertexFogMode\n      { 2, 28, 2 },  // PixelFogMode\n\n      { 3, 0,  16 }, // VertexShaderBools\n      { 3, 16, 16 }, // PixelShaderBools\n\n      { 4, 0,  16 }, // SamplerFetch4\n      { 4, 16,  3 }, // FFLastActiveTextureStage\n\n      { 5, 0, 21 },  // SamplerDrefClamp\n      { 5, 21, 3 },  // ClipPlaneCount\n      { 5, 24, 2 },  // PointMode\n      { 5, 26, 5 },  // DrefScaling\n\n      { 6, 31, 1 },  // FFGlobalSpecularEnabled.\n      // Packed with Texture stage 0 but placed here out of order so every texture stage\n      // has the same number of entries in the Layout array.\n\n      { 6,  0, 5 },  // FFTextureStage0ColorOp\n      { 6,  5, 5 },  // FFTextureStage0ColorArg1\n      { 6, 10, 5 },  // FFTextureStage0ColorArg2\n      { 6, 15, 5 },  // FFTextureStage0AlphaOp\n      { 6, 20, 5 },  // FFTextureStage0AlphaArg1\n      { 6, 25, 5 },  // FFTextureStage0AlphaArg2\n      { 6, 30, 1 },  // FFTextureStage0ResultIsTemp\n\n      { 7,  0, 5 },  // FFTextureStage1ColorOp\n      { 7,  5, 5 },  // FFTextureStage1ColorArg1\n      { 7, 10, 5 },  // FFTextureStage1ColorArg2\n      { 7, 15, 5 },  // FFTextureStage1AlphaOp\n      { 7, 20, 5 },  // FFTextureStage1AlphaArg1\n      { 7, 25, 5 },  // FFTextureStage1AlphaArg2\n      { 7, 30, 1 },  // FFTextureStage1ResultIsTemp\n\n      { 8,  0, 5 },  // FFTextureStage2ColorOp\n      { 8,  5, 5 },  // FFTextureStage2ColorArg1\n      { 8, 10, 5 },  // FFTextureStage2ColorArg2\n      { 8, 15, 5 },  // FFTextureStage2AlphaOp\n      { 8, 20, 5 },  // FFTextureStage2AlphaArg1\n      { 8, 25, 5 },  // FFTextureStage2AlphaArg2\n      { 8, 30, 1 },  // FFTextureStage2ResultIsTemp\n\n      { 9,  0, 5 },  // FFTextureStage3ColorOp\n      { 9,  5, 5 },  // FFTextureStage3ColorArg1\n      { 9, 10, 5 },  // FFTextureStage3ColorArg2\n      { 9, 15, 5 },  // FFTextureStage3AlphaOp\n      { 9, 20, 5 },  // FFTextureStage3AlphaArg1\n      { 9, 25, 5 },  // FFTextureStage3AlphaArg2\n      { 9, 30, 1 },  // FFTextureStage3ResultIsTemp\n\n      { 10,  0, 5 },  // FFTextureStage4ColorOp\n      { 10,  5, 5 },  // FFTextureStage4ColorArg1\n      { 10, 10, 5 },  // FFTextureStage4ColorArg2\n      { 10, 15, 5 },  // FFTextureStage4AlphaOp\n      { 10, 20, 5 },  // FFTextureStage4AlphaArg1\n      { 10, 25, 5 },  // FFTextureStage4AlphaArg2\n      { 10, 30, 1 },  // FFTextureStage4ResultIsTemp\n\n      { 11,  0, 5 },  // FFTextureStage5ColorOp\n      { 11,  5, 5 },  // FFTextureStage5ColorArg1\n      { 11, 10, 5 },  // FFTextureStage5ColorArg2\n      { 11, 15, 5 },  // FFTextureStage5AlphaOp\n      { 11, 20, 5 },  // FFTextureStage5AlphaArg1\n      { 11, 25, 5 },  // FFTextureStage5AlphaArg2\n      { 11, 30, 1 },  // FFTextureStage5ResultIsTemp\n\n      { 12,  0, 5 },  // FFTextureStage6ColorOp\n      { 12,  5, 5 },  // FFTextureStage6ColorArg1\n      { 12, 10, 5 },  // FFTextureStage6ColorArg2\n      { 12, 15, 5 },  // FFTextureStage6AlphaOp\n      { 12, 20, 5 },  // FFTextureStage6AlphaArg1\n      { 12, 25, 5 },  // FFTextureStage6AlphaArg2\n      { 12, 30, 1 },  // FFTextureStage6ResultIsTemp\n\n      { 13,  0, 5 },  // FFTextureStage7ColorOp\n      { 13,  5, 5 },  // FFTextureStage7ColorArg1\n      { 13, 10, 5 },  // FFTextureStage7ColorArg2\n      { 13, 15, 5 },  // FFTextureStage7AlphaOp\n      { 13, 20, 5 },  // FFTextureStage7AlphaArg1\n      { 13, 25, 5 },  // FFTextureStage7AlphaArg2\n      { 13, 30, 1 },  // FFTextureStage7ResultIsTemp\n\n      { 14,  0, 5 },  // FFTextureStage0ColorArg0\n      { 14,  5, 5 },  // FFTextureStage1ColorArg0\n      { 14, 10, 5 },  // FFTextureStage2ColorArg0\n      { 14, 15, 5 },  // FFTextureStage3ColorArg0\n      { 14, 20, 5 },  // FFTextureStage4ColorArg0\n      { 14, 25, 5 },  // FFTextureStage5ColorArg0\n\n      { 15,  0, 5 },  // FFTextureStage6ColorArg0\n      { 15,  5, 5 },  // FFTextureStage7ColorArg0\n      { 15, 10, 5 },  // FFTextureStage0AlphaArg0\n      { 15, 15, 5 },  // FFTextureStage1AlphaArg0\n      { 15, 20, 5 },  // FFTextureStage2AlphaArg0\n      { 15, 25, 5 },  // FFTextureStage3AlphaArg0\n\n      { 16,  0, 5 },  // FFTextureStage4AlphaArg0\n      { 16,  5, 5 },  // FFTextureStage5AlphaArg0\n      { 16, 10, 5 },  // FFTextureStage6AlphaArg0\n      { 16, 15, 5 },  // FFTextureStage7AlphaArg0\n    }};\n\n    template <D3D9SpecConstantId Id, typename T>\n    bool set(const T& value) {\n      const uint32_t x = uint32_t(value);\n      if (get<Id>() == x)\n        return false;\n\n      constexpr auto& layout = Layout[Id];\n\n      data[layout.dwordOffset] &= ~layout.mask();\n      data[layout.dwordOffset] |= (x << layout.bitOffset) & layout.mask();\n\n      return true;\n    }\n\n    template <typename T>\n    bool set(const D3D9SpecConstantId id, const T& value) {\n      const uint32_t x = uint32_t(value);\n      if (get(id) == x)\n        return false;\n\n      const auto& layout = Layout[id];\n\n      data[layout.dwordOffset] &= ~layout.mask();\n      data[layout.dwordOffset] |= (x << layout.bitOffset) & layout.mask();\n\n      return true;\n    }\n\n    template <D3D9SpecConstantId Id>\n    uint32_t get() const {\n      constexpr auto& layout = Layout[Id];\n\n      return (data[layout.dwordOffset] & layout.mask()) >> layout.bitOffset;\n    }\n\n    uint32_t get(const D3D9SpecConstantId id) const {\n      const auto& layout = Layout[id];\n\n      return (data[layout.dwordOffset] & layout.mask()) >> layout.bitOffset;\n    }\n\n    std::array<uint32_t, MaxSpecDwords> data = {};\n  };\n\n  class D3D9ShaderSpecConstantManager {\n  public:\n    uint32_t get(SpirvModule &module, uint32_t specUbo, D3D9SpecConstantId id) {\n      return get(module, specUbo, id, 0, 32);\n    }\n\n    uint32_t get(SpirvModule &module, uint32_t specUbo, D3D9SpecConstantId id, uint32_t bitOffset, uint32_t bitCount, uint32_t uboOverride = 0) {\n      const auto &layout = D3D9SpecializationInfo::Layout[id];\n\n      uint32_t uintType = module.defIntType(32, 0);\n      uint32_t optimized = getOptimizedBool(module);\n\n      uint32_t quickValue     = uboOverride ? uboOverride : getSpecUBODword(module, specUbo, layout.dwordOffset);\n      uint32_t optimizedValue = getSpecConstDword(module, layout.dwordOffset);\n\n      uint32_t val = module.opSelect(uintType, optimized, optimizedValue, quickValue);\n      bitCount = std::min(bitCount, layout.sizeInBits - bitOffset);\n\n      if (bitCount == 32)\n        return val;\n\n      return module.opBitFieldUExtract(\n        module.defIntType(32, 0), val,\n        module.consti32(bitOffset + layout.bitOffset),\n        module.consti32(bitCount));\n    }\n\n  private:\n    uint32_t getSpecConstDword(SpirvModule &module, uint32_t idx) {\n      if (!m_specConstantIds[idx]) {\n        m_specConstantIds[idx] = module.specConst32(module.defIntType(32, 0), 0);\n        module.decorateSpecId(m_specConstantIds[idx], idx);\n      }\n\n      return m_specConstantIds[idx];\n    }\n\n    uint32_t getSpecUBODword(SpirvModule& module, uint32_t specUbo, uint32_t idx) {\n      uint32_t uintType = module.defIntType(32, 0);\n      uint32_t uintPtr  = module.defPointerType(uintType, spv::StorageClassUniform);\n\n      uint32_t member = module.constu32(idx);\n      uint32_t dword  = module.opLoad(uintType, module.opAccessChain(uintPtr, specUbo, 1, &member));\n\n      return dword;\n    }\n\n    uint32_t getOptimizedBool(SpirvModule& module) {\n      uint32_t boolType = module.defBoolType();\n\n      // The spec constant at MaxNumSpecConstants is set to True\n      // when this is an optimized pipeline.\n      uint32_t optimized = getSpecConstDword(module, MaxNumSpecConstants);\n      optimized = module.opINotEqual(boolType, optimized, module.constu32(0));\n\n      return optimized;\n    }\n\n    std::array<uint32_t, MaxNumSpecConstants + 1> m_specConstantIds = {};\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_state.cpp",
    "content": "#include \"d3d9_state.h\"\n\n#include \"d3d9_texture.h\"\n\nnamespace dxvk {\n\n    template <template <typename T> typename ItemType>\n    D3D9State<ItemType>::D3D9State() {\n      for (uint32_t i = 0; i < streamFreq.size(); i++)\n        streamFreq[i] = 1;\n\n      for (uint32_t i = 0; i < enabledLightIndices.size(); i++)\n        enabledLightIndices[i] = std::numeric_limits<uint32_t>::max();\n    }\n\n\n    template <template <typename T> typename ItemType>\n    D3D9State<ItemType>::~D3D9State() {\n      if (textures) {\n        for (uint32_t i = 0; i < textures->size(); i++)\n          TextureChangePrivate(textures[i], nullptr);\n      }\n    }\n\n    template struct D3D9State<dynamic_item>;\n    template struct D3D9State<static_item>;\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_state.h",
    "content": "#pragma once\n\n#include \"d3d9_caps.h\"\n#include \"d3d9_constant_set.h\"\n#include \"../dxso/dxso_common.h\"\n#include \"../util/util_matrix.h\"\n\n#include \"d3d9_surface.h\"\n#include \"d3d9_shader.h\"\n#include \"d3d9_vertex_declaration.h\"\n#include \"d3d9_buffer.h\"\n\n#include <array>\n#include <bitset>\n#include <optional>\n\nnamespace dxvk {\n\n  static constexpr uint32_t RenderStateCount  = 256;\n  static constexpr uint32_t SamplerStateCount = D3DSAMP_DMAPOFFSET + 1;\n  static constexpr uint32_t SamplerCount      = caps::MaxTexturesPS + caps::MaxTexturesVS + 1;\n  static constexpr uint32_t TextureStageStateCount = DXVK_TSS_COUNT;\n  static constexpr uint32_t PaletteEntryCount = 256;\n  \n  struct D3D9ClipPlane {\n    float coeff[4] = {};\n\n    bool operator == (const D3D9ClipPlane& other) {\n      return std::memcmp(this, &other, sizeof(D3D9ClipPlane)) == 0;\n    }\n\n    bool operator != (const D3D9ClipPlane& other) {\n      return !this->operator == (other);\n    }\n  };\n\n  struct D3D9RenderStateInfo {\n    std::array<float, 3> fogColor = { };\n    float fogScale   = 0.0f;\n    float fogEnd     = 1.0f;\n    float fogDensity = 1.0f;\n\n    uint32_t alphaRef = 0u;\n\n    float pointSize    = 1.0f;\n    float pointSizeMin = 1.0f;\n    float pointSizeMax = 64.0f;\n    float pointScaleA  = 1.0f;\n    float pointScaleB  = 0.0f;\n    float pointScaleC  = 0.0f;\n  };\n\n  enum class D3D9RenderStateItem {\n    FogColor   = 0,\n    FogScale   = 1,\n    FogEnd,\n    FogDensity,\n    AlphaRef,\n\n    PointSize,\n    PointSizeMin,\n    PointSizeMax,\n    PointScaleA,\n    PointScaleB,\n    PointScaleC,\n\n    Count\n  };\n\n\n  // This is needed in fixed function for POSITION_T support.\n  // These are constants we need to * and add to move\n  // Window Coords -> Real Coords w/ respect to the viewport.\n  struct D3D9ViewportInfo {\n    Vector4 inverseOffset;\n    Vector4 inverseExtent;\n  };\n\n  struct D3D9Light {\n    D3D9Light(const D3DLIGHT9& light, Matrix4 viewMtx)\n      : Diffuse      ( Vector4(light.Diffuse.r,  light.Diffuse.g,  light.Diffuse.b,  light.Diffuse.a) )\n      , Specular     ( Vector4(light.Specular.r, light.Specular.g, light.Specular.b, light.Specular.a) )\n      , Ambient      ( Vector4(light.Ambient.r,  light.Ambient.g,  light.Ambient.b,  light.Ambient.a) )\n      , Position     ( viewMtx * Vector4(light.Position.x,  light.Position.y,  light.Position.z,  1.0f) )\n      , Direction    ( normalize(viewMtx * Vector4(light.Direction.x, light.Direction.y, light.Direction.z, 0.0f)) )\n      , Type         ( light.Type )\n      , Range        ( light.Range )\n      , Falloff      ( light.Falloff )\n      , Attenuation0 ( light.Attenuation0 )\n      , Attenuation1 ( light.Attenuation1 )\n      , Attenuation2 ( light.Attenuation2 )\n      , Theta        ( cosf(light.Theta / 2.0f) )\n      , Phi          ( cosf(light.Phi / 2.0f) ) { }\n\n    Vector4 Diffuse;\n    Vector4 Specular;\n    Vector4 Ambient;\n\n    Vector4 Position;\n    Vector4 Direction;\n\n    D3DLIGHTTYPE Type;\n    float Range;\n    float Falloff;\n    float Attenuation0;\n    float Attenuation1;\n    float Attenuation2;\n    float Theta;\n    float Phi;\n  };\n\n  struct D3D9FFShaderKeyVSData {\n    union {\n      struct {\n        uint32_t TexcoordIndices : 24;\n\n        uint32_t VertexHasPositionT : 1;\n\n        uint32_t VertexHasColor0 : 1; // Diffuse\n        uint32_t VertexHasColor1 : 1; // Specular\n\n        uint32_t VertexHasPointSize : 1;\n\n        uint32_t UseLighting : 1;\n\n        uint32_t NormalizeNormals : 1;\n        uint32_t LocalViewer : 1;\n        uint32_t RangeFog : 1;\n\n        // End of uint32_t\n\n        uint32_t TexcoordFlags : 24;\n\n        uint32_t DiffuseSource : 2;\n        uint32_t AmbientSource : 2;\n        uint32_t SpecularSource : 2;\n        uint32_t EmissiveSource : 2;\n\n        // Next uint32_t\n\n        uint32_t TransformFlags : 24;\n\n        uint32_t LightCount : 4;\n        uint32_t SpecularEnabled : 1;\n\n        // End of uint32_t\n\n        uint32_t VertexTexcoordDeclMask : 24;\n        uint32_t VertexHasFog : 1;\n\n        uint32_t VertexBlendMode    : 2;\n        uint32_t VertexBlendIndexed : 1;\n        uint32_t VertexBlendCount   : 2;\n\n        uint32_t VertexClipping     : 1;\n\n        // End of uint32_t\n      } Contents;\n\n      uint32_t Primitive[5];\n    };\n  };\n\n  struct D3D9FFShaderKeyVS {\n    D3D9FFShaderKeyVS() {\n      // memcmp safety\n      std::memset(&Data, 0, sizeof(Data));\n    }\n\n    D3D9FFShaderKeyVSData Data;\n  };\n\n  struct D3D9FixedFunctionVS {\n    Matrix4 WorldView;\n    Matrix4 NormalMatrix;\n    Matrix4 InverseView;\n    Matrix4 Projection;\n\n    std::array<Matrix4, 8> TexcoordMatrices;\n\n    D3D9ViewportInfo ViewportInfo;\n\n    Vector4 GlobalAmbient;\n    std::array<D3D9Light, caps::MaxEnabledLights> Lights;\n    D3DMATERIAL9 Material;\n    float TweenFactor;\n\n    D3D9FFShaderKeyVSData Key;\n  };\n\n\n  struct D3D9FixedFunctionVertexBlendDataHW {\n    Matrix4 WorldView[8];\n  };\n\n\n  struct D3D9FixedFunctionVertexBlendDataSW {\n    Matrix4 WorldView[256];\n  };\n\n\n  struct D3D9FFShaderStage {\n    union {\n      struct {\n        uint32_t     ColorOp   : 5;\n        uint32_t     ColorArg0 : 6;\n        uint32_t     ColorArg1 : 6;\n        uint32_t     ColorArg2 : 6;\n\n        uint32_t     AlphaOp   : 5;\n        uint32_t     AlphaArg0 : 6;\n        uint32_t     AlphaArg1 : 6;\n        uint32_t     AlphaArg2 : 6;\n\n        uint32_t     ResultIsTemp : 1;\n\n        // Included in here, read from Stage 0 for packing reasons\n        // Affects all stages.\n        uint32_t     GlobalSpecularEnable : 1;\n      } Contents;\n\n      uint32_t Primitive[2];\n    };\n  };\n\n  struct D3D9FFShaderKeyFS {\n    D3D9FFShaderKeyFS() {\n      // memcmp safety\n      std::memset(Stages, 0, sizeof(Stages));\n\n      // Normalize this. DISABLE != 0.\n      for (uint32_t i = 0; i < caps::TextureStageCount; i++) {\n        Stages[i].Contents.ColorOp = D3DTOP_DISABLE;\n        Stages[i].Contents.AlphaOp = D3DTOP_DISABLE;\n      }\n    }\n\n    D3D9FFShaderStage Stages[caps::TextureStageCount];\n  };\n\n  struct D3D9FixedFunctionPS {\n    Vector4 textureFactor;\n    D3D9FFShaderKeyFS Key;\n  };\n\n  enum D3D9SharedPSStages {\n    D3D9SharedPSStages_Constant,\n    D3D9SharedPSStages_BumpEnvMat0,\n    D3D9SharedPSStages_BumpEnvMat1,\n    D3D9SharedPSStages_BumpEnvLScale,\n    D3D9SharedPSStages_BumpEnvLOffset,\n    D3D9SharedPSStages_Count,\n  };\n\n  struct D3D9SharedPS {\n    struct Stage {\n      float Constant[4];\n      float BumpEnvMat[2][2];\n      float BumpEnvLScale;\n      float BumpEnvLOffset;\n      float Padding[2];\n    } Stages[8];\n  };\n  \n  struct D3D9VBO {\n    Com<D3D9VertexBuffer, false> vertexBuffer;\n\n    UINT              offset = 0;\n    UINT              stride = 0;\n  };\n\n  constexpr D3DLIGHT9 DefaultLight = {\n    D3DLIGHT_DIRECTIONAL,     // Type\n    {1.0f, 1.0f, 1.0f, 0.0f}, // Diffuse\n    {0.0f, 0.0f, 0.0f, 0.0f}, // Specular\n    {0.0f, 0.0f, 0.0f, 0.0f}, // Ambient\n    {0.0f, 0.0f, 0.0f},       // Position\n    {0.0f, 0.0f, 1.0f},       // Direction\n    0.0f,                     // Range\n    0.0f,                     // Falloff\n    0.0f, 0.0f, 0.0f,         // Attenuations [constant, linear, quadratic]\n    0.0f,                     // Theta\n    0.0f                      // Phi\n  };\n\n  template <typename T>\n  class dynamic_item {\n  public:\n          auto& operator [] (size_t idx)       { ensure(); return (*m_data)[idx]; }\n    const auto& operator [] (size_t idx) const { ensure(); return (*m_data)[idx]; }\n\n    T& operator=(const T& x) { ensure(); *m_data = x; return *m_data; }\n\n    const T* operator -> () const { ensure(); return m_data.get(); }\n          T* operator -> ()       { ensure(); return m_data.get(); }\n\n    const T* operator & () const { ensure(); return m_data.get(); }\n          T* operator & ()       { ensure(); return m_data.get(); }\n\n    explicit operator bool() const { return m_data != nullptr; }\n    operator T() { ensure(); return *m_data; }\n\n    void ensure() const { if (!m_data) m_data = std::make_unique<T>(); }\n\n    T& get() { ensure(); return *m_data; }\n  private:\n    mutable std::unique_ptr<T> m_data;\n  };\n\n  template <typename T>\n  class static_item {\n  public:\n          auto& operator [] (size_t idx)       { return m_data[idx]; }\n    const auto& operator [] (size_t idx) const { return m_data[idx]; }\n\n    T& operator=(const T& x) { m_data = x; return m_data; }\n\n    explicit operator bool() const { return true; }\n    operator T() { return m_data; }\n\n    const T* operator -> () const { return &m_data; }\n          T* operator -> ()       { return &m_data; }\n\n    const T* operator & () const { return &m_data; }\n          T* operator & ()       { return &m_data; }\n\n    T& get() { return m_data; }\n  private:\n    T m_data;\n  };\n\n  struct D3D9SamplerInfo {\n    D3D9SamplerInfo(const std::array<DWORD, SamplerStateCount>& state)\n    : addressU(D3DTEXTUREADDRESS(state[D3DSAMP_ADDRESSU]))\n    , addressV(D3DTEXTUREADDRESS(state[D3DSAMP_ADDRESSV]))\n    , addressW(D3DTEXTUREADDRESS(state[D3DSAMP_ADDRESSW]))\n    , borderColor(D3DCOLOR(state[D3DSAMP_BORDERCOLOR]))\n    , magFilter(D3DTEXTUREFILTERTYPE(state[D3DSAMP_MAGFILTER]))\n    , minFilter(D3DTEXTUREFILTERTYPE(state[D3DSAMP_MINFILTER]))\n    , mipFilter(D3DTEXTUREFILTERTYPE(state[D3DSAMP_MIPFILTER]))\n    , mipLodBias(bit::cast<float>(state[D3DSAMP_MIPMAPLODBIAS]))\n    , maxMipLevel(state[D3DSAMP_MAXMIPLEVEL])\n    , maxAnisotropy(state[D3DSAMP_MAXANISOTROPY]) { }\n\n    D3DTEXTUREADDRESS addressU;\n    D3DTEXTUREADDRESS addressV;\n    D3DTEXTUREADDRESS addressW;\n    D3DCOLOR borderColor;\n    D3DTEXTUREFILTERTYPE magFilter;\n    D3DTEXTUREFILTERTYPE minFilter;\n    D3DTEXTUREFILTERTYPE mipFilter;\n    float mipLodBias;\n    DWORD maxMipLevel;\n    DWORD maxAnisotropy;\n  };\n\n  template <template <typename T> typename ItemType>\n  struct D3D9State {\n    D3D9State();\n    ~D3D9State();\n\n    Com<D3D9VertexDecl,  false>                         vertexDecl;\n    Com<D3D9IndexBuffer, false>                         indices;\n\n    ItemType<std::array<DWORD, RenderStateCount>>       renderStates = {};\n\n    ItemType<std::array<\n      std::array<DWORD, SamplerStateCount>,\n      SamplerCount>>                                    samplerStates = {};\n\n    ItemType<std::array<D3D9VBO, caps::MaxStreams>>     vertexBuffers = {};\n\n    ItemType<std::array<\n      IDirect3DBaseTexture9*,\n      SamplerCount>>                                    textures = {};\n\n    Com<D3D9VertexShader, false>                        vertexShader;\n    Com<D3D9PixelShader,  false>                        pixelShader;\n\n    D3DVIEWPORT9                                        viewport = {};\n    RECT                                                scissorRect = {};\n\n    D3DCLIPSTATUS9                                      clipStatus = {0, 0xffffffff};\n\n    ItemType<std::array<\n      D3D9ClipPlane,\n      caps::MaxClipPlanes>>                             clipPlanes = {};\n\n    ItemType<std::array<\n      std::array<DWORD, TextureStageStateCount>,\n      caps::TextureStageCount>>                         textureStages = {};\n\n    std::unordered_map<\n       UINT,\n       std::array<PALETTEENTRY, PaletteEntryCount>>     texturePalettes;\n    UINT                                                texturePaletteNumber = 0u;\n\n    ItemType<D3D9ShaderConstantsVSSoftware>             vsConsts;\n    ItemType<D3D9ShaderConstantsPS>                     psConsts;\n\n    std::array<UINT, caps::MaxStreams>                  streamFreq = {};\n\n    ItemType<std::array<Matrix4, caps::MaxTransforms>>  transforms = {};\n\n    ItemType<D3DMATERIAL9>                              material = {};\n\n    std::vector<std::optional<D3DLIGHT9>>               lights;\n    std::array<DWORD, caps::MaxEnabledLights>           enabledLightIndices;\n\n    float                                               nPatchSegments = 0.0f;\n\n    bool IsLightEnabled(DWORD Index) const {\n      const auto& enabledIndices = enabledLightIndices;\n      return std::find(enabledIndices.begin(), enabledIndices.end(), Index) != enabledIndices.end();\n    }\n  };\n\n  using D3D9CapturableState = D3D9State<dynamic_item>;\n  using D3D9DeviceState = D3D9State<static_item>;\n\n  template <\n    D3D9ShaderType   ShaderType,\n    D3D9ConstantType ConstantType,\n    typename         T,\n    typename         StateType>\n  HRESULT UpdateStateConstants(\n          StateType*           pState,\n          UINT                 StartRegister,\n    const T*                   pConstantData,\n          UINT                 Count,\n          bool                 FloatEmu) {\n    auto UpdateHelper = [&] (auto& set) {\n      if constexpr (ConstantType == D3D9ConstantType::Float) {\n\n        if (!FloatEmu) {\n          size_t size = Count * sizeof(Vector4);\n\n          std::memcpy(set->fConsts[StartRegister].data, pConstantData, size);\n        }\n        else {\n          for (UINT i = 0; i < Count; i++)\n            set->fConsts[StartRegister + i] = replaceNaN(pConstantData + (i * 4));\n        }\n      }\n      else if constexpr (ConstantType == D3D9ConstantType::Int) {\n        size_t size = Count * sizeof(Vector4i);\n\n        std::memcpy(set->iConsts[StartRegister].data, pConstantData, size);\n      }\n      else {\n        for (uint32_t i = 0; i < Count; i++) {\n          const uint32_t constantIdx = StartRegister + i;\n          const uint32_t arrayIdx    = constantIdx / 32;\n          const uint32_t bitIdx      = constantIdx % 32;\n\n          const uint32_t bit = 1u << bitIdx;\n\n          set->bConsts[arrayIdx] &= ~bit;\n          if (pConstantData[i])\n            set->bConsts[arrayIdx] |= bit;\n        }\n      }\n\n      return D3D_OK;\n    };\n\n    return ShaderType == D3D9ShaderType::VertexShader\n      ? UpdateHelper(pState->vsConsts)\n      : UpdateHelper(pState->psConsts);\n  }\n\n  struct Direct3DState9 : public D3D9DeviceState {\n\n    std::array<Com<D3D9Surface, false>, caps::MaxSimultaneousRenderTargets> renderTargets;\n    Com<D3D9Surface, false> depthStencil;\n\n  };\n\n\n  struct D3D9InputAssemblyState {\n    D3DPRIMITIVETYPE primitiveType = D3DPRIMITIVETYPE(0);\n    uint32_t streamsInstanced = 0;\n    uint32_t streamsUsed      = 0;\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_stateblock.cpp",
    "content": "#include \"d3d9_stateblock.h\"\n\n#include \"d3d9_caps.h\"\n#include \"d3d9_device.h\"\n#include \"d3d9_vertex_declaration.h\"\n#include \"d3d9_buffer.h\"\n#include \"d3d9_shader.h\"\n#include \"d3d9_texture.h\"\n\n#include \"d3d9_util.h\"\n\nnamespace dxvk {\n\n  D3D9StateBlock::D3D9StateBlock(D3D9DeviceEx* pDevice, D3D9StateBlockType Type)\n    : D3D9StateBlockBase(pDevice)\n    , m_deviceState     (pDevice->GetRawState()) {\n    CaptureType(Type);\n  }\n\n  D3D9StateBlock::~D3D9StateBlock() {\n    if (!m_parent->IsD3D8Compatible())\n      m_parent->DecrementLosableCounter();\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9StateBlock::QueryInterface(\n          REFIID  riid,\n          void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DStateBlock9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DStateBlock9), riid)) {\n      Logger::warn(\"D3D9StateBlock::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9StateBlock::Capture() {\n    D3D9DeviceLock lock = m_parent->LockDevice();\n\n    // A state block can't capture state while another is being recorded.\n    if (unlikely(m_parent->ShouldRecord()))\n      return D3DERR_INVALIDCALL;\n\n    if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl))\n      SetVertexDeclaration(m_deviceState->vertexDecl.ptr());\n\n    ApplyOrCapture<D3D9StateFunction::Capture, true>();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9StateBlock::Apply() {\n    D3D9DeviceLock lock = m_parent->LockDevice();\n\n    // A state block can't be applied while another is being recorded.\n    if (unlikely(m_parent->ShouldRecord()))\n      return D3DERR_INVALIDCALL;\n\n    if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl) && m_state.vertexDecl != nullptr)\n      m_parent->SetVertexDeclaration(m_state.vertexDecl.ptr());\n\n    ApplyOrCapture<D3D9StateFunction::Apply, false>();\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetVertexDeclaration(D3D9VertexDecl* pDecl) {\n    m_state.vertexDecl = pDecl;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::VertexDecl);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetIndices(D3D9IndexBuffer* pIndexData) {\n    m_state.indices = pIndexData;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::Indices);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) {\n    m_state.renderStates[State] = Value;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::RenderStates);\n    m_captures.renderStates.set(State, true);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetStateSamplerState(\n          DWORD               StateSampler,\n          D3DSAMPLERSTATETYPE Type,\n          DWORD               Value) {\n    m_state.samplerStates[StateSampler][Type] = Value;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);\n    m_captures.samplers.set(StateSampler, true);\n    m_captures.samplerStates[StateSampler].set(Type, true);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetStreamSource(\n          UINT                StreamNumber,\n          D3D9VertexBuffer*   pStreamData,\n          UINT                OffsetInBytes,\n          UINT                Stride) {\n    m_state.vertexBuffers[StreamNumber].vertexBuffer = pStreamData;\n\n    m_state.vertexBuffers[StreamNumber].offset = OffsetInBytes;\n    m_state.vertexBuffers[StreamNumber].stride = Stride;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::VertexBuffers);\n    m_captures.vertexBuffers.set(StreamNumber, true);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetStreamSourceWithoutOffset(\n          UINT                StreamNumber,\n          D3D9VertexBuffer*   pStreamData,\n          UINT                Stride) {\n    m_state.vertexBuffers[StreamNumber].vertexBuffer = pStreamData;\n\n    m_state.vertexBuffers[StreamNumber].stride = Stride;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::VertexBuffers);\n    m_captures.vertexBuffers.set(StreamNumber, true);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) {\n    m_state.streamFreq[StreamNumber] = Setting;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::StreamFreq);\n    m_captures.streamFreq.set(StreamNumber, true);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture) {\n    TextureChangePrivate(m_state.textures[StateSampler], pTexture);\n\n    m_captures.flags.set(D3D9CapturedStateFlag::Textures);\n    m_captures.textures.set(StateSampler, true);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetVertexShader(D3D9VertexShader* pShader) {\n    m_state.vertexShader = pShader;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::VertexShader);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetPixelShader(D3D9PixelShader* pShader) {\n    m_state.pixelShader = pShader;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::PixelShader);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetMaterial(const D3DMATERIAL9* pMaterial) {\n    m_state.material = *pMaterial;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::Material);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetLight(DWORD Index, const D3DLIGHT9* pLight) {\n    if (Index >= m_state.lights.size())\n      m_state.lights.resize(Index + 1);\n\n    m_state.lights[Index] = *pLight;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::Lights);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::LightEnable(DWORD Index, BOOL Enable) {\n    if (unlikely(Index >= m_state.lights.size()))\n      m_state.lights.resize(Index + 1);\n\n    if (unlikely(!m_state.lights[Index]))\n      m_state.lights[Index] = DefaultLight;\n\n    if (m_state.IsLightEnabled(Index) == !!Enable)\n      return D3D_OK;\n\n    uint32_t searchIndex = std::numeric_limits<uint32_t>::max();\n    uint32_t setIndex    = Index;\n\n    if (!Enable)\n      std::swap(searchIndex, setIndex);\n\n    for (auto& idx : m_state.enabledLightIndices) {\n      if (idx == searchIndex) {\n        idx = setIndex;\n        break;\n      }\n    }\n\n    m_captures.lightEnabledChanges.set(Index, true);\n    m_captures.flags.set(D3D9CapturedStateFlag::Lights);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix) {\n    m_state.transforms[idx] = ConvertMatrix(pMatrix);\n\n    m_captures.flags.set(D3D9CapturedStateFlag::Transforms);\n    m_captures.transforms.set(idx, true);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetStateTextureStageState(\n          DWORD                      Stage,\n          D3D9TextureStageStateTypes Type,\n          DWORD                      Value) {\n    Stage = std::min(Stage, DWORD(caps::TextureStageCount - 1));\n    Type = std::min(Type, D3D9TextureStageStateTypes(DXVK_TSS_COUNT - 1));\n\n    m_state.textureStages[Stage][Type] = Value;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::TextureStages);\n    m_captures.textureStages.set(Stage, true);\n    m_captures.textureStageStates[Stage].set(Type, true);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetViewport(const D3DVIEWPORT9* pViewport) {\n    m_state.viewport = *pViewport;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::Viewport);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetScissorRect(const RECT* pRect) {\n    m_state.scissorRect = *pRect;\n\n    m_captures.flags.set(D3D9CapturedStateFlag::ScissorRect);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetClipPlane(DWORD Index, const float* pPlane) {\n    for (uint32_t i = 0; i < 4; i++)\n      m_state.clipPlanes[Index].coeff[i] = pPlane[i];\n\n    m_captures.flags.set(D3D9CapturedStateFlag::ClipPlanes);\n    m_captures.clipPlanes.set(Index, true);\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetVertexShaderConstantF(\n          UINT   StartRegister,\n    const float* pConstantData,\n          UINT   Vector4fCount) {\n    return SetShaderConstants<\n      D3D9ShaderType::VertexShader,\n      D3D9ConstantType::Float>(\n        StartRegister,\n        pConstantData,\n        Vector4fCount);\n  }\n\n\n  HRESULT D3D9StateBlock::SetVertexShaderConstantI(\n          UINT StartRegister,\n    const int* pConstantData,\n          UINT Vector4iCount) {\n    return SetShaderConstants<\n      D3D9ShaderType::VertexShader,\n      D3D9ConstantType::Int>(\n        StartRegister,\n        pConstantData,\n        Vector4iCount);\n  }\n\n\n  HRESULT D3D9StateBlock::SetVertexShaderConstantB(\n          UINT  StartRegister,\n    const BOOL* pConstantData,\n          UINT  BoolCount) {\n    return SetShaderConstants<\n      D3D9ShaderType::VertexShader,\n      D3D9ConstantType::Bool>(\n        StartRegister,\n        pConstantData,\n        BoolCount);\n  }\n\n\n  HRESULT D3D9StateBlock::SetPixelShaderConstantF(\n          UINT   StartRegister,\n    const float* pConstantData,\n          UINT   Vector4fCount) {\n    return SetShaderConstants<\n      D3D9ShaderType::PixelShader,\n      D3D9ConstantType::Float>(\n        StartRegister,\n        pConstantData,\n        Vector4fCount);\n  }\n\n\n  HRESULT D3D9StateBlock::SetPixelShaderConstantI(\n          UINT StartRegister,\n    const int* pConstantData,\n          UINT Vector4iCount) {\n    return SetShaderConstants<\n      D3D9ShaderType::PixelShader,\n      D3D9ConstantType::Int>(\n        StartRegister,\n        pConstantData,\n        Vector4iCount);\n  }\n\n\n  HRESULT D3D9StateBlock::SetPixelShaderConstantB(\n          UINT  StartRegister,\n    const BOOL* pConstantData,\n          UINT  BoolCount) {\n    return SetShaderConstants<\n      D3D9ShaderType::PixelShader,\n      D3D9ConstantType::Bool>(\n        StartRegister,\n        pConstantData,\n        BoolCount);\n  }\n\n\n  HRESULT D3D9StateBlock::SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) {\n    m_state.vsConsts->bConsts[idx] &= ~mask;\n    m_state.vsConsts->bConsts[idx] |= bits & mask;\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9StateBlock::SetPixelBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) {\n    m_state.psConsts->bConsts[idx] &= ~mask;\n    m_state.psConsts->bConsts[idx] |= bits & mask;\n    return D3D_OK;\n  }\n\n\n  void D3D9StateBlock::CapturePixelRenderStates() {\n    m_captures.flags.set(D3D9CapturedStateFlag::RenderStates);\n\n    m_captures.renderStates.set(D3DRS_ZENABLE, true);\n    m_captures.renderStates.set(D3DRS_FILLMODE, true);\n    m_captures.renderStates.set(D3DRS_SHADEMODE, true);\n    m_captures.renderStates.set(D3DRS_ZWRITEENABLE, true);\n    m_captures.renderStates.set(D3DRS_ALPHATESTENABLE, true);\n    m_captures.renderStates.set(D3DRS_LASTPIXEL, true);\n    m_captures.renderStates.set(D3DRS_SRCBLEND, true);\n    m_captures.renderStates.set(D3DRS_DESTBLEND, true);\n    m_captures.renderStates.set(D3DRS_ZFUNC, true);\n    m_captures.renderStates.set(D3DRS_ALPHAREF, true);\n    m_captures.renderStates.set(D3DRS_ALPHAFUNC, true);\n    m_captures.renderStates.set(D3DRS_DITHERENABLE, true);\n    m_captures.renderStates.set(D3DRS_FOGSTART, true);\n    m_captures.renderStates.set(D3DRS_FOGEND, true);\n    m_captures.renderStates.set(D3DRS_FOGDENSITY, true);\n    m_captures.renderStates.set(D3DRS_ALPHABLENDENABLE, true);\n    m_captures.renderStates.set(D3DRS_DEPTHBIAS, true);\n    m_captures.renderStates.set(D3DRS_STENCILENABLE, true);\n    m_captures.renderStates.set(D3DRS_STENCILFAIL, true);\n    m_captures.renderStates.set(D3DRS_STENCILZFAIL, true);\n    m_captures.renderStates.set(D3DRS_STENCILPASS, true);\n    m_captures.renderStates.set(D3DRS_STENCILFUNC, true);\n    m_captures.renderStates.set(D3DRS_STENCILREF, true);\n    m_captures.renderStates.set(D3DRS_STENCILMASK, true);\n    m_captures.renderStates.set(D3DRS_STENCILWRITEMASK, true);\n    m_captures.renderStates.set(D3DRS_TEXTUREFACTOR, true);\n    m_captures.renderStates.set(D3DRS_WRAP0, true);\n    m_captures.renderStates.set(D3DRS_WRAP1, true);\n    m_captures.renderStates.set(D3DRS_WRAP2, true);\n    m_captures.renderStates.set(D3DRS_WRAP3, true);\n    m_captures.renderStates.set(D3DRS_WRAP4, true);\n    m_captures.renderStates.set(D3DRS_WRAP5, true);\n    m_captures.renderStates.set(D3DRS_WRAP6, true);\n    m_captures.renderStates.set(D3DRS_WRAP7, true);\n    m_captures.renderStates.set(D3DRS_WRAP8, true);\n    m_captures.renderStates.set(D3DRS_WRAP9, true);\n    m_captures.renderStates.set(D3DRS_WRAP10, true);\n    m_captures.renderStates.set(D3DRS_WRAP11, true);\n    m_captures.renderStates.set(D3DRS_WRAP12, true);\n    m_captures.renderStates.set(D3DRS_WRAP13, true);\n    m_captures.renderStates.set(D3DRS_WRAP14, true);\n    m_captures.renderStates.set(D3DRS_WRAP15, true);\n    m_captures.renderStates.set(D3DRS_COLORWRITEENABLE, true);\n    m_captures.renderStates.set(D3DRS_BLENDOP, true);\n    m_captures.renderStates.set(D3DRS_SCISSORTESTENABLE, true);\n    m_captures.renderStates.set(D3DRS_SLOPESCALEDEPTHBIAS, true);\n    m_captures.renderStates.set(D3DRS_ANTIALIASEDLINEENABLE, true);\n    m_captures.renderStates.set(D3DRS_TWOSIDEDSTENCILMODE, true);\n    m_captures.renderStates.set(D3DRS_CCW_STENCILFAIL, true);\n    m_captures.renderStates.set(D3DRS_CCW_STENCILZFAIL, true);\n    m_captures.renderStates.set(D3DRS_CCW_STENCILPASS, true);\n    m_captures.renderStates.set(D3DRS_CCW_STENCILFUNC, true);\n    m_captures.renderStates.set(D3DRS_COLORWRITEENABLE1, true);\n    m_captures.renderStates.set(D3DRS_COLORWRITEENABLE2, true);\n    m_captures.renderStates.set(D3DRS_COLORWRITEENABLE3, true);\n    m_captures.renderStates.set(D3DRS_BLENDFACTOR, true);\n    m_captures.renderStates.set(D3DRS_SRGBWRITEENABLE, true);\n    m_captures.renderStates.set(D3DRS_SEPARATEALPHABLENDENABLE, true);\n    m_captures.renderStates.set(D3DRS_SRCBLENDALPHA, true);\n    m_captures.renderStates.set(D3DRS_DESTBLENDALPHA, true);\n    m_captures.renderStates.set(D3DRS_BLENDOPALPHA, true);\n  }\n\n\n  void D3D9StateBlock::CapturePixelSamplerStates() {\n    m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);\n\n    for (uint32_t i = 0; i < FirstVSSamplerSlot; i++) {\n      m_captures.samplers.set(i, true);\n\n      m_captures.samplerStates[i].set(D3DSAMP_ADDRESSU, true);\n      m_captures.samplerStates[i].set(D3DSAMP_ADDRESSV, true);\n      m_captures.samplerStates[i].set(D3DSAMP_ADDRESSW, true);\n      m_captures.samplerStates[i].set(D3DSAMP_BORDERCOLOR, true);\n      m_captures.samplerStates[i].set(D3DSAMP_MAGFILTER, true);\n      m_captures.samplerStates[i].set(D3DSAMP_MINFILTER, true);\n      m_captures.samplerStates[i].set(D3DSAMP_MIPFILTER, true);\n      m_captures.samplerStates[i].set(D3DSAMP_MIPMAPLODBIAS, true);\n      m_captures.samplerStates[i].set(D3DSAMP_MAXMIPLEVEL, true);\n      m_captures.samplerStates[i].set(D3DSAMP_MAXANISOTROPY, true);\n      m_captures.samplerStates[i].set(D3DSAMP_SRGBTEXTURE, true);\n      m_captures.samplerStates[i].set(D3DSAMP_ELEMENTINDEX, true);\n    }\n  }\n\n\n  void D3D9StateBlock::CapturePixelShaderStates() {\n    m_captures.flags.set(D3D9CapturedStateFlag::PixelShader);\n    m_captures.flags.set(D3D9CapturedStateFlag::PsConstants);\n\n    m_captures.psConsts.fConsts.setAll();\n    m_captures.psConsts.iConsts.setAll();\n    m_captures.psConsts.bConsts.setAll();\n  }\n\n\n  void D3D9StateBlock::CaptureVertexRenderStates() {\n    m_captures.flags.set(D3D9CapturedStateFlag::RenderStates);\n\n    m_captures.renderStates.set(D3DRS_CULLMODE, true);\n    m_captures.renderStates.set(D3DRS_FOGENABLE, true);\n    m_captures.renderStates.set(D3DRS_FOGCOLOR, true);\n    m_captures.renderStates.set(D3DRS_FOGTABLEMODE, true);\n    m_captures.renderStates.set(D3DRS_FOGSTART, true);\n    m_captures.renderStates.set(D3DRS_FOGEND, true);\n    m_captures.renderStates.set(D3DRS_FOGDENSITY, true);\n    m_captures.renderStates.set(D3DRS_RANGEFOGENABLE, true);\n    m_captures.renderStates.set(D3DRS_AMBIENT, true);\n    m_captures.renderStates.set(D3DRS_COLORVERTEX, true);\n    m_captures.renderStates.set(D3DRS_FOGVERTEXMODE, true);\n    m_captures.renderStates.set(D3DRS_CLIPPING, true);\n    m_captures.renderStates.set(D3DRS_LIGHTING, true);\n    m_captures.renderStates.set(D3DRS_LOCALVIEWER, true);\n    m_captures.renderStates.set(D3DRS_EMISSIVEMATERIALSOURCE, true);\n    m_captures.renderStates.set(D3DRS_AMBIENTMATERIALSOURCE, true);\n    m_captures.renderStates.set(D3DRS_DIFFUSEMATERIALSOURCE, true);\n    m_captures.renderStates.set(D3DRS_SPECULARMATERIALSOURCE, true);\n    m_captures.renderStates.set(D3DRS_VERTEXBLEND, true);\n    m_captures.renderStates.set(D3DRS_CLIPPLANEENABLE, true);\n    m_captures.renderStates.set(D3DRS_POINTSIZE, true);\n    m_captures.renderStates.set(D3DRS_POINTSIZE_MIN, true);\n    m_captures.renderStates.set(D3DRS_POINTSPRITEENABLE, true);\n    m_captures.renderStates.set(D3DRS_POINTSCALEENABLE, true);\n    m_captures.renderStates.set(D3DRS_POINTSCALE_A, true);\n    m_captures.renderStates.set(D3DRS_POINTSCALE_B, true);\n    m_captures.renderStates.set(D3DRS_POINTSCALE_C, true);\n    m_captures.renderStates.set(D3DRS_MULTISAMPLEANTIALIAS, true);\n    m_captures.renderStates.set(D3DRS_MULTISAMPLEMASK, true);\n    m_captures.renderStates.set(D3DRS_PATCHEDGESTYLE, true);\n    m_captures.renderStates.set(D3DRS_POINTSIZE_MAX, true);\n    m_captures.renderStates.set(D3DRS_INDEXEDVERTEXBLENDENABLE, true);\n    m_captures.renderStates.set(D3DRS_TWEENFACTOR, true);\n    m_captures.renderStates.set(D3DRS_POSITIONDEGREE, true);\n    m_captures.renderStates.set(D3DRS_NORMALDEGREE, true);\n    m_captures.renderStates.set(D3DRS_MINTESSELLATIONLEVEL, true);\n    m_captures.renderStates.set(D3DRS_MAXTESSELLATIONLEVEL, true);\n    m_captures.renderStates.set(D3DRS_ADAPTIVETESS_X, true);\n    m_captures.renderStates.set(D3DRS_ADAPTIVETESS_Y, true);\n    m_captures.renderStates.set(D3DRS_ADAPTIVETESS_Z, true);\n    m_captures.renderStates.set(D3DRS_ADAPTIVETESS_W, true);\n    m_captures.renderStates.set(D3DRS_ENABLEADAPTIVETESSELLATION, true);\n    m_captures.renderStates.set(D3DRS_NORMALIZENORMALS, true);\n    m_captures.renderStates.set(D3DRS_SPECULARENABLE, true);\n    m_captures.renderStates.set(D3DRS_SHADEMODE, true);\n  }\n\n\n  void D3D9StateBlock::CaptureVertexSamplerStates() {\n    m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);\n\n    for (uint32_t i = FirstVSSamplerSlot; i < SamplerCount; i++) {\n      m_captures.samplers.set(i, true);\n      m_captures.samplerStates[i].set(D3DSAMP_DMAPOFFSET, true);\n    }\n  }\n\n\n  void D3D9StateBlock::CaptureVertexShaderStates() {\n    m_captures.flags.set(D3D9CapturedStateFlag::VertexShader);\n    m_captures.flags.set(D3D9CapturedStateFlag::VsConstants);\n\n    m_captures.vsConsts.fConsts.setN(m_parent->GetVertexConstantLayout().floatCount);\n    m_captures.vsConsts.iConsts.setN(m_parent->GetVertexConstantLayout().intCount);\n    m_captures.vsConsts.bConsts.setN(m_parent->GetVertexConstantLayout().boolCount);\n  }\n\n\n  void D3D9StateBlock::CaptureType(D3D9StateBlockType Type) {\n    if (Type == D3D9StateBlockType::PixelState || Type == D3D9StateBlockType::All) {\n      CapturePixelRenderStates();\n      CapturePixelSamplerStates();\n      CapturePixelShaderStates();\n\n      m_captures.flags.set(D3D9CapturedStateFlag::TextureStages);\n      m_captures.textureStages.setAll();\n      for (auto& stage : m_captures.textureStageStates)\n        stage.setAll();\n    }\n\n    if (Type == D3D9StateBlockType::VertexState || Type == D3D9StateBlockType::All) {\n      CaptureVertexRenderStates();\n      CaptureVertexSamplerStates();\n      CaptureVertexShaderStates();\n\n      m_captures.flags.set(D3D9CapturedStateFlag::VertexDecl);\n      m_captures.flags.set(D3D9CapturedStateFlag::StreamFreq);\n      m_captures.flags.set(D3D9CapturedStateFlag::Lights);\n      m_captures.lightEnabledChanges.setN(m_deviceState->lights.size());\n\n      for (uint32_t i = 0; i < caps::MaxStreams; i++)\n        m_captures.streamFreq.set(i, true);\n    }\n\n    if (Type == D3D9StateBlockType::All) {\n      m_captures.flags.set(D3D9CapturedStateFlag::Textures);\n      m_captures.textures.setAll();\n\n      m_captures.flags.set(D3D9CapturedStateFlag::VertexBuffers);\n      m_captures.vertexBuffers.setAll();\n\n      m_captures.flags.set(D3D9CapturedStateFlag::Indices);\n      m_captures.flags.set(D3D9CapturedStateFlag::Viewport);\n      m_captures.flags.set(D3D9CapturedStateFlag::ScissorRect);\n\n      m_captures.flags.set(D3D9CapturedStateFlag::ClipPlanes);\n      m_captures.clipPlanes.setAll();\n\n      m_captures.flags.set(D3D9CapturedStateFlag::Transforms);\n      m_captures.transforms.setAll();\n\n      m_captures.flags.set(D3D9CapturedStateFlag::Material);\n    }\n\n    if (Type != D3D9StateBlockType::None) {\n      if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl))\n        SetVertexDeclaration(m_deviceState->vertexDecl.ptr());\n\n      ApplyOrCapture<D3D9StateFunction::Capture, false>();\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_stateblock.h",
    "content": "#pragma once\n\n#include \"d3d9_device_child.h\"\n#include \"d3d9_device.h\"\n#include \"d3d9_state.h\"\n\n#include \"../util/util_bit.h\"\n\nnamespace dxvk {\n\n  enum class D3D9CapturedStateFlag : uint32_t {\n    VertexDecl,\n    Indices,\n    RenderStates,\n    SamplerStates,\n    VertexBuffers,\n    Textures,\n    VertexShader,\n    PixelShader,\n    Viewport,\n    ScissorRect,\n    ClipPlanes,\n    VsConstants,\n    PsConstants,\n    StreamFreq,\n    Transforms,\n    TextureStages,\n    Material,\n    Lights\n  };\n\n  using D3D9CapturedStateFlags = Flags<D3D9CapturedStateFlag>;\n\n  struct D3D9StateCaptures {\n    D3D9CapturedStateFlags flags;\n\n    bit::bitset<RenderStateCount>                       renderStates;\n\n    bit::bitset<SamplerCount>                           samplers;\n    std::array<\n      bit::bitset<SamplerStateCount>,\n      SamplerCount>                                     samplerStates;\n\n    bit::bitset<caps::MaxStreams>                       vertexBuffers;\n    bit::bitset<SamplerCount>                           textures;\n    bit::bitset<caps::MaxClipPlanes>                    clipPlanes;\n    bit::bitset<caps::MaxStreams>                       streamFreq;\n    bit::bitset<caps::MaxTransforms>                    transforms;\n    bit::bitset<caps::TextureStageCount>                textureStages;\n    std::array<\n      bit::bitset<TextureStageStateCount>,\n      caps::TextureStageCount>                          textureStageStates;\n\n    struct {\n      bit::bitset<caps::MaxFloatConstantsSoftware>      fConsts;\n      bit::bitset<caps::MaxOtherConstantsSoftware>      iConsts;\n      bit::bitset<caps::MaxOtherConstantsSoftware>      bConsts;\n    } vsConsts;\n\n    struct {\n      bit::bitset<caps::MaxSM3FloatConstantsPS>         fConsts;\n      bit::bitset<caps::MaxOtherConstants>              iConsts;\n      bit::bitset<caps::MaxOtherConstants>              bConsts;\n    } psConsts;\n\n    bit::bitvector                                      lightEnabledChanges;\n  };\n\n  enum class D3D9StateBlockType : uint8_t {\n    None,\n    All,\n    PixelState,\n    VertexState,\n    Unknown\n  };\n\n  inline D3D9StateBlockType ConvertStateBlockType(D3DSTATEBLOCKTYPE type) {\n    switch (type) {\n      case D3DSBT_ALL:         return D3D9StateBlockType::All;\n      case D3DSBT_PIXELSTATE:  return D3D9StateBlockType::PixelState;\n      case D3DSBT_VERTEXSTATE: return D3D9StateBlockType::VertexState;\n      default:                 return D3D9StateBlockType::Unknown;\n    }\n  }\n\n  using D3D9StateBlockBase = D3D9DeviceChild<IDirect3DStateBlock9>;\n  class D3D9StateBlock : public D3D9StateBlockBase {\n\n  public:\n\n    D3D9StateBlock(D3D9DeviceEx* pDevice, D3D9StateBlockType Type);\n\n    ~D3D9StateBlock();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n        REFIID  riid,\n        void** ppvObject) final;\n\n    HRESULT STDMETHODCALLTYPE Capture() final;\n    HRESULT STDMETHODCALLTYPE Apply() final;\n\n    HRESULT SetVertexDeclaration(D3D9VertexDecl* pDecl);\n\n    HRESULT SetIndices(D3D9IndexBuffer* pIndexData);\n\n    HRESULT SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);\n\n    HRESULT SetStateSamplerState(\n            DWORD               StateSampler,\n            D3DSAMPLERSTATETYPE Type,\n            DWORD               Value);\n\n    HRESULT SetStreamSource(\n            UINT               StreamNumber,\n            D3D9VertexBuffer*  pStreamData,\n            UINT               OffsetInBytes,\n            UINT               Stride);\n\n    HRESULT SetStreamSourceWithoutOffset(\n            UINT               StreamNumber,\n            D3D9VertexBuffer*  pStreamData,\n            UINT               Stride);\n\n    HRESULT SetStreamSourceFreq(UINT StreamNumber, UINT Setting);\n\n    HRESULT SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture);\n\n    HRESULT SetVertexShader(D3D9VertexShader* pShader);\n\n    HRESULT SetPixelShader(D3D9PixelShader* pShader);\n\n    HRESULT SetMaterial(const D3DMATERIAL9* pMaterial);\n\n    HRESULT SetLight(DWORD Index, const D3DLIGHT9* pLight);\n\n    HRESULT LightEnable(DWORD Index, BOOL Enable);\n\n    HRESULT SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix);\n\n    HRESULT SetStateTextureStageState(\n            DWORD                      Stage,\n            D3D9TextureStageStateTypes Type,\n            DWORD                      Value);\n\n    HRESULT SetViewport(const D3DVIEWPORT9* pViewport);\n\n    HRESULT SetScissorRect(const RECT* pRect);\n\n    HRESULT SetClipPlane(DWORD Index, const float* pPlane);\n\n\n    HRESULT SetVertexShaderConstantF(\n            UINT   StartRegister,\n      const float* pConstantData,\n            UINT   Vector4fCount);\n\n    HRESULT SetVertexShaderConstantI(\n            UINT StartRegister,\n      const int* pConstantData,\n            UINT Vector4iCount);\n\n    HRESULT SetVertexShaderConstantB(\n            UINT  StartRegister,\n      const BOOL* pConstantData,\n            UINT  BoolCount);\n\n\n    HRESULT SetPixelShaderConstantF(\n            UINT   StartRegister,\n      const float* pConstantData,\n            UINT   Vector4fCount);\n\n    HRESULT SetPixelShaderConstantI(\n            UINT StartRegister,\n      const int* pConstantData,\n            UINT Vector4iCount);\n\n    HRESULT SetPixelShaderConstantB(\n            UINT  StartRegister,\n      const BOOL* pConstantData,\n            UINT  BoolCount);\n\n    enum class D3D9StateFunction {\n      Apply,\n      Capture\n    };\n\n    template <typename Dst, typename Src, bool IgnoreStreamOffset>\n    void ApplyOrCapture(Dst* dst, const Src* src) {\n      if (m_captures.flags.test(D3D9CapturedStateFlag::StreamFreq)) {\n        for (uint32_t idx : bit::BitMask(m_captures.streamFreq.dword(0)))\n          dst->SetStreamSourceFreq(idx, src->streamFreq[idx]);\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::Indices))\n        dst->SetIndices(src->indices.ptr());\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::RenderStates)) {\n        for (uint32_t i = 0; i < m_captures.renderStates.dwordCount(); i++) {\n          for (uint32_t rs : bit::BitMask(m_captures.renderStates.dword(i))) {\n            uint32_t idx = i * 32 + rs;\n\n            dst->SetRenderState(D3DRENDERSTATETYPE(idx), src->renderStates[idx]);\n          }\n        }\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::SamplerStates)) {\n        for (uint32_t samplerIdx : bit::BitMask(m_captures.samplers.dword(0))) {\n          for (uint32_t stateIdx : bit::BitMask(m_captures.samplerStates[samplerIdx].dword(0)))\n            dst->SetStateSamplerState(samplerIdx, D3DSAMPLERSTATETYPE(stateIdx), src->samplerStates[samplerIdx][stateIdx]);\n        }\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::VertexBuffers)) {\n        for (uint32_t idx : bit::BitMask(m_captures.vertexBuffers.dword(0))) {\n          const auto& vbo = src->vertexBuffers[idx];\n          if constexpr (!IgnoreStreamOffset) {\n            dst->SetStreamSource(\n              idx,\n              vbo.vertexBuffer.ptr(),\n              vbo.offset,\n              vbo.stride);\n          } else {\n            // For whatever reason, D3D9 doesn't capture the stream offset\n            dst->SetStreamSourceWithoutOffset(\n              idx,\n              vbo.vertexBuffer.ptr(),\n              vbo.stride);\n          }\n        }\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::Material))\n        dst->SetMaterial(&src->material);\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::Textures)) {\n        for (uint32_t idx : bit::BitMask(m_captures.textures.dword(0)))\n          dst->SetStateTexture(idx, src->textures[idx]);\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::VertexShader))\n        dst->SetVertexShader(src->vertexShader.ptr());\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::PixelShader))\n        dst->SetPixelShader(src->pixelShader.ptr());\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::Transforms)) {\n        for (uint32_t i = 0; i < m_captures.transforms.dwordCount(); i++) {\n          for (uint32_t trans : bit::BitMask(m_captures.transforms.dword(i))) {\n            uint32_t idx = i * 32 + trans;\n\n            dst->SetStateTransform(idx, reinterpret_cast<const D3DMATRIX*>(&src->transforms[idx]));\n          }\n        }\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::TextureStages)) {\n        for (uint32_t stageIdx : bit::BitMask(m_captures.textureStages.dword(0))) {\n          for (uint32_t stateIdx : bit::BitMask(m_captures.textureStageStates[stageIdx].dword(0)))\n            dst->SetStateTextureStageState(stageIdx, D3D9TextureStageStateTypes(stateIdx), src->textureStages[stageIdx][stateIdx]);\n        }\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::Viewport))\n        dst->SetViewport(&src->viewport);\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::ScissorRect))\n        dst->SetScissorRect(&src->scissorRect);\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::ClipPlanes)) {\n        for (uint32_t idx : bit::BitMask(m_captures.clipPlanes.dword(0)))\n          dst->SetClipPlane(idx, src->clipPlanes[idx].coeff);\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::VsConstants)) {\n        for (uint32_t i = 0; i < m_captures.vsConsts.fConsts.dwordCount(); i++) {\n          for (uint32_t consts : bit::BitMask(m_captures.vsConsts.fConsts.dword(i))) {\n            uint32_t idx = i * 32 + consts;\n\n            dst->SetVertexShaderConstantF(idx, reinterpret_cast<const float*>(&src->vsConsts->fConsts[idx]), 1);\n          }\n        }\n\n        for (uint32_t i = 0; i < m_captures.vsConsts.iConsts.dwordCount(); i++) {\n          for (uint32_t consts : bit::BitMask(m_captures.vsConsts.iConsts.dword(i))) {\n            uint32_t idx = i * 32 + consts;\n\n            dst->SetVertexShaderConstantI(idx, reinterpret_cast<const int*>(&src->vsConsts->iConsts[idx]), 1);\n          }\n        }\n\n        if (m_captures.vsConsts.bConsts.any()) {\n          for (uint32_t i = 0; i < m_captures.vsConsts.bConsts.dwordCount(); i++)\n            dst->SetVertexBoolBitfield(i, m_captures.vsConsts.bConsts.dword(i), src->vsConsts->bConsts[i]);\n        }\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::PsConstants)) {\n        for (uint32_t i = 0; i < m_captures.psConsts.fConsts.dwordCount(); i++) {\n          for (uint32_t consts : bit::BitMask(m_captures.psConsts.fConsts.dword(i))) {\n            uint32_t idx = i * 32 + consts;\n\n            dst->SetPixelShaderConstantF(idx, reinterpret_cast<const float*>(&src->psConsts->fConsts[idx]), 1);\n          }\n        }\n\n        for (uint32_t i = 0; i < m_captures.psConsts.iConsts.dwordCount(); i++) {\n          for (uint32_t consts : bit::BitMask(m_captures.psConsts.iConsts.dword(i))) {\n            uint32_t idx = i * 32 + consts;\n\n            dst->SetPixelShaderConstantI(idx, reinterpret_cast<const int*>(&src->psConsts->iConsts[idx]), 1);\n          }\n        }\n\n        if (m_captures.psConsts.bConsts.any()) {\n          for (uint32_t i = 0; i < m_captures.psConsts.bConsts.dwordCount(); i++)\n            dst->SetPixelBoolBitfield(i, m_captures.psConsts.bConsts.dword(i), src->psConsts->bConsts[i]);\n        }\n      }\n\n      if (m_captures.flags.test(D3D9CapturedStateFlag::Lights)) {\n        for (uint32_t i = 0; i < src->lights.size(); i++) {\n          if (!src->lights[i].has_value())\n            continue;\n\n          dst->SetLight(i, &src->lights[i].value());\n        }\n        for (uint32_t i = 0; i < m_captures.lightEnabledChanges.dwordCount(); i++) {\n          for (uint32_t consts : bit::BitMask(m_captures.lightEnabledChanges.dword(i))) {\n            uint32_t idx = i * 32 + consts;\n\n            dst->LightEnable(idx, src->IsLightEnabled(idx));\n          }\n        }\n      }\n    }\n\n    template <D3D9StateFunction Func, bool IgnoreStreamOffset>\n    void ApplyOrCapture() {\n      if      constexpr (Func == D3D9StateFunction::Apply)\n        ApplyOrCapture<D3D9DeviceEx, D3D9CapturableState, IgnoreStreamOffset>(m_parent, &m_state);\n      else if constexpr (Func == D3D9StateFunction::Capture)\n        ApplyOrCapture<D3D9StateBlock, D3D9DeviceState, IgnoreStreamOffset>(this, m_deviceState);\n    }\n\n    template <\n      D3D9ShaderType   ShaderType,\n      D3D9ConstantType ConstantType,\n      typename         T>\n    HRESULT SetShaderConstants(\n            UINT  StartRegister,\n      const T*    pConstantData,\n            UINT  Count) {\n      auto SetHelper = [&](auto& setCaptures) {\n        if constexpr (ShaderType == D3D9ShaderType::VertexShader)\n          m_captures.flags.set(D3D9CapturedStateFlag::VsConstants);\n        else\n          m_captures.flags.set(D3D9CapturedStateFlag::PsConstants);\n\n        for (uint32_t i = 0; i < Count; i++) {\n          uint32_t reg = StartRegister + i;\n          if      constexpr (ConstantType == D3D9ConstantType::Float)\n            setCaptures.fConsts.set(reg, true);\n          else if constexpr (ConstantType == D3D9ConstantType::Int)\n            setCaptures.iConsts.set(reg, true);\n          else if constexpr (ConstantType == D3D9ConstantType::Bool)\n            setCaptures.bConsts.set(reg, true);\n        }\n\n        UpdateStateConstants<\n          ShaderType,\n          ConstantType,\n          T>(\n            &m_state,\n            StartRegister,\n            pConstantData,\n            Count,\n            false);\n\n        return D3D_OK;\n      };\n\n      return ShaderType == D3D9ShaderType::VertexShader\n        ? SetHelper(m_captures.vsConsts)\n        : SetHelper(m_captures.psConsts);\n    }\n\n    HRESULT SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits);\n    HRESULT SetPixelBoolBitfield (uint32_t idx, uint32_t mask, uint32_t bits);\n\n  private:\n\n    void CapturePixelRenderStates();\n    void CapturePixelSamplerStates();\n    void CapturePixelShaderStates();\n\n    void CaptureVertexRenderStates();\n    void CaptureVertexSamplerStates();\n    void CaptureVertexShaderStates();\n\n    void CaptureType(D3D9StateBlockType State);\n\n    D3D9CapturableState  m_state;\n    D3D9StateCaptures    m_captures;\n\n    D3D9DeviceState*     m_deviceState;\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_subresource.h",
    "content": "#pragma once\n\n#include \"d3d9_resource.h\"\n#include \"d3d9_common_texture.h\"\n\nnamespace dxvk {\n\n  template <typename... Type>\n  class D3D9Subresource : public D3D9Resource<Type...> {\n\n  public:\n\n    D3D9Subresource(\n            D3D9DeviceEx*           pDevice,\n      const bool                    Extended,\n            D3D9CommonTexture*      pTexture,\n            UINT                    Face,\n            UINT                    MipLevel,\n            IDirect3DBaseTexture9*  pBaseTexture,\n            IUnknown*               pContainer)\n    : D3D9Resource<Type...>(pDevice, pTexture->GetPool(), Extended),\n      m_container          (pContainer),\n      m_baseTexture        (pBaseTexture),\n      m_texture            (pTexture),\n      m_face               (Face),\n      m_mipLevel           (MipLevel),\n      m_isSrgbCompatible   (pTexture->IsSrgbCompatible()),\n      m_isNull             (pTexture->IsNull()) {\n\n    }\n\n    ~D3D9Subresource() {\n      // We own the texture!\n      if (m_baseTexture == nullptr)\n        delete m_texture;\n    }\n\n    ULONG STDMETHODCALLTYPE AddRef() final {\n      if (m_baseTexture != nullptr)\n        return m_baseTexture->AddRef();\n\n      return D3D9Resource<Type...>::AddRef();\n    }\n\n    ULONG STDMETHODCALLTYPE Release() final {\n      if (m_baseTexture != nullptr)\n        return m_baseTexture->Release();\n\n      return D3D9Resource<Type...>::Release();\n    }\n\n    HRESULT STDMETHODCALLTYPE GetContainer(REFIID riid, void** ppContainer) final {\n      if (m_container != nullptr)\n        return m_container->QueryInterface(riid, ppContainer);\n\n      return this->GetDevice()->QueryInterface(riid, ppContainer);\n    }\n\n    void STDMETHODCALLTYPE PreLoad() {\n      m_texture->PreLoadSubresource(GetSubresource());\n    }\n\n    inline D3D9CommonTexture* GetCommonTexture() {\n      return m_texture;\n    }\n\n    inline UINT GetFace() const {\n      return m_face;\n    }\n\n    inline UINT GetMipLevel() const {\n      return m_mipLevel;\n    }\n\n    inline UINT GetSubresource() const {\n      return m_texture->CalcSubresource(m_face, m_mipLevel);\n    }\n\n    inline const Rc<DxvkImageView>& GetRenderTargetView(bool Srgb) {\n      Rc<DxvkImageView>& view = m_renderTargetView.Pick(Srgb);\n\n      if (unlikely(!view && !IsNull())) {\n        // The backend will sort out feedback loop layout and usage on its own\n        VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\n        view = m_texture->CreateView(m_face, m_mipLevel,\n          usage, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,\n          Srgb && m_isSrgbCompatible);\n      }\n\n      return view;\n    }\n\n    inline const Rc<DxvkImageView>& GetDepthStencilView(bool Writable) {\n      Rc<DxvkImageView>& view = Writable\n        ? m_dsvReadWrite\n        : m_dsvReadOnly;\n\n      if (unlikely(!view)) {\n        // The backend will ignore the view layout anyway for images\n        // that have GENERAL (or FEEDBACK_LOOP) as their layout.\n        // Because of that, we don't need to pay special attention here\n        // to whether the image was transitioned because of a feedback loop.\n\n        VkImageUsageFlags usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n        VkImageLayout layout = Writable\n          ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL\n          : VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL;\n\n        view = m_texture->CreateView(m_face, m_mipLevel,\n          usage, layout, false);\n      }\n\n      return view;\n    }\n\n    inline bool IsNull() {\n      return m_isNull;\n    }\n\n    inline IDirect3DBaseTexture9* GetBaseTexture() {\n      return m_baseTexture;\n    }\n\n    inline void Swap(D3D9Subresource* Other) {\n      // Only used for swap chain back buffers that don't\n      // have a container and all have identical properties\n      std::swap(m_texture,          Other->m_texture);\n      std::swap(m_renderTargetView, Other->m_renderTargetView);\n    }\n\n  protected:\n\n    IUnknown*               m_container;\n    IDirect3DBaseTexture9*  m_baseTexture;\n\n    D3D9CommonTexture*      m_texture;\n\n    UINT                    m_face             : 8;\n    UINT                    m_mipLevel         : 16;\n    UINT                    m_isSrgbCompatible : 1;\n    UINT                    m_isNull           : 1;\n\n    D3D9ColorView           m_renderTargetView;\n\n    Rc<DxvkImageView>       m_dsvReadWrite;\n    Rc<DxvkImageView>       m_dsvReadOnly;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_surface.cpp",
    "content": "#include \"d3d9_surface.h\"\n#include \"d3d9_texture.h\"\n#include \"d3d9_swapchain.h\"\n\n#include \"d3d9_device.h\"\n\n#include \"../util/util_win32_compat.h\"\n\nnamespace dxvk {\n\n  D3D9Surface::D3D9Surface(\n          D3D9DeviceEx*             pDevice,\n    const D3D9_COMMON_TEXTURE_DESC* pDesc,\n    const bool                      Extended,\n          IUnknown*                 pContainer,\n          HANDLE*                   pSharedHandle)\n    : D3D9SurfaceBase(\n        pDevice,\n        Extended,\n        new D3D9CommonTexture( pDevice, this, pDesc, D3DRTYPE_SURFACE, pSharedHandle),\n        0, 0,\n        nullptr,\n        pContainer) { }\n\n  D3D9Surface::D3D9Surface(\n          D3D9DeviceEx*             pDevice,\n    const D3D9_COMMON_TEXTURE_DESC* pDesc,\n    const bool                      Extended)\n    : D3D9Surface(\n        pDevice,\n        pDesc,\n        Extended,\n        nullptr,\n        nullptr) { }\n\n  D3D9Surface::D3D9Surface(\n          D3D9DeviceEx*             pDevice,\n    const bool                      Extended,\n          D3D9CommonTexture*        pTexture,\n          UINT                      Face,\n          UINT                      MipLevel,\n          IDirect3DBaseTexture9*    pBaseTexture)\n    : D3D9SurfaceBase(\n        pDevice,\n        Extended,\n        pTexture,\n        Face, MipLevel,\n        pBaseTexture,\n        pBaseTexture) { }\n\n  void D3D9Surface::AddRefPrivate() {\n    if (m_baseTexture != nullptr) {\n      D3DRESOURCETYPE type = m_baseTexture->GetType();\n      if (type == D3DRTYPE_TEXTURE)\n        static_cast<D3D9Texture2D*>  (m_baseTexture)->AddRefPrivate();\n      else //if (type == D3DRTYPE_CUBETEXTURE)\n        static_cast<D3D9TextureCube*>(m_baseTexture)->AddRefPrivate();\n\n      return;\n    }\n\n    D3D9SurfaceBase::AddRefPrivate();\n  }\n\n  void D3D9Surface::ReleasePrivate() {\n    if (m_baseTexture != nullptr) {\n      D3DRESOURCETYPE type = m_baseTexture->GetType();\n      if (type == D3DRTYPE_TEXTURE)\n        static_cast<D3D9Texture2D*>  (m_baseTexture)->ReleasePrivate();\n      else //if (type == D3DRTYPE_CUBETEXTURE)\n        static_cast<D3D9TextureCube*>(m_baseTexture)->ReleasePrivate();\n\n      return;\n    }\n\n    D3D9SurfaceBase::ReleasePrivate();\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9Surface::QueryInterface(REFIID riid, void** ppvObject) {\n    if (unlikely(ppvObject == nullptr))\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DResource9)\n     || riid == __uuidof(IDirect3DSurface9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D9VkInteropTexture)) {\n      *ppvObject = ref(m_texture->GetVkInterop());\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DSurface9), riid)) {\n      Logger::warn(\"D3D9Surface::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D9Surface::GetType() {\n    return D3DRTYPE_SURFACE;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9Surface::GetDesc(D3DSURFACE_DESC *pDesc) {\n    if (unlikely(pDesc == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    auto& desc = *(m_texture->Desc());\n\n    pDesc->Format             = static_cast<D3DFORMAT>(desc.Format);\n    pDesc->Type               = D3DRTYPE_SURFACE;\n    pDesc->Usage              = desc.Usage;\n    pDesc->Pool               = desc.Pool;\n\n    pDesc->MultiSampleType    = desc.MultiSample;\n    pDesc->MultiSampleQuality = desc.MultisampleQuality;\n    pDesc->Width              = std::max(1u, desc.Width  >> m_mipLevel);\n    pDesc->Height             = std::max(1u, desc.Height >> m_mipLevel);\n\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9Surface::LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {\n    if (unlikely(pLockedRect == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3DBOX box;\n    auto& desc = *(m_texture->Desc());\n    D3DRESOURCETYPE type = m_texture->GetType();\n\n    // LockRect clears any existing content present in pLockedRect,\n    // for surfaces in D3DPOOL_DEFAULT. D3D8 additionally clears the content\n    // for non-D3DPOOL_DEFAULT surfaces if their type is not D3DRTYPE_TEXTURE.\n    if (desc.Pool == D3DPOOL_DEFAULT\n     || (m_texture->Device()->IsD3D8Compatible() && type != D3DRTYPE_TEXTURE)) {\n      pLockedRect->pBits = nullptr;\n      pLockedRect->Pitch = 0;\n    }\n\n    if (unlikely(pRect != nullptr)) {\n      D3D9_FORMAT_BLOCK_SIZE blockSize = GetFormatAlignedBlockSize(desc.Format);\n\n      const bool isBlockAlignedFormat = blockSize.Width > 0 && blockSize.Height > 0;\n      const bool isSystemMemSurface   = desc.Pool == D3DPOOL_SYSTEMMEM && type == D3DRTYPE_SURFACE;\n\n      // The boundaries of pRect are validated for D3DPOOL_DEFAULT surfaces\n      // with formats which need to be block aligned, surfaces created via\n      // CreateImageSurface and D3D8 cube textures outside of D3DPOOL_DEFAULT\n      if ((isBlockAlignedFormat && desc.Pool == D3DPOOL_DEFAULT) || isSystemMemSurface\n       || (m_texture->Device()->IsD3D8Compatible() &&\n           desc.Pool != D3DPOOL_DEFAULT && type == D3DRTYPE_CUBETEXTURE)) {\n        const LONG width  = pRect->right  - pRect->left;\n        const LONG height = pRect->bottom - pRect->top;\n\n        const bool invalidDimensions = isSystemMemSurface ? (width < 0 || height < 0) : (width <= 0 || height <= 0);\n\n        // Negative coordinates\n        if (pRect->left < 0 || pRect->right  < 0\n         || pRect->top  < 0 || pRect->bottom < 0\n        // Negative or zero length dimensions (just negative in case of\n        // D3DPOOL_SYSTEMMEM surfaces, as Legends of Eisenwald tries to lock\n        // and unlock a zero-length pRect and crashes if this fails)\n         || invalidDimensions\n        // Exceeding surface dimensions\n         || static_cast<UINT>(pRect->right)  > std::max(1u, desc.Width  >> m_mipLevel)\n         || static_cast<UINT>(pRect->bottom) > std::max(1u, desc.Height >> m_mipLevel))\n          return D3DERR_INVALIDCALL;\n      }\n\n      box.Left   = pRect->left;\n      box.Right  = pRect->right;\n      box.Top    = pRect->top;\n      box.Bottom = pRect->bottom;\n      box.Front  = 0;\n      box.Back   = 1;\n    }\n\n    D3DLOCKED_BOX lockedBox;\n\n    HRESULT hr = m_parent->LockImage(\n      m_texture,\n      m_face, m_mipLevel,\n      &lockedBox,\n      pRect != nullptr ? &box : nullptr,\n      Flags);\n\n    if (FAILED(hr)) return hr;\n\n    pLockedRect->pBits = lockedBox.pBits;\n    pLockedRect->Pitch = lockedBox.RowPitch;\n\n    return hr;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9Surface::UnlockRect() {\n    return m_parent->UnlockImage(\n      m_texture,\n      m_face, m_mipLevel);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9Surface::GetDC(HDC *phDC) {\n    if (unlikely(phDC == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    const D3D9_COMMON_TEXTURE_DESC& desc = *m_texture->Desc();\n\n    if (unlikely(!IsSurfaceGetDCCompatibleFormat(desc.Format)))\n      return D3DERR_INVALIDCALL;\n\n    D3DLOCKED_RECT lockedRect;\n    HRESULT hr = LockRect(&lockedRect, nullptr, 0);\n    if (FAILED(hr))\n      return hr;\n\n    D3DKMT_CREATEDCFROMMEMORY createInfo;\n    // In...\n    createInfo.pMemory     = lockedRect.pBits;\n    createInfo.Format      = static_cast<D3DFORMAT>(desc.Format);\n    createInfo.Width       = desc.Width;\n    createInfo.Height      = desc.Height;\n    createInfo.Pitch       = lockedRect.Pitch;\n    createInfo.hDeviceDc   = CreateCompatibleDC(NULL);\n    createInfo.pColorTable = nullptr;\n\n    // Out...\n    createInfo.hBitmap     = nullptr;\n    createInfo.hDc         = nullptr;\n\n    if (D3DKMTCreateDCFromMemory(&createInfo))\n      Logger::err(\"D3D9: Failed to create GDI DC\");\n\n    DeleteDC(createInfo.hDeviceDc);\n\n    // These should now be set...\n    m_hdc     = createInfo.hDc;\n    m_hbitmap = createInfo.hBitmap;\n\n    *phDC = m_hdc;\n    return D3D_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9Surface::ReleaseDC(HDC hDC) {\n    if (unlikely(m_hdc == nullptr || m_hdc != hDC))\n      return D3DERR_INVALIDCALL;\n\n    D3DKMT_DESTROYDCFROMMEMORY desc;\n    desc.hDc     = m_hdc;\n    desc.hBitmap = m_hbitmap;\n    D3DKMTDestroyDCFromMemory(&desc);\n\n    HRESULT hr = UnlockRect();\n    if (FAILED(hr))\n      return hr;\n\n    return D3D_OK;\n  }\n\n\n  void D3D9Surface::ClearContainer() {\n    m_container = nullptr;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_surface.h",
    "content": "#pragma once\n\n#include \"d3d9_subresource.h\"\n\n#include \"d3d9_common_texture.h\"\n\n#include \"../util/util_gdi.h\"\n\n#include <algorithm>\n\nnamespace dxvk {\n\n  using D3D9SurfaceBase = D3D9Subresource<IDirect3DSurface9>;\n  class D3D9Surface final : public D3D9SurfaceBase {\n\n  public:\n\n    D3D9Surface(\n            D3D9DeviceEx*             pDevice,\n      const D3D9_COMMON_TEXTURE_DESC* pDesc,\n      const bool                      Extended,\n            IUnknown*                 pContainer,\n            HANDLE*                   pSharedHandle);\n\n    D3D9Surface(\n            D3D9DeviceEx*             pDevice,\n      const D3D9_COMMON_TEXTURE_DESC* pDesc,\n      const bool                      Extended);\n\n    D3D9Surface(\n            D3D9DeviceEx*             pDevice,\n      const bool                      Extended,\n            D3D9CommonTexture*        pTexture,\n            UINT                      Face,\n            UINT                      MipLevel,\n            IDirect3DBaseTexture9*    pBaseTexture);\n\n    void AddRefPrivate();\n\n    void ReleasePrivate();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;\n\n    HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC *pDesc) final;\n\n    HRESULT STDMETHODCALLTYPE LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) final;\n\n    HRESULT STDMETHODCALLTYPE UnlockRect() final;\n\n    HRESULT STDMETHODCALLTYPE GetDC(HDC *phDC) final;\n\n    HRESULT STDMETHODCALLTYPE ReleaseDC(HDC hDC) final;\n\n    inline VkExtent2D GetSurfaceExtent() const {\n      const auto* desc = m_texture->Desc();\n\n      return VkExtent2D { \n        std::max(1u, desc->Width  >> GetMipLevel()),\n        std::max(1u, desc->Height >> GetMipLevel())\n      };\n    }\n\n    void ClearContainer();\n\n  private:\n\n    HDC         m_hdc = nullptr;\n    HANDLE      m_hbitmap = nullptr;\n\n  };\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_swapchain.cpp",
    "content": "#include \"d3d9_swapchain.h\"\n#include \"d3d9_surface.h\"\n#include \"d3d9_monitor.h\"\n\n#include \"d3d9_hud.h\"\n#include \"d3d9_window.h\"\n\nnamespace dxvk {\n\n  static uint16_t MapGammaControlPoint(float x) {\n    if (x < 0.0f) x = 0.0f;\n    if (x > 1.0f) x = 1.0f;\n    return uint16_t(65535.0f * x);\n  }\n\n  D3D9SwapChainEx::D3D9SwapChainEx(\n          D3D9DeviceEx*          pDevice,\n          D3DPRESENT_PARAMETERS* pPresentParams,\n    const D3DDISPLAYMODEEX*      pFullscreenDisplayMode,\n          bool                   EnableLatencyTracking)\n    : D3D9SwapChainExBase(pDevice)\n    , m_device           (pDevice->GetDXVKDevice())\n    , m_frameLatencyCap  (pDevice->GetOptions()->maxFrameLatency)\n    , m_latencyTracking  (EnableLatencyTracking)\n    , m_swapchainExt     (this) {\n    this->NormalizePresentParameters(pPresentParams);\n    m_presentParams = *pPresentParams;\n    m_window = m_presentParams.hDeviceWindow;\n\n    UpdateWindowCtx();\n\n    UpdatePresentRegion(nullptr, nullptr);\n\n    if (FAILED(CreateBackBuffers(m_presentParams.BackBufferCount, m_presentParams.Flags)))\n      throw DxvkError(\"D3D9: Failed to create swapchain backbuffers\");\n\n    CreateBlitter();\n\n    InitRamp();\n\n    // Apply initial window mode and fullscreen state\n    if (!m_presentParams.Windowed && FAILED(EnterFullscreenMode(pPresentParams, pFullscreenDisplayMode)))\n      throw DxvkError(\"D3D9: Failed to set initial fullscreen state\");\n  }\n\n\n  D3D9SwapChainEx::~D3D9SwapChainEx() {\n    // Avoids hanging when in this state, see comment\n    // in DxvkDevice::~DxvkDevice.\n    if (this_thread::isInModuleDetachment())\n      return;\n\n    {\n      // Locking here and in Device::GetFrontBufferData\n      // ensures that other threads don't accidentally access a stale pointer.\n      D3D9DeviceLock lock = m_parent->LockDevice();\n\n      if (m_parent->GetMostRecentlyUsedSwapchain() == this) {\n        m_parent->ResetMostRecentlyUsedSwapchain();\n      }\n    }\n\n    DestroyBackBuffers();\n\n    ResetWindowProc(m_window);\n    RestoreDisplayMode(m_monitor);\n\n    for (auto& p : m_presenters) {\n      if (p.second.presenter) {\n        p.second.presenter->destroyResources();\n        p.second.presenter = nullptr;\n      }\n    }\n\n    m_parent->DecrementLosableCounter();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DSwapChain9)\n     || (GetParent()->IsExtended() && riid == __uuidof(IDirect3DSwapChain9Ex))) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D9VkExtSwapchain)) {\n      *ppvObject = ref(&m_swapchainExt);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DSwapChain9), riid)) {\n      Logger::warn(\"D3D9SwapChainEx::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::Present(\n    const RECT*    pSourceRect,\n    const RECT*    pDestRect,\n          HWND     hDestWindowOverride,\n    const RGNDATA* pDirtyRegion,\n          DWORD    dwFlags) {\n    D3D9DeviceLock lock = m_parent->LockDevice();\n\n    m_parent->SetMostRecentlyUsedSwapchain(this);\n\n    if (unlikely(m_parent->IsDeviceLost()))\n      return D3DERR_DEVICELOST;\n\n    // If we have no backbuffers, error out.\n    // This handles the case where a ::Reset failed due to OOM\n    // or whatever.\n    // I am not sure what the actual HRESULT returned here is\n    // or should be, but it is better than crashing... probably!\n    if (m_backBuffers.empty())\n      return D3D_OK;\n\n    uint32_t presentInterval = m_presentParams.PresentationInterval;\n\n    // This is not true directly in d3d9 to to timing differences that don't matter for us.\n    // For our purposes...\n    // D3DPRESENT_INTERVAL_DEFAULT (0) == D3DPRESENT_INTERVAL_ONE (1) which means VSYNC.\n    presentInterval = std::max(presentInterval, 1u);\n\n    if (presentInterval == D3DPRESENT_INTERVAL_IMMEDIATE || (dwFlags & D3DPRESENT_FORCEIMMEDIATE))\n      presentInterval = 0;\n\n    auto options = m_parent->GetOptions();\n\n    if (options->presentInterval >= 0)\n      presentInterval = options->presentInterval;\n\n    HWND window = m_presentParams.hDeviceWindow;\n\n    if (hDestWindowOverride != nullptr)\n      window = hDestWindowOverride;\n\n    if (m_window != window) {\n      m_window = window;\n      m_displayRefreshRateDirty = true;\n    }\n\n    if (!UpdateWindowCtx())\n      return D3D_OK;\n\n    if (options->deferSurfaceCreation && IsDeviceReset(m_wctx))\n      m_wctx->presenter->invalidateSurface();\n\n    m_wctx->presenter->setSyncInterval(presentInterval);\n\n    UpdatePresentRegion(pSourceRect, pDestRect);\n    UpdatePresentParameters();\n\n    if (!SwapWithFrontBuffer() && m_parent->GetOptions()->extraFrontbuffer) {\n      // We never actually rotate in the front buffer.\n      // Just blit to it for GetFrontBufferData.\n\n      // When we have multiple buffers, the last buffer always acts as the front buffer.\n      // (See comment in PresentImage for an explaination why.)\n      // Games with a buffer count of 1 rely on the contents of the previous frame still\n      // being there, so we can't just add another buffer to the rotation.\n      // At the same time, they could call GetFrontBufferData after already rendering to the backbuffer.\n      // So we have to do a copy of the backbuffer that will be copied to the Vulkan backbuffer\n      // and keep that around for the next frame.\n\n      const auto& backbuffer = m_backBuffers[0];\n      const auto& frontbuffer = GetFrontBuffer();\n      if (FAILED(m_parent->StretchRect(backbuffer.ptr(), nullptr, frontbuffer.ptr(), nullptr, D3DTEXF_NONE))) {\n        Logger::err(\"Failed to blit to front buffer\");\n      }\n    }\n\n#ifdef _WIN32\n    const bool useGDIFallback = m_partialCopy && !SwapWithFrontBuffer();\n    if (useGDIFallback)\n      return PresentImageGDI(m_window);\n#endif\n\n    try {\n      UpdateWindowedRefreshRate();\n      UpdateTargetFrameRate(presentInterval);\n      PresentImage(presentInterval);\n      return D3D_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n#ifdef _WIN32\n      return PresentImageGDI(m_window);\n#else\n      return D3DERR_DEVICEREMOVED;\n#endif\n    }\n  }\n\n#ifdef _WIN32\n  #define DCX_USESTYLE 0x00010000\n\n  HRESULT D3D9SwapChainEx::PresentImageGDI(HWND Window) {\n    m_parent->EndFrame(nullptr);\n    m_parent->Flush();\n\n    if (!std::exchange(m_warnedAboutGDIFallback, true))\n      Logger::warn(\"Using GDI for swapchain presentation. This will impact performance.\");\n\n    HDC hDC;\n    HRESULT result = m_backBuffers[0]->GetDC(&hDC);\n    if (result) {\n      Logger::err(\"D3D9SwapChainEx::BlitGDI Surface GetDC failed\");\n      return D3DERR_DEVICEREMOVED;\n    }\n\n    HDC dstDC = GetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);\n    if (!dstDC) {\n      Logger::err(\"D3D9SwapChainEx::BlitGDI: GetDCEx failed\");\n      m_backBuffers[0]->ReleaseDC(hDC);\n      return D3DERR_DEVICEREMOVED;\n    }\n\n    bool success = StretchBlt(dstDC, m_dstRect.left, m_dstRect.top, m_dstRect.right - m_dstRect.left,\n            m_dstRect.bottom - m_dstRect.top, hDC, m_srcRect.left, m_srcRect.top,\n            m_srcRect.right - m_srcRect.left, m_srcRect.bottom - m_srcRect.top, SRCCOPY);\n\n    m_backBuffers[0]->ReleaseDC(hDC);\n    ReleaseDC(Window, dstDC);\n\n    if (!success) {\n      Logger::err(\"D3D9SwapChainEx::BlitGDI: StretchBlt failed\");\n      return D3DERR_DEVICEREMOVED;\n    }\n\n    return S_OK;\n  }\n#endif\n\n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::GetFrontBufferData(IDirect3DSurface9* pDestSurface) {\n    D3D9DeviceLock lock = m_parent->LockDevice();\n\n    // This function can do absolutely everything!\n    // Copies the front buffer between formats with an implicit resolve.\n    // Oh, and the dest is systemmem...\n    // This is a slow function anyway, it waits for the copy to finish.\n    // so there's no reason to not just make and throwaway temp images.\n\n    // If extent of dst > src, then we blit to a subrect of the size\n    // of src onto a temp image of dst's extents,\n    // then copy buffer back to dst (given dst is subresource)\n\n    // For SWAPEFFECT_COPY and windowed SWAPEFFECT_DISCARD with 1 backbuffer, we just copy the backbuffer data instead.\n    // We just copy from the backbuffer instead of the front buffer to avoid having to do another blit.\n    // This mostly impacts windowed mode and our implementation was not accurate in that case anyway as Windows D3D9\n    // takes a screenshot of the entire screen.\n\n    D3D9Surface* dst = static_cast<D3D9Surface*>(pDestSurface);\n\n    if (unlikely(dst == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3D9CommonTexture* dstTexInfo = dst->GetCommonTexture();\n    D3D9CommonTexture* srcTexInfo = GetFrontBuffer()->GetCommonTexture();\n\n    if (unlikely(dstTexInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM && dstTexInfo->Desc()->Pool != D3DPOOL_SCRATCH))\n      return D3DERR_INVALIDCALL;\n    \n    if (unlikely(m_parent->IsDeviceLost())) {\n      return D3DERR_DEVICELOST;\n    }\n\n    VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel());\n    VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(0);\n\n    const bool clearDst = dstTexInfo->Desc()->MipLevels > 1\n                       || dstTexExtent.width > srcTexExtent.width\n                       || dstTexExtent.height > srcTexExtent.height;\n\n    dstTexInfo->CreateBuffer(clearDst, dstTexInfo->GetTotalSize());\n    DxvkBufferSlice dstBufferSlice = dstTexInfo->GetBufferSlice(dst->GetSubresource());\n    Rc<DxvkImage>   srcImage       = srcTexInfo->GetImage();\n\n    if (srcImage->info().sampleCount != VK_SAMPLE_COUNT_1_BIT) {\n      DxvkImageCreateInfo resolveInfo;\n      resolveInfo.type          = VK_IMAGE_TYPE_2D;\n      resolveInfo.format        = srcImage->info().format;\n      resolveInfo.flags         = 0;\n      resolveInfo.sampleCount   = VK_SAMPLE_COUNT_1_BIT;\n      resolveInfo.extent        = srcImage->info().extent;\n      resolveInfo.numLayers     = 1;\n      resolveInfo.mipLevels     = 1;\n      resolveInfo.usage         = VK_IMAGE_USAGE_SAMPLED_BIT\n                                | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n                                | VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n                                | VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n      resolveInfo.stages        = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT\n                                | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT\n                                | VK_PIPELINE_STAGE_TRANSFER_BIT;\n      resolveInfo.access        = VK_ACCESS_SHADER_READ_BIT\n                                | VK_ACCESS_TRANSFER_WRITE_BIT\n                                | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT\n                                | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n      resolveInfo.tiling        = VK_IMAGE_TILING_OPTIMAL;\n      resolveInfo.layout        = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n      \n      Rc<DxvkImage> resolvedSrc = m_device->createImage(\n        resolveInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      m_parent->EmitCs([\n        cDstImage = resolvedSrc,\n        cSrcImage = srcImage\n      ] (DxvkContext* ctx) {\n        VkImageSubresourceLayers resolveSubresource;\n        resolveSubresource.aspectMask      = VK_IMAGE_ASPECT_COLOR_BIT;\n        resolveSubresource.mipLevel        = 0;\n        resolveSubresource.baseArrayLayer  = 0;\n        resolveSubresource.layerCount      = 1;\n\n        VkImageResolve resolveRegion;\n        resolveRegion.srcSubresource = resolveSubresource;\n        resolveRegion.srcOffset      = VkOffset3D { 0, 0, 0 };\n        resolveRegion.dstSubresource = resolveSubresource;\n        resolveRegion.dstOffset      = VkOffset3D { 0, 0, 0 };\n        resolveRegion.extent         = cSrcImage->info().extent;\n\n        ctx->resolveImage(cDstImage, cSrcImage, resolveRegion,\n          cSrcImage->info().format, VK_RESOLVE_MODE_AVERAGE_BIT, VK_RESOLVE_MODE_NONE);\n      });\n\n      srcImage = std::move(resolvedSrc);\n    }\n\n    D3D9Format srcFormat = srcTexInfo->Desc()->Format;\n    D3D9Format dstFormat = dstTexInfo->Desc()->Format;\n\n    bool similar = AreFormatsSimilar(srcFormat, dstFormat);\n\n    if (!similar || srcImage->info().extent != dstTexInfo->GetExtent()) {\n      DxvkImageCreateInfo blitCreateInfo;\n      blitCreateInfo.type          = VK_IMAGE_TYPE_2D;\n      blitCreateInfo.format        = dstTexInfo->GetFormatMapping().FormatColor;\n      blitCreateInfo.flags         = 0;\n      blitCreateInfo.sampleCount   = VK_SAMPLE_COUNT_1_BIT;\n      blitCreateInfo.extent        = dstTexInfo->GetExtent();\n      blitCreateInfo.numLayers     = 1;\n      blitCreateInfo.mipLevels     = 1;\n      blitCreateInfo.usage         = VK_IMAGE_USAGE_SAMPLED_BIT\n                                   | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n                                   | VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n                                   | VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n      blitCreateInfo.stages        = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT\n                                   | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT\n                                   | VK_PIPELINE_STAGE_TRANSFER_BIT;\n      blitCreateInfo.access        = VK_ACCESS_SHADER_READ_BIT\n                                   | VK_ACCESS_TRANSFER_WRITE_BIT\n                                   | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT\n                                   | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n      blitCreateInfo.tiling        = VK_IMAGE_TILING_OPTIMAL;\n      blitCreateInfo.layout        = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n      \n      Rc<DxvkImage> blittedSrc = m_device->createImage(\n        blitCreateInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      const DxvkFormatInfo* dstFormatInfo = lookupFormatInfo(blittedSrc->info().format);\n      const DxvkFormatInfo* srcFormatInfo = lookupFormatInfo(srcImage->info().format);\n\n      const VkImageSubresource dstSubresource = dstTexInfo->GetSubresourceFromIndex(dstFormatInfo->aspectMask, 0);\n      const VkImageSubresource srcSubresource = srcTexInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, 0);\n\n      VkImageSubresourceLayers dstSubresourceLayers = {\n        dstSubresource.aspectMask,\n        dstSubresource.mipLevel,\n        dstSubresource.arrayLayer, 1 };\n\n      VkImageSubresourceLayers srcSubresourceLayers = {\n        srcSubresource.aspectMask,\n        srcSubresource.mipLevel,\n        srcSubresource.arrayLayer, 1 };\n\n      VkExtent3D srcExtent = srcImage->mipLevelExtent(srcSubresource.mipLevel);\n\n      // Blit to a subrect of the src extents\n      VkImageBlit blitInfo;\n      blitInfo.dstSubresource = dstSubresourceLayers;\n      blitInfo.srcSubresource = srcSubresourceLayers;\n      blitInfo.dstOffsets[0] = VkOffset3D{ 0, 0, 0 };\n      blitInfo.dstOffsets[1] = VkOffset3D{ int32_t(srcExtent.width),  int32_t(srcExtent.height),  1 };\n      blitInfo.srcOffsets[0] = VkOffset3D{ 0, 0, 0 };\n      blitInfo.srcOffsets[1] = VkOffset3D{ int32_t(srcExtent.width),  int32_t(srcExtent.height),  1 };\n\n#ifdef _WIN32\n      if (m_presentParams.Windowed) {\n        // In windowed mode, GetFrontBufferData takes a screenshot of the entire screen.\n        // So place the copy of the front buffer at the position of the window.\n        POINT point = { 0, 0 };\n        if (ClientToScreen(m_window, &point) != 0) {\n          blitInfo.dstOffsets[0].x = point.x;\n          blitInfo.dstOffsets[0].y = point.y;\n          blitInfo.dstOffsets[1].x += point.x;\n          blitInfo.dstOffsets[1].y += point.y;\n        }\n      }\n#endif\n\n      DxvkImageViewKey dstViewInfo;\n      dstViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n      dstViewInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n      dstViewInfo.format = blittedSrc->info().format;\n      dstViewInfo.aspects = blitInfo.dstSubresource.aspectMask;\n      dstViewInfo.mipIndex = blitInfo.dstSubresource.mipLevel;\n      dstViewInfo.mipCount = 1;\n      dstViewInfo.layerIndex = blitInfo.dstSubresource.baseArrayLayer;\n      dstViewInfo.layerCount = blitInfo.dstSubresource.layerCount;\n      dstViewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(dstTexInfo->GetMapping().Swizzle);\n\n      DxvkImageViewKey srcViewInfo;\n      srcViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n      srcViewInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;\n      srcViewInfo.format = srcImage->info().format;\n      srcViewInfo.aspects = blitInfo.srcSubresource.aspectMask;\n      srcViewInfo.mipIndex = blitInfo.srcSubresource.mipLevel;\n      srcViewInfo.mipCount = 1;\n      srcViewInfo.layerIndex = blitInfo.srcSubresource.baseArrayLayer;\n      srcViewInfo.layerCount = blitInfo.srcSubresource.layerCount;\n      srcViewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(srcTexInfo->GetMapping().Swizzle);\n\n      m_parent->EmitCs([\n        cDstView  = blittedSrc->createView(dstViewInfo),\n        cSrcView  = srcImage->createView(srcViewInfo),\n        cBlitInfo = blitInfo\n      ] (DxvkContext* ctx) {\n        ctx->blitImageView(\n          cDstView, cBlitInfo.dstOffsets,\n          cSrcView, cBlitInfo.srcOffsets,\n          VK_FILTER_NEAREST);\n      });\n\n      srcImage = std::move(blittedSrc);\n    }\n\n    const DxvkFormatInfo* srcFormatInfo = lookupFormatInfo(srcImage->info().format);\n    const VkImageSubresource srcSubresource = srcTexInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, 0);\n    VkImageSubresourceLayers srcSubresourceLayers = {\n      srcSubresource.aspectMask,\n      srcSubresource.mipLevel,\n      srcSubresource.arrayLayer, 1 };\n    VkExtent3D srcExtent = srcImage->mipLevelExtent(srcSubresource.mipLevel);\n\n    m_parent->EmitCs([\n      cBufferSlice  = std::move(dstBufferSlice),\n      cImage        = std::move(srcImage),\n      cSubresources = srcSubresourceLayers,\n      cLevelExtent  = srcExtent\n    ] (DxvkContext* ctx) {\n      ctx->copyImageToBuffer(cBufferSlice.buffer(),\n        cBufferSlice.offset(), 4, 0, VK_FORMAT_UNDEFINED,\n        cImage, cSubresources, VkOffset3D { 0, 0, 0 },\n        cLevelExtent);\n    });\n\n    dstTexInfo->SetNeedsReadback(dst->GetSubresource(), true);\n    m_parent->TrackTextureMappingBufferSequenceNumber(dstTexInfo, dst->GetSubresource());\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::GetBackBuffer(\n          UINT                iBackBuffer,\n          D3DBACKBUFFER_TYPE  Type,\n          IDirect3DSurface9** ppBackBuffer) {\n    // Could be doing a device reset...\n    D3D9DeviceLock lock = m_parent->LockDevice();\n\n    if (unlikely(ppBackBuffer == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(iBackBuffer >= m_presentParams.BackBufferCount)) {\n      Logger::err(str::format(\"D3D9: GetBackBuffer: Invalid back buffer index: \", iBackBuffer));\n      return D3DERR_INVALIDCALL;\n    }\n\n    if (m_backBuffers.empty()) {\n      // The backbuffers were destroyed and not recreated.\n      // This can happen when a call to Reset fails.\n      *ppBackBuffer = nullptr;\n      return D3D_OK;\n    }\n\n    *ppBackBuffer = ref(m_backBuffers[iBackBuffer].ptr());\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::GetRasterStatus(D3DRASTER_STATUS* pRasterStatus) {\n    // We could use D3DKMTGetScanLine but Wine doesn't implement that.\n    // So... we lie here and make some stuff up\n    // enough that it makes games work.\n\n    // Assume there's 20 lines in a vBlank.\n    constexpr uint32_t vBlankLineCount = 20;\n\n    if (unlikely(pRasterStatus == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    D3DDISPLAYMODEEX mode;\n    mode.Size = sizeof(mode);\n    if (FAILED(this->GetDisplayModeEx(&mode, nullptr)))\n      return D3DERR_INVALIDCALL;\n\n    uint32_t scanLineCount = mode.Height + vBlankLineCount;\n\n    auto nowUs = std::chrono::time_point_cast<std::chrono::microseconds>(\n      dxvk::high_resolution_clock::now())\n      .time_since_epoch();\n\n    auto frametimeUs = std::chrono::microseconds(1000000u / mode.RefreshRate);\n    auto scanLineUs  = frametimeUs / scanLineCount;\n\n    pRasterStatus->ScanLine = (nowUs % frametimeUs) / scanLineUs;\n    pRasterStatus->InVBlank = pRasterStatus->ScanLine >= mode.Height;\n\n    if (pRasterStatus->InVBlank)\n      pRasterStatus->ScanLine = 0;\n\n    return D3D_OK;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::GetDisplayMode(D3DDISPLAYMODE* pMode) {\n    if (unlikely(pMode == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pMode = D3DDISPLAYMODE();\n\n    D3DDISPLAYMODEEX mode;\n    mode.Size = sizeof(mode);\n    HRESULT hr = this->GetDisplayModeEx(&mode, nullptr);\n\n    if (FAILED(hr))\n      return hr;\n\n    pMode->Width       = mode.Width;\n    pMode->Height      = mode.Height;\n    pMode->Format      = mode.Format;\n    pMode->RefreshRate = mode.RefreshRate;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::GetPresentParameters(D3DPRESENT_PARAMETERS* pPresentationParameters) {\n    if (unlikely(pPresentationParameters == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *pPresentationParameters = m_presentParams;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::GetLastPresentCount(UINT* pLastPresentCount) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9SwapChainEx::GetLastPresentCount: Stub\");\n\n    if (likely(pLastPresentCount != nullptr))\n      *pLastPresentCount = 0;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::GetPresentStats(D3DPRESENTSTATS* pPresentationStatistics) {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9SwapChainEx::GetPresentStats: Stub\");\n\n    if (likely(pPresentationStatistics != nullptr)) {\n      D3DPRESENTSTATS presentationStatistics = { };\n      *pPresentationStatistics = presentationStatistics;\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::GetDisplayModeEx(D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) {\n    if (unlikely(pMode == nullptr && pRotation == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    if (pRotation != nullptr)\n      *pRotation = D3DDISPLAYROTATION_IDENTITY;\n\n    if (pMode != nullptr) {\n      if (unlikely(pMode->Size != sizeof(D3DDISPLAYMODEEX)))\n        return D3DERR_INVALIDCALL;\n\n      wsi::WsiMode devMode = { };\n\n      if (!wsi::getCurrentDisplayMode(wsi::getDefaultMonitor(), &devMode)) {\n        Logger::err(\"D3D9SwapChainEx::GetDisplayModeEx: Failed to enum display settings\");\n        return D3DERR_INVALIDCALL;\n      }\n\n      *pMode = ConvertDisplayMode(devMode);\n    }\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9SwapChainEx::Reset(\n          D3DPRESENT_PARAMETERS* pPresentParams,\n          D3DDISPLAYMODEEX*      pFullscreenDisplayMode) {\n    D3D9DeviceLock lock = m_parent->LockDevice();\n\n    HRESULT hr = D3D_OK;\n\n    this->NormalizePresentParameters(pPresentParams);\n\n    bool changeFullscreen = m_presentParams.Windowed != pPresentParams->Windowed;\n\n    if (pPresentParams->Windowed) {\n      if (changeFullscreen)\n        this->LeaveFullscreenMode();\n    }\n    else {\n      m_parent->NotifyFullscreen(m_window, true);\n\n      if (changeFullscreen) {\n        hr = this->EnterFullscreenMode(pPresentParams, pFullscreenDisplayMode);\n        if (FAILED(hr))\n          return hr;\n      }\n\n      D3D9WindowMessageFilter filter(m_window);\n\n      if (!changeFullscreen) {\n        hr = ChangeDisplayMode(pPresentParams, pFullscreenDisplayMode);\n        if (FAILED(hr))\n          return hr;\n\n        wsi::updateFullscreenWindow(m_monitor, m_window, true);\n      }\n    }\n\n    m_presentParams = *pPresentParams;\n\n    if (changeFullscreen)\n      SetGammaRamp(0, &m_ramp);\n\n    UpdatePresentParameters();\n\n    hr = CreateBackBuffers(m_presentParams.BackBufferCount, m_presentParams.Flags);\n    if (FAILED(hr))\n      return hr;\n\n    return D3D_OK;\n  }\n\n\n  HRESULT D3D9SwapChainEx::WaitForVBlank() {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"D3D9SwapChainEx::WaitForVBlank: Stub\");\n\n    return D3D_OK;\n  }\n\n  static bool validateGammaRamp(const WORD (&ramp)[256]) {\n    if (ramp[0] >= ramp[std::size(ramp) - 1]) {\n      Logger::warn(\"validateGammaRamp: ramp inverted or flat\");\n      return false;\n    }\n\n    for (size_t i = 1; i < std::size(ramp); i++) {\n      if (ramp[i] < ramp[i - 1]) {\n        Logger::warn(\"validateGammaRamp: ramp not monotonically increasing\");\n        return false;\n      }\n      if (ramp[i] - ramp[i - 1] >= UINT16_MAX / 2) {\n        Logger::warn(\"validateGammaRamp: huuuge jump\");\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n\n  void    D3D9SwapChainEx::SetGammaRamp(\n            DWORD         Flags,\n      const D3DGAMMARAMP* pRamp) {\n    D3D9DeviceLock lock = m_parent->LockDevice();\n\n    if (unlikely(pRamp == nullptr))\n      return;\n\n    if (unlikely(!validateGammaRamp(pRamp->red)\n              && !validateGammaRamp(pRamp->blue)\n              && !validateGammaRamp(pRamp->green)))\n      return;\n\n    m_ramp = *pRamp;\n\n    bool isIdentity = true;\n\n    std::array<DxvkGammaCp, NumControlPoints> cp;\n      \n    for (uint32_t i = 0; i < NumControlPoints; i++) {\n      uint16_t identity = MapGammaControlPoint(float(i) / float(NumControlPoints - 1));\n\n      cp[i].r = pRamp->red[i];\n      cp[i].g = pRamp->green[i];\n      cp[i].b = pRamp->blue[i];\n      cp[i].a = 0;\n\n      isIdentity &= cp[i].r == identity\n                 && cp[i].g == identity\n                 && cp[i].b == identity;\n    }\n\n    if (!isIdentity && !m_presentParams.Windowed)\n      m_blitter->setGammaRamp(NumControlPoints, cp.data());\n    else\n      m_blitter->setGammaRamp(0, nullptr);\n  }\n\n\n  void    D3D9SwapChainEx::GetGammaRamp(D3DGAMMARAMP* pRamp) {\n    D3D9DeviceLock lock = m_parent->LockDevice();\n\n    if (likely(pRamp != nullptr))\n      *pRamp = m_ramp;\n  }\n\n\n  void    D3D9SwapChainEx::Invalidate(HWND hWindow) {\n    if (!hWindow)\n      hWindow = m_parent->GetWindow();\n\n    auto entry = m_presenters.find(hWindow);\n\n    if (entry != m_presenters.end()) {\n      if (entry->second.presenter) {\n        entry->second.presenter->destroyResources();\n        entry->second.presenter = nullptr;\n\n        if (m_presentParams.hDeviceWindow == hWindow)\n          DestroyLatencyTracker();\n      }\n\n      if (m_wctx == &entry->second)\n        m_wctx = nullptr;\n\n      m_presenters.erase(entry);\n    }\n  }\n\n\n  void D3D9SwapChainEx::SetCursorTexture(UINT Width, UINT Height, uint8_t* pCursorBitmap) {\n      VkExtent2D cursorSize = { uint32_t(Width), uint32_t(Height) };\n\n      m_blitter->setCursorTexture(\n        cursorSize,\n        VK_FORMAT_B8G8R8A8_SRGB,\n        reinterpret_cast<void*>(pCursorBitmap));\n  }\n\n\n  void D3D9SwapChainEx::SetCursorPosition(int32_t X, int32_t Y, UINT Width, UINT Height) {\n      VkOffset2D cursorPosition = { X, Y };\n      VkExtent2D cursorSize     = { uint32_t(Width), uint32_t(Height) };\n\n      VkRect2D   cursorRect     = { cursorPosition, cursorSize };\n\n      m_parent->EmitCs([\n        cBlitter = m_blitter,\n        cRect    = cursorRect\n      ] (DxvkContext* ctx) {\n        cBlitter->setCursorPos(\n          cRect);\n      });\n  }\n\n\n  HRESULT D3D9SwapChainEx::SetDialogBoxMode(bool bEnableDialogs) {\n    // https://docs.microsoft.com/en-us/windows/win32/api/d3d9/nf-d3d9-idirect3ddevice9-setdialogboxmode\n    // The MSDN documentation says this will error out under many weird conditions.\n    // However it doesn't appear to error at all in any of my tests of these\n    // cases described in the documentation.\n    return D3D_OK;\n  }\n\n\n  D3D9Surface* D3D9SwapChainEx::GetBackBuffer(UINT iBackBuffer) {\n    if (iBackBuffer >= m_presentParams.BackBufferCount)\n      return nullptr;\n\n    return m_backBuffers[iBackBuffer].ptr();\n  }\n\n\n  void D3D9SwapChainEx::NormalizePresentParameters(D3DPRESENT_PARAMETERS* pPresentParams) {\n    if (pPresentParams->hDeviceWindow == nullptr)\n      pPresentParams->hDeviceWindow    = m_parent->GetWindow();\n\n    pPresentParams->BackBufferCount    = std::max(pPresentParams->BackBufferCount, 1u);\n\n    if (pPresentParams->Windowed) {\n      wsi::getWindowSize(pPresentParams->hDeviceWindow,\n        pPresentParams->BackBufferWidth  ? nullptr : &pPresentParams->BackBufferWidth,\n        pPresentParams->BackBufferHeight ? nullptr : &pPresentParams->BackBufferHeight);\n    }\n    else {\n      wsi::getMonitorClientSize(wsi::getDefaultMonitor(),\n        pPresentParams->BackBufferWidth  ? nullptr : &pPresentParams->BackBufferWidth,\n        pPresentParams->BackBufferHeight ? nullptr : &pPresentParams->BackBufferHeight);\n    }\n\n    if (pPresentParams->BackBufferFormat == D3DFMT_UNKNOWN)\n      pPresentParams->BackBufferFormat = D3DFMT_X8R8G8B8;\n\n    if (env::getEnvVar(\"DXVK_FORCE_WINDOWED\") == \"1\")\n      pPresentParams->Windowed         = TRUE;\n  }\n\n\n  void D3D9SwapChainEx::PresentImage(UINT SyncInterval) {\n    m_parent->EndFrame(m_latencyTracker);\n    m_parent->Flush();\n\n    if (m_latencyTracker)\n      m_latencyTracker->notifyCpuPresentBegin(m_wctx->frameId + 1u);\n\n    // Retrieve the image and image view to present\n    VkResult status = VK_SUCCESS;\n\n    Rc<DxvkImage> swapImage = m_backBuffers[0]->GetCommonTexture()->GetImage();\n    Rc<DxvkImageView> swapImageView = m_backBuffers[0]->GetCommonTexture()->GetSampleView(false);\n\n    // Presentation semaphores and WSI swap chain image\n    PresenterSync sync = { };\n    Rc<DxvkImage> backBuffer;\n\n    status = m_wctx->presenter->acquireNextImage(sync, backBuffer);\n\n    if (status >= 0 && status != VK_NOT_READY) {\n      VkRect2D srcRect = {\n        {  int32_t(m_srcRect.left),                    int32_t(m_srcRect.top)                    },\n        { uint32_t(m_srcRect.right - m_srcRect.left), uint32_t(m_srcRect.bottom - m_srcRect.top) } };\n\n      VkRect2D dstRect = {\n        {  int32_t(m_dstRect.left),                    int32_t(m_dstRect.top)                    },\n        { uint32_t(m_dstRect.right - m_dstRect.left), uint32_t(m_dstRect.bottom - m_dstRect.top) } };\n\n      // Bump frame ID\n      m_wctx->frameId += 1;\n\n      // Present from CS thread so that we don't\n      // have to synchronize with it first.\n      DxvkImageViewKey viewInfo;\n      viewInfo.viewType   = VK_IMAGE_VIEW_TYPE_2D;\n      viewInfo.usage      = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n      viewInfo.format     = backBuffer->info().format;\n      viewInfo.aspects    = VK_IMAGE_ASPECT_COLOR_BIT;\n      viewInfo.mipIndex   = 0u;\n      viewInfo.mipCount   = 1u;\n      viewInfo.layerIndex = 0u;\n      viewInfo.layerCount = 1u;\n\n      m_parent->EmitCs([\n        cDevice         = m_device,\n        cPresenter      = m_wctx->presenter,\n        cBlitter        = m_blitter,\n        cColorSpace     = m_colorspace,\n        cSrcView        = swapImageView,\n        cSrcRect        = srcRect,\n        cDstView        = backBuffer->createView(viewInfo),\n        cDstRect        = dstRect,\n        cSync           = sync,\n        cFrameId        = m_wctx->frameId,\n        cLatency        = m_latencyTracker\n      ] (DxvkContext* ctx) {\n        // Update back buffer color space as necessary\n        if (cSrcView->image()->info().colorSpace != cColorSpace) {\n          DxvkImageUsageInfo usage = { };\n          usage.colorSpace = cColorSpace;\n\n          ctx->ensureImageCompatibility(cSrcView->image(), usage);\n        }\n\n        // Blit back buffer onto Vulkan swap chain\n        auto contextObjects = ctx->beginExternalRendering();\n\n        cBlitter->present(contextObjects,\n          cDstView, cDstRect, cSrcView, cSrcRect);\n\n        // Submit command list and present\n        ctx->synchronizeWsi(cSync);\n        ctx->flushCommandList(nullptr, nullptr);\n\n        cDevice->presentImage(cPresenter, cLatency, cFrameId, nullptr);\n      });\n\n      m_parent->FlushCsChunk();\n    }\n\n    if (m_latencyTracker) {\n      if (status == VK_SUCCESS)\n        m_latencyTracker->notifyCpuPresentEnd(m_wctx->frameId);\n      else\n        m_latencyTracker->discardTimings();\n    }\n\n    SyncFrameLatency();\n\n    DxvkLatencyStats latencyStats = { };\n\n    if (m_latencyTracker && status == VK_SUCCESS) {\n      latencyStats = m_latencyTracker->getStatistics(m_wctx->frameId);\n      m_latencyTracker->sleepAndBeginFrame(m_wctx->frameId + 1, std::abs(m_targetFrameRate));\n\n      m_parent->BeginFrame(m_latencyTracker, m_wctx->frameId + 1u);\n    }\n\n    if (m_latencyHud)\n      m_latencyHud->accumulateStats(latencyStats);\n\n    // Rotate swap chain buffers so that the back\n    // buffer at index 0 becomes the front buffer.\n    uint32_t rotatingBufferCount = m_backBuffers.size();\n    if (!SwapWithFrontBuffer() && m_parent->GetOptions()->extraFrontbuffer) {\n      // The front buffer only exists for GetFrontBufferData\n      // and the application cannot obserse buffer swapping in GetBackBuffer()\n      rotatingBufferCount -= 1;\n    }\n\n    // Backbuffer 0 is the one that gets copied to the Vulkan swapchain backbuffer.\n    // => m_backBuffers[1] is the next one that gets presented\n    // and the currente m_backBuffers[0] ends up at the end of the vector.\n    for (uint32_t i = 1; i < rotatingBufferCount; i++)\n      m_backBuffers[i]->Swap(m_backBuffers[i - 1].ptr());\n\n    m_parent->m_dirty.set(D3D9DeviceDirtyFlag::Framebuffer);\n  }\n\n\n  Rc<Presenter> D3D9SwapChainEx::CreatePresenter(HWND Window, Rc<sync::Signal> Signal) {\n    PresenterDesc presenterDesc;\n    presenterDesc.deferSurfaceCreation = m_parent->GetOptions()->deferSurfaceCreation;\n\n    Rc<Presenter> presenter = new Presenter(m_device, Signal, presenterDesc, [\n      cDevice = m_device,\n      cWindow = Window\n    ] (VkSurfaceKHR* surface) {\n      auto vki = cDevice->adapter()->vki();\n\n      return wsi::createSurface(cWindow,\n        vki->getLoaderProc(),\n        vki->instance(),\n        surface);\n    });\n\n    presenter->setSurfaceExtent(m_swapchainExtent);\n    presenter->setSurfaceFormat(GetSurfaceFormat());\n\n    if (m_hdrMetadata)\n      presenter->setHdrMetadata(*m_hdrMetadata);\n\n    return presenter;\n  }\n\n\n  void D3D9SwapChainEx::DestroyBackBuffers() {\n    for (auto& backBuffer : m_backBuffers)\n      backBuffer->ClearContainer();\n\n    m_backBuffers.clear();\n  }\n\n\n  bool D3D9SwapChainEx::UpdateWindowCtx() {\n    if (!m_window)\n      return false;\n\n    auto entry = m_presenters.find(m_window);\n\n    if (entry == m_presenters.end()) {\n      entry = m_presenters.emplace(\n        std::piecewise_construct,\n        std::forward_as_tuple(m_window),\n        std::forward_as_tuple()).first;\n\n      entry->second.frameLatencySignal = new sync::Fence(entry->second.frameId);\n      entry->second.presenter = CreatePresenter(m_window, entry->second.frameLatencySignal);\n\n      if (m_presentParams.hDeviceWindow == m_window && m_latencyTracking)\n        m_latencyTracker = m_device->createLatencyTracker(entry->second.presenter);\n    }\n\n    m_wctx = &entry->second;\n    return true;\n  }\n\n\n  HRESULT D3D9SwapChainEx::CreateBackBuffers(uint32_t NumBackBuffers, DWORD Flags) {\n    // Explicitly destroy current swap image before\n    // creating a new one to free up resources\n    DestroyBackBuffers();\n\n    const uint32_t frontBufferCount = (SwapWithFrontBuffer() || m_parent->GetOptions()->extraFrontbuffer) ? 1 : 0;\n    const uint32_t bufferCount = NumBackBuffers + frontBufferCount;\n\n    m_backBuffers.reserve(bufferCount);\n\n    // Create new back buffer\n    D3D9_COMMON_TEXTURE_DESC desc;\n    desc.Width              = std::max(m_presentParams.BackBufferWidth,  1u);\n    desc.Height             = std::max(m_presentParams.BackBufferHeight, 1u);\n    desc.Depth              = 1;\n    desc.MipLevels          = 1;\n    desc.ArraySize          = 1;\n    desc.Format             = EnumerateFormat(m_presentParams.BackBufferFormat);\n    desc.MultiSample        = m_presentParams.MultiSampleType;\n    desc.MultisampleQuality = m_presentParams.MultiSampleQuality;\n    desc.Pool               = D3DPOOL_DEFAULT;\n    desc.Usage              = D3DUSAGE_RENDERTARGET;\n    desc.Discard            = FALSE;\n    desc.IsBackBuffer       = TRUE;\n    // The texture will get sampled for presentation.\n    desc.IsAttachmentOnly   = FALSE;\n    // we cannot respect D3DPRESENTFLAG_LOCKABLE_BACKBUFFER here because\n    // we might need to lock for the BlitGDI fallback path\n    desc.IsLockable         = true;\n\n    for (uint32_t i = 0; i < bufferCount; i++) {\n      D3D9Surface* surface;\n      try {\n        surface = new D3D9Surface(m_parent, &desc, m_parent->IsExtended(), this, nullptr);\n        m_parent->IncrementLosableCounter();\n      } catch (const DxvkError& e) {\n        DestroyBackBuffers();\n        Logger::err(e.message());\n        return D3DERR_OUTOFVIDEOMEMORY;\n      }\n\n      m_backBuffers.emplace_back(surface);\n    }\n\n    // Initialize the image so that we can use it. Clearing\n    // to black prevents garbled output for the first frame.\n    small_vector<Rc<DxvkImage>, 4> images;\n\n    for (size_t i = 0; i < m_backBuffers.size(); i++)\n      images.push_back(m_backBuffers[i]->GetCommonTexture()->GetImage());\n\n    m_parent->InjectCs([\n      cImages = std::move(images)\n    ] (DxvkContext* ctx) {\n      for (size_t i = 0; i < cImages.size(); i++) {\n        ctx->initImage(cImages[i], VK_IMAGE_LAYOUT_UNDEFINED);\n      }\n    });\n\n    return D3D_OK;\n  }\n\n\n  void D3D9SwapChainEx::CreateBlitter() {\n    Rc<hud::Hud> hud = hud::Hud::createHud(m_device);\n\n    if (hud) {\n      m_apiHud = hud->addItem<hud::HudClientApiItem>(\"api\", 1, GetApiName());\n\n      if (m_latencyTracking)\n        m_latencyHud = hud->addItem<hud::HudLatencyItem>(\"latency\", 4);\n\n      hud->addItem<hud::HudFixedFunctionShaders>(\"ffshaders\", -1, m_parent);\n      hud->addItem<hud::HudSWVPState>(\"swvp\", -1, m_parent);\n\n#ifdef D3D9_ALLOW_UNMAPPING\n      hud->addItem<hud::HudTextureMemory>(\"memory\", -1, m_parent);\n#endif\n    }\n\n    m_blitter = new DxvkSwapchainBlitter(m_device, std::move(hud));\n  }\n\n\n  void D3D9SwapChainEx::InitRamp() {\n    for (uint32_t i = 0; i < NumControlPoints; i++) {\n      DWORD identity = DWORD(MapGammaControlPoint(float(i) / float(NumControlPoints - 1)));\n\n      m_ramp.red[i]   = identity;\n      m_ramp.green[i] = identity;\n      m_ramp.blue[i]  = identity;\n    }\n  }\n\n\n  void D3D9SwapChainEx::DestroyLatencyTracker() {\n    if (!m_latencyTracker)\n      return;\n\n    m_parent->InjectCs([\n      cTracker = std::move(m_latencyTracker)\n    ] (DxvkContext* ctx) {\n      ctx->endLatencyTracking(cTracker);\n    });\n  }\n\n\n  void D3D9SwapChainEx::UpdateTargetFrameRate(uint32_t SyncInterval) {\n    double frameRate = double(m_parent->GetOptions()->maxFrameRate);\n\n    if (frameRate != -1.0) {\n      if (frameRate == 0.0 && SyncInterval) {\n        bool engageLimiter = SyncInterval > 1u || m_monitor ||\n          m_device->config().latencySleep == Tristate::True;\n\n        if (engageLimiter)\n          frameRate = -m_displayRefreshRate / double(SyncInterval);\n      }\n\n      m_wctx->presenter->setFrameRateLimit(frameRate, GetActualFrameLatency());\n      m_targetFrameRate = frameRate;\n    }\n  }\n\n\n  void D3D9SwapChainEx::SyncFrameLatency() {\n    // Wait for the sync event so that we respect the maximum frame latency\n    m_wctx->frameLatencySignal->wait(m_wctx->frameId - GetActualFrameLatency());\n  }\n\n  uint32_t D3D9SwapChainEx::GetActualFrameLatency() {\n    uint32_t maxFrameLatency = m_parent->GetFrameLatency();\n\n    if (m_frameLatencyCap)\n      maxFrameLatency = std::min(maxFrameLatency, m_frameLatencyCap);\n\n    maxFrameLatency = std::min(maxFrameLatency, m_presentParams.BackBufferCount + 1);\n    return maxFrameLatency;\n  }\n\n\n  VkSurfaceFormatKHR D3D9SwapChainEx::GetSurfaceFormat() {\n    D3D9Format format = EnumerateFormat(m_presentParams.BackBufferFormat);\n\n    switch (format) {\n      default:\n        Logger::warn(str::format(\"D3D9SwapChainEx: Unexpected format: \", format));\n        [[fallthrough]];\n\n      case D3D9Format::A8R8G8B8:\n      case D3D9Format::X8R8G8B8:\n        return { VK_FORMAT_B8G8R8A8_UNORM, m_colorspace };\n\n      case D3D9Format::A8B8G8R8:\n      case D3D9Format::X8B8G8R8:\n        return { VK_FORMAT_R8G8B8A8_UNORM, m_colorspace };\n\n      case D3D9Format::A2R10G10B10:\n        return { VK_FORMAT_A2R10G10B10_UNORM_PACK32, m_colorspace };\n\n      case D3D9Format::A2B10G10R10:\n        return { VK_FORMAT_A2B10G10R10_UNORM_PACK32, m_colorspace };\n\n      case D3D9Format::X1R5G5B5:\n      case D3D9Format::A1R5G5B5:\n        return { VK_FORMAT_B5G5R5A1_UNORM_PACK16, m_colorspace };\n\n      case D3D9Format::R5G6B5:\n        return { VK_FORMAT_B5G6R5_UNORM_PACK16, m_colorspace };\n\n      case D3D9Format::A16B16G16R16F: {\n        if (!m_parent->HasFormatsUnlocked()) {\n          Logger::warn(str::format(\"D3D9SwapChainEx: Unexpected format: \", format));\n          return VkSurfaceFormatKHR { };\n        }\n\n        return { VK_FORMAT_R16G16B16A16_SFLOAT, m_colorspace };\n      }\n    }\n  }\n\n\n  void D3D9SwapChainEx::UpdateWindowedRefreshRate() {\n    // Ignore call if we are in fullscreen mode and\n    // know the active display mode already anyway\n    if (!m_displayRefreshRateDirty || m_monitor)\n      return;\n\n    m_displayRefreshRate = 0.0;\n    m_displayRefreshRateDirty = false;\n\n    HMONITOR monitor = wsi::getWindowMonitor(m_window);\n\n    if (!monitor)\n      return;\n\n    wsi::WsiMode mode = { };\n\n    if (!wsi::getCurrentDisplayMode(monitor, &mode))\n      return;\n\n    if (mode.refreshRate.denominator) {\n      m_displayRefreshRate = double(mode.refreshRate.numerator)\n                           / double(mode.refreshRate.denominator);\n    }\n  }\n\n\n  HRESULT D3D9SwapChainEx::EnterFullscreenMode(\n          D3DPRESENT_PARAMETERS* pPresentParams,\n    const D3DDISPLAYMODEEX*      pFullscreenDisplayMode) {    \n    if (FAILED(ChangeDisplayMode(pPresentParams, pFullscreenDisplayMode))) {\n      Logger::err(\"D3D9: EnterFullscreenMode: Failed to change display mode\");\n      return D3DERR_INVALIDCALL;\n    }\n\n    // Testing shows we shouldn't hook WM_NCCALCSIZE but we shouldn't change\n    // windows style either.\n    //\n    // Some games restore window styles after we have changed it, so hooking is\n    // also required. Doing it will allow us to create fullscreen windows\n    // regardless of their style and it also appears to work on Windows.\n    HookWindowProc(m_window, this);\n\n    D3D9WindowMessageFilter filter(m_window);\n    \n    m_monitor = wsi::getDefaultMonitor();\n\n    wsi::saveWindowState(m_window, &m_windowState, true);\n\n    if (!wsi::enterFullscreenMode(m_monitor, m_window, &m_windowState, true)) {\n        Logger::err(\"D3D9: EnterFullscreenMode: Failed to enter fullscreen mode\");\n        return D3DERR_INVALIDCALL;\n    }\n    \n    m_parent->NotifyFullscreen(m_window, true);\n    \n    return D3D_OK;\n  }\n  \n  \n  HRESULT D3D9SwapChainEx::LeaveFullscreenMode() {\n    if (!wsi::isWindow(m_window))\n      return D3DERR_INVALIDCALL;\n    \n    if (FAILED(RestoreDisplayMode(m_monitor)))\n      Logger::warn(\"D3D9: LeaveFullscreenMode: Failed to restore display mode\");\n    \n    m_monitor = nullptr;\n\n    ResetWindowProc(m_window);\n    \n    if (!wsi::leaveFullscreenMode(m_window, &m_windowState)) {\n      Logger::err(\"D3D9: LeaveFullscreenMode: Failed to exit fullscreen mode\");\n      return D3DERR_NOTAVAILABLE;\n    }\n    wsi::restoreWindowState(m_window, &m_windowState, false);\n\n    m_parent->NotifyFullscreen(m_window, false);\n    \n    return D3D_OK;\n  }\n  \n  \n  HRESULT D3D9SwapChainEx::ChangeDisplayMode(\n          D3DPRESENT_PARAMETERS* pPresentParams,\n    const D3DDISPLAYMODEEX*      pFullscreenDisplayMode) {\n    D3DDISPLAYMODEEX mode;\n\n    if (pFullscreenDisplayMode) {\n      mode = *pFullscreenDisplayMode;\n    } else {\n      mode.Width            = pPresentParams->BackBufferWidth;\n      mode.Height           = pPresentParams->BackBufferHeight;\n      mode.Format           = pPresentParams->BackBufferFormat;\n      mode.RefreshRate      = pPresentParams->FullScreen_RefreshRateInHz;\n      mode.ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE;\n      mode.Size             = sizeof(D3DDISPLAYMODEEX);\n    }\n\n    wsi::WsiMode wsiMode = ConvertDisplayMode(mode);\n    \n    HMONITOR monitor = wsi::getDefaultMonitor();\n\n    if (!wsi::setWindowMode(monitor, m_window, &m_windowState, wsiMode))\n      return D3DERR_NOTAVAILABLE;\n\n    m_displayRefreshRate = 0.0;\n\n    if (wsi::getCurrentDisplayMode(monitor, &wsiMode)) {\n      m_displayRefreshRate = double(wsiMode.refreshRate.numerator)\n                           / double(wsiMode.refreshRate.denominator);\n    }\n\n    m_displayRefreshRateDirty = false;\n    return D3D_OK;\n  }\n  \n  \n  HRESULT D3D9SwapChainEx::RestoreDisplayMode(HMONITOR hMonitor) {\n    if (hMonitor == nullptr)\n      return D3DERR_INVALIDCALL;\n    \n    if (!wsi::restoreDisplayMode())\n      return D3DERR_NOTAVAILABLE;\n\n    m_displayRefreshRateDirty = true;\n    return D3D_OK;\n  }\n\n  void D3D9SwapChainEx::UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect) {\n    const bool isWindowed = m_presentParams.Windowed;\n\n    // Tests show that present regions are ignored in fullscreen\n\n    if (pSourceRect == nullptr || !isWindowed) {\n      m_srcRect.top    = 0;\n      m_srcRect.left   = 0;\n      m_srcRect.right  = m_presentParams.BackBufferWidth;\n      m_srcRect.bottom = m_presentParams.BackBufferHeight;\n    }\n    else\n      m_srcRect = *pSourceRect;\n\n    \n    UINT width, height;\n    wsi::getWindowSize(m_window, &width, &height);\n\n    RECT dstRect;\n    if (pDestRect == nullptr || !isWindowed) {\n      // TODO: Should we hook WM_SIZE message for this?\n      dstRect.top    = 0;\n      dstRect.left   = 0;\n      dstRect.right  = LONG(width);\n      dstRect.bottom = LONG(height);\n      \n    }\n    else\n      dstRect = *pDestRect;\n\n    m_partialCopy =\n       dstRect.left != 0\n    || dstRect.top != 0\n    || dstRect.right  - dstRect.left != LONG(width)\n    || dstRect.bottom - dstRect.top  != LONG(height);\n\n    m_swapchainExtent = { width, height };\n    m_dstRect = dstRect;\n  }\n\n  void D3D9SwapChainEx::UpdatePresentParameters() {\n    if (m_wctx) {\n      m_wctx->presenter->setSurfaceExtent(m_swapchainExtent);\n      m_wctx->presenter->setSurfaceFormat(GetSurfaceFormat());\n    }\n  }\n\n  VkExtent2D D3D9SwapChainEx::GetPresentExtent() {\n    return m_swapchainExtent;\n  }\n\n\n  std::string D3D9SwapChainEx::GetApiName() {\n    if (this->GetParent()->Is9On12Device())\n      return this->GetParent()->IsExtended() ? \"D3D9On12Ex\" : \"D3D9On12\";\n\n    return this->GetParent()->IsD3D8Compatible() ? \"D3D8\" :\n           this->GetParent()->IsExtended() ? \"D3D9Ex\" : \"D3D9\";\n  }\n\n\n  bool D3D9SwapChainEx::IsDeviceReset(D3D9WindowContext* wctx) {\n    uint32_t counter = m_parent->GetResetCounter();\n\n    if (counter == wctx->deviceResetCounter)\n      return false;\n\n    wctx->deviceResetCounter = counter;\n    return true;\n  }\n\n\n  D3D9VkExtSwapchain::D3D9VkExtSwapchain(D3D9SwapChainEx *pSwapChain)\n    : m_swapchain(pSwapChain) {\n\n  }\n  \n  ULONG STDMETHODCALLTYPE D3D9VkExtSwapchain::AddRef() {\n    return m_swapchain->AddRef();\n  }\n  \n  ULONG STDMETHODCALLTYPE D3D9VkExtSwapchain::Release() {\n    return m_swapchain->Release();\n  }\n  \n  HRESULT STDMETHODCALLTYPE D3D9VkExtSwapchain::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_swapchain->QueryInterface(riid, ppvObject);\n  }\n\n  BOOL STDMETHODCALLTYPE D3D9VkExtSwapchain::CheckColorSpaceSupport(\n          VkColorSpaceKHR           ColorSpace) {\n    return m_swapchain->m_wctx->presenter->supportsColorSpace(ColorSpace);\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9VkExtSwapchain::SetColorSpace(\n          VkColorSpaceKHR           ColorSpace) {\n    if (!CheckColorSpaceSupport(ColorSpace))\n      return D3DERR_INVALIDCALL;\n    \n    m_swapchain->m_colorspace = ColorSpace;\n\n    if (m_swapchain->m_wctx)\n      m_swapchain->m_wctx->presenter->setSurfaceFormat(m_swapchain->GetSurfaceFormat());\n\n    return S_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9VkExtSwapchain::SetHDRMetaData(\n    const VkHdrMetadataEXT          *pHDRMetadata) {\n    if (!pHDRMetadata)\n      return D3DERR_INVALIDCALL;\n\n    m_swapchain->m_hdrMetadata = *pHDRMetadata;\n\n    if (m_swapchain->m_wctx)\n      m_swapchain->m_wctx->presenter->setHdrMetadata(*pHDRMetadata);\n\n    return S_OK;\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9VkExtSwapchain::GetCurrentOutputDesc(\n          D3D9VkExtOutputMetadata   *pOutputDesc) {\n    HMONITOR monitor = m_swapchain->m_monitor;\n    if (!monitor)\n      monitor = wsi::getDefaultMonitor();\n    // ^ this should be the display we are mostly covering someday.\n\n    wsi::WsiEdidData edidData = wsi::getMonitorEdid(monitor);\n    wsi::WsiDisplayMetadata metadata = {};\n    {\n      std::optional<wsi::WsiDisplayMetadata> r_metadata = std::nullopt;\n      if (!edidData.empty())\n        r_metadata = wsi::parseColorimetryInfo(edidData);\n\n      if (r_metadata)\n        metadata = *r_metadata;\n      else\n        Logger::err(\"D3D9: Failed to parse display metadata + colorimetry info, using blank.\");\n    }\n\n\n    NormalizeDisplayMetadata(CheckColorSpaceSupport(VK_COLOR_SPACE_HDR10_ST2084_EXT), metadata);\n\n    pOutputDesc->RedPrimary[0]         = metadata.redPrimary[0];\n    pOutputDesc->RedPrimary[1]         = metadata.redPrimary[1];\n    pOutputDesc->GreenPrimary[0]       = metadata.greenPrimary[0];\n    pOutputDesc->GreenPrimary[1]       = metadata.greenPrimary[1];\n    pOutputDesc->BluePrimary[0]        = metadata.bluePrimary[0];\n    pOutputDesc->BluePrimary[1]        = metadata.bluePrimary[1];\n    pOutputDesc->WhitePoint[0]         = metadata.whitePoint[0];\n    pOutputDesc->WhitePoint[1]         = metadata.whitePoint[1];\n    pOutputDesc->MinLuminance          = metadata.minLuminance;\n    pOutputDesc->MaxLuminance          = metadata.maxLuminance;\n    pOutputDesc->MaxFullFrameLuminance = metadata.maxFullFrameLuminance;\n    return S_OK;\n  }\n\n  void STDMETHODCALLTYPE D3D9VkExtSwapchain::UnlockAdditionalFormats() {\n    Logger::err(\"ID3D9VkExtSwapchain::UnlockAdditionalFormats is deprecated.\\n\"\n                \"Please use ID3D9VkExtInterface::UnlockAdditionalFormats instead.\\n\");\n\n    Com<ID3D9VkExtInterface> iface;\n\n    if (SUCCEEDED(m_swapchain->GetParent()->QueryInterface(\n        __uuidof(ID3D9VkExtInterface), reinterpret_cast<void**>(&iface))))\n      iface->UnlockAdditionalFormats();\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_swapchain.h",
    "content": "#pragma once\n\n#include \"d3d9_device_child.h\"\n#include \"d3d9_device.h\"\n#include \"d3d9_format.h\"\n\n#include \"../dxvk/hud/dxvk_hud.h\"\n\n#include \"../dxvk/dxvk_swapchain_blitter.h\"\n\n#include \"../util/sync/sync_signal.h\"\n\n#include \"../wsi/wsi_window.h\"\n#include \"../wsi/wsi_monitor.h\"\n\n#include <vector>\n\nnamespace dxvk {\n\n  class D3D9Surface;\n  class D3D9SwapChainEx;\n\n  class D3D9VkExtSwapchain final : public ID3D9VkExtSwapchain {\n  public:\n    D3D9VkExtSwapchain(D3D9SwapChainEx *pSwapChain);\n    \n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n\n    BOOL STDMETHODCALLTYPE CheckColorSpaceSupport(\n            VkColorSpaceKHR           ColorSpace);\n\n    HRESULT STDMETHODCALLTYPE SetColorSpace(\n            VkColorSpaceKHR           ColorSpace);\n\n    HRESULT STDMETHODCALLTYPE SetHDRMetaData(\n      const VkHdrMetadataEXT          *pHDRMetadata);\n\n    HRESULT STDMETHODCALLTYPE GetCurrentOutputDesc(\n            D3D9VkExtOutputMetadata   *pOutputDesc);\n\n    void STDMETHODCALLTYPE UnlockAdditionalFormats();\n\n  private:\n    D3D9SwapChainEx *m_swapchain;\n  };\n\n  struct D3D9WindowContext {\n    Rc<Presenter>                  presenter;\n\n    uint64_t                       frameId = D3D9DeviceEx::MaxFrameLatency;\n    Rc<sync::Fence>                frameLatencySignal;\n\n    uint32_t                       deviceResetCounter = 0u;\n  };\n\n  using D3D9SwapChainExBase = D3D9DeviceChild<IDirect3DSwapChain9Ex>;\n  class D3D9SwapChainEx final : public D3D9SwapChainExBase {\n    static constexpr uint32_t NumControlPoints = 256;\n\n    friend class D3D9VkExtSwapchain;\n  public:\n\n    D3D9SwapChainEx(\n            D3D9DeviceEx*          pDevice,\n            D3DPRESENT_PARAMETERS* pPresentParams,\n      const D3DDISPLAYMODEEX*      pFullscreenDisplayMode,\n            bool                   EnableLatencyTracking);\n\n    ~D3D9SwapChainEx();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    HRESULT STDMETHODCALLTYPE Present(\n      const RECT*    pSourceRect,\n      const RECT*    pDestRect,\n            HWND     hDestWindowOverride,\n      const RGNDATA* pDirtyRegion,\n            DWORD    dwFlags);\n\n#ifdef _WIN32\n    HRESULT PresentImageGDI(HWND Window);\n#endif\n\n    HRESULT STDMETHODCALLTYPE GetFrontBufferData(IDirect3DSurface9* pDestSurface);\n\n    HRESULT STDMETHODCALLTYPE GetBackBuffer(\n            UINT                iBackBuffer,\n            D3DBACKBUFFER_TYPE  Type,\n            IDirect3DSurface9** ppBackBuffer);\n\n    HRESULT STDMETHODCALLTYPE GetRasterStatus(D3DRASTER_STATUS* pRasterStatus);\n\n    HRESULT STDMETHODCALLTYPE GetDisplayMode(D3DDISPLAYMODE* pMode);\n\n    HRESULT STDMETHODCALLTYPE GetPresentParameters(D3DPRESENT_PARAMETERS* pPresentationParameters);\n\n    HRESULT STDMETHODCALLTYPE GetLastPresentCount(UINT* pLastPresentCount);\n\n    HRESULT STDMETHODCALLTYPE GetPresentStats(D3DPRESENTSTATS* pPresentationStatistics);\n\n    HRESULT STDMETHODCALLTYPE GetDisplayModeEx(D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation);\n\n    HRESULT Reset(\n            D3DPRESENT_PARAMETERS* pPresentParams,\n            D3DDISPLAYMODEEX*      pFullscreenDisplayMode);\n\n    HRESULT WaitForVBlank();\n\n    void    SetGammaRamp(\n            DWORD         Flags,\n      const D3DGAMMARAMP* pRamp);\n\n    void    GetGammaRamp(D3DGAMMARAMP* pRamp);\n\n    void    Invalidate(HWND hWindow);\n\n    void SetCursorTexture(UINT Width, UINT Height, uint8_t* pCursorBitmap);\n\n    void SetCursorPosition(int32_t X, int32_t Y, UINT Width, UINT Height);\n\n    HRESULT SetDialogBoxMode(bool bEnableDialogs);\n\n    D3D9Surface* GetBackBuffer(UINT iBackBuffer);\n\n    const D3DPRESENT_PARAMETERS* GetPresentParams() const { return &m_presentParams; }\n\n    void SyncFrameLatency();\n\n    void DestroyBackBuffers();\n\n    bool UpdateWindowCtx();\n\n  private:\n\n    enum BindingIds : uint32_t {\n      Image = 0,\n      Gamma = 1,\n    };\n\n    D3DPRESENT_PARAMETERS     m_presentParams;\n    D3DGAMMARAMP              m_ramp;\n\n    Rc<DxvkDevice>            m_device;\n    Rc<DxvkSwapchainBlitter>  m_blitter;\n\n    std::unordered_map<\n      HWND,\n      D3D9WindowContext>      m_presenters;\n\n    D3D9WindowContext*        m_wctx = nullptr;\n\n    std::vector<Com<D3D9Surface, false>> m_backBuffers;\n    \n    RECT                      m_srcRect;\n    RECT                      m_dstRect;\n    VkExtent2D                m_swapchainExtent = { 0u, 0u };\n    bool                      m_partialCopy = false;\n\n    uint32_t                  m_frameLatencyCap = 0;\n\n    HWND                      m_window   = nullptr;\n    HMONITOR                  m_monitor  = nullptr;\n\n    wsi::DxvkWindowState      m_windowState;\n\n    double                    m_targetFrameRate = 0.0;\n\n    double                    m_displayRefreshRate = 0.0;\n    bool                      m_displayRefreshRateDirty = true;\n\n    bool                      m_warnedAboutGDIFallback = false;\n\n    VkColorSpaceKHR           m_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n\n    bool                      m_latencyTracking = false;\n    Rc<DxvkLatencyTracker>    m_latencyTracker = nullptr;\n\n    Rc<hud::HudClientApiItem> m_apiHud;\n    Rc<hud::HudLatencyItem>   m_latencyHud;\n\n    std::optional<VkHdrMetadataEXT> m_hdrMetadata;\n\n    D3D9VkExtSwapchain m_swapchainExt;\n\n    void PresentImage(UINT PresentInterval);\n\n    Rc<Presenter> CreatePresenter(\n            HWND                Window,\n            Rc<sync::Signal>    Signal);\n\n    HRESULT CreateBackBuffers(\n            uint32_t            NumBackBuffers,\n            DWORD               Flags);\n\n    void CreateBlitter();\n\n    void DestroyLatencyTracker();\n\n    void InitRamp();\n\n    void UpdateTargetFrameRate(uint32_t SyncInterval);\n\n    uint32_t GetActualFrameLatency();\n\n    VkSurfaceFormatKHR GetSurfaceFormat();\n    \n    void NormalizePresentParameters(D3DPRESENT_PARAMETERS* pPresentParams);\n\n    void UpdateWindowedRefreshRate();\n\n    HRESULT EnterFullscreenMode(\n            D3DPRESENT_PARAMETERS*  pPresentParams,\n      const D3DDISPLAYMODEEX*       pFullscreenDisplayMode);\n    \n    HRESULT LeaveFullscreenMode();\n    \n    HRESULT ChangeDisplayMode(\n            D3DPRESENT_PARAMETERS*  pPresentParams,\n      const D3DDISPLAYMODEEX*       pFullscreenDisplayMode);\n    \n    HRESULT RestoreDisplayMode(HMONITOR hMonitor);\n\n    void UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect);\n\n    void UpdatePresentParameters();\n\n    VkExtent2D GetPresentExtent();\n\n    std::string GetApiName();\n\n    bool IsDeviceReset(D3D9WindowContext* wctx);\n\n    const Com<D3D9Surface, false>& GetFrontBuffer() const {\n      // Buffer 0 is the one that gets copied to the Vulkan backbuffer.\n      // We rotate buffers after presenting, so buffer 0 becomes the last buffer in the vector.\n      return m_backBuffers.back();\n    }\n\n    bool SwapWithFrontBuffer() const {\n      if (m_presentParams.SwapEffect == D3DSWAPEFFECT_COPY)\n        return false;\n\n      // Tests show that SWAPEEFFECT_DISCARD with 1 backbuffer in windowed mode behaves identically to SWAPEFFECT_COPY\n      // For SWAPEFFECT_COPY we don't swap buffers but do another blit to the front buffer instead.\n      if (m_presentParams.SwapEffect == D3DSWAPEFFECT_DISCARD && m_presentParams.BackBufferCount == 1 && m_presentParams.Windowed)\n        return false;\n\n      return true;\n    }\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_swvp_emu.cpp",
    "content": "#include \"d3d9_swvp_emu.h\"\n\n#include \"d3d9_device.h\"\n#include \"d3d9_vertex_declaration.h\"\n\n#include \"../dxvk/dxvk_shader_spirv.h\"\n\n#include \"../spirv/spirv_module.h\"\n\nnamespace dxvk {\n\n  // Doesn't compare everything, only what we use in SWVP.\n\n  size_t D3D9VertexDeclHash::operator () (const D3D9CompactVertexElements& key) const {\n    DxvkHashState hash;\n\n    std::hash<BYTE> bytehash;\n    std::hash<WORD> wordhash;\n\n    for (uint32_t i = 0; i < key.size(); i++) {\n      const auto& element = key[i];\n      hash.add(wordhash(element.Stream));\n      hash.add(wordhash(element.Offset));\n      hash.add(bytehash(element.Type));\n      hash.add(bytehash(element.Method));\n      hash.add(bytehash(element.Usage));\n      hash.add(bytehash(element.UsageIndex));\n    }\n\n    return hash;\n  }\n\n  bool D3D9VertexDeclEq::operator () (const D3D9CompactVertexElements& a, const D3D9CompactVertexElements& b) const {\n    if (a.size() != b.size())\n      return false;\n\n    bool equal = true;\n\n    for (uint32_t i = 0; i < a.size(); i++)\n      equal &= std::memcmp(&a[i], &b[i], sizeof(a[0])) == 0;\n\n    return equal;\n  }\n\n  enum class DecltypeClass {\n    Float, Byte, Short, Dec, Half\n  };\n\n  enum DecltypeFlags {\n    Signed     = 1,\n    Normalize  = 2,\n    ReverseRGB = 4\n  };\n\n  struct Decltype {\n    DecltypeClass Class;\n    uint32_t      VectorCount;\n    uint32_t      Flags;\n  };\n\n  Decltype ClassifyDecltype(D3DDECLTYPE Type) {\n    switch (Type) {\n      case D3DDECLTYPE_FLOAT1:    return { DecltypeClass::Float, 1, DecltypeFlags::Signed };\n      case D3DDECLTYPE_FLOAT2:    return { DecltypeClass::Float, 2, DecltypeFlags::Signed };\n      case D3DDECLTYPE_FLOAT3:    return { DecltypeClass::Float, 3, DecltypeFlags::Signed };\n      case D3DDECLTYPE_FLOAT4:    return { DecltypeClass::Float, 4, DecltypeFlags::Signed };\n      case D3DDECLTYPE_D3DCOLOR:  return { DecltypeClass::Byte,  4, DecltypeFlags::Normalize | DecltypeFlags::ReverseRGB };\n      case D3DDECLTYPE_UBYTE4:    return { DecltypeClass::Byte,  4, 0 };\n      case D3DDECLTYPE_SHORT2:    return { DecltypeClass::Short, 2, DecltypeFlags::Signed };\n      case D3DDECLTYPE_SHORT4:    return { DecltypeClass::Short, 4, DecltypeFlags::Signed };\n      case D3DDECLTYPE_UBYTE4N:   return { DecltypeClass::Byte,  4, DecltypeFlags::Normalize };\n      case D3DDECLTYPE_SHORT2N:   return { DecltypeClass::Short, 2, DecltypeFlags::Signed | DecltypeFlags::Normalize };\n      case D3DDECLTYPE_SHORT4N:   return { DecltypeClass::Short, 4, DecltypeFlags::Signed | DecltypeFlags::Normalize };\n      case D3DDECLTYPE_USHORT2N:  return { DecltypeClass::Short, 2, DecltypeFlags::Normalize };\n      case D3DDECLTYPE_USHORT4N:  return { DecltypeClass::Short, 4, DecltypeFlags::Normalize };\n      case D3DDECLTYPE_UDEC3:     return { DecltypeClass::Dec,   3, 0 };\n      case D3DDECLTYPE_DEC3N:     return { DecltypeClass::Dec,   3, DecltypeFlags::Signed | DecltypeFlags::Normalize };\n      case D3DDECLTYPE_FLOAT16_2: return { DecltypeClass::Half,  2, DecltypeFlags::Signed };\n      case D3DDECLTYPE_FLOAT16_4: return { DecltypeClass::Half,  4, DecltypeFlags::Signed };\n      default:                    return { DecltypeClass::Float, 4, DecltypeFlags::Signed };\n    }\n  }\n\n  class D3D9SWVPEmulatorGenerator {\n\n  public:\n\n    D3D9SWVPEmulatorGenerator(const std::string& name)\n    : m_module(spvVersion(1, 3)) {\n      m_entryPointId = m_module.allocateId();\n\n      m_module.setDebugSource(\n        spv::SourceLanguageUnknown, 0,\n        m_module.addDebugString(name.c_str()),\n        nullptr);\n\n      m_module.setMemoryModel(\n        spv::AddressingModelLogical,\n        spv::MemoryModelGLSL450);\n\n      m_module.enableCapability(spv::CapabilityGeometry);\n\n      m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeInputPoints);\n      m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeOutputPoints);\n      // This has to be > 0 for some reason even though\n      // we will never emit a vertex\n      m_module.setOutputVertices(m_entryPointId, 1);\n      m_module.setInvocations(m_entryPointId, 1);\n\n      m_module.functionBegin(m_module.defVoidType(), m_entryPointId, m_module.defFunctionType(\n        m_module.defVoidType(), 0, nullptr), spv::FunctionControlMaskNone);\n      m_module.opLabel(m_module.allocateId());\n    }\n\n    void compile(const D3D9CompactVertexElements& elements) {\n      uint32_t uint_t     = m_module.defIntType(32, false);\n      uint32_t float_t    = m_module.defFloatType(32);\n      uint32_t vec4_t     = m_module.defVectorType(float_t, 4);\n\n      uint32_t vec4_singular_array_t = m_module.defArrayType(vec4_t, m_module.constu32(1));\n\n      uint32_t push_const_t = m_module.defStructTypeUnique(1, &uint_t);\n      m_module.decorate(push_const_t, spv::DecorationBlock);\n      m_module.setDebugName(push_const_t, \"pc_t\");\n      m_module.setDebugMemberName(push_const_t, 0, \"offset\");\n      m_module.memberDecorateOffset(push_const_t, 0, MaxSharedPushDataSize);\n\n      uint32_t push_const_uint_ptr_t = m_module.defPointerType(uint_t, spv::StorageClassPushConstant);\n      uint32_t push_const_ptr_t = m_module.defPointerType(push_const_t, spv::StorageClassPushConstant);\n\n      uint32_t pushConst = m_module.newVar(push_const_ptr_t, spv::StorageClassPushConstant);\n      m_module.setDebugName(pushConst, \"pc\");\n\n      // Setup the buffer\n      uint32_t bufferSlot = getSWVPBufferSlot();\n\n      uint32_t arrayType    = m_module.defRuntimeArrayTypeUnique(uint_t);\n      m_module.decorateArrayStride(arrayType, sizeof(uint32_t));\n\n      uint32_t buffer_t     = m_module.defStructTypeUnique(1, &arrayType);\n      m_module.memberDecorateOffset(buffer_t, 0, 0);\n      m_module.decorate(buffer_t, spv::DecorationBufferBlock);\n\n      uint32_t buffer       = m_module.newVar(m_module.defPointerType(buffer_t, spv::StorageClassUniform), spv::StorageClassUniform);\n      m_module.decorateDescriptorSet(buffer, 0);\n      m_module.decorateBinding(buffer, bufferSlot);\n\n      m_bufferBinding = { };\n      m_bufferBinding.set             = 0u;\n      m_bufferBinding.binding         = bufferSlot;\n      m_bufferBinding.resourceIndex   = bufferSlot;\n      m_bufferBinding.descriptorType  = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n      m_bufferBinding.access          = VK_ACCESS_SHADER_WRITE_BIT;\n\n      // Load our builtins\n      uint32_t primitiveIdPtr = m_module.newVar(m_module.defPointerType(uint_t, spv::StorageClassInput), spv::StorageClassInput);\n      m_module.decorateBuiltIn(primitiveIdPtr, spv::BuiltInPrimitiveId);\n\n      uint32_t primitiveId = m_module.opLoad(uint_t, primitiveIdPtr);\n      \n      // The size of any given vertex\n      uint32_t size = 0;\n      for (uint32_t i = 0; i < elements.size(); i++) {\n        const auto& element = elements[i];\n        if (element.Stream == 0 && element.Type != D3DDECLTYPE_UNUSED) {\n          size = std::max(size, element.Offset + GetDecltypeSize(D3DDECLTYPE(element.Type)));\n        }\n      }\n\n      uint32_t vertexSize       = m_module.constu32(size / sizeof(uint32_t));\n\n      // The offset of this vertex from the beginning of the buffer\n      uint32_t pushConstIndex = m_module.constu32(0u);\n\n      uint32_t globalOffset = m_module.opLoad(uint_t, m_module.opAccessChain(\n        push_const_uint_ptr_t, pushConst, 1, &pushConstIndex));\n               globalOffset = m_module.opShiftRightLogical(uint_t, globalOffset, m_module.constu32(2u));\n\n      uint32_t thisVertexOffset = m_module.opIMul(uint_t, vertexSize, primitiveId);\n               thisVertexOffset = m_module.opIAdd(uint_t, thisVertexOffset, globalOffset);\n\n      for (uint32_t i = 0; i < elements.size(); i++) {\n        const auto& element = elements[i];\n        // Load the slot associated with this element\n        DxsoSemantic semantic = { DxsoUsage(element.Usage), element.UsageIndex };\n\n        uint32_t elementPtr;\n        uint32_t elementVar;\n\n        elementPtr = m_module.newVar(m_module.defPointerType(vec4_singular_array_t, spv::StorageClassInput), spv::StorageClassInput);\n        if ((semantic.usage == DxsoUsage::Position || semantic.usage == DxsoUsage::PositionT) && element.UsageIndex == 0) {\n          // Load from builtin\n          m_module.decorateBuiltIn(elementPtr, spv::BuiltInPosition);\n        }\n        else {\n          // Load from slot\n          uint32_t slotIdx      = RegisterLinkerSlot(semantic);\n\n          m_module.decorateLocation(elementPtr, slotIdx);\n          m_inputMask |= 1u << slotIdx;\n        }\n\n        uint32_t zero = m_module.constu32(0);\n        elementVar = m_module.opAccessChain(m_module.defPointerType(vec4_t, spv::StorageClassInput), elementPtr, 1, &zero);\n        elementVar = m_module.opLoad(vec4_t, elementVar);\n\n        // The offset of this element from the beginning of any given vertex\n        uint32_t perVertexElementOffset = m_module.constu32(element.Offset / sizeof(uint32_t));\n\n        // The offset of this element from the beginning of the buffer for **THIS** vertex\n        uint32_t elementOffset          = m_module.opIAdd(uint_t, thisVertexOffset, perVertexElementOffset);\n\n        // Write to the buffer at the element offset for each part of the vector.\n        Decltype elementInfo = ClassifyDecltype(D3DDECLTYPE(element.Type));\n\n        if (elementInfo.Class == DecltypeClass::Dec) {\n          // TODO!\n          Logger::warn(\"Encountered DEC3/UDEC3N class, ignoring...\");\n          continue;\n        }\n\n        uint32_t vecn_t = m_module.defVectorType(float_t, elementInfo.VectorCount);\n        uint32_t componentSet;\n        \n        // Modifiers...\n        if (elementInfo.Flags & DecltypeFlags::ReverseRGB) {\n          std::array<uint32_t, 4> indices = { 2, 1, 0, 3 };\n          componentSet = m_module.opVectorShuffle(vecn_t, elementVar, elementVar, elementInfo.VectorCount, indices.data());\n        }\n        else {\n          std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };\n          componentSet = m_module.opVectorShuffle(vecn_t, elementVar, elementVar, elementInfo.VectorCount, indices.data());\n        }\n\n        if (elementInfo.Flags & DecltypeFlags::Normalize)\n          componentSet = m_module.opVectorTimesScalar(vecn_t, componentSet, m_module.constf32(255.0f));\n\n\n        bool isSigned = elementInfo.Flags & DecltypeFlags::Signed;\n\n        // Convert the component to the correct type/value.\n        switch (elementInfo.Class) {\n          case DecltypeClass::Float: break; // Do nothing!\n          case DecltypeClass::Byte: {\n            m_module.enableCapability(spv::CapabilityInt8);\n\n            uint32_t type = m_module.defIntType(8, isSigned);\n                     type = m_module.defVectorType(type, elementInfo.VectorCount);\n\n            componentSet = isSigned\n              ? m_module.opConvertFtoS(type, componentSet)\n              : m_module.opConvertFtoU(type, componentSet);\n\n            break;\n          }\n          case DecltypeClass::Short: {\n            m_module.enableCapability(spv::CapabilityInt16);\n\n            uint32_t type = m_module.defIntType(16, isSigned);\n                     type = m_module.defVectorType(type, elementInfo.VectorCount);\n\n            componentSet = isSigned\n              ? m_module.opConvertFtoS(type, componentSet)\n              : m_module.opConvertFtoU(type, componentSet);\n\n            break;\n          }\n          case DecltypeClass::Half: {\n            m_module.enableCapability(spv::CapabilityFloat16);\n\n            uint32_t type = m_module.defFloatType(16);\n                     type = m_module.defVectorType(type, elementInfo.VectorCount);\n            componentSet = m_module.opFConvert(type, componentSet);\n\n            break;\n          }\n          case DecltypeClass::Dec: {\n            // TODO!\n            break;\n          }\n        }\n\n        // Bitcast to dwords before we write.\n        uint32_t dwordCount  = GetDecltypeSize(D3DDECLTYPE(element.Type)) / sizeof(uint32_t);\n        uint32_t dwordVal = m_module.opBitcast(\n          dwordCount != 1 ? m_module.defVectorType(uint_t, dwordCount) : uint_t,\n          componentSet);\n\n        // Finally write each dword to the buffer!\n        for (uint32_t i = 0; i < dwordCount; i++) {\n          std::array<uint32_t, 2> bufferIndices = { m_module.constu32(0), elementOffset };\n\n          uint32_t writeDest = m_module.opAccessChain(m_module.defPointerType(uint_t, spv::StorageClassUniform), buffer, bufferIndices.size(), bufferIndices.data());\n          uint32_t currentDword = dwordCount != 1 ? m_module.opCompositeExtract(uint_t, dwordVal, 1, &i) : dwordVal;\n\n          m_module.opStore(writeDest, currentDword);\n\n          elementOffset = m_module.opIAdd(uint_t, elementOffset, m_module.constu32(1));\n        }\n      }\n    }\n\n    Rc<DxvkShader> finalize() {\n      m_module.opReturn();\n      m_module.functionEnd();\n\n      m_module.addEntryPoint(m_entryPointId,\n        spv::ExecutionModelGeometry, \"main\");\n      m_module.setDebugName(m_entryPointId, \"main\");\n\n      DxvkSpirvShaderCreateInfo info = { };\n      info.bindingCount = 1;\n      info.bindings = &m_bufferBinding;\n      info.localPushData = DxvkPushDataBlock(MaxSharedPushDataSize, sizeof(D3D9SwvpEmuArgs), 4u, 0u);\n\n      return new DxvkSpirvShader(info, m_module.compile());\n    }\n\n  private:\n\n    SpirvModule m_module;\n\n    uint32_t              m_entryPointId = 0;\n    uint32_t              m_inputMask = 0u;\n    DxvkBindingInfo       m_bufferBinding;\n\n  };\n\n  Rc<DxvkShader> D3D9SWVPEmulator::GetShaderModule(D3D9DeviceEx* pDevice, D3D9CompactVertexElements&& elements) {\n    // Use the shader's unique key for the lookup\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      \n      auto entry = m_modules.find(elements);\n      if (entry != m_modules.end())\n        return entry->second;\n    }\n\n    Sha1Hash hash = Sha1Hash::compute(\n      elements.data(), elements.size() * sizeof(elements[0]));\n\n    DxvkShaderHash key(VK_SHADER_STAGE_GEOMETRY_BIT, 0u, hash.digest(), hash.digestLength());\n    std::string name = str::format(\"SWVP_\", key.toString());\n    \n    // This shader has not been compiled yet, so we have to create a\n    // new module. This takes a while, so we won't lock the structure.\n    D3D9SWVPEmulatorGenerator generator(name);\n    generator.compile(elements);\n    Rc<DxvkShader> shader = generator.finalize();\n\n    pDevice->GetDXVKDevice()->registerShader(shader);\n\n    const std::string& dumpPath = pDevice->GetOptions()->shaderDumpPath;\n\n    if (dumpPath.size() != 0) {\n      std::ofstream dumpStream(\n        str::format(dumpPath, \"/\", name, \".spv\"),\n        std::ios_base::binary | std::ios_base::trunc);\n\n      shader->dump(dumpStream);\n    }\n    \n    // Insert the new module into the lookup table. If another thread\n    // has compiled the same shader in the meantime, we should return\n    // that object instead and discard the newly created module.\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      \n      std::pair<D3D9CompactVertexElements, Rc<DxvkShader>> pair = { std::move(elements), shader };\n      auto status = m_modules.insert(std::move(pair));\n      if (!status.second)\n        return status.first->second;\n    }\n\n    return shader;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_swvp_emu.h",
    "content": "#pragma once\n\n#include <unordered_map>\n\n#include \"d3d9_include.h\"\n\n#include \"../dxvk/dxvk_shader.h\"\n\nnamespace dxvk {\n\n  class D3D9VertexDecl;\n  class D3D9DeviceEx;\n\n  struct D3D9SwvpEmuArgs {\n    uint32_t exportOffset;\n  };\n\n  struct D3D9CompactVertexElement {\n      uint16_t Stream : 4;\n      uint16_t Type : 5;\n      uint16_t Method : 3;\n      uint16_t Usage : 4;\n      uint16_t UsageIndex;\n      uint16_t Offset;\n\n      D3D9CompactVertexElement(const D3DVERTEXELEMENT9& element) \n        : Stream(element.Stream), Type(element.Type), Method(element.Method),\n          Usage(element.Usage), UsageIndex(element.UsageIndex), Offset(element.Offset) {}\n  };\n\n  using D3D9CompactVertexElements = small_vector<D3D9CompactVertexElement, 4>;\n\n  struct D3D9VertexDeclHash {\n    size_t operator () (const D3D9CompactVertexElements& key) const;\n  };\n\n  struct D3D9VertexDeclEq {\n    bool operator () (const D3D9CompactVertexElements& a, const D3D9CompactVertexElements& b) const;\n  };\n\n  class D3D9SWVPEmulator {\n\n  public:\n\n    Rc<DxvkShader> GetShaderModule(D3D9DeviceEx* pDevice,  D3D9CompactVertexElements&& elements);\n\n    UINT GetShaderCount() const {\n      return m_modules.size();\n    }\n\n  private:\n\n    dxvk::mutex                               m_mutex;\n\n    std::unordered_map<\n      D3D9CompactVertexElements, Rc<DxvkShader>,\n      D3D9VertexDeclHash, D3D9VertexDeclEq>   m_modules;\n\n  };\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_texture.cpp",
    "content": "#include \"d3d9_texture.h\"\n\n#include \"d3d9_util.h\"\n\nnamespace dxvk {\n\n  // Direct3DTexture9\n\n  D3D9Texture2D::D3D9Texture2D(\n          D3D9DeviceEx*             pDevice,\n    const D3D9_COMMON_TEXTURE_DESC* pDesc,\n    const bool                      Extended,\n          HANDLE*                   pSharedHandle)\n    : D3D9Texture2DBase( pDevice, pDesc, Extended, D3DRTYPE_TEXTURE, pSharedHandle ) { }\n\n  D3D9Texture2D::D3D9Texture2D(\n          D3D9DeviceEx*             pDevice,\n    const D3D9_COMMON_TEXTURE_DESC* pDesc,\n    const bool                      Extended)\n    : D3D9Texture2D( pDevice, pDesc, Extended, nullptr ) { }\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture2D::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DResource9)\n     || riid == __uuidof(IDirect3DBaseTexture9)\n     || riid == __uuidof(IDirect3DTexture9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D9VkInteropTexture)) {\n      *ppvObject = ref(m_texture.GetVkInterop());\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DTexture9), riid)) {\n      Logger::warn(\"D3D9Texture2D::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D9Texture2D::GetType() {\n    return D3DRTYPE_TEXTURE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture2D::GetLevelDesc(UINT Level, D3DSURFACE_DESC *pDesc) {\n    if (unlikely(Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    return GetSubresource(Level)->GetDesc(pDesc);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture2D::GetSurfaceLevel(UINT Level, IDirect3DSurface9** ppSurfaceLevel) {\n    InitReturnPtr(ppSurfaceLevel);\n\n    if (unlikely(Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ppSurfaceLevel == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *ppSurfaceLevel = ref(GetSubresource(Level));\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture2D::LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {\n    if (unlikely(Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    return GetSubresource(Level)->LockRect(pLockedRect, pRect, Flags);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture2D::UnlockRect(UINT Level) {\n    if (unlikely(Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    return GetSubresource(Level)->UnlockRect();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture2D::AddDirtyRect(CONST RECT* pDirtyRect) {\n    if (pDirtyRect) {\n      D3DBOX box = { UINT(pDirtyRect->left), UINT(pDirtyRect->top), UINT(pDirtyRect->right), UINT(pDirtyRect->bottom), 0, 1 };\n      m_texture.AddDirtyBox(&box, 0);\n    } else {\n      m_texture.AddDirtyBox(nullptr, 0);\n    }\n\n    // Some games keep using the pointer returned in LockRect() after calling Unlock()\n    // and purely rely on AddDirtyRect to notify D3D9 that contents have changed.\n    // We have no way of knowing which mip levels were actually changed.\n    if (m_texture.IsManaged())\n      m_texture.SetAllNeedUpload();\n\n    m_parent->TouchMappedTexture(&m_texture);\n    return D3D_OK;\n  }\n\n\n  // Direct3DVolumeTexture9\n\n\n  D3D9Texture3D::D3D9Texture3D(\n          D3D9DeviceEx*             pDevice,\n    const D3D9_COMMON_TEXTURE_DESC* pDesc,\n    const bool                      Extended)\n    : D3D9Texture3DBase( pDevice, pDesc, Extended, D3DRTYPE_VOLUMETEXTURE, nullptr ) { }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture3D::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DResource9)\n     || riid == __uuidof(IDirect3DBaseTexture9)\n     || riid == __uuidof(IDirect3DVolumeTexture9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D9VkInteropTexture)) {\n      *ppvObject = ref(m_texture.GetVkInterop());\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DVolumeTexture9), riid)) {\n      Logger::warn(\"D3D9Texture3D::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D9Texture3D::GetType() {\n    return D3DRTYPE_VOLUMETEXTURE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture3D::GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) {\n    if (unlikely(Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    return GetSubresource(Level)->GetDesc(pDesc);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture3D::GetVolumeLevel(UINT Level, IDirect3DVolume9** ppVolumeLevel) {\n    InitReturnPtr(ppVolumeLevel);\n\n    if (unlikely(Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ppVolumeLevel == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *ppVolumeLevel = ref(GetSubresource(Level));\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture3D::LockBox(UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) {\n    if (unlikely(Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    return GetSubresource(Level)->LockBox(pLockedBox, pBox, Flags);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture3D::UnlockBox(UINT Level) {\n    if (unlikely(Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    return GetSubresource(Level)->UnlockBox();\n  }\n\n  HRESULT STDMETHODCALLTYPE D3D9Texture3D::AddDirtyBox(CONST D3DBOX* pDirtyBox) {\n    m_texture.AddDirtyBox(pDirtyBox, 0);\n\n    // Some games keep using the pointer returned in LockBox() after calling Unlock()\n    // and purely rely on AddDirtyBox to notify D3D9 that contents have changed.\n    // We have no way of knowing which mip levels were actually changed.\n    if (m_texture.IsManaged())\n      m_texture.SetAllNeedUpload();\n\n    m_parent->TouchMappedTexture(&m_texture);\n    return D3D_OK;\n  }\n\n\n  // Direct3DCubeTexture9\n\n\n  D3D9TextureCube::D3D9TextureCube(\n          D3D9DeviceEx*             pDevice,\n    const D3D9_COMMON_TEXTURE_DESC* pDesc,\n    const bool                      Extended)\n    : D3D9TextureCubeBase( pDevice, pDesc, Extended, D3DRTYPE_CUBETEXTURE, nullptr ) { }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9TextureCube::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DResource9)\n     || riid == __uuidof(IDirect3DBaseTexture9)\n     || riid == __uuidof(IDirect3DCubeTexture9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D9VkInteropTexture)) {\n      *ppvObject = ref(m_texture.GetVkInterop());\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DCubeTexture9), riid)) {\n      Logger::warn(\"D3D9TextureCube::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  D3DRESOURCETYPE STDMETHODCALLTYPE D3D9TextureCube::GetType() {\n    return D3DRTYPE_CUBETEXTURE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9TextureCube::GetLevelDesc(UINT Level, D3DSURFACE_DESC *pDesc) {\n    if (unlikely(Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    return GetSubresource(Level)->GetDesc(pDesc);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9TextureCube::GetCubeMapSurface(D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface9** ppSurfaceLevel) {\n    InitReturnPtr(ppSurfaceLevel);\n\n    if (unlikely(Level >= m_texture.ExposedMipLevels() || Face >= D3DCUBEMAP_FACES(6)))\n      return D3DERR_INVALIDCALL;\n\n    if (unlikely(ppSurfaceLevel == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    *ppSurfaceLevel = ref(GetSubresource(m_texture.CalcSubresource(UINT(Face), Level)));\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9TextureCube::LockRect(D3DCUBEMAP_FACES Face, UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {\n    if (unlikely(Face > D3DCUBEMAP_FACE_NEGATIVE_Z || Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    return GetSubresource(m_texture.CalcSubresource(UINT(Face), Level))->LockRect(pLockedRect, pRect, Flags);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9TextureCube::UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) {\n    if (unlikely(Face > D3DCUBEMAP_FACE_NEGATIVE_Z || Level >= m_texture.ExposedMipLevels()))\n      return D3DERR_INVALIDCALL;\n\n    return GetSubresource(m_texture.CalcSubresource(UINT(Face), Level))->UnlockRect();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9TextureCube::AddDirtyRect(D3DCUBEMAP_FACES Face, CONST RECT* pDirtyRect) {\n    if (pDirtyRect) {\n      D3DBOX box = { UINT(pDirtyRect->left), UINT(pDirtyRect->top), UINT(pDirtyRect->right), UINT(pDirtyRect->bottom), 0, 1 };\n      m_texture.AddDirtyBox(&box, Face);\n    } else {\n      m_texture.AddDirtyBox(nullptr, Face);\n    }\n\n    // Some games keep using the pointer returned in LockRect() after calling Unlock()\n    // and purely rely on AddDirtyRect to notify D3D9 that contents have changed.\n    // We have no way of knowing which mip levels were actually changed.\n    if (m_texture.IsManaged()) {\n      for (uint32_t m = 0; m < m_texture.ExposedMipLevels(); m++) {\n        m_texture.SetNeedsUpload(m_texture.CalcSubresource(Face, m), true);\n      }\n    }\n\n    m_parent->TouchMappedTexture(&m_texture);\n    return D3D_OK;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_texture.h",
    "content": "#pragma once\n\n#include \"d3d9_device.h\"\n#include \"d3d9_surface.h\"\n#include \"d3d9_volume.h\"\n#include \"d3d9_util.h\"\n\n#include <vector>\n#include <list>\n#include <mutex>\n#include <new>\n#include <type_traits>\n\nnamespace dxvk {\n\n  template <typename SubresourceType, typename... Base>\n  class D3D9BaseTexture : public D3D9Resource<Base...> {\n\n  public:\n\n    using SubresourceData = std::aligned_storage_t<sizeof(SubresourceType), alignof(SubresourceType)>;\n\n    D3D9BaseTexture(\n            D3D9DeviceEx*             pDevice,\n      const D3D9_COMMON_TEXTURE_DESC* pDesc,\n      const bool                      Extended,\n            D3DRESOURCETYPE           ResourceType,\n            HANDLE*                   pSharedHandle)\n      : D3D9Resource<Base...> ( pDevice, pDesc->Pool, Extended )\n      , m_texture             ( pDevice, this, pDesc, ResourceType, pSharedHandle )\n      , m_lod                 ( 0 ) {\n      const uint32_t arraySlices = m_texture.Desc()->ArraySize;\n      const uint32_t mipLevels   = m_texture.Desc()->MipLevels;\n\n      m_subresources.resize(arraySlices * mipLevels);\n\n      for (uint32_t i = 0; i < arraySlices; i++) {\n        for (uint32_t j = 0; j < mipLevels; j++) {\n          const uint32_t subresource = m_texture.CalcSubresource(i, j);\n\n          SubresourceType* subObj = this->GetSubresource(subresource);\n\n          new (subObj) SubresourceType(\n            pDevice,\n            Extended,\n            &m_texture,\n            i, j,\n            this);\n        }\n      }\n    }\n\n    ~D3D9BaseTexture() {\n      for (uint32_t i = 0; i < m_subresources.size(); i++) {\n        SubresourceType* subObj = this->GetSubresource(i);\n        subObj->~SubresourceType();\n      }\n    }\n\n    DWORD STDMETHODCALLTYPE SetLOD(DWORD LODNew) final {\n      DWORD oldLod = m_lod;\n      m_lod = std::min<DWORD>(LODNew, m_texture.Desc()->MipLevels - 1);\n\n      if (m_lod != oldLod) {\n        m_texture.CreateSampleView(m_lod);\n        if (this->GetPrivateRefCount() > 0)\n          this->m_parent->MarkTextureBindingDirty(this);\n      }\n\n      return oldLod;\n    }\n\n    DWORD STDMETHODCALLTYPE GetLOD() final {\n      return m_lod;\n    }\n\n    DWORD STDMETHODCALLTYPE GetLevelCount() final {\n      return m_texture.ExposedMipLevels();\n    }\n\n    HRESULT STDMETHODCALLTYPE SetAutoGenFilterType(D3DTEXTUREFILTERTYPE FilterType) final {\n      if (unlikely(FilterType == D3DTEXF_NONE))\n        return D3DERR_INVALIDCALL;\n\n      auto lock = this->m_parent->LockDevice();\n\n      m_texture.SetMipFilter(FilterType);\n      if (m_texture.IsAutomaticMip())\n        this->m_parent->MarkTextureMipsDirty(&m_texture);\n      return D3D_OK;\n    }\n\n    D3DTEXTUREFILTERTYPE STDMETHODCALLTYPE GetAutoGenFilterType() final {\n      return m_texture.GetMipFilter();\n    }\n\n    void STDMETHODCALLTYPE GenerateMipSubLevels() final {\n      if (!m_texture.NeedsMipGen())\n        return;\n\n      auto lock = this->m_parent->LockDevice();\n\n      this->m_parent->MarkTextureMipsUnDirty(&m_texture);\n      this->m_parent->EmitGenerateMips(&m_texture);\n    }\n\n    void STDMETHODCALLTYPE PreLoad() final {\n      m_texture.PreLoadAll();\n    }\n\n    D3D9CommonTexture* GetCommonTexture() {\n      return &m_texture;\n    }\n\n    SubresourceType* GetSubresource(UINT Subresource) {\n      return reinterpret_cast<SubresourceType*>(&m_subresources[Subresource]);\n    }\n\n  protected:\n\n    D3D9CommonTexture m_texture;\n\n    std::vector<SubresourceData> m_subresources;\n\n    DWORD m_lod;\n\n  };\n\n  using D3D9Texture2DBase = D3D9BaseTexture<D3D9Surface, IDirect3DTexture9>;\n  class D3D9Texture2D final : public D3D9Texture2DBase {\n\n  public:\n\n    D3D9Texture2D(\n            D3D9DeviceEx*             pDevice,\n      const D3D9_COMMON_TEXTURE_DESC* pDesc,\n      const bool                      Extended,\n            HANDLE*                   pSharedHandle);\n\n    D3D9Texture2D(\n            D3D9DeviceEx*             pDevice,\n      const D3D9_COMMON_TEXTURE_DESC* pDesc,\n      const bool                      Extended);\n\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType();\n\n    HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC *pDesc);\n\n    HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface9** ppSurfaceLevel);\n\n    HRESULT STDMETHODCALLTYPE LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags);\n\n    HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level);\n\n    HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect);\n\n  };\n\n  using D3D9Texture3DBase = D3D9BaseTexture<D3D9Volume, IDirect3DVolumeTexture9>;\n  class D3D9Texture3D final : public D3D9Texture3DBase {\n\n  public:\n\n    D3D9Texture3D(\n            D3D9DeviceEx*             pDevice,\n      const D3D9_COMMON_TEXTURE_DESC* pDesc,\n      const bool                      Extended);\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType();\n\n    HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc);\n\n    HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume9** ppSurfaceLevel);\n\n    HRESULT STDMETHODCALLTYPE LockBox(UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags);\n\n    HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level);\n\n    HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox);\n\n  };\n\n  using D3D9TextureCubeBase = D3D9BaseTexture<D3D9Surface, IDirect3DCubeTexture9>;\n  class D3D9TextureCube final : public D3D9TextureCubeBase {\n\n  public:\n\n    D3D9TextureCube(\n            D3D9DeviceEx*             pDevice,\n      const D3D9_COMMON_TEXTURE_DESC* pDesc,\n      const bool                      Extended);\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    D3DRESOURCETYPE STDMETHODCALLTYPE GetType();\n\n    HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC *pDesc);\n\n    HRESULT STDMETHODCALLTYPE GetCubeMapSurface(D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface9** ppSurfaceLevel);\n\n    HRESULT STDMETHODCALLTYPE LockRect(D3DCUBEMAP_FACES Face, UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags);\n\n    HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level);\n\n    HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, CONST RECT* pDirtyRect);\n\n  };\n\n  static_assert(sizeof(D3D9Texture2D) == sizeof(D3D9Texture3D) &&\n                sizeof(D3D9Texture2D) == sizeof(D3D9TextureCube));\n\n  inline D3D9CommonTexture* GetCommonTexture(IDirect3DBaseTexture9* ptr) {\n    if (ptr == nullptr)\n      return nullptr;\n\n    // We can avoid needing to get the type as m_texture has the same offset\n    // no matter the texture type.\n    // The compiler is not smart enough to eliminate the call to GetType as it is\n    // not marked const.\n    return static_cast<D3D9Texture2D*>(ptr)->GetCommonTexture();\n  }\n\n  inline D3D9CommonTexture* GetCommonTexture(D3D9Surface* ptr) {\n    if (ptr == nullptr)\n      return nullptr;\n\n    return ptr->GetCommonTexture();\n  }\n\n  inline D3D9CommonTexture* GetCommonTexture(IDirect3DSurface9* ptr) {\n    return GetCommonTexture(static_cast<D3D9Surface*>(ptr));\n  }\n\n  inline void TextureRefPrivate(IDirect3DBaseTexture9* tex, bool AddRef) {\n    if (tex == nullptr)\n      return;\n\n    // We can avoid needing to get the type as m_refCount has the same offset\n    // no matter the texture type.\n    // The compiler is not smart enough to eliminate the call to GetType as it is\n    // not marked const.\n    return CastRefPrivate<D3D9Texture2D>(tex, AddRef);\n  }\n\n  inline void TextureChangePrivate(IDirect3DBaseTexture9*& dst, IDirect3DBaseTexture9* src) {\n    TextureRefPrivate(dst, false);\n    TextureRefPrivate(src, true);\n    dst = src;\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_util.cpp",
    "content": "#include \"d3d9_util.h\"\n\n#include \"../util/util_win32_compat.h\"\n\nnamespace dxvk {\n\n  typedef HRESULT (STDMETHODCALLTYPE *D3DXDisassembleShader) (\n    const void*      pShader, \n          BOOL       EnableColorCode, \n          char*      pComments, \n          ID3DBlob** ppDisassembly); // ppDisassembly is actually a D3DXBUFFER, but it has the exact same vtable as a ID3DBlob at the start.\n\n  D3DXDisassembleShader g_pfnDisassembleShader = nullptr;\n\n  HRESULT DisassembleShader(\n    const void*      pShader, \n          BOOL       EnableColorCode, \n          char*      pComments, \n          ID3DBlob** ppDisassembly) {\n    if (g_pfnDisassembleShader == nullptr) {\n      HMODULE d3d9x = LoadLibraryA(\"d3dx9.dll\");\n\n      if (d3d9x == nullptr)\n        d3d9x = LoadLibraryA(\"d3dx9_43.dll\");\n\n      g_pfnDisassembleShader = \n        reinterpret_cast<D3DXDisassembleShader>(GetProcAddress(d3d9x, \"D3DXDisassembleShader\"));\n    }\n\n    if (g_pfnDisassembleShader == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    return g_pfnDisassembleShader(\n      pShader,\n      EnableColorCode,\n      pComments,\n      ppDisassembly);\n  }\n\n\n  HRESULT DecodeMultiSampleType(\n          D3DMULTISAMPLE_TYPE       MultiSample,\n          DWORD                     MultisampleQuality,\n          VkSampleCountFlagBits*    pSampleCount) {\n    uint32_t sampleCount = std::max<uint32_t>(MultiSample, 1u);\n\n    if (MultiSample == D3DMULTISAMPLE_NONMASKABLE)\n      sampleCount = 1u << MultisampleQuality;\n\n    // 16 is the largest entry in the D3DMULTISAMPLE_TYPE enum\n    // and the largest sample count realistically supported by any hardware.\n    if (sampleCount > 16u)\n      return D3DERR_INVALIDCALL;\n\n    // Check if this is a power of two...\n    if (sampleCount & (sampleCount - 1))\n      return D3DERR_NOTAVAILABLE;\n\n    if (pSampleCount)\n      *pSampleCount = VkSampleCountFlagBits(sampleCount);\n\n    return D3D_OK;\n  }\n\n\n  VkFormat GetPackedDepthStencilFormat(D3D9Format Format) {\n    switch (Format) {\n    case D3D9Format::D15S1:\n      return VK_FORMAT_D16_UNORM_S8_UINT; // This should never happen!\n\n    case D3D9Format::D16:\n    case D3D9Format::D16_LOCKABLE:\n    case D3D9Format::DF16:\n      return VK_FORMAT_D16_UNORM;\n\n    case D3D9Format::D24X8:\n    case D3D9Format::DF24:\n      return VK_FORMAT_X8_D24_UNORM_PACK32;\n\n    case D3D9Format::D24X4S4:\n    case D3D9Format::D24FS8:\n    case D3D9Format::D24S8:\n    case D3D9Format::INTZ:\n      return VK_FORMAT_D24_UNORM_S8_UINT;\n\n    case D3D9Format::D32:\n    case D3D9Format::D32_LOCKABLE:\n    case D3D9Format::D32F_LOCKABLE:\n      return VK_FORMAT_D32_SFLOAT;\n\n    case D3D9Format::S8_LOCKABLE:\n      return VK_FORMAT_S8_UINT;\n\n    default:\n      return VK_FORMAT_UNDEFINED;\n    }\n  }\n\n\n  VkFormatFeatureFlags2 GetImageFormatFeatures(DWORD Usage) {\n    VkFormatFeatureFlags2 features = VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT;\n\n    if (Usage & D3DUSAGE_DEPTHSTENCIL)\n      features |= VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n    if (Usage & D3DUSAGE_RENDERTARGET)\n      features |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT;\n\n    return features;\n  }\n\n\n  VkImageUsageFlags GetImageUsageFlags(DWORD Usage) {\n    VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n\n    if (Usage & D3DUSAGE_DEPTHSTENCIL)\n      usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n    if (Usage & D3DUSAGE_RENDERTARGET)\n      usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\n    return usage;\n  }\n\n\n  uint32_t GetVertexCount(D3DPRIMITIVETYPE type, UINT count) {\n    switch (type) {\n      default:\n      case D3DPT_TRIANGLELIST:  return count * 3;\n      case D3DPT_POINTLIST:     return count;\n      case D3DPT_LINELIST:      return count * 2;\n      case D3DPT_LINESTRIP:     return count + 1;\n      case D3DPT_TRIANGLESTRIP: return count + 2;\n      case D3DPT_TRIANGLEFAN:   return count + 2;\n    }\n  }\n\n\n  DxvkInputAssemblyState DecodeInputAssemblyState(D3DPRIMITIVETYPE type) {\n    switch (type) {\n      default:\n      case D3DPT_TRIANGLELIST:\n        return DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false);\n\n      case D3DPT_POINTLIST:\n        return DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_POINT_LIST, false);\n\n      case D3DPT_LINELIST:\n        return DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_LIST, false);\n\n      case D3DPT_LINESTRIP:\n        return DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, false);\n\n      case D3DPT_TRIANGLESTRIP:\n        return DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, false);\n\n      case D3DPT_TRIANGLEFAN:\n        return DxvkInputAssemblyState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, false);\n    }\n  }\n\n\n  VkBlendFactor DecodeBlendFactor(D3DBLEND BlendFactor, bool IsAlpha) {\n    switch (BlendFactor) {\n      default:\n      case D3DBLEND_ZERO:            return VK_BLEND_FACTOR_ZERO;\n      case D3DBLEND_ONE:             return VK_BLEND_FACTOR_ONE;\n      case D3DBLEND_SRCCOLOR:        return VK_BLEND_FACTOR_SRC_COLOR;\n      case D3DBLEND_INVSRCCOLOR:     return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;\n      case D3DBLEND_SRCALPHA:        return VK_BLEND_FACTOR_SRC_ALPHA;\n      case D3DBLEND_INVSRCALPHA:     return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n      case D3DBLEND_DESTALPHA:       return VK_BLEND_FACTOR_DST_ALPHA;\n      case D3DBLEND_INVDESTALPHA:    return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;\n      case D3DBLEND_DESTCOLOR:       return VK_BLEND_FACTOR_DST_COLOR;\n      case D3DBLEND_INVDESTCOLOR:    return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;\n      case D3DBLEND_SRCALPHASAT:     return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;\n      case D3DBLEND_BOTHSRCALPHA:    return VK_BLEND_FACTOR_SRC_ALPHA;\n      case D3DBLEND_BOTHINVSRCALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n      case D3DBLEND_BLENDFACTOR:     return IsAlpha ? VK_BLEND_FACTOR_CONSTANT_ALPHA : VK_BLEND_FACTOR_CONSTANT_COLOR;\n      case D3DBLEND_INVBLENDFACTOR:  return IsAlpha ? VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA : VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;\n      case D3DBLEND_SRCCOLOR2:       return VK_BLEND_FACTOR_SRC1_COLOR;\n      case D3DBLEND_INVSRCCOLOR2:    return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;\n    }\n  }\n\n\n  VkBlendOp DecodeBlendOp(D3DBLENDOP BlendOp) {\n    switch (BlendOp) {\n      default:\n      case D3DBLENDOP_ADD:          return VK_BLEND_OP_ADD;\n      case D3DBLENDOP_SUBTRACT:     return VK_BLEND_OP_SUBTRACT;\n      case D3DBLENDOP_REVSUBTRACT:  return VK_BLEND_OP_REVERSE_SUBTRACT;\n      case D3DBLENDOP_MIN:          return VK_BLEND_OP_MIN;\n      case D3DBLENDOP_MAX:          return VK_BLEND_OP_MAX;\n    }\n  }\n\n\n  VkCompareOp DecodeCompareOp(D3DCMPFUNC Func) {\n    switch (Func) {\n      default:\n      case D3DCMP_NEVER:        return VK_COMPARE_OP_NEVER;\n      case D3DCMP_LESS:         return VK_COMPARE_OP_LESS;\n      case D3DCMP_EQUAL:        return VK_COMPARE_OP_EQUAL;\n      case D3DCMP_LESSEQUAL:    return VK_COMPARE_OP_LESS_OR_EQUAL;\n      case D3DCMP_GREATER:      return VK_COMPARE_OP_GREATER;\n      case D3DCMP_NOTEQUAL:     return VK_COMPARE_OP_NOT_EQUAL;\n      case D3DCMP_GREATEREQUAL: return VK_COMPARE_OP_GREATER_OR_EQUAL;\n      case D3DCMP_ALWAYS:       return VK_COMPARE_OP_ALWAYS;\n    }\n  }\n\n\n  VkStencilOp DecodeStencilOp(D3DSTENCILOP Op) {\n    switch (Op) {\n      default:\n      case D3DSTENCILOP_KEEP:    return VK_STENCIL_OP_KEEP;\n      case D3DSTENCILOP_ZERO:    return VK_STENCIL_OP_ZERO;\n      case D3DSTENCILOP_REPLACE: return VK_STENCIL_OP_REPLACE;\n      case D3DSTENCILOP_INCRSAT: return VK_STENCIL_OP_INCREMENT_AND_CLAMP;\n      case D3DSTENCILOP_DECRSAT: return VK_STENCIL_OP_DECREMENT_AND_CLAMP;\n      case D3DSTENCILOP_INVERT:  return VK_STENCIL_OP_INVERT;\n      case D3DSTENCILOP_INCR:    return VK_STENCIL_OP_INCREMENT_AND_WRAP;\n      case D3DSTENCILOP_DECR:    return VK_STENCIL_OP_DECREMENT_AND_WRAP;\n    }\n  }\n\n\n  VkCullModeFlags DecodeCullMode(D3DCULL Mode) {\n    switch (Mode) {\n      case D3DCULL_CW:   return VK_CULL_MODE_FRONT_BIT;\n      case D3DCULL_CCW:  return VK_CULL_MODE_BACK_BIT;\n      default:\n      case D3DCULL_NONE: return VK_CULL_MODE_NONE;\n    }\n  }\n\n\n  VkPolygonMode DecodeFillMode(D3DFILLMODE Mode) {\n    switch (Mode) {\n      case D3DFILL_POINT:     return VK_POLYGON_MODE_POINT;\n      case D3DFILL_WIREFRAME: return VK_POLYGON_MODE_LINE;\n      default:\n      case D3DFILL_SOLID:     return VK_POLYGON_MODE_FILL;\n    }\n  }\n\n\n  VkIndexType DecodeIndexType(D3D9Format Format) {\n    return Format == D3D9Format::INDEX16\n                   ? VK_INDEX_TYPE_UINT16\n                   : VK_INDEX_TYPE_UINT32;\n  }\n\n\n  VkFormat DecodeDecltype(D3DDECLTYPE Type) {\n    switch (Type) {\n      case D3DDECLTYPE_FLOAT1:    return VK_FORMAT_R32_SFLOAT;\n      case D3DDECLTYPE_FLOAT2:    return VK_FORMAT_R32G32_SFLOAT;\n      case D3DDECLTYPE_FLOAT3:    return VK_FORMAT_R32G32B32_SFLOAT;\n      case D3DDECLTYPE_FLOAT4:    return VK_FORMAT_R32G32B32A32_SFLOAT;\n      case D3DDECLTYPE_D3DCOLOR:  return VK_FORMAT_B8G8R8A8_UNORM;\n      case D3DDECLTYPE_UBYTE4:    return VK_FORMAT_R8G8B8A8_USCALED;\n      case D3DDECLTYPE_SHORT2:    return VK_FORMAT_R16G16_SSCALED;\n      case D3DDECLTYPE_SHORT4:    return VK_FORMAT_R16G16B16A16_SSCALED;\n      case D3DDECLTYPE_UBYTE4N:   return VK_FORMAT_R8G8B8A8_UNORM;\n      case D3DDECLTYPE_SHORT2N:   return VK_FORMAT_R16G16_SNORM;\n      case D3DDECLTYPE_SHORT4N:   return VK_FORMAT_R16G16B16A16_SNORM;\n      case D3DDECLTYPE_USHORT2N:  return VK_FORMAT_R16G16_UNORM;\n      case D3DDECLTYPE_USHORT4N:  return VK_FORMAT_R16G16B16A16_UNORM;\n      case D3DDECLTYPE_UDEC3:     return VK_FORMAT_A2B10G10R10_USCALED_PACK32;\n      case D3DDECLTYPE_FLOAT16_2: return VK_FORMAT_R16G16_SFLOAT;\n      case D3DDECLTYPE_FLOAT16_4: return VK_FORMAT_R16G16B16A16_SFLOAT;\n      case D3DDECLTYPE_DEC3N:     return VK_FORMAT_A2B10G10R10_SNORM_PACK32;\n      case D3DDECLTYPE_UNUSED:\n      default:                    return VK_FORMAT_UNDEFINED;\n    }\n  }\n\n  void ConvertBox(D3DBOX box, VkOffset3D& offset, VkExtent3D& extent) {\n    offset.x = box.Left;\n    offset.y = box.Top;\n    offset.z = box.Front;\n\n    extent.width  = box.Right  - box.Left;\n    extent.height = box.Bottom - box.Top;\n    extent.depth  = box.Back   - box.Front;\n  }\n\n\n  void ConvertRect(RECT rect, VkOffset3D& offset, VkExtent3D& extent) {\n    offset.x = rect.left;\n    offset.y = rect.top;\n    offset.z = 0;\n\n    extent.width  = rect.right  - rect.left;\n    extent.height = rect.bottom - rect.top;\n    extent.depth  = 1;\n  }\n\n\n  void ConvertRect(RECT rect, VkOffset2D& offset, VkExtent2D& extent) {\n    offset.x = rect.left;\n    offset.y = rect.top;\n\n    extent.width  = rect.right  - rect.left;\n    extent.height = rect.bottom - rect.top;\n  }\n\n\n  uint32_t GetDecltypeSize(D3DDECLTYPE Type) {\n    switch (Type) {\n      case D3DDECLTYPE_FLOAT1:    return 1 * sizeof(float);\n      case D3DDECLTYPE_FLOAT2:    return 2 * sizeof(float);\n      case D3DDECLTYPE_FLOAT3:    return 3 * sizeof(float);\n      case D3DDECLTYPE_FLOAT4:    return 4 * sizeof(float);\n      case D3DDECLTYPE_D3DCOLOR:  return 1 * sizeof(DWORD);\n      case D3DDECLTYPE_UBYTE4:    return 4 * sizeof(BYTE);\n      case D3DDECLTYPE_SHORT2:    return 2 * sizeof(short);\n      case D3DDECLTYPE_SHORT4:    return 4 * sizeof(short);\n      case D3DDECLTYPE_UBYTE4N:   return 4 * sizeof(BYTE);\n      case D3DDECLTYPE_SHORT2N:   return 2 * sizeof(short);\n      case D3DDECLTYPE_SHORT4N:   return 4 * sizeof(short);\n      case D3DDECLTYPE_USHORT2N:  return 2 * sizeof(short);\n      case D3DDECLTYPE_USHORT4N:  return 4 * sizeof(short);\n      case D3DDECLTYPE_UDEC3:     return 4;\n      case D3DDECLTYPE_DEC3N:     return 4;\n      case D3DDECLTYPE_FLOAT16_2: return 2 * 2;\n      case D3DDECLTYPE_FLOAT16_4: return 4 * 2;\n      default:                    return 0;\n    }\n  }\n\n\n  uint32_t GetDecltypeCount(D3DDECLTYPE Type) {\n    switch (Type) {\n      case D3DDECLTYPE_FLOAT1:    return 1;\n      case D3DDECLTYPE_FLOAT2:    return 2;\n      case D3DDECLTYPE_FLOAT3:    return 3;\n      case D3DDECLTYPE_FLOAT4:    return 4;\n      case D3DDECLTYPE_D3DCOLOR:  return 4;\n      case D3DDECLTYPE_UBYTE4:    return 4;\n      case D3DDECLTYPE_SHORT2:    return 2;\n      case D3DDECLTYPE_SHORT4:    return 4;\n      case D3DDECLTYPE_UBYTE4N:   return 4;\n      case D3DDECLTYPE_SHORT2N:   return 2;\n      case D3DDECLTYPE_SHORT4N:   return 4;\n      case D3DDECLTYPE_USHORT2N:  return 2;\n      case D3DDECLTYPE_USHORT4N:  return 4;\n      case D3DDECLTYPE_UDEC3:     return 3;\n      case D3DDECLTYPE_DEC3N:     return 3;\n      case D3DDECLTYPE_FLOAT16_2: return 2;\n      case D3DDECLTYPE_FLOAT16_4: return 4;\n      default:                    return 0;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_util.h",
    "content": "#pragma once\n\n#define D3D11_NO_HELPERS\n\n#include \"d3d9_include.h\"\n#include \"d3d9_caps.h\"\n\n#include \"d3d9_format.h\"\n\n#include \"../dxso/dxso_common.h\"\n#include \"../dxvk/dxvk_device.h\"\n\n#include \"../util/util_matrix.h\"\n#include \"../util/util_misc.h\"\n\n#include <d3dcommon.h>\n\nnamespace dxvk {\n\n  struct D3D9ShaderMasks {\n    uint32_t samplerMask;\n    uint32_t rtMask;\n  };\n\n  static constexpr D3D9ShaderMasks FixedFunctionMask =\n    { 0b11111111, 0b1 };\n\n  struct D3D9BlendState {\n    D3DBLEND   Src;\n    D3DBLEND   Dst;\n    D3DBLENDOP Op;\n  };\n\n  inline void FixupBlendState(D3D9BlendState& State) {\n    // Old DirectX 6 HW feature that still exists...\n    // Yuck!\n    if (unlikely(State.Src == D3DBLEND_BOTHSRCALPHA)) {\n      State.Src = D3DBLEND_SRCALPHA;\n      State.Dst = D3DBLEND_INVSRCALPHA;\n    }\n    else if (unlikely(State.Src == D3DBLEND_BOTHINVSRCALPHA)) {\n      State.Src = D3DBLEND_INVSRCALPHA;\n      State.Dst = D3DBLEND_SRCALPHA;\n    }\n  }\n\n  /**\n   * @brief Returns whether or not the sampler index is valid\n   *\n   * @param Sampler Sampler index (according to the API)\n   */\n  inline bool InvalidSampler(DWORD Sampler) {\n    if (Sampler >= caps::MaxTexturesPS && Sampler < D3DDMAPSAMPLER)\n      return true;\n\n    if (Sampler > D3DVERTEXTEXTURESAMPLER3)\n      return true;\n\n    return false;\n  }\n\n  /**\n   * @brief The first sampler that belongs to the vertex shader according to our internal way of storing samplers\n   */\n  constexpr uint32_t FirstVSSamplerSlot = caps::MaxTexturesPS + 1;\n\n  /**\n   * @brief Remaps a sampler index by the API to an internal one\n   *\n   * Remaps the sampler index according to the way the API counts them to how we count and store them internally.\n   *\n   * @param Sampler Sampler index (according to API)\n   * @return DWORD Sampler index (according to our internal way of storing samplers)\n   */\n  inline DWORD RemapSamplerState(DWORD Sampler) {\n    if (Sampler >= D3DDMAPSAMPLER)\n      Sampler = caps::MaxTexturesPS + (Sampler - D3DDMAPSAMPLER);\n\n    return Sampler;\n  }\n\n  /**\n   * @brief Remaps the sampler from an index applying to the entire pipeline to one relative to the shader stage and returns the shader type\n   *\n   * The displacement map sampler will be treated as a 17th pixel shader sampler.\n   *\n   * @param Sampler Sampler index (according to our internal way of storing samplers)\n   * @return std::pair<D3D9ShaderType, DWORD> Shader stage that it belongs to and the relative sampler index\n   */\n  inline std::pair<D3D9ShaderType, DWORD> RemapStateSamplerShader(DWORD Sampler) {\n    if (Sampler >= FirstVSSamplerSlot)\n      return std::make_pair(D3D9ShaderType::VertexShader, Sampler - FirstVSSamplerSlot);\n\n    return std::make_pair(D3D9ShaderType::PixelShader, Sampler);\n  }\n\n  /**\n   * @brief Returns whether the sampler belongs to the vertex shader.\n   *\n   * The displacement map sampler is part of a fixed function feature,\n   * so it does not belong to the vertex shader.\n   *\n   * @param Sampler Sampler index (according to our internal way of storing samplers)\n   */\n  inline bool IsVSSampler(uint32_t Sampler) {\n    return Sampler >= FirstVSSamplerSlot;\n  }\n\n  /**\n   * @brief Returns whether the sampler belongs to the pixel shader.\n   *\n   * The displacement map sampler is part of a fixed function feature,\n   * so (unlike in RemapStateSamplerShader) it does not belong to the pixel shader.\n   *\n   * @param Sampler Sampler index (according to our internal way of storing samplers)\n   */\n  inline bool IsPSSampler(uint32_t Sampler) {\n    return Sampler <= caps::MaxTexturesPS;\n  }\n\n  /**\n   * @brief Remaps the sampler from an index (counted according to the API) to one relative to the shader stage and returns the shader type\n   *\n   * @param Sampler Sampler index (according to the API)\n   * @return std::pair<D3D9ShaderType, DWORD> Shader stage that it belongs to and the relative sampler index\n   */\n  inline std::pair<D3D9ShaderType, DWORD> RemapSamplerShader(DWORD Sampler) {\n    Sampler = RemapSamplerState(Sampler);\n\n    return RemapStateSamplerShader(Sampler);\n  }\n\n  template <typename T, typename J>\n  void CastRefPrivate(J* ptr, bool AddRef) {\n    if (ptr == nullptr)\n      return;\n\n    T* castedPtr = reinterpret_cast<T*>(ptr);\n    AddRef ? castedPtr->AddRefPrivate() : castedPtr->ReleasePrivate();\n  }\n\n  HRESULT DisassembleShader(\n    const void*      pShader, \n          BOOL       EnableColorCode, \n          char*      pComments, \n          ID3DBlob** ppDisassembly);\n\n  HRESULT DecodeMultiSampleType(\n          D3DMULTISAMPLE_TYPE       MultiSample,\n          DWORD                     MultisampleQuality,\n          VkSampleCountFlagBits*    pSampleCount);\n\n  VkFormat GetPackedDepthStencilFormat(D3D9Format Format);\n\n  VkFormatFeatureFlags2 GetImageFormatFeatures(DWORD Usage);\n\n  VkImageUsageFlags GetImageUsageFlags(DWORD Usage);\n\n  inline VkFormat PickSRGB(VkFormat format, VkFormat srgbFormat, bool srgb) {\n    return srgb ? srgbFormat : format;\n  }\n\n  constexpr VkShaderStageFlagBits GetShaderStage(D3D9ShaderType ShaderType) {\n    switch (ShaderType) {\n      case D3D9ShaderType::VertexShader: return VK_SHADER_STAGE_VERTEX_BIT;\n      case D3D9ShaderType::PixelShader:  return VK_SHADER_STAGE_FRAGMENT_BIT;\n      default:                           return VkShaderStageFlagBits(0);\n    }\n  }\n\n  inline uint32_t GetTransformIndex(D3DTRANSFORMSTATETYPE Type) {\n    if (Type == D3DTS_VIEW)\n      return 0;\n\n    if (Type == D3DTS_PROJECTION)\n      return 1;\n\n    if (Type >= D3DTS_TEXTURE0 && Type <= D3DTS_TEXTURE7)\n      return 2 + (Type - D3DTS_TEXTURE0);\n\n    return 10 + (Type - D3DTS_WORLD);\n  }\n\n  inline Matrix4 ConvertMatrix(const D3DMATRIX* Matrix) {\n    if (Matrix == nullptr) // Identity.\n      return Matrix4();\n\n    return Matrix4(Matrix->m);\n  }\n\n  uint32_t GetVertexCount(D3DPRIMITIVETYPE type, UINT count);\n\n  DxvkInputAssemblyState DecodeInputAssemblyState(D3DPRIMITIVETYPE type);\n\n  VkBlendFactor DecodeBlendFactor(D3DBLEND BlendFactor, bool IsAlpha);\n\n  VkBlendOp DecodeBlendOp(D3DBLENDOP BlendOp);\n\n  inline VkFilter DecodeFilter(D3DTEXTUREFILTERTYPE Filter) {\n    return Filter > D3DTEXF_POINT ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;\n  }\n\n  inline VkSamplerMipmapMode DecodeMipFilter(D3DTEXTUREFILTERTYPE Filter) {\n    return Filter > D3DTEXF_POINT ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;\n  }\n\n  inline VkSamplerAddressMode DecodeAddressMode(D3DTEXTUREADDRESS Mode) {\n    constexpr uint32_t Lut =\n      (uint32_t(VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT)      << (3 * D3DTADDRESS_MIRROR)) |\n      (uint32_t(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE)        << (3 * D3DTADDRESS_CLAMP)) |\n      (uint32_t(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER)      << (3 * D3DTADDRESS_BORDER)) |\n      (uint32_t(VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE) << (3 * D3DTADDRESS_MIRRORONCE));\n\n    // VK_SAMPLER_ADDRESS_MODE_REPEAT has a value of 0, so we\n    // get it for free if the app passes an unsupported value\n    uint32_t shift = std::min(uint32_t(Mode) * 3u, 31u);\n    return VkSamplerAddressMode((uint32_t(Lut) >> shift) & 0x7u);\n  }\n\n  VkCompareOp DecodeCompareOp(D3DCMPFUNC Func);\n\n  VkStencilOp DecodeStencilOp(D3DSTENCILOP Op);\n\n  VkCullModeFlags DecodeCullMode(D3DCULL Mode);\n\n  VkPolygonMode DecodeFillMode(D3DFILLMODE Mode);\n\n  VkIndexType DecodeIndexType(D3D9Format Format);\n\n  VkFormat DecodeDecltype(D3DDECLTYPE Type);\n\n  uint32_t GetDecltypeSize(D3DDECLTYPE Type);\n\n  uint32_t GetDecltypeCount(D3DDECLTYPE Type);\n\n  void ConvertBox(D3DBOX box, VkOffset3D& offset, VkExtent3D& extent);\n\n  void ConvertRect(RECT rect, VkOffset3D& offset, VkExtent3D& extent);\n\n  void ConvertRect(RECT rect, VkOffset2D& offset, VkExtent2D& extent);\n\n  inline float GetDepthBufferRValue(VkFormat Format, int32_t vendorId, bool exact, bool forceUnorm) {\n    switch (Format) {\n      case VK_FORMAT_D16_UNORM_S8_UINT:\n      case VK_FORMAT_D16_UNORM:\n        return (vendorId == 0x10de && !exact) ? float(1 << 15) : float(1 << 16);\n\n      case VK_FORMAT_D24_UNORM_S8_UINT:\n        return (vendorId == 0x10de && !exact) ? float(1 << 23) : float(1 << 24);\n\n      default:\n      case VK_FORMAT_D32_SFLOAT_S8_UINT:\n      case VK_FORMAT_D32_SFLOAT:\n        return forceUnorm ? float(1 << 24) : float(1 << 23);\n    }\n  }\n\n  template<typename T>\n  UINT CompactSparseList(T* pData, UINT Mask) {\n    uint32_t count = 0;\n\n    for (uint32_t id : bit::BitMask(Mask))\n      pData[count++] = pData[id];\n\n    return count;\n  }\n\n  inline bool IsPoolManaged(D3DPOOL Pool) {\n    return Pool == D3DPOOL_MANAGED || Pool == D3DPOOL_MANAGED_EX;\n  }\n\n  inline D3DRENDERSTATETYPE ColorWriteIndex(uint32_t i) {\n    return D3DRENDERSTATETYPE(i ? D3DRENDERSTATETYPE(D3DRS_COLORWRITEENABLE1 + i - 1) : D3DRS_COLORWRITEENABLE);\n  }\n\n  inline bool AreFormatsSimilar(D3D9Format srcFormat, D3D9Format dstFormat) {\n    return (srcFormat == dstFormat)\n        || (srcFormat == D3D9Format::A8B8G8R8 && dstFormat == D3D9Format::X8B8G8R8)\n        || (srcFormat == D3D9Format::A8R8G8B8 && dstFormat == D3D9Format::X8R8G8B8)\n        || (srcFormat == D3D9Format::A1R5G5B5 && dstFormat == D3D9Format::X1R5G5B5)\n        || (srcFormat == D3D9Format::A4R4G4B4 && dstFormat == D3D9Format::X4R4G4B4);\n  }\n\n  inline bool IsBlitRegionInvalid(VkOffset3D offsets[2], VkExtent3D extent) {\n    // Only bother checking x, y as we don't have 3D blits.\n    return offsets[1].x < offsets[0].x ||\n           offsets[1].y < offsets[0].y ||\n           offsets[0].x < 0 ||\n           offsets[0].y < 0 ||\n           uint32_t(offsets[1].x) > extent.width ||\n           uint32_t(offsets[1].y) > extent.height;\n  }\n\n  /**\n   * @brief Mirrors D3DTEXTURESTAGESTATETYPE but starts at 0\n   */\n  enum D3D9TextureStageStateTypes : uint32_t\n  {\n      DXVK_TSS_COLOROP        =  0,\n      DXVK_TSS_COLORARG1      =  1,\n      DXVK_TSS_COLORARG2      =  2,\n      DXVK_TSS_ALPHAOP        =  3,\n      DXVK_TSS_ALPHAARG1      =  4,\n      DXVK_TSS_ALPHAARG2      =  5,\n      DXVK_TSS_BUMPENVMAT00   =  6,\n      DXVK_TSS_BUMPENVMAT01   =  7,\n      DXVK_TSS_BUMPENVMAT10   =  8,\n      DXVK_TSS_BUMPENVMAT11   =  9,\n      DXVK_TSS_TEXCOORDINDEX  = 10,\n      DXVK_TSS_BUMPENVLSCALE  = 21,\n      DXVK_TSS_BUMPENVLOFFSET = 22,\n      DXVK_TSS_TEXTURETRANSFORMFLAGS = 23,\n      DXVK_TSS_COLORARG0      = 25,\n      DXVK_TSS_ALPHAARG0      = 26,\n      DXVK_TSS_RESULTARG      = 27,\n      DXVK_TSS_CONSTANT       = 31,\n      DXVK_TSS_COUNT          = 32\n  };\n\n  constexpr uint32_t DXVK_TSS_TCI_PASSTHRU                      = 0x00000000;\n  constexpr uint32_t DXVK_TSS_TCI_CAMERASPACENORMAL             = 0x00010000;\n  constexpr uint32_t DXVK_TSS_TCI_CAMERASPACEPOSITION           = 0x00020000;\n  constexpr uint32_t DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR   = 0x00030000;\n  constexpr uint32_t DXVK_TSS_TCI_SPHEREMAP                     = 0x00040000;\n\n  /**\n   * @brief Remaps a texture stage type by the API to an internal one\n   *\n   * @param Type Texture stage type according to the API\n   * @return D3D9TextureStageStateTypes Texture stage type according to our internal way of storing them\n   */\n  inline D3D9TextureStageStateTypes RemapTextureStageStateType(D3DTEXTURESTAGESTATETYPE Type) {\n    return D3D9TextureStageStateTypes(Type - 1);\n  }\n\n}\n\n\ninline bool operator == (const D3DVIEWPORT9& a, const D3DVIEWPORT9& b) {\n  return a.X      == b.X      &&\n         a.Y      == b.Y      &&\n         a.Width  == b.Width  &&\n         a.Height == b.Height &&\n         a.MinZ   == b.MinZ   &&\n         a.MaxZ   == b.MaxZ;\n}\n\ninline bool operator != (const D3DVIEWPORT9& a, const D3DVIEWPORT9& b) {\n  return !(a == b);\n}\n\n\n// Missing in some versions of mingw headers\n#ifndef _MSC_VER\ninline bool operator == (const RECT& a, const RECT& b) {\n  return a.left   == b.left  &&\n         a.right  == b.right &&\n         a.top    == b.top   &&\n         a.bottom == b.bottom;\n}\n\ninline bool operator != (const RECT& a, const RECT& b) {\n  return !(a == b);\n}\n#endif /* _MSC_VER */\n\ninline bool operator == (const POINT& a, const POINT& b) {\n  return a.x == b.x && a.y == b.y;\n}\n\ninline bool operator != (const POINT& a, const POINT& b) {\n  return !(a == b);\n}\n\ninline bool operator == (const D3DDISPLAYMODEEX& a, const D3DDISPLAYMODEEX& b) {\n  return a.Size             == b.Size             &&\n         a.Width            == b.Width            &&\n         a.Height           == b.Height           &&\n         a.RefreshRate      == b.RefreshRate      &&\n         a.Format           == b.Format           &&\n         a.ScanLineOrdering == b.ScanLineOrdering;\n}\n\n"
  },
  {
    "path": "src/d3d9/d3d9_vertex_declaration.cpp",
    "content": "#include \"d3d9_vertex_declaration.h\"\n#include \"d3d9_util.h\"\n\n#include <algorithm>\n#include <cstring>\n\nnamespace dxvk {\n\n  D3D9VertexDecl::D3D9VertexDecl(\n          D3D9DeviceEx*      pDevice,\n          DWORD              FVF)\n    : D3D9VertexDeclBase(pDevice) {\n    this->SetFVF(FVF);\n    this->Classify();\n  }\n\n\n  D3D9VertexDecl::D3D9VertexDecl(\n          D3D9DeviceEx*      pDevice,\n    const D3DVERTEXELEMENT9* pVertexElements,\n          uint32_t           DeclCount)\n    : D3D9VertexDeclBase( pDevice )\n    , m_elements        ( pVertexElements, pVertexElements + DeclCount )\n    , m_fvf             ( this->MapD3D9VertexElementsToFvf() ) {\n    this->Classify();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9VertexDecl::QueryInterface(\n          REFIID  riid,\n          void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DVertexDeclaration9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DVertexDeclaration9), riid)) {\n      Logger::warn(\"D3D9VertexDecl::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9VertexDecl::GetDeclaration(\n          D3DVERTEXELEMENT9* pElement,\n          UINT*              pNumElements) {\n    if (pNumElements == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    *pNumElements = UINT(m_elements.size()) + 1u; // Account for D3DDECL_END\n\n    if (pElement == nullptr)\n      return D3D_OK;\n\n    // The native runtime ignores pNumElements here...\n    std::copy(m_elements.begin(), m_elements.end(), pElement);\n    pElement[m_elements.size()] = D3DDECL_END();\n\n    return D3D_OK;\n  }\n\n\n  void D3D9VertexDecl::SetFVF(DWORD FVF) {\n    m_fvf = FVF;\n\n    std::array<D3DVERTEXELEMENT9, 16> elements;\n    uint32_t elemCount = 0;\n    uint32_t texCount = 0;\n\n    uint32_t betas = 0;\n    uint8_t betaIdx = 0xFF;\n\n    switch (FVF & D3DFVF_POSITION_MASK) {\n      case D3DFVF_XYZ:\n      case D3DFVF_XYZB1:\n      case D3DFVF_XYZB2:\n      case D3DFVF_XYZB3:\n      case D3DFVF_XYZB4:\n      case D3DFVF_XYZB5:\n        elements[elemCount].Type = D3DDECLTYPE_FLOAT3;\n        elements[elemCount].Usage = D3DDECLUSAGE_POSITION;\n        elements[elemCount].UsageIndex = 0;\n        elemCount++;\n\n        if ((FVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZ)\n          break;\n\n        betas = (((FVF & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1) + 1;\n        if (FVF & D3DFVF_LASTBETA_D3DCOLOR)\n          betaIdx = D3DDECLTYPE_D3DCOLOR;\n        else if (FVF & D3DFVF_LASTBETA_UBYTE4)\n          betaIdx = D3DDECLTYPE_UBYTE4;\n        else if ((FVF & D3DFVF_XYZB5) == D3DFVF_XYZB5)\n          betaIdx = D3DDECLTYPE_FLOAT1;\n\n        if (betaIdx != 0xFF)\n          betas--;\n\n        if (betas > 0) {\n          switch (betas) {\n            case 1: elements[elemCount].Type = D3DDECLTYPE_FLOAT1; break;\n            case 2: elements[elemCount].Type = D3DDECLTYPE_FLOAT2; break;\n            case 3: elements[elemCount].Type = D3DDECLTYPE_FLOAT3; break;\n            case 4: elements[elemCount].Type = D3DDECLTYPE_FLOAT4; break;\n            default: break;\n          }\n          elements[elemCount].Usage = D3DDECLUSAGE_BLENDWEIGHT;\n          elements[elemCount].UsageIndex = 0;\n          elemCount++;\n        }\n\n        if (betaIdx != 0xFF) {\n          elements[elemCount].Type = betaIdx;\n          elements[elemCount].Usage = D3DDECLUSAGE_BLENDINDICES;\n          elements[elemCount].UsageIndex = 0;\n          elemCount++;\n        }\n        break;\n\n      case D3DFVF_XYZW:\n      case D3DFVF_XYZRHW:\n        elements[elemCount].Type = D3DDECLTYPE_FLOAT4;\n        elements[elemCount].Usage =\n          ((FVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZW)\n          ? D3DDECLUSAGE_POSITION\n          : D3DDECLUSAGE_POSITIONT;\n        elements[elemCount].UsageIndex = 0;\n        elemCount++;\n        break;\n\n      default:\n        break;\n    }\n\n    if (FVF & D3DFVF_NORMAL) {\n      elements[elemCount].Type = D3DDECLTYPE_FLOAT3;\n      elements[elemCount].Usage = D3DDECLUSAGE_NORMAL;\n      elements[elemCount].UsageIndex = 0;\n      elemCount++;\n    }\n    if (FVF & D3DFVF_PSIZE) {\n      elements[elemCount].Type = D3DDECLTYPE_FLOAT1;\n      elements[elemCount].Usage = D3DDECLUSAGE_PSIZE;\n      elements[elemCount].UsageIndex = 0;\n      elemCount++;\n    }\n    if (FVF & D3DFVF_DIFFUSE) {\n      elements[elemCount].Type = D3DDECLTYPE_D3DCOLOR;\n      elements[elemCount].Usage = D3DDECLUSAGE_COLOR;\n      elements[elemCount].UsageIndex = 0;\n      elemCount++;\n    }\n    if (FVF & D3DFVF_SPECULAR) {\n      elements[elemCount].Type = D3DDECLTYPE_D3DCOLOR;\n      elements[elemCount].Usage = D3DDECLUSAGE_COLOR;\n      elements[elemCount].UsageIndex = 1;\n      elemCount++;\n    }\n\n    texCount = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;\n    texCount = std::min(texCount, 8u);\n\n    for (uint32_t i = 0; i < texCount; i++) {\n      switch ((FVF >> (16 + i * 2)) & 0x3) {\n        case D3DFVF_TEXTUREFORMAT1:\n          elements[elemCount].Type = D3DDECLTYPE_FLOAT1;\n          break;\n\n        case D3DFVF_TEXTUREFORMAT2:\n          elements[elemCount].Type = D3DDECLTYPE_FLOAT2;\n          break;\n\n        case D3DFVF_TEXTUREFORMAT3:\n          elements[elemCount].Type = D3DDECLTYPE_FLOAT3;\n          break;\n\n        case D3DFVF_TEXTUREFORMAT4:\n          elements[elemCount].Type = D3DDECLTYPE_FLOAT4;\n          break;\n\n        default:\n          break;\n      }\n      elements[elemCount].Usage = D3DDECLUSAGE_TEXCOORD;\n      elements[elemCount].UsageIndex = i;\n      elemCount++;\n    }\n\n    for (uint32_t i = 0; i < elemCount; i++) {\n      elements[i].Stream = 0;\n      elements[i].Offset = (i == 0)\n        ? 0\n        : (elements[i - 1].Offset + GetDecltypeSize(D3DDECLTYPE(elements[i - 1].Type)));\n\n      elements[i].Method = D3DDECLMETHOD_DEFAULT;\n    }\n\n    m_elements.resize(elemCount);\n    std::copy(elements.begin(), elements.begin() + elemCount, m_elements.data());\n  }\n\n  bool D3D9VertexDecl::MapD3DDeclToFvf(\n    const D3DVERTEXELEMENT9& element,\n          DWORD fvf,\n          DWORD& outFvf,\n          DWORD& texCountPostUpdate) {\n\n    // Mapping between a Direct3D Declaration and FVF Codes (Direct3D 9)\n    // This table maps members of a D3DVERTEXELEMENT9 declaration to a FVF code.\n    //\n    // Data type              Usage                       Usage index     FVF\n    // ----------------------------------------------------------------------------------------\n    // D3DDECLTYPE_FLOAT3     D3DDECLUSAGE_POSITION       0               D3DFVF_XYZ\n    // D3DDECLTYPE_FLOAT4     D3DDECLUSAGE_POSITIONT      0               D3DFVF_XYZRHW\n    // D3DDECLTYPE_FLOATn     D3DDECLUSAGE_BLENDWEIGHT    0               D3DFVF_XYZBn\n    // D3DDECLTYPE_UBYTE4     D3DDECLUSAGE_BLENDINDICES   0               D3DFVF_XYZB(nWeights + 1)\n    // D3DDECLTYPE_FLOAT3     D3DDECLUSAGE_NORMAL         0               D3DFVF_NORMAL\n    // D3DDECLTYPE_FLOAT1     D3DDECLUSAGE_PSIZE          0               D3DFVF_PSIZE\n    // D3DDECLTYPE_D3DCOLOR   D3DDECLUSAGE_COLOR          0               D3DFVF_DIFFUSE\n    // D3DDECLTYPE_D3DCOLOR   D3DDECLUSAGE_COLOR          1               D3DFVF_SPECULAR\n    // D3DDECLTYPE_FLOATm     D3DDECLUSAGE_TEXCOORD       n               D3DFVF_TEXCOORDSIZEm(n)\n    // D3DDECLTYPE_FLOAT3     D3DDECLUSAGE_POSITION       1               N / A\n    // D3DDECLTYPE_FLOAT3     D3DDECLUSAGE_NORMAL         1               N / A\n\n\n    if (element.Usage == D3DDECLUSAGE_POSITION && element.Type == D3DDECLTYPE_FLOAT3 && element.UsageIndex == 0) {\n      outFvf = D3DFVF_XYZ;\n      return true;\n    }\n\n    if (element.Usage == D3DDECLUSAGE_POSITIONT && element.Type == D3DDECLTYPE_FLOAT4 && element.UsageIndex == 0) {\n      outFvf = D3DFVF_XYZRHW;\n      return true;\n    }\n\n    if (element.Usage == D3DDECLUSAGE_BLENDWEIGHT && element.UsageIndex == 0) {\n      DWORD fvfRet = MapD3DDeclTypeFloatToFvfXYZBn(element.Type);\n      if (likely(fvfRet != 0)) {\n        outFvf = fvfRet;\n        return true;\n      } else {\n        return false;\n      }\n    }\n\n    if (element.Usage == D3DDECLUSAGE_BLENDINDICES && element.Type == D3DDECLTYPE_UBYTE4 && element.UsageIndex == 0) {\n      outFvf = D3DFVF_XYZB1;\n      return true;\n    }\n\n    if (element.Usage == D3DDECLUSAGE_NORMAL && element.Type == D3DDECLTYPE_FLOAT3 && element.UsageIndex == 0) {\n      outFvf = D3DFVF_NORMAL;\n      return true;\n    }\n\n    if (element.Usage == D3DDECLUSAGE_PSIZE && element.Type == D3DDECLTYPE_FLOAT1 && element.UsageIndex == 0) {\n      outFvf = D3DFVF_PSIZE;\n      return true;\n    }\n\n    if (element.Usage == D3DDECLUSAGE_COLOR && element.Type == D3DDECLTYPE_D3DCOLOR) {\n      switch (element.UsageIndex) {\n        case 0:\n          outFvf = D3DFVF_DIFFUSE;\n          return true;\n        case 1:\n          outFvf = D3DFVF_SPECULAR;\n          return true;\n        default:\n          return false;\n      }\n    }\n\n    if (element.Usage == D3DDECLUSAGE_TEXCOORD && element.UsageIndex < 8) {\n      return MapD3DDeclUsageTexCoordToFvfTexCoordSize(element, fvf, outFvf, texCountPostUpdate);\n    }\n\n    return false;\n  }\n\n\n  DWORD D3D9VertexDecl::MapD3DDeclTypeFloatToFvfXYZBn(BYTE type) {\n\n    switch (type) {\n      case D3DDECLTYPE_FLOAT1: return D3DFVF_XYZB1;\n      case D3DDECLTYPE_FLOAT2: return D3DFVF_XYZB2;\n      case D3DDECLTYPE_FLOAT3: return D3DFVF_XYZB3;\n      case D3DDECLTYPE_FLOAT4: return D3DFVF_XYZB4;\n      default:                 return 0;\n    }\n  }\n\n\n  bool D3D9VertexDecl::MapD3DDeclUsageTexCoordToFvfTexCoordSize(\n      const D3DVERTEXELEMENT9& element,\n      DWORD fvf,\n      DWORD& outFvf,\n      DWORD& texCountPostUpdate) {\n\n    // Check if bits of format for current UsageIndex are free in the fvf\n    // It is necessary to skip multiple initializations of the bitfield because\n    // returned value is bitwise or-ed to final fvf DWORD.\n    // The D3DFVF_TEXCOORDSIZE1 is used below because it covers all formats bits.\n    if ((D3DFVF_TEXCOORDSIZE1(element.UsageIndex) & fvf) != 0)\n      return false;\n\n    // Update max texture's index in fvf\n    DWORD currentTexCount = element.UsageIndex + 1;\n    bool retStatus = true;\n\n    if (texCountPostUpdate < currentTexCount)\n      texCountPostUpdate = currentTexCount;\n\n    if (element.Type == D3DDECLTYPE_FLOAT1)\n      outFvf = D3DFVF_TEXCOORDSIZE1(element.UsageIndex);\n    else if (element.Type == D3DDECLTYPE_FLOAT2)\n      outFvf = D3DFVF_TEXCOORDSIZE2(element.UsageIndex);\n    else if (element.Type == D3DDECLTYPE_FLOAT3)\n      outFvf = D3DFVF_TEXCOORDSIZE3(element.UsageIndex);\n    else if (element.Type == D3DDECLTYPE_FLOAT4)\n      outFvf = D3DFVF_TEXCOORDSIZE4(element.UsageIndex);\n    else\n      retStatus = false;\n\n    return retStatus;\n  }\n\n\n  DWORD D3D9VertexDecl::MapD3D9VertexElementsToFvf() {\n    DWORD fvf = 0;\n    DWORD texCountPostUpdate = 0;\n\n    for (const auto& element : m_elements) {\n      DWORD elementFvf = 0;\n      if (!MapD3DDeclToFvf(element, fvf, elementFvf, texCountPostUpdate)) {\n        return 0;\n      }\n      fvf |= elementFvf;\n    }\n\n    fvf |= (texCountPostUpdate << 8);\n\n    return fvf;\n  }\n\n\n  void D3D9VertexDecl::Classify() {\n    for (const auto& element : m_elements) {\n      if (element.Type != D3DDECLTYPE_UNUSED)\n        m_sizes[element.Stream] = std::max(m_sizes[element.Stream], element.Offset + GetDecltypeSize(D3DDECLTYPE(element.Type)));\n\n      if (element.Usage == D3DDECLUSAGE_COLOR && element.UsageIndex == 0)\n        m_flags.set(D3D9VertexDeclFlag::HasColor0);\n      else if (element.Usage == D3DDECLUSAGE_COLOR && element.UsageIndex == 1)\n        m_flags.set(D3D9VertexDeclFlag::HasColor1);\n      else if (element.Usage == D3DDECLUSAGE_POSITIONT)\n        m_flags.set(D3D9VertexDeclFlag::HasPositionT);\n      else if (element.Usage == D3DDECLUSAGE_PSIZE)\n        m_flags.set(D3D9VertexDeclFlag::HasPointSize);\n      else if (element.Usage == D3DDECLUSAGE_FOG)\n        m_flags.set(D3D9VertexDeclFlag::HasFog);\n      else if (element.Usage == D3DDECLUSAGE_BLENDWEIGHT)\n        m_flags.set(D3D9VertexDeclFlag::HasBlendWeight);\n      else if (element.Usage == D3DDECLUSAGE_BLENDINDICES)\n        m_flags.set(D3D9VertexDeclFlag::HasBlendIndices);\n\n      if (element.Usage == D3DDECLUSAGE_TEXCOORD)\n        m_texcoordMask |= GetDecltypeCount(D3DDECLTYPE(element.Type)) << (element.UsageIndex * 3);\n\n      m_streamMask |= 1 << element.Stream;\n    }\n  }\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_vertex_declaration.h",
    "content": "#pragma once\n\n#include \"d3d9_device_child.h\"\n#include \"d3d9_util.h\"\n\n#include <vector>\n\nnamespace dxvk {\n\n  enum class D3D9VertexDeclFlag {\n    HasColor0,\n    HasColor1,\n    HasPositionT,\n    HasPointSize,\n    HasFog,\n    HasBlendWeight,\n    HasBlendIndices\n  };\n  using D3D9VertexDeclFlags = Flags<D3D9VertexDeclFlag>;\n\n  using D3D9VertexDeclBase = D3D9DeviceChild<IDirect3DVertexDeclaration9>;\n  class D3D9VertexDecl final : public D3D9VertexDeclBase {\n\n  public:\n\n    D3D9VertexDecl(\n            D3D9DeviceEx*      pDevice,\n            DWORD              FVF);\n\n    D3D9VertexDecl(\n            D3D9DeviceEx*      pDevice,\n      const D3DVERTEXELEMENT9* pVertexElements,\n            uint32_t           DeclCount);\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID  riid,\n            void** ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetDeclaration(\n            D3DVERTEXELEMENT9* pElement,\n            UINT*              pNumElements);\n\n    inline DWORD GetFVF() {\n      return m_fvf;\n    }\n\n    void SetFVF(DWORD FVF);\n\n    const D3D9VertexElements& GetElements() const {\n      return m_elements;\n    }\n\n    UINT GetSize(UINT Stream) const {\n      return m_sizes[Stream];\n    }\n\n    bool TestFlag(D3D9VertexDeclFlag flag) const {\n      return m_flags.test(flag);\n    }\n\n    D3D9VertexDeclFlags GetFlags() const {\n      return m_flags;\n    }\n\n    uint32_t GetTexcoordMask() const {\n      return m_texcoordMask;\n    }\n\n    uint32_t GetStreamMask() const {\n      return m_streamMask;\n    }\n\n  private:\n\n    bool MapD3DDeclToFvf(\n      const D3DVERTEXELEMENT9& element,\n            DWORD fvf,\n            DWORD& outFvf,\n            DWORD& texCountPostUpdate);\n\n    DWORD MapD3D9VertexElementsToFvf();\n\n    DWORD MapD3DDeclTypeFloatToFvfXYZBn(BYTE type);\n\n    bool MapD3DDeclUsageTexCoordToFvfTexCoordSize(\n      const D3DVERTEXELEMENT9& element,\n            DWORD fvf,\n            DWORD& outFvf,\n            DWORD& texCountPostUpdate);\n\n    void Classify();\n\n    D3D9VertexDeclFlags            m_flags;\n\n    D3D9VertexElements             m_elements;\n\n    DWORD                          m_fvf;\n\n    uint32_t                       m_texcoordMask = 0;\n\n    uint32_t                       m_streamMask = 0;\n\n    std::array<uint32_t, caps::MaxStreams> m_sizes = {};\n\n  };\n\n}"
  },
  {
    "path": "src/d3d9/d3d9_volume.cpp",
    "content": "#include \"d3d9_volume.h\"\n\n#include \"d3d9_device.h\"\n#include \"d3d9_texture.h\"\n\nnamespace dxvk {\n\n  D3D9Volume::D3D9Volume(\n          D3D9DeviceEx*             pDevice,\n    const D3D9_COMMON_TEXTURE_DESC* pDesc,\n    const bool                      Extended)\n    : D3D9VolumeBase(\n        pDevice,\n        Extended,\n        new D3D9CommonTexture( pDevice, this, pDesc, D3DRTYPE_VOLUMETEXTURE, nullptr ),\n        0, 0,\n        nullptr,\n        nullptr) { }\n\n\n  D3D9Volume::D3D9Volume(\n          D3D9DeviceEx*             pDevice,\n    const bool                      Extended,\n          D3D9CommonTexture*        pTexture,\n          UINT                      Face,\n          UINT                      MipLevel,\n          IDirect3DBaseTexture9*    pContainer)\n    : D3D9VolumeBase(\n        pDevice,\n        Extended,\n        pTexture,\n        Face, MipLevel,\n        pContainer,\n        pContainer) { }\n\n\n  void D3D9Volume::AddRefPrivate() {\n    // Can't have a swapchain container for a volume.\n    if (m_baseTexture != nullptr) {\n      static_cast<D3D9Texture3D*>(m_baseTexture)->AddRefPrivate();\n      return;\n    }\n\n    D3D9VolumeBase::AddRefPrivate();\n  }\n\n\n  void D3D9Volume::ReleasePrivate() {\n    // Can't have a swapchain container for a volume.\n    if (m_baseTexture != nullptr) {\n      static_cast<D3D9Texture3D*>(m_baseTexture)->ReleasePrivate();\n      return;\n    }\n\n    D3D9VolumeBase::ReleasePrivate();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Volume::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDirect3DResource9)\n     || riid == __uuidof(IDirect3DVolume9)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3D9VkInteropTexture)) {\n      *ppvObject = ref(m_texture->GetVkInterop());\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDirect3DVolume9), riid)) {\n      Logger::warn(\"D3D9Volume::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Volume::GetDesc(D3DVOLUME_DESC *pDesc) {\n    if (pDesc == nullptr)\n      return D3DERR_INVALIDCALL;\n\n    auto& desc = *(m_texture->Desc());\n\n    pDesc->Format = static_cast<D3DFORMAT>(desc.Format);\n    pDesc->Type   = D3DRTYPE_VOLUME;\n    pDesc->Usage  = desc.Usage;\n    pDesc->Pool   = desc.Pool;\n\n    pDesc->Width  = std::max(1u, desc.Width  >> m_mipLevel);\n    pDesc->Height = std::max(1u, desc.Height >> m_mipLevel);\n    pDesc->Depth  = std::max(1u, desc.Depth  >> m_mipLevel);\n\n    return D3D_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Volume::LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) {\n    if (unlikely(pLockedBox == nullptr))\n      return D3DERR_INVALIDCALL;\n\n    // LockBox clears any existing content present in pLockedBox\n    pLockedBox->pBits = nullptr;\n    pLockedBox->RowPitch = 0;\n    pLockedBox->SlicePitch = 0;\n\n    if (unlikely(pBox != nullptr)) {\n      auto& desc = *(m_texture->Desc());\n\n      // Negative or zero length dimensions\n      if ( static_cast<LONG>(pBox->Right)  - static_cast<LONG>(pBox->Left)  <= 0\n        || static_cast<LONG>(pBox->Bottom) - static_cast<LONG>(pBox->Top)   <= 0\n        || static_cast<LONG>(pBox->Back)   - static_cast<LONG>(pBox->Front) <= 0\n      // Exceeding surface dimensions\n        || pBox->Right  > std::max(1u, desc.Width  >> m_mipLevel)\n        || pBox->Bottom > std::max(1u, desc.Height >> m_mipLevel)\n        || pBox->Back   > std::max(1u, desc.Depth  >> m_mipLevel))\n        return D3DERR_INVALIDCALL;\n    }\n\n    D3DLOCKED_BOX lockedBox;\n\n    HRESULT hr = m_parent->LockImage(\n      m_texture,\n      m_face, m_mipLevel,\n      &lockedBox,\n      pBox,\n      Flags);\n\n    if (FAILED(hr)) return hr;\n\n    pLockedBox->pBits      = lockedBox.pBits;\n    pLockedBox->RowPitch   = lockedBox.RowPitch;\n    pLockedBox->SlicePitch = lockedBox.SlicePitch;\n\n    return hr;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3D9Volume::UnlockBox() {\n    return m_parent->UnlockImage(\n      m_texture,\n      m_face, m_mipLevel);\n  }\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_volume.h",
    "content": "#pragma once\n\n#include \"d3d9_subresource.h\"\n\n#include \"d3d9_common_texture.h\"\n\nnamespace dxvk {\n\n  using D3D9VolumeBase = D3D9Subresource<IDirect3DVolume9>;\n  class D3D9Volume final : public D3D9VolumeBase {\n\n  public:\n\n    D3D9Volume(\n            D3D9DeviceEx*             pDevice,\n      const D3D9_COMMON_TEXTURE_DESC* pDesc,\n      const bool                      Extended);\n\n    D3D9Volume(\n            D3D9DeviceEx*             pDevice,\n      const bool                      Extended,\n            D3D9CommonTexture*        pTexture,\n            UINT                      Face,\n            UINT                      MipLevel,\n            IDirect3DBaseTexture9*    pContainer);\n\n    void AddRefPrivate();\n\n    void ReleasePrivate();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);\n\n    HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC *pDesc) final;\n\n    HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final;\n\n    HRESULT STDMETHODCALLTYPE UnlockBox() final;\n\n  };\n}"
  },
  {
    "path": "src/d3d9/d3d9_window.cpp",
    "content": "#include \"d3d9_window.h\"\n\n#include \"d3d9_swapchain.h\"\n\nnamespace dxvk\n{\n\n#ifdef _WIN32\n  struct D3D9WindowData {\n    bool unicode;\n    bool filter;\n    bool activateProcessed;\n    bool deactivateProcessed;\n    WNDPROC proc;\n    D3D9SwapChainEx* swapchain;\n  };\n\n  static dxvk::recursive_mutex g_windowProcMapMutex;\n  static std::unordered_map<HWND, D3D9WindowData> g_windowProcMap;\n\n  D3D9WindowMessageFilter::D3D9WindowMessageFilter(HWND window, bool filter)\n    : m_window(window) {\n    std::lock_guard lock(g_windowProcMapMutex);\n    auto it = g_windowProcMap.find(m_window);\n    m_filter = std::exchange(it->second.filter, filter);\n  }\n\n  D3D9WindowMessageFilter::~D3D9WindowMessageFilter() {\n    std::lock_guard lock(g_windowProcMapMutex);\n    auto it = g_windowProcMap.find(m_window);\n    it->second.filter = m_filter;\n  }\n\n  LRESULT CALLBACK D3D9WindowProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) {\n    if (message == WM_NCCALCSIZE && wparam == TRUE)\n      return 0;\n\n    D3D9WindowData windowData = {};\n\n    {\n      std::lock_guard lock(g_windowProcMapMutex);\n\n      auto it = g_windowProcMap.find(window);\n      if (it != g_windowProcMap.end())\n        windowData = it->second;\n    }\n\n    bool unicode = windowData.proc\n      ? windowData.unicode\n      : IsWindowUnicode(window);\n\n    if (!windowData.proc || windowData.filter)\n      return CallCharsetFunction(\n        DefWindowProcW, DefWindowProcA, unicode,\n          window, message, wparam, lparam);\n\n    \n    D3D9DeviceEx* device = windowData.swapchain->GetParent();\n\n    if (message == WM_DESTROY)\n      ResetWindowProc(window);\n    else if (message == WM_ACTIVATEAPP) {\n      D3DDEVICE_CREATION_PARAMETERS create_parms;\n      device->GetCreationParameters(&create_parms);\n\n      if (!(create_parms.BehaviorFlags & D3DCREATE_NOWINDOWCHANGES)) {\n        D3D9WindowMessageFilter filter(window);\n        if (wparam && !windowData.activateProcessed) {\n          // Heroes of Might and Magic V needs this to resume drawing after a focus loss\n          D3DPRESENT_PARAMETERS params;\n          RECT rect;\n\n          wsi::getDesktopCoordinates(wsi::getDefaultMonitor(), &rect);\n          windowData.swapchain->GetPresentParameters(&params);\n          SetWindowPos(window, nullptr, rect.left, rect.top, params.BackBufferWidth, params.BackBufferHeight,\n                       SWP_NOACTIVATE | SWP_NOZORDER);\n        }\n        else if (!wparam) {\n          if (IsWindowVisible(window))\n            ShowWindow(window, SW_MINIMIZE);\n        }\n      }\n\n      if ((wparam && !windowData.activateProcessed)\n        || (!wparam && !windowData.deactivateProcessed)) {\n        device->NotifyWindowActivated(window, wparam);\n      }\n\n      SetActivateProcessed(window, !!wparam);\n    }\n    else if (message == WM_SIZE)\n    {\n      D3DDEVICE_CREATION_PARAMETERS create_parms;\n      device->GetCreationParameters(&create_parms);\n\n      if (!(create_parms.BehaviorFlags & D3DCREATE_NOWINDOWCHANGES) && !IsIconic(window))\n        PostMessageW(window, WM_ACTIVATEAPP, 1, GetCurrentThreadId());\n    }\n\n    return CallCharsetFunction(\n      CallWindowProcW, CallWindowProcA, unicode,\n        windowData.proc, window, message, wparam, lparam);\n  }\n\n  void ResetWindowProc(HWND window) {\n    std::lock_guard lock(g_windowProcMapMutex);\n\n    auto it = g_windowProcMap.find(window);\n    if (it == g_windowProcMap.end())\n      return;\n\n    auto proc = reinterpret_cast<WNDPROC>(\n      CallCharsetFunction(\n      GetWindowLongPtrW, GetWindowLongPtrA, it->second.unicode,\n        window, GWLP_WNDPROC));\n\n\n    if (proc == D3D9WindowProc)\n      CallCharsetFunction(\n        SetWindowLongPtrW, SetWindowLongPtrA, it->second.unicode,\n          window, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(it->second.proc));\n\n    g_windowProcMap.erase(window);\n  }\n\n\n  void HookWindowProc(HWND window, D3D9SwapChainEx* swapchain) {\n    std::lock_guard lock(g_windowProcMapMutex);\n\n    ResetWindowProc(window);\n\n    D3D9WindowData windowData;\n    windowData.unicode = IsWindowUnicode(window);\n    windowData.filter  = false;\n    windowData.activateProcessed = false;\n    windowData.deactivateProcessed = false;\n    windowData.proc = reinterpret_cast<WNDPROC>(\n      CallCharsetFunction(\n      SetWindowLongPtrW, SetWindowLongPtrA, windowData.unicode,\n        window, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(D3D9WindowProc)));\n    windowData.swapchain = swapchain;\n\n    g_windowProcMap[window] = std::move(windowData);\n  }\n\n  void SetActivateProcessed(HWND window, bool processed)\n  {\n      std::lock_guard lock(g_windowProcMapMutex);\n      auto it = g_windowProcMap.find(window);\n      if (it != g_windowProcMap.end()) {\n        it->second.activateProcessed = processed;\n        it->second.deactivateProcessed = !processed;\n      }\n  }\n\n  void ActivateFocusWindow(HWND window) {\n      SetWindowPos(window, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);\n  }\n#else\n  D3D9WindowMessageFilter::D3D9WindowMessageFilter(HWND window, bool filter) {\n\n  }\n\n  D3D9WindowMessageFilter::~D3D9WindowMessageFilter() {\n\n  }\n\n  void ResetWindowProc(HWND window) {\n\n  }\n\n  void HookWindowProc(HWND window, D3D9SwapChainEx* swapchain) {\n\n  }\n\n  void SetActivateProcessed(HWND window, bool processed) {\n  }\n\n  void ActivateFocusWindow(HWND window) {\n  }\n\n#endif\n\n}\n"
  },
  {
    "path": "src/d3d9/d3d9_window.h",
    "content": "#pragma once\n\n#include <windows.h>\n\nnamespace dxvk {\n\n  class D3D9SwapChainEx;\n\n  class D3D9WindowMessageFilter {\n\n  public:\n\n    D3D9WindowMessageFilter(HWND window, bool filter = true);\n    ~D3D9WindowMessageFilter();\n\n    D3D9WindowMessageFilter             (const D3D9WindowMessageFilter&) = delete;\n    D3D9WindowMessageFilter& operator = (const D3D9WindowMessageFilter&) = delete;\n\n  private:\n\n    HWND m_window;\n    bool m_filter = false;\n\n  };\n\n  template <typename T, typename J, typename ... Args>\n  auto CallCharsetFunction(T unicode, J ascii, bool isUnicode, Args... args) {\n    return isUnicode\n      ? unicode(args...)\n      : ascii  (args...);\n  }\n\n  void ResetWindowProc(HWND window);\n  void HookWindowProc(HWND window, D3D9SwapChainEx* swapchain);\n  void SetActivateProcessed(HWND window, bool processed);\n  void ActivateFocusWindow(HWND window);\n\n}\n"
  },
  {
    "path": "src/d3d9/meson.build",
    "content": "d3d9_res = wrc_generator.process('version.rc')\n\nd3d9_shaders = files([\n  'shaders/d3d9_convert_yuy2_uyvy.comp',\n  'shaders/d3d9_convert_l6v5u5.comp',\n  'shaders/d3d9_convert_x8l8v8u8.comp',\n  'shaders/d3d9_convert_a2w10v10u10.comp',\n  'shaders/d3d9_convert_w11v11u10.comp',\n  'shaders/d3d9_convert_nv12.comp',\n  'shaders/d3d9_convert_yv12.comp',\n  'shaders/d3d9_fixed_function_vert.vert',\n  'shaders/d3d9_fixed_function_frag.frag',\n  'shaders/d3d9_fixed_function_frag_sample.frag',\n])\n\nd3d9_src = [\n  'd3d9_main.cpp',\n  'd3d9_interface.cpp',\n  'd3d9_adapter.cpp',\n  'd3d9_monitor.cpp',\n  'd3d9_device.cpp',\n  'd3d9_state.cpp',\n  'd3d9_cursor.cpp',\n  'd3d9_swapchain.cpp',\n  'd3d9_format.cpp',\n  'd3d9_common_texture.cpp',\n  'd3d9_constant_buffer.cpp',\n  'd3d9_texture.cpp',\n  'd3d9_surface.cpp',\n  'd3d9_volume.cpp',\n  'd3d9_common_buffer.cpp',\n  'd3d9_buffer.cpp',\n  'd3d9_shader.cpp',\n  'd3d9_vertex_declaration.cpp',\n  'd3d9_query.cpp',\n  'd3d9_shader_validator.cpp',\n  'd3d9_multithread.cpp',\n  'd3d9_options.cpp',\n  'd3d9_stateblock.cpp',\n  'd3d9_util.cpp',\n  'd3d9_initializer.cpp',\n  'd3d9_fixed_function.cpp',\n  'd3d9_names.cpp',\n  'd3d9_swvp_emu.cpp',\n  'd3d9_format_helpers.cpp',\n  'd3d9_hud.cpp',\n  'd3d9_annotation.cpp',\n  'd3d9_mem.cpp',\n  'd3d9_window.cpp',\n  'd3d9_interop.cpp',\n  'd3d9_on_12.cpp',\n  'd3d9_bridge.cpp'\n]\n\nd3d9_ld_args      = []\nd3d9_link_depends = []\n\nif platform != 'windows'\n  d3d9_ld_args      += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'd3d9.sym') ]\n  d3d9_link_depends += files('d3d9.sym')\nendif\n\nd3d9_dll = shared_library(dxvk_name_prefix+'d3d9', d3d9_src, glsl_generator.process(d3d9_shaders), d3d9_res,\n  dependencies        : [ dxso_dep, dxvk_dep ],\n  include_directories : dxvk_include_path,\n  install             : true,\n  vs_module_defs      : 'd3d9'+def_spec_ext,\n  link_args           : d3d9_ld_args,\n  link_depends        : [ d3d9_link_depends ],\n  kwargs              : dxvk_so_version,\n)\n\nd3d9_dep = declare_dependency(\n  link_with           : [ d3d9_dll ],\n  include_directories : [ dxvk_include_path ],\n)\n\nif platform != 'windows'\n  pkg.generate(d3d9_dll,\n    filebase: dxvk_pkg_prefix + 'd3d9',\n    subdirs:  'dxvk',\n  )\nendif\n"
  },
  {
    "path": "src/d3d9/shaders/d3d9_convert_a2w10v10u10.comp",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"d3d9_convert_common.h\"\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image2D dst;\n\nlayout(binding = 1) uniform usamplerBuffer src;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec2 extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n\n  if (all(lessThan(thread_id.xy, u_info.extent))) {\n    uint offset = thread_id.x\n                + thread_id.y * u_info.extent.x;\n\n    uint value = texelFetch(src, int(offset)).r;\n\n    // Sign-extend magic!\n    int  u10 = bitfieldExtract(int (value), 0,  10);\n    int  v10 = bitfieldExtract(int (value), 10, 10);\n    int  w10 = bitfieldExtract(int (value), 20, 10);\n    uint a2  = bitfieldExtract(uint(value), 30, 2);\n\n    vec4 color = vec4(\n      snormalize(u10, 10),\n      snormalize(v10, 10),\n      snormalize(w10, 10),\n      unormalize(a2,  2));\n    \n    imageStore(dst, thread_id.xy, color);\n  }\n}"
  },
  {
    "path": "src/d3d9/shaders/d3d9_convert_common.h",
    "content": "float unormalize(uint value, int bits) {\n  const int range = (1 << bits) - 1;\n\n  return float(value) / float(range);\n}\n\nfloat snormalize(int value, int bits) {\n  const int range = (1 << (bits - 1)) - 1;\n\n  // Min because, -32 and -31 map to -1.0f, and we\n  // divide by 31.\n  return max(float(value) / float(range), -1.0);\n}\n\nfloat unpackUnorm(uint p) {\n  return float(p) / 255.0;\n}\n\nvec2 unpackUnorm2x8(uint p) {\n  uvec2 value = uvec2(p & 0xFF, p >> 8);\n  return vec2(unpackUnorm(value.x), unpackUnorm(value.y));\n}\n\nmat3x4 g_yuv_to_rgb = {\n  { 298 / 256,  0,          409 / 256, 0.5 },\n  { 298 / 256, -100 / 256, -208 / 256, 0.5 },\n  { 298 / 256,  516 / 256,  0,         0.5 }\n};\n\nvec4 convertYUV(vec3 yuv) {\n  vec3 value = vec4(yuv, 1 / 255.0) * g_yuv_to_rgb;\n\n  return vec4(clamp(value, 0, 1), 1);\n}\n\nmat3x3 g_bt709_to_rgb = {\n  { 1.164,  0,          1.793    },\n  { 1.164, -0.213,     -0.533    },\n  { 1.164,  2.112,      0        }\n};\n\nvec4 convertBT_709(vec3 cde) {\n  return vec4(clamp(cde * g_bt709_to_rgb, 0, 1), 1);\n}\n"
  },
  {
    "path": "src/d3d9/shaders/d3d9_convert_l6v5u5.comp",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"d3d9_convert_common.h\"\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image2D dst;\n\nlayout(binding = 1) uniform usamplerBuffer src;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec2 extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n\n  if (all(lessThan(thread_id.xy, u_info.extent))) {\n    uint offset = thread_id.x\n                + thread_id.y * u_info.extent.x;\n\n    uint value = texelFetch(src, int(offset)).r;\n\n    // Sign-extend magic!\n    int u5  = bitfieldExtract(int (value), 0,  5);\n    int v5  = bitfieldExtract(int (value), 5,  5);\n    uint l6 = bitfieldExtract(uint(value), 10, 6);\n\n    vec4 color = vec4(\n      snormalize(u5, 5),\n      snormalize(v5, 5),\n      unormalize(l6, 6),\n      1.0f);\n    \n    imageStore(dst, thread_id.xy, color);\n  }\n}"
  },
  {
    "path": "src/d3d9/shaders/d3d9_convert_nv12.comp",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"d3d9_convert_common.h\"\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image2D dst;\n\nlayout(binding = 1) uniform usamplerBuffer src;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec2 extent;\n} u_info;\n\nvec2 fetchUnorm2x8(usamplerBuffer source, uint offset) {\n  return unpackUnorm2x8(texelFetch(src, int(offset)).r);\n}\n\n// Format is:\n// YYYYYYYYYYYYYYY...\n// YYYYYYYYYYYYYYY...\n// UVUVUVUVUVUVUVU...\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n\n  if (all(lessThan(thread_id.xy, u_info.extent))) {\n    uvec2 pitch = uvec2(u_info.extent.x, u_info.extent.y);\n\n    uint offset = thread_id.x\n                + thread_id.y * pitch.x;\n\n    // Fetch 2 luminance samples.\n    vec2 y = fetchUnorm2x8(src, offset) - (16 / 255.0);        \n\n    // Go into the second plane to get the chroma data.\n    // UV data is subsampled as [2, 2]\n    // So we need to divide thread_id.y by 2.\n    // thread_id.x is already accounted for as we read uint16\n    offset = thread_id.x\n           + thread_id.y / 2 * pitch.x\n           + pitch.x * pitch.y;\n\n    vec2 uv = fetchUnorm2x8(src, offset) - (128 / 255.0);\n\n    // The NV12 format seems to use the BT.709 color space.\n    vec4 color0 = convertBT_709(vec3(y.x, uv.x, uv.y));\n    vec4 color1 = convertBT_709(vec3(y.y, uv.x, uv.y));\n\n    // We write as a macropixel of [2, 1]\n    // So write out 2 pixels in this run.\n    ivec2 writePos = thread_id.xy * ivec2(2, 1);\n    \n    imageStore(dst, ivec2(writePos.x,     writePos.y), color0);\n    imageStore(dst, ivec2(writePos.x + 1, writePos.y), color1);\n  }\n}"
  },
  {
    "path": "src/d3d9/shaders/d3d9_convert_w11v11u10.comp",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"d3d9_convert_common.h\"\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image2D dst;\n\nlayout(binding = 1) uniform usamplerBuffer src;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec2 extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n\n  if (all(lessThan(thread_id.xy, u_info.extent))) {\n    uint offset = thread_id.x\n                + thread_id.y * u_info.extent.x;\n\n    uint value = texelFetch(src, int(offset)).r;\n\n    // Sign-extend magic!\n    int  u10 = bitfieldExtract(int (value), 0,  10);\n    int  v11 = bitfieldExtract(int (value), 10, 11);\n    int  w11 = bitfieldExtract(int (value), 21, 11);\n\n    vec4 color = vec4(\n      snormalize(u10, 10),\n      snormalize(v11, 10),\n      snormalize(w11, 10),\n      1.0);\n\n    imageStore(dst, thread_id.xy, color);\n  }\n}\n"
  },
  {
    "path": "src/d3d9/shaders/d3d9_convert_x8l8v8u8.comp",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"d3d9_convert_common.h\"\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image2D dst;\n\nlayout(binding = 1) uniform usamplerBuffer src;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec2 extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n\n  if (all(lessThan(thread_id.xy, u_info.extent))) {\n    uint offset = thread_id.x\n                + thread_id.y * u_info.extent.x;\n\n    uint value = texelFetch(src, int(offset)).r;\n\n    // Sign-extend magic!\n    int  u8 = bitfieldExtract(int (value), 0,  8);\n    int  v8 = bitfieldExtract(int (value), 8,  8);\n    uint l8 = bitfieldExtract(uint(value), 16, 8);\n\n    vec4 color = vec4(\n      snormalize(u8, 8),\n      snormalize(v8, 8),\n      unormalize(l8, 8),\n      1.0f);\n    \n    imageStore(dst, thread_id.xy, color);\n  }\n}"
  },
  {
    "path": "src/d3d9/shaders/d3d9_convert_yuy2_uyvy.comp",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"d3d9_convert_common.h\"\n\nlayout(constant_id = 0) const bool s_is_uyvy = false;\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image2D dst;\n\nlayout(binding = 1) uniform usamplerBuffer src;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec2 extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n\n  if (all(lessThan(thread_id.xy, u_info.extent))) {\n    uint offset = thread_id.x\n                + thread_id.y * u_info.extent.x;\n\n    uint value = texelFetch(src, int(offset)).r;\n\n    vec4 data = unpackUnorm4x8(value);\n\n    // Flip around stuff for UYVY\n    if (s_is_uyvy)\n      data = data.yxwz;\n\n    float y0 = data.x - (16   / 255.0);\n    float u  = data.y - (128  / 255.0);\n    float y1 = data.z - (16   / 255.0);\n    float v  = data.w - (128  / 255.0);\n\n    vec4 color0 = convertYUV(vec3(y0, u, v));\n    vec4 color1 = convertYUV(vec3(y1, u, v));\n\n    // YUY2 has a macropixel of [2, 1]\n    // so we write 2 pixels in this run.\n    ivec2 writePos = thread_id.xy * ivec2(2, 1);\n    \n    imageStore(dst, ivec2(writePos.x,     writePos.y), color0);\n    imageStore(dst, ivec2(writePos.x + 1, writePos.y), color1);\n  }\n}"
  },
  {
    "path": "src/d3d9/shaders/d3d9_convert_yv12.comp",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"d3d9_convert_common.h\"\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image2D dst;\n\nlayout(binding = 1) uniform usamplerBuffer src;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec2 extent;\n} u_info;\n\n// Format is:\n// YYYYYYYY...\n// VVVV...\n// UUUU...\n\nfloat fetchUnorm(usamplerBuffer source, uint offset) {\n  return unpackUnorm(texelFetch(src, int(offset)).r);\n}\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n\n  if (all(lessThan(thread_id.xy, u_info.extent))) {\n    uvec2 pitch = uvec2(u_info.extent.x, u_info.extent.y);\n\n    uint offset = thread_id.x\n                + thread_id.y * pitch.x;\n\n    // Fetch a Y, luminance sample.\n    float y = fetchUnorm(src, offset) - (16 / 255.0);        \n\n    // Go into the second plane to get a V, chroma sample\n    offset = (thread_id.x / 2)\n           + (thread_id.y / 2) * (pitch.x / 2)\n           + pitch.x * pitch.y;\n\n    float v = fetchUnorm(src, offset) - (128 / 255.0);\n\n    // Go into the third plane to get a U, chroma sample\n    offset += (pitch.x / 2) * (pitch.y / 2);\n    float u = fetchUnorm(src, offset) - (128 / 255.0);\n\n    // TODO: Is this the right color space?\n    vec4 color = convertBT_709(vec3(y, u, v));\n\n    imageStore(dst, thread_id.xy, color);\n  }\n}\n"
  },
  {
    "path": "src/d3d9/shaders/d3d9_fixed_function_common.glsl",
    "content": "const float FloatMaxValue = 340282346638528859811704183484516925440.0;\n\nconst uint TextureStageCount = 8;\n\n#define D3DFOGMODE uint\nconst uint D3DFOG_NONE   = 0;\nconst uint D3DFOG_EXP    = 1;\nconst uint D3DFOG_EXP2   = 2;\nconst uint D3DFOG_LINEAR = 3;\n\nstruct D3D9RenderStateInfo {\n    float fogColor[3];\n    float fogScale;\n    float fogEnd;\n    float fogDensity;\n\n    uint alphaRef;\n\n    float pointSize;\n    float pointSizeMin;\n    float pointSizeMax;\n    float pointScaleA;\n    float pointScaleB;\n    float pointScaleC;\n};\n\n\n// Thanks SPIRV-Cross\nspirv_instruction(set = \"GLSL.std.450\", id = 79) float spvNMin(float, float);\nspirv_instruction(set = \"GLSL.std.450\", id = 79) vec2 spvNMin(vec2, vec2);\nspirv_instruction(set = \"GLSL.std.450\", id = 79) vec3 spvNMin(vec3, vec3);\nspirv_instruction(set = \"GLSL.std.450\", id = 79) vec4 spvNMin(vec4, vec4);\nspirv_instruction(set = \"GLSL.std.450\", id = 81) float spvNClamp(float, float, float);\nspirv_instruction(set = \"GLSL.std.450\", id = 81) vec2 spvNClamp(vec2, vec2, vec2);\nspirv_instruction(set = \"GLSL.std.450\", id = 81) vec3 spvNClamp(vec3, vec3, vec3);\nspirv_instruction(set = \"GLSL.std.450\", id = 81) vec4 spvNClamp(vec4, vec4, vec4);\n\n\n// Dynamic \"spec constants\"\n// Binding has to match with getSpecConstantBufferSlot in dxso_util.h\nlayout(set = 0, binding = 31, scalar) uniform SpecConsts {\n    uint dynamicSpecConstDword[20];\n};\n\nlayout (constant_id = 0) const uint SpecConstDword0 = 0;\nlayout (constant_id = 1) const uint SpecConstDword1 = 0;\nlayout (constant_id = 2) const uint SpecConstDword2 = 0;\nlayout (constant_id = 3) const uint SpecConstDword3 = 0;\nlayout (constant_id = 4) const uint SpecConstDword4 = 0;\nlayout (constant_id = 5) const uint SpecConstDword5 = 0;\nlayout (constant_id = 6) const uint SpecConstDword6 = 0;\nlayout (constant_id = 7) const uint SpecConstDword7 = 0;\nlayout (constant_id = 8) const uint SpecConstDword8 = 0;\nlayout (constant_id = 9) const uint SpecConstDword9 = 0;\nlayout (constant_id = 10) const uint SpecConstDword10 = 0;\nlayout (constant_id = 11) const uint SpecConstDword11 = 0;\nlayout (constant_id = 12) const uint SpecConstDword12 = 0;\nlayout (constant_id = 13) const uint SpecConstDword13 = 0;\nlayout (constant_id = 14) const uint SpecConstDword14 = 0;\nlayout (constant_id = 15) const uint SpecConstDword15 = 0;\nlayout (constant_id = 16) const uint SpecConstDword16 = 0;\nlayout (constant_id = 17) const uint SpecConstDword17 = 0;\nlayout (constant_id = 18) const uint SpecConstDword18 = 0;\nlayout (constant_id = 19) const uint SpecConstDword19 = 0;\nlayout (constant_id = 20) const uint SpecConstDword20 = 0;\n\nconst uint SpecSamplerType = 0;\nconst uint SpecSamplerDepthMode = 1;\nconst uint SpecAlphaCompareOp = 2;\nconst uint SpecSamplerProjected = 3;\nconst uint SpecSamplerNull = 4;\nconst uint SpecAlphaPrecisionBits = 5;\nconst uint SpecFogEnabled = 6;\nconst uint SpecVertexFogMode = 7;\nconst uint SpecPixelFogMode = 8;\nconst uint SpecVertexShaderBools = 9;\nconst uint SpecPixelShaderBools = 10;\nconst uint SpecSamplerFetch4 = 11;\nconst uint SpecFFLastActiveTextureStage = 12;\nconst uint SpecSamplerDrefClamp = 13;\nconst uint SpecClipPlaneCount = 14;\nconst uint SpecPointMode = 15;\nconst uint SpecDrefScaling = 16;\nconst uint SpecFFGlobalSpecularEnabled = 17;\nconst uint SpecFFTextureStage0ColorOp = 18;\nconst uint SpecFFTextureStage0ColorArg1 = 19;\nconst uint SpecFFTextureStage0ColorArg2 = 20;\nconst uint SpecFFTextureStage0AlphaOp = 21;\nconst uint SpecFFTextureStage0AlphaArg1 = 22;\nconst uint SpecFFTextureStage0AlphaArg2 = 23;\nconst uint SpecFFTextureStage0ResultIsTemp = 24;\nconst uint SpecFFTextureStage1ColorOp = 25;\nconst uint SpecFFTextureStage1ColorArg1 = 26;\nconst uint SpecFFTextureStage1ColorArg2 = 27;\nconst uint SpecFFTextureStage1AlphaOp = 28;\nconst uint SpecFFTextureStage1AlphaArg1 = 29;\nconst uint SpecFFTextureStage1AlphaArg2 = 30;\nconst uint SpecFFTextureStage1ResultIsTemp = 31;\nconst uint SpecFFTextureStage2ColorOp = 32;\nconst uint SpecFFTextureStage2ColorArg1 = 33;\nconst uint SpecFFTextureStage2ColorArg2 = 34;\nconst uint SpecFFTextureStage2AlphaOp = 35;\nconst uint SpecFFTextureStage2AlphaArg1 = 36;\nconst uint SpecFFTextureStage2AlphaArg2 = 37;\nconst uint SpecFFTextureStage2ResultIsTemp = 38;\nconst uint SpecFFTextureStage3ColorOp = 39;\nconst uint SpecFFTextureStage3ColorArg1 = 40;\nconst uint SpecFFTextureStage3ColorArg2 = 41;\nconst uint SpecFFTextureStage3AlphaOp = 42;\nconst uint SpecFFTextureStage3AlphaArg1 = 43;\nconst uint SpecFFTextureStage3AlphaArg2 = 44;\nconst uint SpecFFTextureStage3ResultIsTemp = 45;\nconst uint SpecFFTextureStage4ColorOp = 46;\nconst uint SpecFFTextureStage4ColorArg1 = 47;\nconst uint SpecFFTextureStage4ColorArg2 = 48;\nconst uint SpecFFTextureStage4AlphaOp = 49;\nconst uint SpecFFTextureStage4AlphaArg1 = 50;\nconst uint SpecFFTextureStage4AlphaArg2 = 51;\nconst uint SpecFFTextureStage4ResultIsTemp = 52;\nconst uint SpecFFTextureStage5ColorOp = 53;\nconst uint SpecFFTextureStage5ColorArg1 = 54;\nconst uint SpecFFTextureStage5ColorArg2 = 55;\nconst uint SpecFFTextureStage5AlphaOp = 56;\nconst uint SpecFFTextureStage5AlphaArg1 = 57;\nconst uint SpecFFTextureStage5AlphaArg2 = 58;\nconst uint SpecFFTextureStage5ResultIsTemp = 59;\nconst uint SpecFFTextureStage6ColorOp = 60;\nconst uint SpecFFTextureStage6ColorArg1 = 61;\nconst uint SpecFFTextureStage6ColorArg2 = 62;\nconst uint SpecFFTextureStage6AlphaOp = 63;\nconst uint SpecFFTextureStage6AlphaArg1 = 64;\nconst uint SpecFFTextureStage6AlphaArg2 = 65;\nconst uint SpecFFTextureStage6ResultIsTemp = 66;\nconst uint SpecFFTextureStage7ColorOp = 67;\nconst uint SpecFFTextureStage7ColorArg1 = 68;\nconst uint SpecFFTextureStage7ColorArg2 = 69;\nconst uint SpecFFTextureStage7AlphaOp = 70;\nconst uint SpecFFTextureStage7AlphaArg1 = 71;\nconst uint SpecFFTextureStage7AlphaArg2 = 72;\nconst uint SpecFFTextureStage7ResultIsTemp = 73;\nconst uint SpecFFTextureStage0ColorArg0 = 74;\nconst uint SpecFFTextureStage1ColorArg0 = 75;\nconst uint SpecFFTextureStage2ColorArg0 = 76;\nconst uint SpecFFTextureStage3ColorArg0 = 77;\nconst uint SpecFFTextureStage4ColorArg0 = 78;\nconst uint SpecFFTextureStage5ColorArg0 = 79;\nconst uint SpecFFTextureStage6ColorArg0 = 80;\nconst uint SpecFFTextureStage7ColorArg0 = 81;\nconst uint SpecFFTextureStage0AlphaArg0 = 82;\nconst uint SpecFFTextureStage1AlphaArg0 = 83;\nconst uint SpecFFTextureStage2AlphaArg0 = 84;\nconst uint SpecFFTextureStage3AlphaArg0 = 85;\nconst uint SpecFFTextureStage4AlphaArg0 = 86;\nconst uint SpecFFTextureStage5AlphaArg0 = 87;\nconst uint SpecFFTextureStage6AlphaArg0 = 88;\nconst uint SpecFFTextureStage7AlphaArg0 = 89;\nconst uint SpecConstantCount = 90;\n\nstruct BitfieldPosition {\n    uint dwordOffset;\n    uint bitOffset;\n    uint sizeInBits;\n};\n\n// Needs to match d3d9_spec_constants.h\nBitfieldPosition SpecConstLayout[SpecConstantCount] = {\n    { 0, 0, 32 },  // SamplerType\n\n    { 1, 0,  21 }, // SamplerDepthMode\n    { 1, 21, 3 },  // AlphaCompareOp\n    { 1, 24, 8 },  // SamplerProjected\n\n    { 2, 0,  21 }, // SamplerNull\n    { 2, 21, 4 },  // AlphaPrecisionBits\n    { 2, 25, 1 },  // FogEnabled\n    { 2, 26, 2 },  // VertexFogMode\n    { 2, 28, 2 },  // PixelFogMode\n\n    { 3, 0,  16 }, // VertexShaderBools\n    { 3, 16, 16 }, // PixelShaderBools\n\n    { 4, 0,  16 }, // SamplerFetch4\n    { 4, 16,  3 }, // FFLastActiveTextureStage\n\n    { 5, 0, 21 },  // SamplerDrefClamp\n    { 5, 21, 3 },  // ClipPlaneCount\n    { 5, 24, 2 },  // PointMode\n    { 5, 26, 5 },  // DrefScaling\n\n    { 6, 31, 1 },  // FFGlobalSpecularEnabled.\n\n    { 6,  0, 5 },  // FFTextureStage0ColorOp\n    { 6,  5, 5 },  // FFTextureStage0ColorArg1\n    { 6, 10, 5 },  // FFTextureStage0ColorArg2\n    { 6, 15, 5 },  // FFTextureStage0AlphaOp\n    { 6, 20, 5 },  // FFTextureStage0AlphaArg1\n    { 6, 25, 5 },  // FFTextureStage0AlphaArg2\n    { 6, 30, 1 },  // FFTextureStage0ResultIsTemp\n\n    { 7,  0, 5 },  // FFTextureStage1ColorOp\n    { 7,  5, 5 },  // FFTextureStage1ColorArg1\n    { 7, 10, 5 },  // FFTextureStage1ColorArg2\n    { 7, 15, 5 },  // FFTextureStage1AlphaOp\n    { 7, 20, 5 },  // FFTextureStage1AlphaArg1\n    { 7, 25, 5 },  // FFTextureStage1AlphaArg2\n    { 7, 30, 1 },  // FFTextureStage1ResultIsTemp\n\n    { 8,  0, 5 },  // FFTextureStage2ColorOp\n    { 8,  5, 5 },  // FFTextureStage2ColorArg1\n    { 8, 10, 5 },  // FFTextureStage2ColorArg2\n    { 8, 15, 5 },  // FFTextureStage2AlphaOp\n    { 8, 20, 5 },  // FFTextureStage2AlphaArg1\n    { 8, 25, 5 },  // FFTextureStage2AlphaArg2\n    { 8, 30, 1 },  // FFTextureStage2ResultIsTemp\n\n    { 9,  0, 5 },  // FFTextureStage3ColorOp\n    { 9,  5, 5 },  // FFTextureStage3ColorArg1\n    { 9, 10, 5 },  // FFTextureStage3ColorArg2\n    { 9, 15, 5 },  // FFTextureStage3AlphaOp\n    { 9, 20, 5 },  // FFTextureStage3AlphaArg1\n    { 9, 25, 5 },  // FFTextureStage3AlphaArg2\n    { 9, 30, 1 },  // FFTextureStage3ResultIsTemp\n\n    { 10,  0, 5 },  // FFTextureStage4ColorOp\n    { 10,  5, 5 },  // FFTextureStage4ColorArg1\n    { 10, 10, 5 },  // FFTextureStage4ColorArg2\n    { 10, 15, 5 },  // FFTextureStage4AlphaOp\n    { 10, 20, 5 },  // FFTextureStage4AlphaArg1\n    { 10, 25, 5 },  // FFTextureStage4AlphaArg2\n    { 10, 30, 1 },  // FFTextureStage4ResultIsTemp\n\n    { 11,  0, 5 },  // FFTextureStage5ColorOp\n    { 11,  5, 5 },  // FFTextureStage5ColorArg1\n    { 11, 10, 5 },  // FFTextureStage5ColorArg2\n    { 11, 15, 5 },  // FFTextureStage5AlphaOp\n    { 11, 20, 5 },  // FFTextureStage5AlphaArg1\n    { 11, 25, 5 },  // FFTextureStage5AlphaArg2\n    { 11, 30, 1 },  // FFTextureStage5ResultIsTemp\n\n    { 12,  0, 5 },  // FFTextureStage6ColorOp\n    { 12,  5, 5 },  // FFTextureStage6ColorArg1\n    { 12, 10, 5 },  // FFTextureStage6ColorArg2\n    { 12, 15, 5 },  // FFTextureStage6AlphaOp\n    { 12, 20, 5 },  // FFTextureStage6AlphaArg1\n    { 12, 25, 5 },  // FFTextureStage6AlphaArg2\n    { 12, 30, 1 },  // FFTextureStage6ResultIsTemp\n\n    { 13,  0, 5 },  // FFTextureStage7ColorOp\n    { 13,  5, 5 },  // FFTextureStage7ColorArg1\n    { 13, 10, 5 },  // FFTextureStage7ColorArg2\n    { 13, 15, 5 },  // FFTextureStage7AlphaOp\n    { 13, 20, 5 },  // FFTextureStage7AlphaArg1\n    { 13, 25, 5 },  // FFTextureStage7AlphaArg2\n    { 13, 30, 1 },  // FFTextureStage7ResultIsTemp\n\n    { 14,  0, 5 },  // FFTextureStage0ColorArg0\n    { 14,  5, 5 },  // FFTextureStage1ColorArg0\n    { 14, 10, 5 },  // FFTextureStage2ColorArg0\n    { 14, 15, 5 },  // FFTextureStage3ColorArg0\n    { 14, 20, 5 },  // FFTextureStage4ColorArg0\n    { 14, 25, 5 },  // FFTextureStage5ColorArg0\n\n    { 15,  0, 5 },  // FFTextureStage6ColorArg0\n    { 15,  5, 5 },  // FFTextureStage7ColorArg0\n    { 15, 10, 5 },  // FFTextureStage0AlphaArg0\n    { 15, 15, 5 },  // FFTextureStage1AlphaArg0\n    { 15, 20, 5 },  // FFTextureStage2AlphaArg0\n    { 15, 25, 5 },  // FFTextureStage3AlphaArg0\n\n    { 16,  0, 5 },  // FFTextureStage4AlphaArg0\n    { 16,  5, 5 },  // FFTextureStage5AlphaArg0\n    { 16, 10, 5 },  // FFTextureStage6AlphaArg0\n    { 16, 15, 5 },  // FFTextureStage7AlphaArg0\n};\n\nbool specIsOptimized() {\n    return SpecConstDword20 != 0u;\n}\n\nuint specDword(uint index) {\n    if (!specIsOptimized()) {\n        return dynamicSpecConstDword[index];\n    }\n\n    switch (index) {\n        case 0u:\n            return SpecConstDword0;\n        case 1u:\n            return SpecConstDword1;\n        case 2u:\n            return SpecConstDword2;\n        case 3u:\n            return SpecConstDword3;\n        case 4u:\n            return SpecConstDword4;\n        case 5u:\n            return SpecConstDword5;\n        case 6u:\n            return SpecConstDword6;\n        case 7u:\n            return SpecConstDword7;\n        case 8u:\n            return SpecConstDword8;\n        case 9u:\n            return SpecConstDword9;\n        case 10u:\n            return SpecConstDword10;\n        case 11u:\n            return SpecConstDword11;\n        case 12u:\n            return SpecConstDword12;\n        case 13u:\n            return SpecConstDword13;\n        case 14u:\n            return SpecConstDword14;\n        case 15u:\n            return SpecConstDword15;\n        case 16u:\n            return SpecConstDword16;\n        case 17u:\n            return SpecConstDword17;\n        case 18u:\n            return SpecConstDword18;\n        case 19u:\n            return SpecConstDword19;\n        case 20u:\n            return SpecConstDword20;\n        default:\n            return 0u;\n    }\n}\n\nuint specUint(uint specConstIdx, uint bitOffset, uint bits) {\n    BitfieldPosition pos = SpecConstLayout[specConstIdx];\n    uint dword = specDword(pos.dwordOffset);\n    return bitfieldExtract(dword, int(pos.bitOffset + bitOffset), int(bits));\n}\n\nuint specUint(uint specConstIdx) {\n    BitfieldPosition pos = SpecConstLayout[specConstIdx];\n    uint dword = specDword(pos.dwordOffset);\n    return bitfieldExtract(dword, int(pos.bitOffset), int(pos.sizeInBits));\n}\n\nbool specBool(uint specConstIdx, uint bitOffset) {\n    return specUint(specConstIdx, bitOffset, 1u) != 0u;\n}\n\nbool specBool(uint specConstIdx) {\n    return specUint(specConstIdx) != 0u;\n}\n"
  },
  {
    "path": "src/d3d9/shaders/d3d9_fixed_function_frag.frag",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"d3d9_fixed_function_frag.glsl\"\n"
  },
  {
    "path": "src/d3d9/shaders/d3d9_fixed_function_frag.glsl",
    "content": "#extension GL_GOOGLE_include_directive : enable\n#extension GL_EXT_scalar_block_layout : require\n#extension GL_EXT_spirv_intrinsics : require\n#extension GL_EXT_demote_to_helper_invocation : require\n#extension GL_ARB_derivative_control : require\n#extension GL_EXT_control_flow_attributes : require\n#extension GL_EXT_nonuniform_qualifier : require\n\n\n// The locations need to match with RegisterLinkerSlot in dxso_util.cpp\n#ifndef INTERP_MODE\n#define INTERP_MODE\n#endif\n\nlayout(location = 0) INTERP_MODE in vec4 in_Normal;\nlayout(location = 1) INTERP_MODE in vec4 in_Texcoord0;\nlayout(location = 2) INTERP_MODE in vec4 in_Texcoord1;\nlayout(location = 3) INTERP_MODE in vec4 in_Texcoord2;\nlayout(location = 4) INTERP_MODE in vec4 in_Texcoord3;\nlayout(location = 5) INTERP_MODE in vec4 in_Texcoord4;\nlayout(location = 6) INTERP_MODE in vec4 in_Texcoord5;\nlayout(location = 7) INTERP_MODE in vec4 in_Texcoord6;\nlayout(location = 8) INTERP_MODE in vec4 in_Texcoord7;\nlayout(location = 9) INTERP_MODE in vec4 in_Color0;\nlayout(location = 10) INTERP_MODE in vec4 in_Color1;\nlayout(location = 11) INTERP_MODE in float in_Fog;\n\nlayout(location = 0) out vec4 out_Color0;\n\n\nconst uint TextureArgCount = 3;\nconst uint MaxSharedPushDataSize = 64;\n\n#include \"d3d9_fixed_function_common.glsl\"\n\nstruct D3D9FFTextureStage {\n    uint Primitive[2];\n};\n\nstruct D3D9FixedFunctionPS {\n    vec4 textureFactor;\n    D3D9FFTextureStage Stages[8];\n};\n\nstruct D3D9SharedPSStage {\n    float Constant[4];\n    float BumpEnvMat[2][2];\n    float BumpEnvLScale;\n    float BumpEnvLOffset;\n    float Padding[2];\n};\n\nstruct D3D9SharedPS {\n    D3D9SharedPSStage Stages[TextureStageCount];\n};\n\nconst uint D3DTOP_DISABLE                   = 1;\nconst uint D3DTOP_SELECTARG1                = 2;\nconst uint D3DTOP_SELECTARG2                = 3;\nconst uint D3DTOP_MODULATE                  = 4;\nconst uint D3DTOP_MODULATE2X                = 5;\nconst uint D3DTOP_MODULATE4X                = 6;\nconst uint D3DTOP_ADD                       = 7;\nconst uint D3DTOP_ADDSIGNED                 = 8;\nconst uint D3DTOP_ADDSIGNED2X               = 9;\nconst uint D3DTOP_SUBTRACT                  = 10;\nconst uint D3DTOP_ADDSMOOTH                 = 11;\nconst uint D3DTOP_BLENDDIFFUSEALPHA         = 12;\nconst uint D3DTOP_BLENDTEXTUREALPHA         = 13;\nconst uint D3DTOP_BLENDFACTORALPHA          = 14;\nconst uint D3DTOP_BLENDTEXTUREALPHAPM       = 15;\nconst uint D3DTOP_BLENDCURRENTALPHA         = 16;\nconst uint D3DTOP_PREMODULATE               = 17;\nconst uint D3DTOP_MODULATEALPHA_ADDCOLOR    = 18;\nconst uint D3DTOP_MODULATECOLOR_ADDALPHA    = 19;\nconst uint D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20;\nconst uint D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21;\nconst uint D3DTOP_BUMPENVMAP                = 22;\nconst uint D3DTOP_BUMPENVMAPLUMINANCE       = 23;\nconst uint D3DTOP_DOTPRODUCT3               = 24;\nconst uint D3DTOP_MULTIPLYADD               = 25;\nconst uint D3DTOP_LERP                      = 26;\n\nconst uint D3DTA_SELECTMASK     = 0x0000000f;\nconst uint D3DTA_DIFFUSE        = 0x00000000;\nconst uint D3DTA_CURRENT        = 0x00000001;\nconst uint D3DTA_TEXTURE        = 0x00000002;\nconst uint D3DTA_TFACTOR        = 0x00000003;\nconst uint D3DTA_SPECULAR       = 0x00000004;\nconst uint D3DTA_TEMP           = 0x00000005;\nconst uint D3DTA_CONSTANT       = 0x00000006;\nconst uint D3DTA_COMPLEMENT     = 0x00000010;\nconst uint D3DTA_ALPHAREPLICATE = 0x00000020;\n\nconst uint D3DRTYPE_SURFACE       = 1;\nconst uint D3DRTYPE_VOLUME        = 2;\nconst uint D3DRTYPE_TEXTURE       = 3;\nconst uint D3DRTYPE_VOLUMETEXTURE = 4;\nconst uint D3DRTYPE_CUBETEXTURE   = 5;\nconst uint D3DRTYPE_VERTEXBUFFER  = 6;\nconst uint D3DRTYPE_INDEXBUFFER   = 7;\n\nconst uint VK_COMPARE_OP_NEVER            = 0;\nconst uint VK_COMPARE_OP_LESS             = 1;\nconst uint VK_COMPARE_OP_EQUAL            = 2;\nconst uint VK_COMPARE_OP_LESS_OR_EQUAL    = 3;\nconst uint VK_COMPARE_OP_GREATER          = 4;\nconst uint VK_COMPARE_OP_NOT_EQUAL        = 5;\nconst uint VK_COMPARE_OP_GREATER_OR_EQUAL = 6;\nconst uint VK_COMPARE_OP_ALWAYS           = 7;\n\nconst uint PerTextureStageSpecConsts = SpecFFTextureStage1ColorOp - SpecFFTextureStage0ColorOp;\n\n\n// Bindings have to match with computeResourceSlotId in dxso_util.h\n// computeResourceSlotId(\n//     D3D9ShaderType::PixelShader,\n//     DxsoBindingType::ConstantBuffer,\n//     DxsoConstantBuffers::PSFixedFunction\n// ) = 11\nlayout(set = 0, binding = 11, scalar, row_major) uniform ShaderData {\n    D3D9FixedFunctionPS data;\n};\n\n// Bindings have to match with computeResourceSlotId in dxso_util.h\n// computeResourceSlotId(\n//     D3D9ShaderType::PixelShader,\n//     DxsoBindingType::ConstantBuffer,\n//     DxsoConstantBuffers::PSShared\n// ) = 12\nlayout(set = 0, binding = 12, scalar, row_major) uniform SharedData {\n    D3D9SharedPS sharedData;\n};\n\nlayout(push_constant, scalar, row_major) uniform RenderStates {\n    D3D9RenderStateInfo rs;\n\n    layout(offset = MaxSharedPushDataSize) uint packedSamplerIndices[TextureStageCount / 2];\n};\n\nlayout(set = 0, binding = 13) uniform texture2D t2d[TextureStageCount];\nlayout(set = 0, binding = 13) uniform textureCube tcube[TextureStageCount];\nlayout(set = 0, binding = 13) uniform texture3D t3d[TextureStageCount];\n\nlayout(set = 1, binding = 0) uniform sampler sampler_heap[];\n\n\n// Functions to extract information from the packed texture stages\nuint colorOp(uint stageIndex) {\n    return bitfieldExtract(data.Stages[stageIndex].Primitive[0], 0, 5);\n}\nuint colorArg0(uint stageIndex) {\n    return bitfieldExtract(data.Stages[stageIndex].Primitive[0], 5, 6);\n}\nuint colorArg1(uint stageIndex) {\n    return bitfieldExtract(data.Stages[stageIndex].Primitive[0], 11, 6);\n}\nuint colorArg2(uint stageIndex) {\n    return bitfieldExtract(data.Stages[stageIndex].Primitive[0], 17, 6);\n}\n\nuint alphaOp(uint stageIndex) {\n    return bitfieldExtract(data.Stages[stageIndex].Primitive[0], 23, 5);\n}\nuint alphaArg0(uint stageIndex) {\n    return bitfieldExtract(data.Stages[stageIndex].Primitive[1], 0, 6);\n}\nuint alphaArg1(uint stageIndex) {\n    return bitfieldExtract(data.Stages[stageIndex].Primitive[1], 6, 6);\n}\nuint alphaArg2(uint stageIndex) {\n    return bitfieldExtract(data.Stages[stageIndex].Primitive[1], 12, 6);\n}\n\nbool resultIsTemp(uint stageIndex) {\n    return bitfieldExtract(data.Stages[stageIndex].Primitive[1], 18, 1) != 0;\n}\n\n\nvec4 calculateFog(vec4 vPos, vec4 oColor) {\n    vec3 fogColor = vec3(rs.fogColor[0], rs.fogColor[1], rs.fogColor[2]);\n    float fogScale = rs.fogScale;\n    float fogEnd = rs.fogEnd;\n    float fogDensity = rs.fogDensity;\n    D3DFOGMODE fogMode = specUint(SpecPixelFogMode);\n    bool fogEnabled = specBool(SpecFogEnabled);\n    if (!fogEnabled) {\n        return oColor;\n    }\n\n    float w = vPos.w;\n    float z = vPos.z;\n    float depth = z * (1.0 / w);\n    float fogFactor;\n    switch (fogMode) {\n        case D3DFOG_NONE:\n            fogFactor = in_Fog;\n            break;\n\n        // (end - d) / (end - start)\n        case D3DFOG_LINEAR:\n            fogFactor = fogEnd - depth;\n            fogFactor = fogFactor * fogScale;\n            fogFactor = spvNClamp(fogFactor, 0.0, 1.0);\n            break;\n\n        // 1 / (e^[d * density])^2\n        case D3DFOG_EXP2:\n        // 1 / (e^[d * density])\n        case D3DFOG_EXP:\n            fogFactor = depth * fogDensity;\n\n            if (fogMode == D3DFOG_EXP2)\n                fogFactor *= fogFactor;\n\n            // Provides the rcp.\n            fogFactor = -fogFactor;\n            fogFactor = exp(fogFactor);\n            break;\n    }\n\n    vec4 color = oColor;\n    vec3 color3 = color.rgb;\n    vec3 fogFact3 = vec3(fogFactor);\n    vec3 lerpedFrog = mix(fogColor, color3, fogFact3);\n    return vec4(lerpedFrog.r, lerpedFrog.g, lerpedFrog.b, color.a);\n}\n\n\n// [D3D8] Scale Dref to [0..(2^N - 1)] for D24S8 and D16 if Dref scaling is enabled\nfloat adjustDref(float reference, uint samplerIndex) {\n    uint drefScaleFactor = specUint(SpecDrefScaling);\n    if (drefScaleFactor != 0) {\n        float maxDref = 1.0 / (float(1 << drefScaleFactor) - 1.0);\n        reference *= maxDref;\n    }\n    if (specBool(SpecSamplerDrefClamp, samplerIndex)) {\n        reference = clamp(reference, 0.0, 1.0);\n    }\n    return reference;\n}\n\n\nvec4 calculateBumpmapCoords(uint stage, vec4 baseCoords, vec4 previousStageTextureVal) {\n    uint previousStage = stage - 1;\n\n    vec4 coords = baseCoords;\n    [[unroll]]\n    for (uint i = 0; i < 2; i++) {\n        float tc_m_n = coords[i];\n        vec2 bm = vec2(sharedData.Stages[previousStage].BumpEnvMat[i][0], sharedData.Stages[previousStage].BumpEnvMat[i][1]);\n        vec2 t = previousStageTextureVal.xy;\n        float result = tc_m_n + dot(bm, t);\n        coords[i] = result;\n    }\n    return coords;\n}\n\n\nuint loadSamplerHeapIndex(uint samplerBindingIndex) {\n    uint packedSamplerIndex = packedSamplerIndices[samplerBindingIndex / 2u];\n    return bitfieldExtract(packedSamplerIndex, 16 * (int(samplerBindingIndex) & 1), 16);\n}\n\n\nvec4 sampleTexture(uint stage, vec4 texcoord, vec4 previousStageTextureVal) {\n    if (specBool(SpecSamplerProjected, stage)) {\n        texcoord /= texcoord.w;\n    }\n\n    uint previousStageColorOp = 0;\n    if (stage > 0) {\n        previousStageColorOp = specIsOptimized() ? specUint(SpecFFTextureStage0ColorOp + PerTextureStageSpecConsts * (stage - 1)) : colorOp(stage - 1);\n    }\n\n    if (stage != 0 && (\n        previousStageColorOp == D3DTOP_BUMPENVMAP\n        || previousStageColorOp == D3DTOP_BUMPENVMAPLUMINANCE)) {\n        texcoord = calculateBumpmapCoords(stage, texcoord, previousStageTextureVal);\n    }\n\n    vec4 texVal;\n    uint textureType = D3DRTYPE_TEXTURE + specUint(SpecSamplerType, 2u * stage, 2u);\n    switch (textureType) {\n        case D3DRTYPE_TEXTURE:\n            if (specBool(SpecSamplerDepthMode, stage)) {\n                texcoord.z = adjustDref(texcoord.z, stage);\n                texVal = texture(sampler2DShadow(t2d[stage], sampler_heap[loadSamplerHeapIndex(stage)]), texcoord.xyz).xxxx;\n            } else {\n                texVal = texture(sampler2D(t2d[stage], sampler_heap[loadSamplerHeapIndex(stage)]), texcoord.xy);\n            }\n            break;\n        case D3DRTYPE_CUBETEXTURE:\n            if (specBool(SpecSamplerDepthMode, stage)) {\n                texcoord.w = adjustDref(texcoord.w, stage);\n                texVal = texture(samplerCubeShadow(tcube[stage], sampler_heap[loadSamplerHeapIndex(stage)]), texcoord).xxxx;\n            } else {\n                texVal = texture(samplerCube(tcube[stage], sampler_heap[loadSamplerHeapIndex(stage)]), texcoord.xyz);\n            }\n            break;\n        case D3DRTYPE_VOLUMETEXTURE:\n            texVal = texture(sampler3D(t3d[stage], sampler_heap[loadSamplerHeapIndex(stage)]), texcoord.xyz);\n            break;\n        default:\n            // This should never happen unless there's a major bug in the API implementation.\n            // Produce a value that's obviously wrong to make it obvious when it somehow does happen.\n            texVal = vec4(999.9);\n            break;\n    }\n\n    if (stage != 0 && previousStageColorOp == D3DTOP_BUMPENVMAPLUMINANCE) {\n        float lScale = sharedData.Stages[stage - 1].BumpEnvLScale;\n        float lOffset = sharedData.Stages[stage - 1].BumpEnvLOffset;\n        float scale = texVal.z;\n        scale *= lScale;\n        scale += lOffset;\n        scale = clamp(scale, 0.0, 1.0);\n        texVal *= scale;\n    }\n\n    return texVal;\n}\n\n\nvec4 readArgValue(uint stage, uint arg, vec4 current, vec4 temp, vec4 textureVal) {\n    vec4 reg = vec4(1.0);\n    switch (arg & D3DTA_SELECTMASK) {\n        case D3DTA_CONSTANT:\n            reg = vec4(\n                sharedData.Stages[stage].Constant[0],\n                sharedData.Stages[stage].Constant[1],\n                sharedData.Stages[stage].Constant[2],\n                sharedData.Stages[stage].Constant[3]\n            );\n            break;\n        case D3DTA_CURRENT:\n            reg = current;\n            break;\n        case D3DTA_DIFFUSE:\n            reg = in_Color0;\n            break;\n        case D3DTA_SPECULAR:\n            reg = in_Color1;\n            break;\n        case D3DTA_TEMP:\n            reg = temp;\n            break;\n        case D3DTA_TEXTURE:\n            reg = textureVal;\n            break;\n        case D3DTA_TFACTOR:\n            reg = data.textureFactor;\n            break;\n    }\n\n    // reg = 1 - reg\n    if ((arg & D3DTA_COMPLEMENT) != 0)\n        reg = vec4(1.0) - reg;\n\n    // reg = reg.wwww\n    if ((arg & D3DTA_ALPHAREPLICATE) != 0)\n        reg = reg.aaaa;\n\n    return reg;\n}\n\nstruct TextureStageArguments {\n    uint arg0;\n    uint arg1;\n    uint arg2;\n};\n\nstruct TextureStageArgumentValues {\n    vec4 arg0;\n    vec4 arg1;\n    vec4 arg2;\n};\n\nTextureStageArgumentValues readArgValues(uint stage, const TextureStageArguments args, vec4 current, vec4 temp, vec4 textureVal) {\n    TextureStageArgumentValues argVals;\n    argVals.arg0 = readArgValue(stage, args.arg0, current, temp, textureVal);\n    argVals.arg1 = readArgValue(stage, args.arg1, current, temp, textureVal);\n    argVals.arg2 = readArgValue(stage, args.arg2, current, temp, textureVal);\n    return argVals;\n}\n\nuint repackArg(uint arg) {\n    // Move the flags by 1 bit. 0x18 = 0b11000\n    return (arg & ~0x18) | ((arg & 0x18) << 1u);\n}\n\nvec4 complement(vec4 val) {\n    return vec4(1.0) - val;\n}\n\nvec4 saturate(vec4 val) {\n    return clamp(val, vec4(0.0), vec4(1.0));\n}\n\nvec4 calculateTextureStage(uint op, vec4 dst, const TextureStageArgumentValues arg, vec4 current, vec4 textureVal) {\n    switch (op) {\n        case D3DTOP_SELECTARG1:\n            return arg.arg1;\n\n        case D3DTOP_SELECTARG2:\n            return arg.arg2;\n\n        case D3DTOP_MODULATE4X:\n            return arg.arg1 * arg.arg2 * 4.0;\n\n        case D3DTOP_MODULATE2X:\n            return arg.arg1 * arg.arg2 * 2.0;\n\n        case D3DTOP_MODULATE:\n            return arg.arg1 * arg.arg2;\n\n        case D3DTOP_ADDSIGNED2X:\n            return saturate(2.0 * (arg.arg1 + (arg.arg2 - vec4(0.5))));\n\n        case D3DTOP_ADDSIGNED:\n            return saturate(arg.arg1 + (arg.arg2 - vec4(0.5)));\n\n        case D3DTOP_ADD:\n            return saturate(arg.arg1 + arg.arg2);\n\n        case D3DTOP_SUBTRACT:\n            return saturate(arg.arg1 - arg.arg2);\n\n        case D3DTOP_ADDSMOOTH:\n            return fma(complement(arg.arg1), arg.arg2, arg.arg1);\n\n        case D3DTOP_BLENDDIFFUSEALPHA:\n            return mix(arg.arg2, arg.arg1, in_Color0.aaaa);\n\n        case D3DTOP_BLENDTEXTUREALPHA:\n            return mix(arg.arg2, arg.arg1, textureVal.aaaa);\n\n        case D3DTOP_BLENDFACTORALPHA:\n            return mix(arg.arg2, arg.arg1, data.textureFactor.aaaa);\n\n        case D3DTOP_BLENDTEXTUREALPHAPM:\n            return saturate(fma(arg.arg2, complement(textureVal.aaaa), arg.arg1));\n\n        case D3DTOP_BLENDCURRENTALPHA:\n            return mix(arg.arg2, arg.arg1, current.aaaa);\n\n        case D3DTOP_PREMODULATE:\n            return dst; // Not implemented\n\n        case D3DTOP_MODULATEALPHA_ADDCOLOR:\n            return saturate(fma(arg.arg1.aaaa, arg.arg2, arg.arg1));\n\n        case D3DTOP_MODULATECOLOR_ADDALPHA:\n            return saturate(fma(arg.arg1, arg.arg2, arg.arg1.aaaa));\n\n        case D3DTOP_MODULATEINVALPHA_ADDCOLOR:\n            return saturate(fma(complement(arg.arg1.aaaa), arg.arg2, arg.arg1));\n\n        case D3DTOP_MODULATEINVCOLOR_ADDALPHA:\n            return saturate(fma(complement(arg.arg1), arg.arg2, arg.arg1.aaaa));\n\n        case D3DTOP_BUMPENVMAPLUMINANCE:\n        case D3DTOP_BUMPENVMAP:\n            // Load texture for the next stage...\n            return dst;\n\n        case D3DTOP_DOTPRODUCT3:\n            return saturate(vec4(dot(arg.arg1.rgb - vec3(0.5), arg.arg2.rgb - vec3(0.5)) * 4.0));\n\n        case D3DTOP_MULTIPLYADD:\n            return saturate(fma(arg.arg1, arg.arg2, arg.arg0));\n\n        case D3DTOP_LERP:\n            return mix(arg.arg2, arg.arg1, arg.arg0);\n\n        default:\n            // Unhandled texture op!\n            return dst;\n\n    }\n\n    return vec4(0.0);\n}\n\n\nvoid alphaTest() {\n    uint alphaFunc = specUint(SpecAlphaCompareOp);\n    uint alphaPrecision = specUint(SpecAlphaPrecisionBits);\n    uint alphaRefInitial = rs.alphaRef;\n    float alphaRef;\n    float alpha = out_Color0.a;\n\n    if (alphaFunc == VK_COMPARE_OP_ALWAYS) {\n        return;\n    }\n\n    // Check if the given bit precision is supported\n    bool useIntPrecision = alphaPrecision <= 8;\n    if (useIntPrecision) {\n        // Adjust alpha ref to the given range\n        uint alphaRefInt = (alphaRefInitial << alphaPrecision) | (alphaRefInitial >> (8 - alphaPrecision));\n\n        // Convert alpha ref to float since we'll do the comparison based on that\n        alphaRef = float(alphaRefInt);\n\n        // Adjust alpha to the given range and round\n        float alphaFactor = float((256u << alphaPrecision) - 1u);\n\n        alpha = round(alpha * alphaFactor);\n    } else {\n        alphaRef = float(alphaRefInitial) / 255.0;\n    }\n\n    bool atestResult;\n    switch (alphaFunc) {\n        case VK_COMPARE_OP_NEVER:\n            atestResult = false;\n            break;\n\n        case VK_COMPARE_OP_LESS:\n            atestResult = alpha < alphaRef;\n            break;\n\n        case VK_COMPARE_OP_EQUAL:\n            atestResult = alpha == alphaRef;\n            break;\n\n        case VK_COMPARE_OP_LESS_OR_EQUAL:\n            atestResult = alpha <= alphaRef;\n            break;\n\n        case VK_COMPARE_OP_GREATER:\n            atestResult = alpha > alphaRef;\n            break;\n\n        case VK_COMPARE_OP_NOT_EQUAL:\n            atestResult = alpha != alphaRef;\n            break;\n\n        case VK_COMPARE_OP_GREATER_OR_EQUAL:\n            atestResult = alpha >= alphaRef;\n            break;\n\n        default:\n        case VK_COMPARE_OP_ALWAYS:\n            atestResult = true;\n            break;\n    }\n\n    bool atestDiscard = !atestResult;\n    if (atestDiscard) {\n        demote;\n    }\n}\n\nstruct TextureStageState {\n    vec4 current;\n    vec4 temp;\n    vec4 previousStageTextureVal;\n};\n\nTextureStageState runTextureStage(uint stage, TextureStageState state) {\n    if (stage > specUint(SpecFFLastActiveTextureStage)) {\n        return state;\n    }\n\n    const uint colorOp = specIsOptimized() ? specUint(SpecFFTextureStage0ColorOp + PerTextureStageSpecConsts * stage) : colorOp(stage);\n\n    // This cancels all subsequent stages.\n    if (colorOp == D3DTOP_DISABLE)\n        return state;\n\n    const bool resultIsTemp = specIsOptimized() ? specBool(SpecFFTextureStage0ResultIsTemp + PerTextureStageSpecConsts * stage) : resultIsTemp(stage);\n    vec4 dst = resultIsTemp ? state.temp : state.current;\n\n    const uint alphaOp = specIsOptimized() ? specUint(SpecFFTextureStage0AlphaOp + PerTextureStageSpecConsts * stage) : alphaOp(stage);\n\n    const TextureStageArguments colorArgs = {\n        // Color arg0 and alpha arg0 for all stages are packed after all the other FF spec consts\n        specIsOptimized() ? repackArg(specUint(SpecFFTextureStage0ColorArg0 + stage))                             : colorArg0(stage),\n        specIsOptimized() ? repackArg(specUint(SpecFFTextureStage0ColorArg1 + PerTextureStageSpecConsts * stage)) : colorArg1(stage),\n        specIsOptimized() ? repackArg(specUint(SpecFFTextureStage0ColorArg2 + PerTextureStageSpecConsts * stage)) : colorArg2(stage)\n    };\n    const TextureStageArguments alphaArgs = {\n        // Color arg0 and alpha arg0 for all stages are packed after all the other FF spec consts\n        specIsOptimized() ? repackArg(specUint(SpecFFTextureStage0AlphaArg0 + stage))                             : alphaArg0(stage),\n        specIsOptimized() ? repackArg(specUint(SpecFFTextureStage0AlphaArg1 + PerTextureStageSpecConsts * stage)) : alphaArg1(stage),\n        specIsOptimized() ? repackArg(specUint(SpecFFTextureStage0AlphaArg2 + PerTextureStageSpecConsts * stage)) : alphaArg2(stage)\n    };\n\n    vec4 textureVal = vec4(0.0);\n    bool usesTexture = (colorArgs.arg0 & D3DTA_SELECTMASK) == D3DTA_TEXTURE\n        || (colorArgs.arg1 & D3DTA_SELECTMASK) == D3DTA_TEXTURE\n        || (colorArgs.arg2 & D3DTA_SELECTMASK) == D3DTA_TEXTURE\n        || (alphaArgs.arg0 & D3DTA_SELECTMASK) == D3DTA_TEXTURE\n        || (alphaArgs.arg1 & D3DTA_SELECTMASK) == D3DTA_TEXTURE\n        || (alphaArgs.arg2 & D3DTA_SELECTMASK) == D3DTA_TEXTURE;\n\n    if (usesTexture) {\n        // We need to replace TEXCOORD inputs with gl_PointCoord\n        // if D3DRS_POINTSPRITEENABLE is set.\n        const uint pointMode = specUint(SpecPointMode);\n        const bool isSprite = bitfieldExtract(pointMode, 1, 1) == 1u;\n\n        vec4 texCoord;\n        if (isSprite) {\n            texCoord = vec4(gl_PointCoord, 0.0, 0.0);\n        } else {\n            switch (stage) {\n                case 0: texCoord = in_Texcoord0; break;\n                case 1: texCoord = in_Texcoord1; break;\n                case 2: texCoord = in_Texcoord2; break;\n                case 3: texCoord = in_Texcoord3; break;\n                case 4: texCoord = in_Texcoord4; break;\n                case 5: texCoord = in_Texcoord5; break;\n                case 6: texCoord = in_Texcoord6; break;\n                case 7: texCoord = in_Texcoord7; break;\n            }\n        }\n        const vec4 unboundTextureConst = vec4(0.0, 0.0, 0.0, 1.0);\n        textureVal = !specBool(SpecSamplerNull, stage) ? sampleTexture(stage, texCoord, state.previousStageTextureVal) : unboundTextureConst;\n    }\n\n    // Fast path if alpha/color path is identical.\n    // D3DTOP_DOTPRODUCT3 also has special quirky behaviour here.\n    const bool fastPath = colorOp == alphaOp && colorArgs == alphaArgs;\n    if (fastPath || colorOp == D3DTOP_DOTPRODUCT3) {\n        TextureStageArgumentValues colorArgVals = readArgValues(stage, colorArgs, state.current, state.temp, textureVal);\n        dst = calculateTextureStage(colorOp, dst, colorArgVals, state.current, textureVal);\n    } else {\n        vec4 colorResult = dst;\n        vec4 alphaResult = dst;\n\n        TextureStageArgumentValues colorArgVals = readArgValues(stage, colorArgs, state.current, state.temp, textureVal);\n        colorResult = calculateTextureStage(colorOp, dst, colorArgVals, state.current, textureVal);\n\n        if (alphaOp != D3DTOP_DISABLE) {\n            TextureStageArgumentValues alphaArgVals = readArgValues(stage, alphaArgs, state.current, state.temp, textureVal);\n            alphaResult = calculateTextureStage(alphaOp, dst, alphaArgVals, state.current, textureVal);\n        }\n\n        dst.xyz = colorResult.xyz;\n\n        // src0.x, src0.y, src0.z src1.w\n        if (alphaOp != D3DTOP_DISABLE) {\n            dst.a = alphaResult.a;\n        }\n    }\n\n    if (resultIsTemp) {\n        state.temp = dst;\n    } else {\n        state.current = dst;\n    }\n    state.previousStageTextureVal = textureVal;\n\n    return state;\n}\n\nvoid main() {\n    // in_Color0 is diffuse\n    // in_Color1 is specular\n\n    TextureStageState state;\n    // Current starts of as equal to diffuse.\n    state.current = in_Color0;\n    // Temp starts off as equal to vec4(0)\n    state.temp = vec4(0.0);\n    state.previousStageTextureVal = vec4(0.0);\n\n    // If we turn this into a loop, performance becomes very poor on the proprietary Nvidia driver\n    // because it fails to unroll it.\n    state = runTextureStage(0, state);\n    state = runTextureStage(1, state);\n    state = runTextureStage(2, state);\n    state = runTextureStage(3, state);\n    state = runTextureStage(4, state);\n    state = runTextureStage(5, state);\n    state = runTextureStage(6, state);\n    state = runTextureStage(7, state);\n\n    if (specBool(SpecFFGlobalSpecularEnabled)) {\n        state.current.xyz += in_Color1.xyz;\n    }\n\n    state.current = calculateFog(gl_FragCoord, state.current);\n\n    out_Color0 = state.current;\n\n    alphaTest();\n}\n"
  },
  {
    "path": "src/d3d9/shaders/d3d9_fixed_function_frag_sample.frag",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n\n#define INTERP_MODE sample\n\n#include \"d3d9_fixed_function_frag.glsl\"\n"
  },
  {
    "path": "src/d3d9/shaders/d3d9_fixed_function_vert.vert",
    "content": "#version 450\n#extension GL_GOOGLE_include_directive : enable\n#extension GL_EXT_scalar_block_layout : require\n#extension GL_EXT_spirv_intrinsics : require\n\nlayout(location = 0) in vec4 in_Position0;\nlayout(location = 1) in vec4 in_Normal0;\nlayout(location = 2) in vec4 in_Position1;\nlayout(location = 3) in vec4 in_Normal1;\nlayout(location = 4) in vec4 in_Texcoord0;\nlayout(location = 5) in vec4 in_Texcoord1;\nlayout(location = 6) in vec4 in_Texcoord2;\nlayout(location = 7) in vec4 in_Texcoord3;\nlayout(location = 8) in vec4 in_Texcoord4;\nlayout(location = 9) in vec4 in_Texcoord5;\nlayout(location = 10) in vec4 in_Texcoord6;\nlayout(location = 11) in vec4 in_Texcoord7;\nlayout(location = 12) in vec4 in_Color0;\nlayout(location = 13) in vec4 in_Color1;\nlayout(location = 14) in float in_Fog;\nlayout(location = 15) in float in_PointSize;\nlayout(location = 16) in vec4 in_BlendWeight;\nlayout(location = 17) in vec4 in_BlendIndices;\n\n\n// The locations need to match with RegisterLinkerSlot in dxso_util.cpp\nprecise gl_Position;\nconst uint MaxClipPlaneCount = 6;\nout float gl_ClipDistance[MaxClipPlaneCount];\nlayout(location = 0) out vec4 out_Normal;\nlayout(location = 1) out vec4 out_Texcoord0;\nlayout(location = 2) out vec4 out_Texcoord1;\nlayout(location = 3) out vec4 out_Texcoord2;\nlayout(location = 4) out vec4 out_Texcoord3;\nlayout(location = 5) out vec4 out_Texcoord4;\nlayout(location = 6) out vec4 out_Texcoord5;\nlayout(location = 7) out vec4 out_Texcoord6;\nlayout(location = 8) out vec4 out_Texcoord7;\nlayout(location = 9) out vec4 out_Color0;\nlayout(location = 10) out vec4 out_Color1;\nlayout(location = 11) out float out_Fog;\n\n\n#include \"d3d9_fixed_function_common.glsl\"\n\nconst uint MaxEnabledLights = 8;\n\nstruct D3D9ViewportInfo {\n    vec4 inverseOffset;\n    vec4 inverseExtent;\n};\n\n#define D3DLIGHTTYPE uint\nconst uint D3DLIGHT_POINT       = 1;\nconst uint D3DLIGHT_SPOT        = 2;\nconst uint D3DLIGHT_DIRECTIONAL = 3;\n\nstruct D3D9Light {\n    vec4 Diffuse;\n    vec4 Specular;\n    vec4 Ambient;\n\n    vec4 Position;\n    vec4 Direction;\n\n    D3DLIGHTTYPE Type;\n    float Range;\n    float Falloff;\n    float Attenuation0;\n    float Attenuation1;\n    float Attenuation2;\n    float Theta;\n    float Phi;\n};\n\n#define D3DCOLORVALUE vec4\n\nstruct D3DMATERIAL9 {\n    D3DCOLORVALUE   Diffuse;\n    D3DCOLORVALUE   Ambient;\n    D3DCOLORVALUE   Specular;\n    D3DCOLORVALUE   Emissive;\n    float           Power;\n};\n\nstruct D3D9FixedFunctionVS {\n    mat4 WorldView;\n    mat4 NormalMatrix;\n    mat4 InverseView;\n    mat4 Projection;\n\n    mat4 TexcoordMatrices[TextureStageCount];\n\n    D3D9ViewportInfo ViewportInfo;\n\n    vec4 GlobalAmbient;\n    D3D9Light Lights[MaxEnabledLights];\n    D3DMATERIAL9 Material;\n    float TweenFactor;\n\n    uint KeyPrimitives[4];\n};\n\n#define D3D9FF_VertexBlendMode uint\nconst uint D3D9FF_VertexBlendMode_Disabled = 0;\nconst uint D3D9FF_VertexBlendMode_Normal   = 1;\nconst uint D3D9FF_VertexBlendMode_Tween    = 2;\n\n#define D3DMATERIALCOLORSOURCE uint\nconst uint D3DMCS_MATERIAL = 0;\nconst uint D3DMCS_COLOR1   = 1;\nconst uint D3DMCS_COLOR2   = 2;\n\n#define D3DTEXTURETRANSFORMFLAGS uint\nconst uint D3DTTFF_DISABLE   = 0;\nconst uint D3DTTFF_COUNT1    = 1;\nconst uint D3DTTFF_COUNT2    = 2;\nconst uint D3DTTFF_COUNT3    = 3;\nconst uint D3DTTFF_COUNT4    = 4;\nconst uint D3DTTFF_PROJECTED = 256;\n\nconst uint DXVK_TSS_TCI_PASSTHRU                    = 0x00000000;\nconst uint DXVK_TSS_TCI_CAMERASPACENORMAL           = 0x00010000;\nconst uint DXVK_TSS_TCI_CAMERASPACEPOSITION         = 0x00020000;\nconst uint DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR = 0x00030000;\nconst uint DXVK_TSS_TCI_SPHEREMAP                   = 0x00040000;\n\nconst uint TCIOffset = 16;\nconst uint TCIMask = (7 << TCIOffset);\n\n\n// Bindings have to match with computeResourceSlotId in dxso_util.h\n// computeResourceSlotId(\n//     D3D9ShaderType::VertexShader,\n//     DxsoBindingType::ConstantBuffer,\n//     DxsoConstantBuffers::VSFixedFunction\n// ) = 4\nlayout(set = 0, binding = 4, scalar, row_major) uniform ShaderData {\n    D3D9FixedFunctionVS data;\n};\n\nlayout(push_constant, scalar, row_major) uniform RenderStates {\n    D3D9RenderStateInfo rs;\n};\n\n// Bindings have to match with computeResourceSlotId in dxso_util.h\n// computeResourceSlotId(\n//     D3D9ShaderType::VertexShader,\n//     DxsoBindingType::ConstantBuffer,\n//     DxsoConstantBuffers::VSVertexBlendData\n// ) = 5\nlayout(set = 0, binding = 5, std140, row_major) readonly buffer VertexBlendData {\n    mat4 WorldViewArray[];\n};\n\n\n// Bindings have to match with computeResourceSlotId in dxso_util.h\n// computeResourceSlotId(\n//     D3D9ShaderType::VertexShader,\n//     DxsoBindingType::ConstantBuffer,\n//     DxsoConstantBuffers::VSClipPlanes\n// ) = 3\nlayout(set = 0, binding = 3, std140) uniform ClipPlanes {\n    vec4 clipPlanes[MaxClipPlaneCount];\n};\n\n\n// Functions to extract information from the packed VS key\n// See D3D9FFShaderKeyVSData in d3d9_shader_types.h\n// Please, dearest compiler, inline all of this.\nuint texcoordIndices() {\n    return bitfieldExtract(data.KeyPrimitives[0], 0, 24);\n}\nbool vertexHasPositionT() {\n    return bitfieldExtract(data.KeyPrimitives[0], 24, 1) != 0;\n}\nbool vertexHasColor0() {\n    return bitfieldExtract(data.KeyPrimitives[0], 25, 1) != 0;\n}\nbool vertexHasColor1() {\n    return bitfieldExtract(data.KeyPrimitives[0], 26, 1) != 0;\n}\nbool vertexHasPointSize() {\n    return bitfieldExtract(data.KeyPrimitives[0], 27, 1) != 0;\n}\nbool useLighting() {\n    return bitfieldExtract(data.KeyPrimitives[0], 28, 1) != 0;\n}\nbool normalizeNormals() {\n    return bitfieldExtract(data.KeyPrimitives[0], 29, 1) != 0;\n}\nbool localViewer() {\n    return bitfieldExtract(data.KeyPrimitives[0], 30, 1) != 0;\n}\nbool rangeFog() {\n    return bitfieldExtract(data.KeyPrimitives[0], 31, 1) != 0;\n}\n\nuint texcoordFlags() {\n    return bitfieldExtract(data.KeyPrimitives[1], 0, 24);\n}\nuint diffuseSource() {\n    return bitfieldExtract(data.KeyPrimitives[1], 24, 2);\n}\nuint ambientSource() {\n    return bitfieldExtract(data.KeyPrimitives[1], 26, 2);\n}\nuint specularSource() {\n    return bitfieldExtract(data.KeyPrimitives[1], 28, 2);\n}\nuint emissiveSource() {\n    return bitfieldExtract(data.KeyPrimitives[1], 30, 2);\n}\n\nuint transformFlags() {\n    return bitfieldExtract(data.KeyPrimitives[2], 0, 24);\n}\nuint lightCount() {\n    return bitfieldExtract(data.KeyPrimitives[2], 24, 4);\n}\nbool specularEnabled() {\n    return bitfieldExtract(data.KeyPrimitives[2], 28, 1) != 0;\n}\n\nuint vertexTexcoordDeclMask() {\n    return bitfieldExtract(data.KeyPrimitives[3], 0, 24);\n}\nbool vertexHasFog() {\n    return bitfieldExtract(data.KeyPrimitives[3], 24, 1) != 0;\n}\nD3D9FF_VertexBlendMode blendMode() {\n    return bitfieldExtract(data.KeyPrimitives[3], 25, 2);\n}\nbool vertexBlendIndexed() {\n    return bitfieldExtract(data.KeyPrimitives[3], 27, 1) != 0;\n}\nuint vertexBlendCount() {\n    return bitfieldExtract(data.KeyPrimitives[3], 28, 2);\n}\nbool vertexClipping() {\n    return bitfieldExtract(data.KeyPrimitives[3], 30, 1) != 0;\n}\n\n\nfloat calculateFog(vec4 vPos, vec4 oColor) {\n    vec4 specular = in_Color1;\n    bool hasSpecular = vertexHasColor1();\n\n    vec3 fogColor = vec3(rs.fogColor[0], rs.fogColor[1], rs.fogColor[2]);\n    float fogScale = rs.fogScale;\n    float fogEnd = rs.fogEnd;\n    float fogDensity = rs.fogDensity;\n    D3DFOGMODE fogMode = specUint(SpecVertexFogMode);\n    bool fogEnabled = specBool(SpecFogEnabled);\n    if (!fogEnabled) {\n        return 0.0;\n    }\n\n    float w = vPos.w;\n    float z = vPos.z;\n    float depth;\n    if (rangeFog()) {\n        vec3 pos3 = vPos.xyz;\n        depth = length(pos3);\n    } else {\n        depth = vertexHasFog() ? in_Fog : abs(z);\n    }\n    float fogFactor;\n    if (vertexHasPositionT()) {\n        fogFactor = hasSpecular ? specular.w : 1.0;\n    } else {\n        switch (fogMode) {\n            case D3DFOG_NONE:\n                fogFactor = hasSpecular ? specular.w : 1.0;\n                break;\n\n            // (end - d) / (end - start)\n            case D3DFOG_LINEAR:\n                fogFactor = fogEnd - depth;\n                fogFactor = fogFactor * fogScale;\n                fogFactor = spvNClamp(fogFactor, 0.0, 1.0);\n                break;\n\n            // 1 / (e^[d * density])^2\n            case D3DFOG_EXP2:\n            // 1 / (e^[d * density])\n            case D3DFOG_EXP:\n                fogFactor = depth * fogDensity;\n\n                if (fogMode == D3DFOG_EXP2)\n                    fogFactor *= fogFactor;\n\n                // Provides the rcp.\n                fogFactor = -fogFactor;\n                fogFactor = exp(fogFactor);\n                break;\n        }\n    }\n\n    return fogFactor;\n}\n\n\nfloat calculatePointSize(vec4 vtx) {\n    float value = vertexHasPointSize() ? in_PointSize : rs.pointSize;\n    uint pointMode = specUint(SpecPointMode);\n    bool isScale = bitfieldExtract(pointMode, 0, 1) != 0;\n    float scaleC = rs.pointScaleC;\n    float scaleB = rs.pointScaleB;\n    float scaleA = rs.pointScaleA;\n\n    vec3 vtx3 = vtx.xyz;\n\n    float DeSqr = dot(vtx3, vtx3);\n    float De    = sqrt(DeSqr);\n    float scaleValue = scaleC * DeSqr;\n    scaleValue = fma(scaleB, De, scaleValue);\n    scaleValue += scaleA;\n    scaleValue = sqrt(scaleValue);\n    scaleValue = value / scaleValue;\n\n    value = isScale ? scaleValue : value;\n\n    float pointSizeMin = rs.pointSizeMin;\n    float pointSizeMax = rs.pointSizeMax;\n\n    return clamp(value, pointSizeMin, pointSizeMax);\n}\n\n\nvoid emitVsClipping(vec4 vtx) {\n    vec4 worldPos = data.InverseView * vtx;\n\n    // Always consider clip planes enabled when doing GPL by forcing 6 for the quick value.\n    uint clipPlaneCount = specUint(SpecClipPlaneCount);\n\n    // Compute clip distances\n    for (uint i = 0; i < MaxClipPlaneCount; i++) {\n        vec4 clipPlane = clipPlanes[i];\n        float dist = dot(worldPos, clipPlane);\n        bool clipPlaneEnabled = i < clipPlaneCount;\n        float value = clipPlaneEnabled ? dist : 0.0;\n        gl_ClipDistance[i] = value;\n    }\n}\n\n\nvec4 pickMaterialSource(uint source, vec4 material) {\n    if (source == D3DMCS_COLOR1 && vertexHasColor0())\n        return in_Color0;\n    else if (source == D3DMCS_COLOR2 && vertexHasColor1())\n        return in_Color1;\n    else\n        return material;\n}\n\n\nvoid main() {\n    vec4 vtx = in_Position0;\n    gl_Position = in_Position0;\n    vec3 normal = in_Normal0.xyz;\n\n    if (blendMode() == D3D9FF_VertexBlendMode_Tween) {\n        vec4 vtx1 = in_Position1;\n        vec3 normal1 = in_Normal1.xyz;\n        vtx = mix(vtx, vtx1, data.TweenFactor);\n        normal = mix(normal, normal1, data.TweenFactor);\n    }\n\n    if (!vertexHasPositionT()) {\n        if (blendMode() == D3D9FF_VertexBlendMode_Normal) {\n            float blendWeightRemaining = 1.0;\n            vec4 vtxSum = vec4(0.0);\n            vec3 nrmSum = vec3(0.0);\n\n            for (uint i = 0; i <= vertexBlendCount(); i++) {\n                uint arrayIndex;\n                if (vertexBlendIndexed()) {\n                    arrayIndex = uint(round(in_BlendIndices[i]));\n                } else {\n                    arrayIndex = i;\n                }\n                mat4 worldView = WorldViewArray[arrayIndex];\n\n                mat3 nrmMtx;\n                for (uint j = 0; j < 3; j++) {\n                    nrmMtx[j] = worldView[j].xyz;\n                }\n\n                vec4 vtxResult = vtx * worldView;\n                vec3 nrmResult = normal * nrmMtx;\n\n                float weight;\n                if (i != vertexBlendCount()) {\n                    weight = in_BlendWeight[i];\n                    blendWeightRemaining -= weight;\n                } else {\n                    weight = blendWeightRemaining;\n                }\n\n                vec4 weightVec4 = vec4(weight, weight, weight, weight);\n\n                vtxSum = fma(vtxResult, weightVec4, vtxSum);\n                nrmSum = fma(nrmResult, weightVec4.xyz, nrmSum);\n            }\n\n            vtx = vtxSum;\n            normal = nrmSum;\n        } else {\n            vtx = vtx * data.WorldView;\n\n            mat3 nrmMtx = mat3(data.NormalMatrix);\n\n            normal = nrmMtx * normal;\n        }\n\n        // Some games rely on normals not being normal.\n        if (normalizeNormals()) {\n            bool isZeroNormal = all(equal(normal, vec3(0.0, 0.0, 0.0)));\n            normal = isZeroNormal ? normal : normalize(normal);\n        }\n\n        gl_Position = vtx * data.Projection;\n    } else {\n        gl_Position *= data.ViewportInfo.inverseExtent;\n        gl_Position += data.ViewportInfo.inverseOffset;\n\n        // We still need to account for perspective correction here...\n\n        float w = gl_Position.w;\n        float rhw = w == 0.0 ? 1.0 : 1.0 / w;\n        gl_Position.xyz *= rhw;\n        gl_Position.w = rhw;\n    }\n\n    vec4 outNrm = vec4(normal, 1.0);\n    out_Normal = outNrm;\n\n    vec4 texCoords[TextureStageCount];\n    texCoords[0] = in_Texcoord0;\n    texCoords[1] = in_Texcoord1;\n    texCoords[2] = in_Texcoord2;\n    texCoords[3] = in_Texcoord3;\n    texCoords[4] = in_Texcoord4;\n    texCoords[5] = in_Texcoord5;\n    texCoords[6] = in_Texcoord6;\n    texCoords[7] = in_Texcoord7;\n\n    vec4 transformedTexCoords[TextureStageCount];\n\n    for (uint i = 0; i < TextureStageCount; i++) {\n        // 0b111 = 7\n        uint inputIndex = (texcoordIndices() >> (i * 3)) & 7;\n        uint inputFlags = (texcoordFlags() >> (i * 3)) & 7;\n        uint texcoordCount = (vertexTexcoordDeclMask() >> (inputIndex * 3)) & 7;\n\n        vec4 transformed;\n\n        uint flags = (transformFlags() >> (i * 3)) & 7;\n\n        // Passing 0xffffffff results in it getting clamped to the dimensions of the texture coords and getting treated as PROJECTED\n        // but D3D9 does not apply the transformation matrix.\n        bool applyTransform = flags > D3DTTFF_COUNT1 && flags <= D3DTTFF_COUNT4;\n\n        uint count = min(flags, 4u);\n\n        // A projection component index of 4 means we won't do projection\n        uint projIndex = count != 0 ? count - 1 : 4;\n\n        switch (inputFlags) {\n            default:\n            case (DXVK_TSS_TCI_PASSTHRU >> TCIOffset):\n                transformed = texCoords[inputIndex & 0xFF];\n\n                if (texcoordCount < 4) {\n                    // Vulkan sets the w component to 1.0 if that's not provided by the vertex buffer, D3D9 expects 0 here\n                    transformed.w = 0.0;\n                }\n\n                if (applyTransform && !vertexHasPositionT()) {\n                    /*This doesn't happen every time and I cannot figure out the difference between when it does and doesn't.\n                    Keep it disabled for now, it's more likely that games rely on the zero texcoord than the weird 1 here.\n                    if (texcoordCount <= 1) {\n                      // y gets padded to 1 for some reason\n                      transformed.y = 1.0;\n                    }*/\n\n                    if (texcoordCount >= 1 && texcoordCount < 4) {\n                        // The first component after the last one thats backed by a vertex buffer gets padded to 1 for some reason.\n                        uint idx = texcoordCount;\n                        transformed[idx] = 1.0;\n                    }\n                } else if (texcoordCount != 0 && !applyTransform) {\n                    // COUNT0, COUNT1, COUNT > 4 => take count from vertex decl if that's not zero\n                    count = texcoordCount;\n                }\n\n                projIndex = count != 0 ? count - 1 : 4;\n                break;\n\n            case (DXVK_TSS_TCI_CAMERASPACENORMAL >> TCIOffset):\n                transformed = outNrm;\n                if (!applyTransform) {\n                    count = 3;\n                    projIndex = 4;\n                }\n                break;\n\n            case (DXVK_TSS_TCI_CAMERASPACEPOSITION >> TCIOffset):\n                transformed = vtx;\n                if (!applyTransform) {\n                    count = 3;\n                    projIndex = 4;\n                }\n                break;\n\n            case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): {\n                vec3 vtx3 = vtx.xyz;\n                vtx3 = normalize(vtx3);\n\n                vec3 reflection = reflect(vtx3, normal);\n                transformed = vec4(reflection, 1.0);\n                if (!applyTransform) {\n                    count = 3;\n                    projIndex = 4;\n                }\n                break;\n            }\n\n            case (DXVK_TSS_TCI_SPHEREMAP >> TCIOffset): {\n                vec3 vtx3 = vtx.xyz;\n                vtx3 = normalize(vtx3);\n\n                vec3 reflection = reflect(vtx3, normal);\n                float m = length(reflection + vec3(0.0, 0.0, 1.0)) * 2.0;\n\n                transformed = vec4(\n                    reflection.x / m + 0.5,\n                    reflection.y / m + 0.5,\n                    0.0,\n                    1.0\n                );\n                break;\n            }\n        }\n\n        if (applyTransform && !vertexHasPositionT()) {\n            transformed = transformed * data.TexcoordMatrices[i];\n        }\n\n        // TODO: Shouldn't projected be checked per texture stage?\n        if (specUint(SpecSamplerProjected) != 0u && projIndex < 4) {\n            // The projection idx is always based on the flags, even when the input mode is not DXVK_TSS_TCI_PASSTHRU.\n            float projValue = transformed[projIndex];\n\n            // The w component is only used for projection or unused, so always insert the component that's supposed to be divided by there.\n            // The fragment shader will then decide whether to project or not.\n            transformed.w = projValue;\n        }\n\n        // TODO: Shouldn't projected be checked per texture stage?\n        uint totalComponents = (specUint(SpecSamplerProjected) != 0u && projIndex < 4) ? 3 : 4;\n        for (uint j = count; j < totalComponents; j++) {\n            // Discard the components that exceed the specified D3DTTFF_COUNT\n            transformed[j] = 0.0;\n        }\n\n        transformedTexCoords[i] = transformed;\n    }\n\n    out_Texcoord0 = transformedTexCoords[0];\n    out_Texcoord1 = transformedTexCoords[1];\n    out_Texcoord2 = transformedTexCoords[2];\n    out_Texcoord3 = transformedTexCoords[3];\n    out_Texcoord4 = transformedTexCoords[4];\n    out_Texcoord5 = transformedTexCoords[5];\n    out_Texcoord6 = transformedTexCoords[6];\n    out_Texcoord7 = transformedTexCoords[7];\n\n    if (useLighting()) {\n        vec4 diffuseValue = vec4(0.0);\n        vec4 specularValue = vec4(0.0);\n        vec4 ambientValue = vec4(0.0);\n\n        for (uint i = 0; i < lightCount(); i++) {\n            D3D9Light light = data.Lights[i];\n\n            vec4 diffuse = light.Diffuse;\n            vec4 specular = light.Specular;\n            vec4 ambient = light.Ambient;\n            vec3 position = light.Position.xyz;\n            vec3 direction = light.Direction.xyz;\n            uint type = light.Type;\n            float range = light.Range;\n            float falloff = light.Falloff;\n            float atten0 = light.Attenuation0;\n            float atten1 = light.Attenuation1;\n            float atten2 = light.Attenuation2;\n            float theta = light.Theta;\n            float phi = light.Phi;\n\n            bool isSpot = type == D3DLIGHT_SPOT;\n            bool isDirectional = type == D3DLIGHT_DIRECTIONAL;\n\n            bvec3 isDirectional3 = bvec3(isDirectional);\n\n            vec3 vtx3 = vtx.xyz;\n\n            vec3 delta = position - vtx3;\n            float d = length(delta);\n            vec3 hitDir = -direction;\n                 hitDir = mix(delta, hitDir, isDirectional3);\n                 hitDir = normalize(hitDir);\n\n            float atten = fma(d, atten2, atten1);\n                  atten = fma(d, atten, atten0);\n                  atten = 1.0 / atten;\n                  atten = spvNMin(atten, FloatMaxValue);\n\n                  atten = d > range ? 0.0 : atten;\n                  atten = isDirectional ? 1.0 : atten;\n\n            // Spot Lighting\n            {\n                float rho = dot(-hitDir, direction);\n                float spotAtten = rho - phi;\n                      spotAtten = spotAtten / (theta - phi);\n                      spotAtten = pow(spotAtten, falloff);\n\n                bool insideThetaAndPhi = rho <= theta;\n                bool insidePhi = rho > phi;\n                     spotAtten = insidePhi ? spotAtten : 0.0;\n                     spotAtten = insideThetaAndPhi ? spotAtten : 1.0;\n                     spotAtten = clamp(spotAtten, 0.0, 1.0);\n\n                     spotAtten = atten * spotAtten;\n                     atten     = isSpot ? spotAtten : atten;\n            }\n\n            float hitDot = dot(normal, hitDir);\n                  hitDot = clamp(hitDot, 0.0, 1.0);\n\n            float diffuseness = hitDot * atten;\n\n            vec3 mid;\n            if (localViewer()) {\n                mid = normalize(vtx3);\n                mid = hitDir - mid;\n            } else {\n                mid = hitDir - vec3(0.0, 0.0, 1.0);\n            }\n\n            mid = normalize(mid);\n\n            float midDot = dot(normal, mid);\n                  midDot = clamp(midDot, 0.0, 1.0);\n            bool doSpec = midDot > 0.0;\n                 doSpec = doSpec && hitDot > 0.0;\n\n            float specularness = pow(midDot, data.Material.Power);\n                  specularness *= atten;\n                  specularness = doSpec ? specularness : 0.0;\n\n            vec4 lightAmbient  = ambient * atten;\n            vec4 lightDiffuse  = diffuse * diffuseness;\n            vec4 lightSpecular = specular * specularness;\n\n            ambientValue  += lightAmbient;\n            diffuseValue  += lightDiffuse;\n            specularValue += lightSpecular;\n        }\n\n        vec4 matDiffuse  = pickMaterialSource(diffuseSource(), data.Material.Diffuse);\n        vec4 matAmbient  = pickMaterialSource(ambientSource(), data.Material.Ambient);\n        vec4 matEmissive = pickMaterialSource(emissiveSource(), data.Material.Emissive);\n        vec4 matSpecular = pickMaterialSource(specularSource(), data.Material.Specular);\n\n        vec4 finalColor0 = fma(matAmbient, data.GlobalAmbient, matEmissive);\n             finalColor0 = fma(matAmbient, ambientValue, finalColor0);\n             finalColor0 = fma(matDiffuse, diffuseValue, finalColor0);\n             finalColor0.a = matDiffuse.a;\n\n        vec4 finalColor1 = matSpecular * specularValue;\n\n        // Saturate\n        finalColor0 = clamp(finalColor0, vec4(0.0), vec4(1.0));\n\n        finalColor1 = clamp(finalColor1, vec4(0.0), vec4(1.0));\n\n        out_Color0 = finalColor0;\n        if (specularEnabled()) {\n            out_Color1 = finalColor1;\n        } else {\n            out_Color1 = vertexHasColor1() ? in_Color1 : vec4(0.0, 0.0, 0.0, 1.0);\n            // TODO: SM3 behavior, see below.\n        }\n    } else {\n        out_Color0 = vertexHasColor0() ? in_Color0 : vec4(1.0, 1.0, 1.0, 1.0);\n        out_Color1 = vertexHasColor1() ? in_Color1 : vec4(0.0, 0.0, 0.0, 1.0);\n        // TODO: If it's used with a SM3 PS, we need to export 0,0,0,0 as the default for color1.\n        //       Implement that using a spec constant.\n    }\n\n    out_Fog = calculateFog(vtx, vec4(0.0));\n\n    gl_PointSize = calculatePointSize(vtx);\n\n    // We statically declare 6 clip planes, so we always need to write values.\n    emitVsClipping(vtx);\n}\n"
  },
  {
    "path": "src/d3d9/version.rc",
    "content": "#include <windows.h>\n\n// DLL version information.\nVS_VERSION_INFO    VERSIONINFO\nFILEVERSION        10,0,17763,1\nPRODUCTVERSION     10,0,17763,1\nFILEFLAGSMASK      VS_FFI_FILEFLAGSMASK\nFILEFLAGS          0\nFILEOS VOS_NT_WINDOWS32\nFILETYPE VFT_DLL\nFILESUBTYPE VFT2_UNKNOWN\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"080904b0\"\n    BEGIN\n      VALUE \"CompanyName\",      \"DXVK\"\n      VALUE \"FileDescription\",  \"Direct3D 9 Runtime\"\n      VALUE \"FileVersion\",      \"10.0.17763.1 (WinBuild.160101.0800)\"\n      VALUE \"InternalName\",     \"D3D9.dll\"\n      VALUE \"LegalCopyright\",   \"zlib/libpng license\"\n      VALUE \"OriginalFilename\", \"D3D9.dll\"\n      VALUE \"ProductName\",      \"DXVK\"\n      VALUE \"ProductVersion\",   \"10.0.17763.1\"\n    END\n  END\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0809, 1200\n  END\nEND\n"
  },
  {
    "path": "src/dxgi/dxgi.def",
    "content": "LIBRARY DXGI.DLL\nEXPORTS  \n    CreateDXGIFactory @9\n    CreateDXGIFactory1 @10\n    CreateDXGIFactory2 @11\n    DXGIDeclareAdapterRemovalSupport @16\n    DXGIGetDebugInterface1 @17\n"
  },
  {
    "path": "src/dxgi/dxgi.sym",
    "content": "{\n  global:\n    CreateDXGIFactory2;\n    CreateDXGIFactory1;\n    CreateDXGIFactory;\n    DXGIDeclareAdapterRemovalSupport;\n    DXGIGetDebugInterface1;\n\n  local:\n    *;\n};\n"
  },
  {
    "path": "src/dxgi/dxgi_adapter.cpp",
    "content": "#include <cstdlib>\n#include <cstring>\n\n#include <d3d10_1.h>\n\n#include \"dxgi_adapter.h\"\n#include \"dxgi_enums.h\"\n#include \"dxgi_factory.h\"\n#include \"dxgi_format.h\"\n#include \"dxgi_options.h\"\n#include \"dxgi_output.h\"\n\n#include \"../util/util_luid.h\"\n#include \"../util/util_win32_compat.h\"\n\n#include \"../wsi/wsi_monitor.h\"\n\nnamespace dxvk {\n\n  DxgiVkAdapter::DxgiVkAdapter(DxgiAdapter* pAdapter)\n  : m_adapter(pAdapter) {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE DxgiVkAdapter::AddRef() {\n    return m_adapter->AddRef();\n  }\n  \n\n  ULONG STDMETHODCALLTYPE DxgiVkAdapter::Release() {\n    return m_adapter->Release();\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE DxgiVkAdapter::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_adapter->QueryInterface(riid, ppvObject);\n  }\n\n  \n  void STDMETHODCALLTYPE DxgiVkAdapter::GetVulkanHandles(\n          VkInstance*               pInstance,\n          VkPhysicalDevice*         pPhysDev) {\n    auto adapter  = m_adapter->GetDXVKAdapter();\n    auto instance = m_adapter->GetDXVKInstance();\n\n    if (pInstance)\n      *pInstance = instance->handle();\n    \n    if (pPhysDev)\n      *pPhysDev = adapter->handle();\n  }\n\n\n\n\n  DxgiAdapter::DxgiAdapter(\n          DxgiFactory*      factory,\n    const Rc<DxvkAdapter>&  adapter,\n          UINT              index)\n  : m_factory (factory),\n    m_adapter (adapter),\n    m_interop (this),\n    m_index   (index),\n    m_desc    (GetAdapterDesc()),\n    m_destructionNotifier(this) {\n    \n  }\n  \n  \n  DxgiAdapter::~DxgiAdapter() {\n    if (m_eventThread.joinable()) {\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n      m_eventCookie = ~0u;\n      m_cond.notify_one();\n\n      lock.unlock();\n      m_eventThread.join();\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiAdapter::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDXGIObject)\n     || riid == __uuidof(IDXGIAdapter)\n     || riid == __uuidof(IDXGIAdapter1)\n     || riid == __uuidof(IDXGIAdapter2)\n     || riid == __uuidof(IDXGIAdapter3)\n     || riid == __uuidof(IDXGIAdapter4)\n     || riid == __uuidof(IDXGIDXVKAdapter)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(IDXGIVkInteropAdapter)) {\n      *ppvObject = ref(&m_interop);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n    \n    if (logQueryInterfaceError(__uuidof(IDXGIAdapter), riid)) {\n      Logger::warn(\"DxgiAdapter::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiAdapter::GetParent(REFIID riid, void** ppParent) {\n    return m_factory->QueryInterface(riid, ppParent);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiAdapter::CheckInterfaceSupport(\n          REFGUID                   InterfaceName,\n          LARGE_INTEGER*            pUMDVersion) {\n    HRESULT hr = DXGI_ERROR_UNSUPPORTED;\n\n    if (InterfaceName == __uuidof(IDXGIDevice)\n     || InterfaceName == __uuidof(ID3D10Device)\n     || InterfaceName == __uuidof(ID3D10Device1))\n      hr = S_OK;\n\n    // Windows drivers return something along the lines of 32.0.xxxxx.yyyy,\n    // so just be conservative here and return a high number. We cannot\n    // reconstruct meaningful UMD versions from Vulkan driver versions.\n    if (SUCCEEDED(hr) && pUMDVersion) {\n      pUMDVersion->HighPart = 0x00200000u;\n      pUMDVersion->LowPart  = 0xffffffffu;\n    }\n\n    if (FAILED(hr)) {\n      Logger::err(\"DXGI: CheckInterfaceSupport: Unsupported interface\");\n      Logger::err(str::format(InterfaceName));\n    }\n\n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiAdapter::EnumOutputs(\n          UINT                      Output,\n          IDXGIOutput**             ppOutput) {\n    InitReturnPtr(ppOutput);\n    \n    if (ppOutput == nullptr)\n      return E_INVALIDARG;\n\n    const auto& deviceId = m_adapter->deviceProperties().vk11;\n\n    std::array<const LUID*, 2> adapterLUIDs = { };\n    uint32_t numLUIDs = 0;\n\n    if (m_adapter->isLinkedToDGPU())\n      return DXGI_ERROR_NOT_FOUND;\n\n    if (deviceId.deviceLUIDValid)\n      adapterLUIDs[numLUIDs++] = reinterpret_cast<const LUID*>(deviceId.deviceLUID);\n\n    auto linkedAdapter = m_adapter->linkedIGPUAdapter();\n\n    // If either LUID is not valid, enumerate all monitors.\n    if (numLUIDs && linkedAdapter != nullptr) {\n      const auto& deviceId = linkedAdapter->deviceProperties().vk11;\n\n      if (deviceId.deviceLUIDValid)\n        adapterLUIDs[numLUIDs++] = reinterpret_cast<const LUID*>(deviceId.deviceLUID);\n      else\n        numLUIDs = 0;\n    }\n\n    // Enumerate all monitors if the robustness fallback is active.\n    if (m_factory->UseMonitorFallback())\n      numLUIDs = 0;\n\n    HMONITOR monitor = wsi::enumMonitors(adapterLUIDs.data(), numLUIDs, Output);\n\n    if (monitor == nullptr)\n      return DXGI_ERROR_NOT_FOUND;\n\n    *ppOutput = ref(new DxgiOutput(m_factory, this, monitor));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiAdapter::GetDesc(DXGI_ADAPTER_DESC* pDesc) {\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n\n    std::memcpy(pDesc->Description, m_desc.Description, sizeof(pDesc->Description));\n    pDesc->VendorId               = m_desc.VendorId;\n    pDesc->DeviceId               = m_desc.DeviceId;\n    pDesc->SubSysId               = m_desc.SubSysId;\n    pDesc->Revision               = m_desc.Revision;\n    pDesc->DedicatedVideoMemory   = m_desc.DedicatedVideoMemory;\n    pDesc->DedicatedSystemMemory  = m_desc.DedicatedSystemMemory;\n    pDesc->SharedSystemMemory     = m_desc.SharedSystemMemory;\n    pDesc->AdapterLuid            = m_desc.AdapterLuid;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiAdapter::GetDesc1(DXGI_ADAPTER_DESC1* pDesc) {\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n\n    std::memcpy(pDesc->Description, m_desc.Description, sizeof(pDesc->Description));\n    pDesc->VendorId               = m_desc.VendorId;\n    pDesc->DeviceId               = m_desc.DeviceId;\n    pDesc->SubSysId               = m_desc.SubSysId;\n    pDesc->Revision               = m_desc.Revision;\n    pDesc->DedicatedVideoMemory   = m_desc.DedicatedVideoMemory;\n    pDesc->DedicatedSystemMemory  = m_desc.DedicatedSystemMemory;\n    pDesc->SharedSystemMemory     = m_desc.SharedSystemMemory;\n    pDesc->AdapterLuid            = m_desc.AdapterLuid;\n    pDesc->Flags                  = m_desc.Flags;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiAdapter::GetDesc2(DXGI_ADAPTER_DESC2* pDesc) {\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n\n    std::memcpy(pDesc->Description, m_desc.Description, sizeof(pDesc->Description));\n    pDesc->VendorId               = m_desc.VendorId;\n    pDesc->DeviceId               = m_desc.DeviceId;\n    pDesc->SubSysId               = m_desc.SubSysId;\n    pDesc->Revision               = m_desc.Revision;\n    pDesc->DedicatedVideoMemory   = m_desc.DedicatedVideoMemory;\n    pDesc->DedicatedSystemMemory  = m_desc.DedicatedSystemMemory;\n    pDesc->SharedSystemMemory     = m_desc.SharedSystemMemory;\n    pDesc->AdapterLuid            = m_desc.AdapterLuid;\n    pDesc->Flags                  = m_desc.Flags;\n    pDesc->GraphicsPreemptionGranularity = m_desc.GraphicsPreemptionGranularity;\n    pDesc->ComputePreemptionGranularity  = m_desc.ComputePreemptionGranularity;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiAdapter::GetDesc3(\n          DXGI_ADAPTER_DESC3*       pDesc) {\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n    \n    *pDesc = m_desc;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiAdapter::QueryVideoMemoryInfo(\n          UINT                          NodeIndex,\n          DXGI_MEMORY_SEGMENT_GROUP     MemorySegmentGroup,\n          DXGI_QUERY_VIDEO_MEMORY_INFO* pVideoMemoryInfo) {\n    if (NodeIndex > 0 || !pVideoMemoryInfo)\n      return E_INVALIDARG;\n    \n    if (MemorySegmentGroup != DXGI_MEMORY_SEGMENT_GROUP_LOCAL\n     && MemorySegmentGroup != DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL)\n      return E_INVALIDARG;\n    \n    DxvkAdapterMemoryInfo memInfo = m_adapter->getMemoryHeapInfo();\n\n    VkMemoryHeapFlags heapFlagMask = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;\n    VkMemoryHeapFlags heapFlags    = 0;\n\n    if (MemorySegmentGroup == DXGI_MEMORY_SEGMENT_GROUP_LOCAL)\n      heapFlags |= VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;\n    \n    pVideoMemoryInfo->Budget = 0;\n    pVideoMemoryInfo->CurrentUsage = 0;\n    pVideoMemoryInfo->AvailableForReservation = 0;\n\n    for (uint32_t i = 0; i < memInfo.heapCount; i++) {\n      if ((memInfo.heaps[i].heapFlags & heapFlagMask) != heapFlags)\n        continue;\n      \n      pVideoMemoryInfo->Budget += memInfo.heaps[i].memoryBudget;\n      pVideoMemoryInfo->CurrentUsage += memInfo.heaps[i].memoryAllocated;\n      pVideoMemoryInfo->AvailableForReservation += memInfo.heaps[i].heapSize / 2;\n    }\n\n    // We don't implement reservation, but the observable\n    // behaviour should match that of Windows drivers\n    uint32_t segmentId = uint32_t(MemorySegmentGroup);\n\n    pVideoMemoryInfo->CurrentReservation = m_memReservation[segmentId];\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiAdapter::SetVideoMemoryReservation(\n          UINT                          NodeIndex,\n          DXGI_MEMORY_SEGMENT_GROUP     MemorySegmentGroup,\n          UINT64                        Reservation) {\n    DXGI_QUERY_VIDEO_MEMORY_INFO info;\n\n    HRESULT hr = QueryVideoMemoryInfo(\n      NodeIndex, MemorySegmentGroup, &info);\n    \n    if (FAILED(hr))\n      return hr;\n    \n    if (Reservation > info.AvailableForReservation)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    uint32_t segmentId = uint32_t(MemorySegmentGroup);\n    m_memReservation[segmentId] = Reservation;\n    return S_OK;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE DxgiAdapter::RegisterHardwareContentProtectionTeardownStatusEvent(\n          HANDLE                        hEvent,\n          DWORD*                        pdwCookie) {\n    Logger::err(\"DxgiAdapter::RegisterHardwareContentProtectionTeardownStatusEvent: Not implemented\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiAdapter::RegisterVideoMemoryBudgetChangeNotificationEvent(\n          HANDLE                        hEvent,\n          DWORD*                        pdwCookie) {\n    if (!hEvent || !pdwCookie)\n      return DXGI_ERROR_INVALID_CALL;\n\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n    DWORD cookie = ++m_eventCookie;\n\n    m_eventMap.insert({ cookie, hEvent });\n\n    if (!m_eventThread.joinable())\n      m_eventThread = dxvk::thread([this] { runEventThread(); });\n\n    // This method seems to fire the\n    // event immediately on Windows\n    SetEvent(hEvent);\n\n    *pdwCookie = cookie;\n    return S_OK;\n  }\n  \n\n  void STDMETHODCALLTYPE DxgiAdapter::UnregisterHardwareContentProtectionTeardownStatus(\n          DWORD                         dwCookie) {\n    Logger::err(\"DxgiAdapter::UnregisterHardwareContentProtectionTeardownStatus: Not implemented\");\n  }\n\n\n  void STDMETHODCALLTYPE DxgiAdapter::UnregisterVideoMemoryBudgetChangeNotification(\n          DWORD                         dwCookie) {\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n    m_eventMap.erase(dwCookie);\n  }\n\n\n  Rc<DxvkAdapter> STDMETHODCALLTYPE DxgiAdapter::GetDXVKAdapter() {\n    return m_adapter;\n  }\n\n\n  Rc<DxvkInstance> STDMETHODCALLTYPE DxgiAdapter::GetDXVKInstance() {\n    return m_factory->GetDXVKInstance();\n  }\n\n\n  DXGI_ADAPTER_DESC3 DxgiAdapter::GetAdapterDesc() const {\n    DXGI_ADAPTER_DESC3 desc = { };\n\n    const DxgiOptions* options = m_factory->GetOptions();\n\n    auto deviceProp = m_adapter->deviceProperties();\n    auto memoryProp = m_adapter->memoryProperties();\n\n    // Custom Vendor / Device ID\n    if (options->customVendorId >= 0)\n      deviceProp.core.properties.vendorID = options->customVendorId;\n\n    if (options->customDeviceId >= 0)\n      deviceProp.core.properties.deviceID = options->customDeviceId;\n\n    std::string description = options->customDeviceDesc.empty()\n      ? std::string(deviceProp.core.properties.deviceName)\n      : options->customDeviceDesc;\n\n    if (options->customVendorId < 0) {\n      uint16_t fallbackVendor = 0xdead;\n      uint16_t fallbackDevice = 0xbeef;\n\n      if (!options->hideAmdGpu) {\n        // AMD RX 6700 XT\n        fallbackVendor = uint16_t(DxvkGpuVendor::Amd);\n        fallbackDevice = 0x73df;\n      } else if (!options->hideNvidiaGpu) {\n        // Nvidia RTX 3060\n        fallbackVendor = uint16_t(DxvkGpuVendor::Nvidia);\n        fallbackDevice = 0x2487;\n      }\n\n      bool hideNvidiaGpu = deviceProp.vk12.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY\n        ? options->hideNvidiaGpu : options->hideNvkGpu;\n\n      bool hideGpu = (deviceProp.core.properties.vendorID == uint16_t(DxvkGpuVendor::Nvidia) && hideNvidiaGpu)\n                  || (deviceProp.core.properties.vendorID == uint16_t(DxvkGpuVendor::Amd) && options->hideAmdGpu)\n                  || (deviceProp.core.properties.vendorID == uint16_t(DxvkGpuVendor::Intel) && options->hideIntelGpu);\n\n      if (hideGpu) {\n        deviceProp.core.properties.vendorID = fallbackVendor;\n\n        if (options->customDeviceId < 0)\n          deviceProp.core.properties.deviceID = fallbackDevice;\n\n        Logger::info(str::format(\"DXGI: Hiding actual GPU, reporting:\\n\",\n                                 \"  vendor ID: 0x\", std::hex, deviceProp.core.properties.vendorID, \"\\n\",\n                                 \"  device ID: 0x\", std::hex, deviceProp.core.properties.deviceID, \"\\n\"));\n      }\n    }\n\n    // Convert device name\n    str::transcodeString(desc.Description,\n      sizeof(desc.Description) / sizeof(desc.Description[0]) - 1,\n      description.c_str(), description.size());\n\n    // Get amount of video memory based on the Vulkan heaps\n    VkDeviceSize deviceMemory = 0;\n    VkDeviceSize sharedMemory = 0;\n\n    for (uint32_t i = 0; i < memoryProp.memoryHeapCount; i++) {\n      VkMemoryHeap heap = memoryProp.memoryHeaps[i];\n\n      if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {\n        // In general we'll have one large device-local heap, and an additional\n        // smaller heap on dGPUs in case ReBAR is not supported. Assume that\n        // the largest available heap is the total amount of available VRAM.\n        deviceMemory = std::max(heap.size, deviceMemory);\n      } else {\n        // This is typically plain sysmem, don't care too much about limits here\n        sharedMemory += heap.size;\n      }\n    }\n\n    // This can happen on integrated GPUs with one memory heap, over-report\n    // here since some games may be allergic to reporting no shared memory.\n    if (!sharedMemory)\n      sharedMemory = deviceMemory;\n\n    // Some games will default to the GPU with the highest amount of dedicated memory,\n    // which can be an integrated GPU on some systems. Report available memory as shared\n    // memory and a small amount as dedicated carve-out if a dedicated GPU is present,\n    // otherwise report memory normally to not unnecessarily confuse games on Deck.\n    if ((m_adapter->isLinkedToDGPU() && deviceProp.core.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)) {\n      sharedMemory = std::max(sharedMemory, deviceMemory);\n      deviceMemory = 512ull << 20;\n    }\n\n    // Make sure to never return exact powers of two outside the 32-bit range\n    // because some games don't understand the concept of actually having VRAM\n    constexpr VkDeviceSize adjustment = 32ull << 20;\n\n    if (deviceMemory && !(deviceMemory & 0xffffffffull))\n      deviceMemory -= adjustment;\n\n    if (sharedMemory && !(sharedMemory & 0xffffffffull))\n      sharedMemory -= adjustment;\n\n    // Some games are silly and need their memory limited\n    if (options->maxDeviceMemory > 0\n     && options->maxDeviceMemory < deviceMemory)\n      deviceMemory = options->maxDeviceMemory;\n\n    if (options->maxSharedMemory > 0\n     && options->maxSharedMemory < sharedMemory)\n      sharedMemory = options->maxSharedMemory;\n\n    if (env::is32BitHostPlatform()) {\n      // The value returned by DXGI is a 32-bit value\n      // on 32-bit platforms, so we need to clamp it\n      VkDeviceSize maxMemory = 0xC0000000;\n      deviceMemory = std::min(deviceMemory, maxMemory);\n      sharedMemory = std::min(sharedMemory, maxMemory);\n    }\n\n    desc.VendorId                       = deviceProp.core.properties.vendorID;\n    desc.DeviceId                       = deviceProp.core.properties.deviceID;\n    desc.SubSysId                       = 0;\n    desc.Revision                       = 0;\n    desc.DedicatedVideoMemory           = deviceMemory;\n    desc.DedicatedSystemMemory          = 0;\n    desc.SharedSystemMemory             = sharedMemory;\n    desc.AdapterLuid                    = LUID { 0, 0 };\n    desc.Flags                          = DXGI_ADAPTER_FLAG3_NONE;\n    desc.GraphicsPreemptionGranularity  = DXGI_GRAPHICS_PREEMPTION_DMA_BUFFER_BOUNDARY;\n    desc.ComputePreemptionGranularity   = DXGI_COMPUTE_PREEMPTION_DMA_BUFFER_BOUNDARY;\n\n    if (deviceProp.vk11.deviceLUIDValid)\n      std::memcpy(&desc.AdapterLuid, deviceProp.vk11.deviceLUID, VK_LUID_SIZE);\n    else\n      desc.AdapterLuid = GetAdapterLUID(m_index);\n\n    return desc;\n  }\n\n\n  void DxgiAdapter::runEventThread() {\n    env::setThreadName(str::format(\"dxvk-adapter-\", m_index));\n\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n    DxvkAdapterMemoryInfo memoryInfoOld = m_adapter->getMemoryHeapInfo();\n\n    while (true) {\n      m_cond.wait_for(lock, std::chrono::milliseconds(1500),\n        [this] { return m_eventCookie == ~0u; });\n\n      if (m_eventCookie == ~0u)\n        return;\n\n      auto memoryInfoNew = m_adapter->getMemoryHeapInfo();\n      bool budgetChanged = false;\n\n      for (uint32_t i = 0; i < memoryInfoNew.heapCount; i++) {\n        budgetChanged |= memoryInfoNew.heaps[i].memoryBudget\n                      != memoryInfoOld.heaps[i].memoryBudget;\n      }\n\n      if (budgetChanged) {\n        memoryInfoOld = memoryInfoNew;\n\n        for (const auto& pair : m_eventMap)\n          SetEvent(pair.second);\n      }\n    }\n  }\n  \n}\n"
  },
  {
    "path": "src/dxgi/dxgi_adapter.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <unordered_map>\n\n#include \"dxgi_format.h\"\n#include \"dxgi_interfaces.h\"\n#include \"dxgi_output.h\"\n\nnamespace dxvk {\n  \n  class DxgiAdapter;\n  class DxgiFactory;\n  class DxgiOutput;\n\n\n  class DxgiVkAdapter : public IDXGIVkInteropAdapter {\n\n  public:\n\n    DxgiVkAdapter(DxgiAdapter* pAdapter);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n    \n    void STDMETHODCALLTYPE GetVulkanHandles(\n            VkInstance*               pInstance,\n            VkPhysicalDevice*         pPhysDev);\n\n  private:\n\n    DxgiAdapter* m_adapter;\n\n  };\n\n  \n  class DxgiAdapter : public DxgiObject<IDXGIDXVKAdapter> {\n    \n  public:\n    \n    DxgiAdapter(\n            DxgiFactory*              factory,\n      const Rc<DxvkAdapter>&          adapter,\n            UINT                      index);\n\n    ~DxgiAdapter();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject) final;\n    \n    HRESULT STDMETHODCALLTYPE GetParent(\n            REFIID                    riid,\n            void**                    ppParent) final;\n    \n    HRESULT STDMETHODCALLTYPE CheckInterfaceSupport(\n            REFGUID                   InterfaceName,\n            LARGE_INTEGER*            pUMDVersion) final;\n    \n    HRESULT STDMETHODCALLTYPE EnumOutputs(\n            UINT                      Output,\n            IDXGIOutput**             ppOutput) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDesc(\n            DXGI_ADAPTER_DESC*        pDesc) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDesc1(\n            DXGI_ADAPTER_DESC1*       pDesc) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDesc2(\n            DXGI_ADAPTER_DESC2*       pDesc) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDesc3(\n            DXGI_ADAPTER_DESC3*       pDesc) final;\n\n    HRESULT STDMETHODCALLTYPE QueryVideoMemoryInfo(\n            UINT                          NodeIndex,\n            DXGI_MEMORY_SEGMENT_GROUP     MemorySegmentGroup,\n            DXGI_QUERY_VIDEO_MEMORY_INFO* pVideoMemoryInfo) final;\n\n    HRESULT STDMETHODCALLTYPE SetVideoMemoryReservation(\n            UINT                          NodeIndex,\n            DXGI_MEMORY_SEGMENT_GROUP     MemorySegmentGroup,\n            UINT64                        Reservation) final;\n    \n    HRESULT STDMETHODCALLTYPE RegisterHardwareContentProtectionTeardownStatusEvent(\n            HANDLE                        hEvent,\n            DWORD*                        pdwCookie) final;\n\n    HRESULT STDMETHODCALLTYPE RegisterVideoMemoryBudgetChangeNotificationEvent(\n            HANDLE                        hEvent,\n            DWORD*                        pdwCookie) final;\n    \n    void STDMETHODCALLTYPE UnregisterHardwareContentProtectionTeardownStatus(\n            DWORD                         dwCookie) final;\n\n    void STDMETHODCALLTYPE UnregisterVideoMemoryBudgetChangeNotification(\n            DWORD                         dwCookie) final;\n\n    Rc<DxvkAdapter> STDMETHODCALLTYPE GetDXVKAdapter() final;\n    \n    Rc<DxvkInstance> STDMETHODCALLTYPE GetDXVKInstance() final;\n\n  private:\n    \n    Com<DxgiFactory>  m_factory;\n    Rc<DxvkAdapter>   m_adapter;\n    DxgiVkAdapter     m_interop;\n\n    UINT              m_index = 0u;\n    UINT64            m_memReservation[2] = { 0, 0 };\n\n    DXGI_ADAPTER_DESC3 m_desc = { };\n\n    dxvk::mutex                       m_mutex;\n    dxvk::condition_variable          m_cond;\n\n    DWORD                             m_eventCookie = 0;\n    std::unordered_map<DWORD, HANDLE> m_eventMap;\n    dxvk::thread                      m_eventThread;\n\n    D3DDestructionNotifier m_destructionNotifier;\n\n    DXGI_ADAPTER_DESC3 GetAdapterDesc() const;\n\n    void runEventThread();\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxgi/dxgi_enums.cpp",
    "content": "#include \"dxgi_enums.h\"\n\nstd::ostream& operator << (std::ostream& os, DXGI_FORMAT e) {\n  switch (e) {\n    ENUM_NAME(DXGI_FORMAT_UNKNOWN);\n    ENUM_NAME(DXGI_FORMAT_R32G32B32A32_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R32G32B32A32_FLOAT);\n    ENUM_NAME(DXGI_FORMAT_R32G32B32A32_UINT);\n    ENUM_NAME(DXGI_FORMAT_R32G32B32A32_SINT);\n    ENUM_NAME(DXGI_FORMAT_R32G32B32_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R32G32B32_FLOAT);\n    ENUM_NAME(DXGI_FORMAT_R32G32B32_UINT);\n    ENUM_NAME(DXGI_FORMAT_R32G32B32_SINT);\n    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_FLOAT);\n    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_UINT);\n    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_SNORM);\n    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_SINT);\n    ENUM_NAME(DXGI_FORMAT_R32G32_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R32G32_FLOAT);\n    ENUM_NAME(DXGI_FORMAT_R32G32_UINT);\n    ENUM_NAME(DXGI_FORMAT_R32G32_SINT);\n    ENUM_NAME(DXGI_FORMAT_R32G8X24_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_D32_FLOAT_S8X24_UINT);\n    ENUM_NAME(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT);\n    ENUM_NAME(DXGI_FORMAT_R10G10B10A2_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R10G10B10A2_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R10G10B10A2_UINT);\n    ENUM_NAME(DXGI_FORMAT_R11G11B10_FLOAT);\n    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);\n    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_UINT);\n    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_SNORM);\n    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_SINT);\n    ENUM_NAME(DXGI_FORMAT_R16G16_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R16G16_FLOAT);\n    ENUM_NAME(DXGI_FORMAT_R16G16_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R16G16_UINT);\n    ENUM_NAME(DXGI_FORMAT_R16G16_SNORM);\n    ENUM_NAME(DXGI_FORMAT_R16G16_SINT);\n    ENUM_NAME(DXGI_FORMAT_R32_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_D32_FLOAT);\n    ENUM_NAME(DXGI_FORMAT_R32_FLOAT);\n    ENUM_NAME(DXGI_FORMAT_R32_UINT);\n    ENUM_NAME(DXGI_FORMAT_R32_SINT);\n    ENUM_NAME(DXGI_FORMAT_R24G8_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_D24_UNORM_S8_UINT);\n    ENUM_NAME(DXGI_FORMAT_R24_UNORM_X8_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_X24_TYPELESS_G8_UINT);\n    ENUM_NAME(DXGI_FORMAT_R8G8_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R8G8_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R8G8_UINT);\n    ENUM_NAME(DXGI_FORMAT_R8G8_SNORM);\n    ENUM_NAME(DXGI_FORMAT_R8G8_SINT);\n    ENUM_NAME(DXGI_FORMAT_R16_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R16_FLOAT);\n    ENUM_NAME(DXGI_FORMAT_D16_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R16_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R16_UINT);\n    ENUM_NAME(DXGI_FORMAT_R16_SNORM);\n    ENUM_NAME(DXGI_FORMAT_R16_SINT);\n    ENUM_NAME(DXGI_FORMAT_R8_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_R8_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R8_UINT);\n    ENUM_NAME(DXGI_FORMAT_R8_SNORM);\n    ENUM_NAME(DXGI_FORMAT_R8_SINT);\n    ENUM_NAME(DXGI_FORMAT_A8_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R1_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R9G9B9E5_SHAREDEXP);\n    ENUM_NAME(DXGI_FORMAT_R8G8_B8G8_UNORM);\n    ENUM_NAME(DXGI_FORMAT_G8R8_G8B8_UNORM);\n    ENUM_NAME(DXGI_FORMAT_BC1_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_BC1_UNORM);\n    ENUM_NAME(DXGI_FORMAT_BC1_UNORM_SRGB);\n    ENUM_NAME(DXGI_FORMAT_BC2_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_BC2_UNORM);\n    ENUM_NAME(DXGI_FORMAT_BC2_UNORM_SRGB);\n    ENUM_NAME(DXGI_FORMAT_BC3_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_BC3_UNORM);\n    ENUM_NAME(DXGI_FORMAT_BC3_UNORM_SRGB);\n    ENUM_NAME(DXGI_FORMAT_BC4_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_BC4_UNORM);\n    ENUM_NAME(DXGI_FORMAT_BC4_SNORM);\n    ENUM_NAME(DXGI_FORMAT_BC5_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_BC5_UNORM);\n    ENUM_NAME(DXGI_FORMAT_BC5_SNORM);\n    ENUM_NAME(DXGI_FORMAT_B5G6R5_UNORM);\n    ENUM_NAME(DXGI_FORMAT_B5G5R5A1_UNORM);\n    ENUM_NAME(DXGI_FORMAT_B8G8R8A8_UNORM);\n    ENUM_NAME(DXGI_FORMAT_B8G8R8X8_UNORM);\n    ENUM_NAME(DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM);\n    ENUM_NAME(DXGI_FORMAT_B8G8R8A8_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB);\n    ENUM_NAME(DXGI_FORMAT_B8G8R8X8_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_B8G8R8X8_UNORM_SRGB);\n    ENUM_NAME(DXGI_FORMAT_BC6H_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_BC6H_UF16);\n    ENUM_NAME(DXGI_FORMAT_BC6H_SF16);\n    ENUM_NAME(DXGI_FORMAT_BC7_TYPELESS);\n    ENUM_NAME(DXGI_FORMAT_BC7_UNORM);\n    ENUM_NAME(DXGI_FORMAT_BC7_UNORM_SRGB);\n    ENUM_NAME(DXGI_FORMAT_AYUV);\n    ENUM_NAME(DXGI_FORMAT_Y410);\n    ENUM_NAME(DXGI_FORMAT_Y416);\n    ENUM_NAME(DXGI_FORMAT_NV12);\n    ENUM_NAME(DXGI_FORMAT_P010);\n    ENUM_NAME(DXGI_FORMAT_P016);\n    ENUM_NAME(DXGI_FORMAT_420_OPAQUE);\n    ENUM_NAME(DXGI_FORMAT_YUY2);\n    ENUM_NAME(DXGI_FORMAT_Y210);\n    ENUM_NAME(DXGI_FORMAT_Y216);\n    ENUM_NAME(DXGI_FORMAT_NV11);\n    ENUM_NAME(DXGI_FORMAT_AI44);\n    ENUM_NAME(DXGI_FORMAT_IA44);\n    ENUM_NAME(DXGI_FORMAT_P8);\n    ENUM_NAME(DXGI_FORMAT_A8P8);\n    ENUM_NAME(DXGI_FORMAT_B4G4R4A4_UNORM);\n    ENUM_DEFAULT(e);\n  }\n}"
  },
  {
    "path": "src/dxgi/dxgi_enums.h",
    "content": "#pragma once\n\n#include \"dxgi_include.h\"\n\nstd::ostream& operator << (std::ostream& os, DXGI_FORMAT e);\n"
  },
  {
    "path": "src/dxgi/dxgi_factory.cpp",
    "content": "#include <algorithm>\n\n#include \"dxgi_factory.h\"\n#include \"dxgi_surface.h\"\n#include \"dxgi_swapchain.h\"\n#include \"dxgi_swapchain_dispatcher.h\"\n\n#include \"../util/util_singleton.h\"\n\nnamespace dxvk {\n\n  static Singleton<DxvkInstance>   g_dxvkInstance;\n\n  static dxvk::mutex               s_globalHDRStateMutex;\n  static DXVK_VK_GLOBAL_HDR_STATE  s_globalHDRState{};\n\n  DxgiVkFactory::DxgiVkFactory(DxgiFactory* pFactory)\n  : m_factory(pFactory) {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE DxgiVkFactory::AddRef() {\n    return m_factory->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE DxgiVkFactory::Release() {\n    return m_factory->Release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiVkFactory::QueryInterface(\n          REFIID                    riid,\n          void**                    ppvObject) {\n    return m_factory->QueryInterface(riid, ppvObject);\n  }\n\n\n  void STDMETHODCALLTYPE DxgiVkFactory::GetVulkanInstance(\n          VkInstance*               pInstance,\n          PFN_vkGetInstanceProcAddr* ppfnVkGetInstanceProcAddr) {\n    auto instance = m_factory->GetDXVKInstance();\n\n    if (pInstance)\n      *pInstance = instance->handle();\n\n    if (ppfnVkGetInstanceProcAddr)\n      *ppfnVkGetInstanceProcAddr = instance->vki()->getLoaderProc();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiVkFactory::GetGlobalHDRState(\n          DXGI_COLOR_SPACE_TYPE   *pOutColorSpace,\n          DXGI_HDR_METADATA_HDR10 *pOutMetadata) {\n    std::unique_lock lock(s_globalHDRStateMutex);\n    if (!s_globalHDRState.Serial)\n      return S_FALSE;\n\n    *pOutColorSpace = s_globalHDRState.ColorSpace;\n    *pOutMetadata   = s_globalHDRState.Metadata.HDR10;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiVkFactory::SetGlobalHDRState(\n          DXGI_COLOR_SPACE_TYPE    ColorSpace,\n    const DXGI_HDR_METADATA_HDR10 *pMetadata) {\n    std::unique_lock lock(s_globalHDRStateMutex);\n    static uint32_t s_GlobalHDRStateSerial = 0;\n\n    s_globalHDRState.Serial     = ++s_GlobalHDRStateSerial;\n    s_globalHDRState.ColorSpace = ColorSpace;\n    s_globalHDRState.Metadata.Type  = DXGI_HDR_METADATA_TYPE_HDR10;\n    s_globalHDRState.Metadata.HDR10 = *pMetadata;\n\n    return S_OK;\n  }\n\n\n  DxgiFactory::DxgiFactory(UINT Flags)\n  : m_instance        (g_dxvkInstance.acquire(0)),\n    m_interop         (this),\n    m_options         (m_instance->config()),\n    m_monitorInfo     (this, m_options),\n    m_flags           (Flags),\n    m_monitorFallback (false),\n    m_destructionNotifier(this) {\n    // Be robust against situations where some monitors are not\n    // associated with any adapter. This can happen if device\n    // filter options are used.\n    std::vector<HMONITOR> monitors;\n\n    for (uint32_t i = 0; ; i++) {\n      HMONITOR hmon = wsi::enumMonitors(i);\n\n      if (!hmon)\n        break;\n\n      monitors.push_back(hmon);\n    }\n\n    for (uint32_t i = 0; m_instance->enumAdapters(i) != nullptr; i++) {\n      auto adapter = m_instance->enumAdapters(i);\n\n      // Remove all monitors that are associated\n      // with the current adapter from the list.\n      const auto& vk11 = adapter->deviceProperties().vk11;\n\n      if (vk11.deviceLUIDValid) {\n        auto luid = reinterpret_cast<const LUID*>(&vk11.deviceLUID);\n\n        for (uint32_t j = 0; ; j++) {\n          HMONITOR hmon = wsi::enumMonitors(&luid, 1, j);\n\n          if (!hmon)\n            break;\n\n          auto entry = std::find(monitors.begin(), monitors.end(), hmon);\n\n          if (entry != monitors.end())\n            monitors.erase(entry);\n        }\n      }\n    }\n\n    // If any monitors are left on the list, enable the\n    // fallback to always enumerate all monitors.\n    if ((m_monitorFallback = !monitors.empty()))\n      Logger::warn(\"DXGI: Found monitors not associated with any adapter, using fallback\");\n  }\n  \n  \n  DxgiFactory::~DxgiFactory() {\n    g_dxvkInstance.release();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDXGIObject)\n     || riid == __uuidof(IDXGIFactory)\n     || riid == __uuidof(IDXGIFactory1)\n     || riid == __uuidof(IDXGIFactory2)\n     || riid == __uuidof(IDXGIFactory3)\n     || riid == __uuidof(IDXGIFactory4)\n     || riid == __uuidof(IDXGIFactory5)\n     || riid == __uuidof(IDXGIFactory6)\n     || riid == __uuidof(IDXGIFactory7)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(IDXGIVkInteropFactory)\n     || riid == __uuidof(IDXGIVkInteropFactory1)) {\n      *ppvObject = ref(&m_interop);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(IDXGIVkMonitorInfo)) {\n      *ppvObject = ref(&m_monitorInfo);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n    \n    if (logQueryInterfaceError(__uuidof(IDXGIFactory), riid)) {\n      Logger::warn(\"DxgiFactory::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::GetParent(REFIID riid, void** ppParent) {\n    InitReturnPtr(ppParent);\n    \n    Logger::warn(\"DxgiFactory::GetParent: Unknown interface query\");\n    return E_NOINTERFACE;\n  }\n  \n  \n  BOOL STDMETHODCALLTYPE DxgiFactory::IsWindowedStereoEnabled() {\n    // We don't support Stereo 3D at the moment\n    return FALSE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSoftwareAdapter(\n          HMODULE         Module,\n          IDXGIAdapter**  ppAdapter) {\n    InitReturnPtr(ppAdapter);\n    \n    if (ppAdapter == nullptr)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    Logger::err(\"DXGI: CreateSoftwareAdapter: Software adapters not supported\");\n    return DXGI_ERROR_UNSUPPORTED;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChain(\n          IUnknown*             pDevice,\n          DXGI_SWAP_CHAIN_DESC* pDesc,\n          IDXGISwapChain**      ppSwapChain) {\n    if (!ppSwapChain || !pDesc || !pDesc->OutputWindow || !pDevice)\n      return DXGI_ERROR_INVALID_CALL;\n\n    DXGI_SWAP_CHAIN_DESC1 desc;\n    desc.Width              = pDesc->BufferDesc.Width;\n    desc.Height             = pDesc->BufferDesc.Height;\n    desc.Format             = pDesc->BufferDesc.Format;\n    desc.Stereo             = FALSE;\n    desc.SampleDesc         = pDesc->SampleDesc;\n    desc.BufferUsage        = pDesc->BufferUsage;\n    desc.BufferCount        = pDesc->BufferCount;\n    desc.Scaling            = DXGI_SCALING_STRETCH;\n    desc.SwapEffect         = pDesc->SwapEffect;\n    desc.AlphaMode          = DXGI_ALPHA_MODE_IGNORE;\n    desc.Flags              = pDesc->Flags;\n\n    DXGI_SWAP_CHAIN_FULLSCREEN_DESC descFs;\n    descFs.RefreshRate      = pDesc->BufferDesc.RefreshRate;\n    descFs.ScanlineOrdering = pDesc->BufferDesc.ScanlineOrdering;\n    descFs.Scaling          = pDesc->BufferDesc.Scaling;\n    descFs.Windowed         = pDesc->Windowed;\n    \n    IDXGISwapChain1* swapChain = nullptr;\n\n    HRESULT hr = CreateSwapChainBase(pDevice,\n      pDesc->OutputWindow, &desc, &descFs, nullptr, &swapChain);\n\n    *ppSwapChain = swapChain;\n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForHwnd(\n          IUnknown*             pDevice,\n          HWND                  hWnd,\n    const DXGI_SWAP_CHAIN_DESC1* pDesc,\n    const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,\n          IDXGIOutput*          pRestrictToOutput,\n          IDXGISwapChain1**     ppSwapChain) {\n    InitReturnPtr(ppSwapChain);\n\n    if (!ppSwapChain || !pDesc || !hWnd || !pDevice)\n      return DXGI_ERROR_INVALID_CALL;\n\n    return CreateSwapChainBase(pDevice, hWnd,\n      pDesc, pFullscreenDesc, pRestrictToOutput,\n      ppSwapChain);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForCoreWindow(\n          IUnknown*             pDevice,\n          IUnknown*             pWindow,\n    const DXGI_SWAP_CHAIN_DESC1* pDesc,\n          IDXGIOutput*          pRestrictToOutput,\n          IDXGISwapChain1**     ppSwapChain) {\n    InitReturnPtr(ppSwapChain);\n    \n    Logger::err(\"DxgiFactory::CreateSwapChainForCoreWindow: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForComposition(\n          IUnknown*             pDevice,\n    const DXGI_SWAP_CHAIN_DESC1* pDesc,\n          IDXGIOutput*          pRestrictToOutput,\n          IDXGISwapChain1**     ppSwapChain) {\n    InitReturnPtr(ppSwapChain);\n\n    if (!m_options.enableDummyCompositionSwapchain) {\n      Logger::err(\"DxgiFactory::CreateSwapChainForComposition: Not implemented\");\n      return E_NOTIMPL;\n    }\n\n    Logger::warn(\"DxgiFactory::CreateSwapChainForComposition: Creating dummy swap chain\");\n\n    return CreateSwapChainBase(pDevice,\n      nullptr, pDesc, nullptr, pRestrictToOutput, ppSwapChain);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapters(\n          UINT            Adapter,\n          IDXGIAdapter**  ppAdapter) {\n    InitReturnPtr(ppAdapter);\n    \n    if (ppAdapter == nullptr)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    IDXGIAdapter1* handle = nullptr;\n    HRESULT hr = this->EnumAdapters1(Adapter, &handle);\n    *ppAdapter = handle;\n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapters1(\n          UINT            Adapter,\n          IDXGIAdapter1** ppAdapter) {\n    InitReturnPtr(ppAdapter);\n    \n    if (ppAdapter == nullptr)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    Rc<DxvkAdapter> dxvkAdapter\n      = m_instance->enumAdapters(Adapter);\n    \n    if (dxvkAdapter == nullptr)\n      return DXGI_ERROR_NOT_FOUND;\n    \n    *ppAdapter = ref(new DxgiAdapter(this, dxvkAdapter, Adapter));\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapterByLuid(\n          LUID                  AdapterLuid,\n          REFIID                riid,\n          void**                ppvAdapter) {\n    InitReturnPtr(ppvAdapter);\n    uint32_t adapterId = 0;\n\n    while (true) {\n      Com<IDXGIAdapter> adapter;\n      HRESULT hr = EnumAdapters(adapterId++, &adapter);\n\n      if (FAILED(hr))\n        return hr;\n      \n      DXGI_ADAPTER_DESC desc;\n      adapter->GetDesc(&desc);\n\n      if (!std::memcmp(&AdapterLuid, &desc.AdapterLuid, sizeof(LUID)))\n        return adapter->QueryInterface(riid, ppvAdapter);\n    }\n\n    // This should be unreachable\n    return DXGI_ERROR_NOT_FOUND;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapterByGpuPreference(\n          UINT                  Adapter,\n          DXGI_GPU_PREFERENCE   GpuPreference,\n          REFIID                riid,\n          void**                ppvAdapter) {\n    InitReturnPtr(ppvAdapter);\n    uint32_t adapterCount = m_instance->adapterCount();\n\n    if (Adapter >= adapterCount)\n      return DXGI_ERROR_NOT_FOUND;\n\n    // We know that the backend lists dedicated GPUs before\n    // any integrated ones, so just list adapters in reverse\n    // order. We have no other way to estimate performance.\n    if (GpuPreference == DXGI_GPU_PREFERENCE_MINIMUM_POWER)\n      Adapter = adapterCount - Adapter - 1;\n\n    Com<IDXGIAdapter> adapter;\n    HRESULT hr = this->EnumAdapters(Adapter, &adapter);\n\n    if (FAILED(hr))\n      return hr;\n\n    return adapter->QueryInterface(riid, ppvAdapter);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiFactory::EnumWarpAdapter(\n          REFIID                riid,\n          void**                ppvAdapter) {\n    InitReturnPtr(ppvAdapter);\n\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"DxgiFactory::EnumWarpAdapter: WARP not supported, returning first hardware adapter\");\n\n    Com<IDXGIAdapter1> adapter;\n    HRESULT hr = EnumAdapters1(0, &adapter);\n\n    if (FAILED(hr))\n      return hr;\n\n    return adapter->QueryInterface(riid, ppvAdapter);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiFactory::GetWindowAssociation(HWND *pWindowHandle) {\n    if (pWindowHandle == nullptr)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    // Wine tests show that this is always null for whatever reason\n    *pWindowHandle = nullptr;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::GetSharedResourceAdapterLuid(\n          HANDLE                hResource,\n          LUID*                 pLuid) {\n    Logger::err(\"DxgiFactory::GetSharedResourceAdapterLuid: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::MakeWindowAssociation(HWND WindowHandle, UINT Flags) {\n    Logger::warn(\"DXGI: MakeWindowAssociation: Ignoring flags\");\n    return S_OK;\n  }\n  \n  \n  BOOL STDMETHODCALLTYPE DxgiFactory::IsCurrent() {\n    return TRUE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterOcclusionStatusWindow(\n          HWND                  WindowHandle,\n          UINT                  wMsg,\n          DWORD*                pdwCookie) {\n    Logger::err(\"DxgiFactory::RegisterOcclusionStatusWindow: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterStereoStatusEvent(\n          HANDLE                hEvent,\n          DWORD*                pdwCookie) {\n    Logger::err(\"DxgiFactory::RegisterStereoStatusEvent: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterStereoStatusWindow(\n          HWND                  WindowHandle,\n          UINT                  wMsg,\n          DWORD*                pdwCookie) {\n    Logger::err(\"DxgiFactory::RegisterStereoStatusWindow: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterOcclusionStatusEvent(\n          HANDLE                hEvent,\n          DWORD*                pdwCookie) {\n    Logger::err(\"DxgiFactory::RegisterOcclusionStatusEvent: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n\n  void STDMETHODCALLTYPE DxgiFactory::UnregisterStereoStatus(\n          DWORD                 dwCookie) {\n    Logger::err(\"DxgiFactory::UnregisterStereoStatus: Not implemented\");\n  }\n  \n  \n  void STDMETHODCALLTYPE DxgiFactory::UnregisterOcclusionStatus(\n          DWORD                 dwCookie) {\n    Logger::err(\"DxgiFactory::UnregisterOcclusionStatus: Not implemented\");\n  }\n\n\n  UINT STDMETHODCALLTYPE DxgiFactory::GetCreationFlags() {\n    return m_flags;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiFactory::CheckFeatureSupport(\n          DXGI_FEATURE          Feature,\n          void*                 pFeatureSupportData,\n          UINT                  FeatureSupportDataSize) {\n    switch (Feature) {\n      case DXGI_FEATURE_PRESENT_ALLOW_TEARING: {\n        auto info = static_cast<BOOL*>(pFeatureSupportData);\n\n        if (FeatureSupportDataSize != sizeof(*info))\n          return E_INVALIDARG;\n        \n        *info = TRUE;\n      } return S_OK;\n\n      default:\n        Logger::err(str::format(\"DxgiFactory: CheckFeatureSupport: Unknown feature: \", uint32_t(Feature)));\n        return E_INVALIDARG;\n    }\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterAdaptersChangedEvent(\n          HANDLE                hEvent,\n          DWORD*                pdwCookie) {\n    Logger::err(\"DxgiFactory: RegisterAdaptersChangedEvent: Stub\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiFactory::UnregisterAdaptersChangedEvent(\n          DWORD                 Cookie) {\n    Logger::err(\"DxgiFactory: UnregisterAdaptersChangedEvent: Stub\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainBase(\n          IUnknown*             pDevice,\n          HWND                  hWnd,\n    const DXGI_SWAP_CHAIN_DESC1* pDesc,\n    const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,\n          IDXGIOutput*          pRestrictToOutput,\n          IDXGISwapChain1**     ppSwapChain) {\n    // Make sure the back buffer size is not zero\n    DXGI_SWAP_CHAIN_DESC1 desc = *pDesc;\n\n    if (hWnd) {\n      wsi::getWindowSize(hWnd,\n        desc.Width  ? nullptr : &desc.Width,\n        desc.Height ? nullptr : &desc.Height);\n    }\n\n    // If necessary, set up a default set of\n    // fullscreen parameters for the swap chain\n    DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsDesc;\n\n    if (pFullscreenDesc) {\n      fsDesc = *pFullscreenDesc;\n    } else {\n      fsDesc.RefreshRate      = { 0, 0 };\n      fsDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;\n      fsDesc.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;\n      fsDesc.Windowed         = TRUE;\n    }\n\n    // Probe various modes to create the swap chain object\n    Com<IDXGISwapChain4> frontendSwapChain;\n\n    Com<IDXGIVkSwapChainFactory> dxvkFactory;\n\n    if (SUCCEEDED(pDevice->QueryInterface(IID_PPV_ARGS(&dxvkFactory)))) {\n      Com<IDXGIVkSurfaceFactory> surfaceFactory = new DxgiSurfaceFactory(\n        m_instance->vki()->getLoaderProc(), hWnd);\n\n      Com<IDXGIVkSwapChain> presenter;\n      HRESULT hr = dxvkFactory->CreateSwapChain(surfaceFactory.ptr(), &desc, &presenter);\n\n      if (FAILED(hr)) {\n        Logger::err(str::format(\"DXGI: CreateSwapChainForHwnd: Failed to create swap chain, hr \", hr));\n        return hr;\n      }\n\n      frontendSwapChain = new DxgiSwapChain(this, presenter.ptr(), hWnd, &desc, &fsDesc, pDevice);\n    } else {\n      Logger::err(\"DXGI: CreateSwapChainForHwnd: Unsupported device type\");\n      return DXGI_ERROR_UNSUPPORTED;\n    }\n\n    // Wrap object in swap chain dispatcher\n    *ppSwapChain = new DxgiSwapChainDispatcher(frontendSwapChain.ref(), pDevice);\n    return S_OK;\n  }\n\n\n  DXVK_VK_GLOBAL_HDR_STATE DxgiFactory::GlobalHDRState() {\n    std::unique_lock lock(s_globalHDRStateMutex);\n    return s_globalHDRState;\n  }\n\n}\n"
  },
  {
    "path": "src/dxgi/dxgi_factory.h",
    "content": "#pragma once\n\n#include <vector>\n#include <mutex>\n\n#include \"dxgi_adapter.h\"\n#include \"dxgi_monitor.h\"\n#include \"dxgi_options.h\"\n\n#include \"../dxvk/dxvk_instance.h\"\n\nnamespace dxvk {\n\n  class DxgiFactory;\n\n  struct DXVK_VK_GLOBAL_HDR_STATE {\n    uint32_t Serial;\n    DXGI_COLOR_SPACE_TYPE ColorSpace;\n    DXGI_VK_HDR_METADATA  Metadata;\n  };\n\n  class DxgiVkFactory : public IDXGIVkInteropFactory1 {\n\n  public:\n\n    DxgiVkFactory(DxgiFactory* pFactory);\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    void STDMETHODCALLTYPE GetVulkanInstance(\n            VkInstance*               pInstance,\n            PFN_vkGetInstanceProcAddr* ppfnVkGetInstanceProcAddr);\n\n    HRESULT STDMETHODCALLTYPE GetGlobalHDRState(\n            DXGI_COLOR_SPACE_TYPE   *pOutColorSpace,\n            DXGI_HDR_METADATA_HDR10 *pOutMetadata);\n\n    HRESULT STDMETHODCALLTYPE SetGlobalHDRState(\n            DXGI_COLOR_SPACE_TYPE    ColorSpace,\n      const DXGI_HDR_METADATA_HDR10 *pMetadata);\n\n  private:\n\n    DxgiFactory* m_factory;\n\n  };\n\n\n  class DxgiFactory : public DxgiObject<IDXGIFactory7> {\n    \n  public:\n    \n    DxgiFactory(UINT Flags);\n    ~DxgiFactory();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject) final;\n    \n    HRESULT STDMETHODCALLTYPE GetParent(\n            REFIID                riid,\n            void**                ppParent) final;\n    \n    BOOL STDMETHODCALLTYPE IsWindowedStereoEnabled() final;\n\n    HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter(\n            HMODULE               Module,\n            IDXGIAdapter**        ppAdapter) final;\n    \n    HRESULT STDMETHODCALLTYPE CreateSwapChain(\n            IUnknown*             pDevice,\n            DXGI_SWAP_CHAIN_DESC* pDesc,\n            IDXGISwapChain**      ppSwapChain) final;\n    \n    HRESULT STDMETHODCALLTYPE CreateSwapChainForHwnd(\n            IUnknown*             pDevice,\n            HWND                  hWnd,\n      const DXGI_SWAP_CHAIN_DESC1* pDesc,\n      const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,\n            IDXGIOutput*          pRestrictToOutput,\n            IDXGISwapChain1**     ppSwapChain) final;\n\n    HRESULT STDMETHODCALLTYPE CreateSwapChainForCoreWindow(\n            IUnknown*             pDevice,\n            IUnknown*             pWindow,\n      const DXGI_SWAP_CHAIN_DESC1* pDesc,\n            IDXGIOutput*          pRestrictToOutput,\n            IDXGISwapChain1**     ppSwapChain) final;\n    \n    HRESULT STDMETHODCALLTYPE CreateSwapChainForComposition(\n            IUnknown*             pDevice,\n      const DXGI_SWAP_CHAIN_DESC1* pDesc,\n            IDXGIOutput*          pRestrictToOutput,\n            IDXGISwapChain1**     ppSwapChain) final;\n    \n    HRESULT STDMETHODCALLTYPE EnumAdapters(\n            UINT                  Adapter,\n            IDXGIAdapter**        ppAdapter) final;\n    \n    HRESULT STDMETHODCALLTYPE EnumAdapters1(\n            UINT                  Adapter,\n            IDXGIAdapter1**       ppAdapter) final;\n    \n    HRESULT STDMETHODCALLTYPE EnumAdapterByLuid(\n            LUID                  AdapterLuid,\n            REFIID                riid,\n            void**                ppvAdapter) final;\n    \n    HRESULT STDMETHODCALLTYPE EnumAdapterByGpuPreference(\n            UINT                  Adapter,\n            DXGI_GPU_PREFERENCE   GpuPreference,\n            REFIID                riid,\n            void**                ppvAdapter);\n\n    HRESULT STDMETHODCALLTYPE EnumWarpAdapter(\n            REFIID                riid,\n            void**                ppvAdapter) final;\n\n    HRESULT STDMETHODCALLTYPE GetWindowAssociation(\n            HWND*                 pWindowHandle) final;\n    \n    HRESULT STDMETHODCALLTYPE GetSharedResourceAdapterLuid(\n            HANDLE                hResource,\n            LUID*                 pLuid) final;\n\n    HRESULT STDMETHODCALLTYPE MakeWindowAssociation(\n            HWND                  WindowHandle,\n            UINT                  Flags) final;\n    \n    BOOL STDMETHODCALLTYPE IsCurrent() final;\n    \n    HRESULT STDMETHODCALLTYPE RegisterOcclusionStatusWindow(\n            HWND                  WindowHandle,\n            UINT                  wMsg,\n            DWORD*                pdwCookie) final;\n    \n    HRESULT STDMETHODCALLTYPE RegisterStereoStatusEvent(\n            HANDLE                hEvent,\n            DWORD*                pdwCookie) final;\n    \n    HRESULT STDMETHODCALLTYPE RegisterStereoStatusWindow(\n            HWND                  WindowHandle,\n            UINT                  wMsg,\n            DWORD*                pdwCookie) final;\n\n    HRESULT STDMETHODCALLTYPE RegisterOcclusionStatusEvent(\n            HANDLE                hEvent,\n            DWORD*                pdwCookie) final;\n\n    void STDMETHODCALLTYPE UnregisterStereoStatus(\n            DWORD                 dwCookie) final;\n\n    void STDMETHODCALLTYPE UnregisterOcclusionStatus(\n            DWORD                 dwCookie) final;\n    \n    UINT STDMETHODCALLTYPE GetCreationFlags() final;\n\n    HRESULT STDMETHODCALLTYPE CheckFeatureSupport(\n            DXGI_FEATURE          Feature,\n            void*                 pFeatureSupportData,\n            UINT                  FeatureSupportDataSize) final;\n\n    HRESULT STDMETHODCALLTYPE RegisterAdaptersChangedEvent(\n            HANDLE                hEvent,\n            DWORD*                pdwCookie);\n\n    HRESULT STDMETHODCALLTYPE UnregisterAdaptersChangedEvent(\n            DWORD                 Cookie);\n\n    BOOL UseMonitorFallback() const {\n      return m_monitorFallback;\n    }\n\n    Rc<DxvkInstance> GetDXVKInstance() const {\n      return m_instance;\n    }\n\n    const DxgiOptions* GetOptions() const {\n      return &m_options;\n    }\n\n    DxgiMonitorInfo* GetMonitorInfo() {\n      return &m_monitorInfo;\n    }\n\n    DXVK_VK_GLOBAL_HDR_STATE GlobalHDRState();\n    \n  private:\n    \n    Rc<DxvkInstance> m_instance;\n    DxgiVkFactory    m_interop;\n    DxgiOptions      m_options;\n    DxgiMonitorInfo  m_monitorInfo;\n    UINT             m_flags;\n    BOOL             m_monitorFallback;\n\n    D3DDestructionNotifier m_destructionNotifier;\n      \n\n    HRESULT STDMETHODCALLTYPE CreateSwapChainBase(\n            IUnknown*             pDevice,\n            HWND                  hWnd,\n      const DXGI_SWAP_CHAIN_DESC1* pDesc,\n      const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,\n            IDXGIOutput*          pRestrictToOutput,\n            IDXGISwapChain1**     ppSwapChain);\n  };\n  \n}\n"
  },
  {
    "path": "src/dxgi/dxgi_format.cpp",
    "content": "#include \"dxgi_format.h\"\n\n#include <array>\n\nnamespace dxvk {\n  \n  const std::array<DXGI_VK_FORMAT_MAPPING, 133> g_dxgiFormats = {{\n    // DXGI_FORMAT_UNKNOWN\n    { },\n    // DXGI_FORMAT_R32G32B32A32_TYPELESS\n    { VK_FORMAT_R32G32B32A32_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32B32A32_UINT },\n    // DXGI_FORMAT_R32G32B32A32_FLOAT\n    { VK_FORMAT_R32G32B32A32_SFLOAT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32B32A32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32G32B32A32_UINT\n    { VK_FORMAT_R32G32B32A32_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32B32A32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32G32B32A32_SINT\n    { VK_FORMAT_R32G32B32A32_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32B32A32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32G32B32_TYPELESS\n    { VK_FORMAT_R32G32B32_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32B32_UINT },\n    // DXGI_FORMAT_R32G32B32_FLOAT\n    { VK_FORMAT_R32G32B32_SFLOAT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32B32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32G32B32_UINT\n    { VK_FORMAT_R32G32B32_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32B32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32G32B32_SINT\n    { VK_FORMAT_R32G32B32_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32B32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16B16A16_TYPELESS\n    { VK_FORMAT_R16G16B16A16_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16B16A16_UINT },\n    // DXGI_FORMAT_R16G16B16A16_FLOAT\n    { VK_FORMAT_R16G16B16A16_SFLOAT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16B16A16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16B16A16_UNORM\n    { VK_FORMAT_R16G16B16A16_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16B16A16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16B16A16_UINT\n    { VK_FORMAT_R16G16B16A16_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16B16A16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16B16A16_SNORM\n    { VK_FORMAT_R16G16B16A16_SNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16B16A16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16B16A16_SINT\n    { VK_FORMAT_R16G16B16A16_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16B16A16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32G32_TYPELESS\n    { VK_FORMAT_R32G32_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32_UINT },\n    // DXGI_FORMAT_R32G32_FLOAT\n    { VK_FORMAT_R32G32_SFLOAT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32G32_UINT\n    { VK_FORMAT_R32G32_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32G32_SINT\n    { VK_FORMAT_R32G32_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32G32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32G8X24_TYPELESS\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D32_SFLOAT_S8_UINT,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_D32_FLOAT_S8X24_UINT\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D32_SFLOAT_S8_UINT,\n      VK_FORMAT_UNDEFINED,\n      0, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT },\n    // DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D32_SFLOAT_S8_UINT,\n      VK_FORMAT_UNDEFINED,\n      0, VK_IMAGE_ASPECT_DEPTH_BIT },\n    // DXGI_FORMAT_X32_TYPELESS_G8X24_UINT\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D32_SFLOAT_S8_UINT,\n      VK_FORMAT_UNDEFINED,\n      0, VK_IMAGE_ASPECT_STENCIL_BIT },\n    // DXGI_FORMAT_R10G10B10A2_TYPELESS\n    { VK_FORMAT_A2B10G10R10_UNORM_PACK32,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_A2B10G10R10_UINT_PACK32 },\n    // DXGI_FORMAT_R10G10B10A2_UNORM\n    { VK_FORMAT_A2B10G10R10_UNORM_PACK32,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_A2B10G10R10_UINT_PACK32,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R10G10B10A2_UINT\n    { VK_FORMAT_A2B10G10R10_UINT_PACK32,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_A2B10G10R10_UINT_PACK32,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R11G11B10_FLOAT\n    { VK_FORMAT_B10G11R11_UFLOAT_PACK32,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8G8B8A8_TYPELESS\n    { VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8B8A8_UINT },\n    // DXGI_FORMAT_R8G8B8A8_UNORM\n    { VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8B8A8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8G8B8A8_UNORM_SRGB\n    { VK_FORMAT_R8G8B8A8_SRGB,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8B8A8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8G8B8A8_UINT\n    { VK_FORMAT_R8G8B8A8_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8B8A8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8G8B8A8_SNORM\n    { VK_FORMAT_R8G8B8A8_SNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8B8A8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8G8B8A8_SINT\n    { VK_FORMAT_R8G8B8A8_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8B8A8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16_TYPELESS\n    { VK_FORMAT_R16G16_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16_UINT },\n    // DXGI_FORMAT_R16G16_FLOAT\n    { VK_FORMAT_R16G16_SFLOAT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16_UNORM\n    { VK_FORMAT_R16G16_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16_UINT\n    { VK_FORMAT_R16G16_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16_SNORM\n    { VK_FORMAT_R16G16_SNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16G16_SINT\n    { VK_FORMAT_R16G16_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16G16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32_TYPELESS\n    { VK_FORMAT_R32_UINT,\n      VK_FORMAT_D32_SFLOAT,\n      VK_FORMAT_R32_UINT },\n    // DXGI_FORMAT_D32_FLOAT\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D32_SFLOAT,\n      VK_FORMAT_UNDEFINED,\n      0, VK_IMAGE_ASPECT_DEPTH_BIT },\n    // DXGI_FORMAT_R32_FLOAT\n    { VK_FORMAT_R32_SFLOAT,\n      VK_FORMAT_D32_SFLOAT,\n      VK_FORMAT_R32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT,\n      VK_IMAGE_ASPECT_DEPTH_BIT },\n    // DXGI_FORMAT_R32_UINT\n    { VK_FORMAT_R32_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R32_SINT\n    { VK_FORMAT_R32_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R32_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R24G8_TYPELESS\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D24_UNORM_S8_UINT,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_D24_UNORM_S8_UINT\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D24_UNORM_S8_UINT,\n      VK_FORMAT_UNDEFINED,\n      0, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT },\n    // DXGI_FORMAT_R24_UNORM_X8_TYPELESS\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D24_UNORM_S8_UINT,\n      VK_FORMAT_UNDEFINED,\n      0, VK_IMAGE_ASPECT_DEPTH_BIT },\n    // DXGI_FORMAT_X24_TYPELESS_G8_UINT\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D24_UNORM_S8_UINT,\n      VK_FORMAT_UNDEFINED,\n      0, VK_IMAGE_ASPECT_STENCIL_BIT },\n    // DXGI_FORMAT_R8G8_TYPELESS\n    { VK_FORMAT_R8G8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8_UINT },\n    // DXGI_FORMAT_R8G8_UNORM\n    { VK_FORMAT_R8G8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8G8_UINT\n    { VK_FORMAT_R8G8_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8G8_SNORM\n    { VK_FORMAT_R8G8_SNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8G8_SINT\n    { VK_FORMAT_R8G8_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16_TYPELESS\n    { VK_FORMAT_R16_UNORM,\n      VK_FORMAT_D16_UNORM,\n      VK_FORMAT_R16_UINT },\n    // DXGI_FORMAT_R16_FLOAT\n    { VK_FORMAT_R16_SFLOAT,\n      VK_FORMAT_D16_UNORM,\n      VK_FORMAT_R16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT,\n      VK_IMAGE_ASPECT_DEPTH_BIT },\n    // DXGI_FORMAT_D16_UNORM\n    { VK_FORMAT_UNDEFINED,\n      VK_FORMAT_D16_UNORM,\n      VK_FORMAT_UNDEFINED,\n      0, VK_IMAGE_ASPECT_DEPTH_BIT },\n    // DXGI_FORMAT_R16_UNORM\n    { VK_FORMAT_R16_UNORM,\n      VK_FORMAT_D16_UNORM,\n      VK_FORMAT_R16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT,\n      VK_IMAGE_ASPECT_DEPTH_BIT },\n    // DXGI_FORMAT_R16_UINT\n    { VK_FORMAT_R16_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16_SNORM\n    { VK_FORMAT_R16_SNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R16_SINT\n    { VK_FORMAT_R16_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R16_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8_TYPELESS\n    { VK_FORMAT_R8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8_UINT },\n    // DXGI_FORMAT_R8_UNORM\n    { VK_FORMAT_R8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8_UINT\n    { VK_FORMAT_R8_UINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8_SNORM\n    { VK_FORMAT_R8_SNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8_SINT\n    { VK_FORMAT_R8_SINT,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_A8_UNORM\n    { VK_FORMAT_A8_UNORM_KHR,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R1_UNORM\n    { }, // Unsupported\n    // DXGI_FORMAT_R9G9B9E5_SHAREDEXP\n    { VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_R8G8_B8G8_UNORM\n    { VK_FORMAT_B8G8R8G8_422_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_G8R8_G8B8_UNORM\n    { VK_FORMAT_G8B8G8R8_422_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC1_TYPELESS\n    { VK_FORMAT_BC1_RGBA_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_BC1_UNORM\n    { VK_FORMAT_BC1_RGBA_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC1_UNORM_SRGB\n    { VK_FORMAT_BC1_RGBA_SRGB_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC2_TYPELESS\n    { VK_FORMAT_BC2_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_BC2_UNORM\n    { VK_FORMAT_BC2_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC2_UNORM_SRGB\n    { VK_FORMAT_BC2_SRGB_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC3_TYPELESS\n    { VK_FORMAT_BC3_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_BC3_UNORM\n    { VK_FORMAT_BC3_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC3_UNORM_SRGB\n    { VK_FORMAT_BC3_SRGB_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC4_TYPELESS\n    { VK_FORMAT_BC4_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_BC4_UNORM\n    { VK_FORMAT_BC4_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC4_SNORM\n    { VK_FORMAT_BC4_SNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC5_TYPELESS\n    { VK_FORMAT_BC5_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_BC5_UNORM\n    { VK_FORMAT_BC5_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC5_SNORM\n    { VK_FORMAT_BC5_SNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_B5G6R5_UNORM\n    { VK_FORMAT_R5G6B5_UNORM_PACK16,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_B5G5R5A1_UNORM\n    { VK_FORMAT_A1R5G5B5_UNORM_PACK16,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_B8G8R8A8_UNORM\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_B8G8R8X8_UNORM\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT, 0,\n      { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,\n        VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }},\n    // DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM\n    { }, // Unsupported\n    // DXGI_FORMAT_B8G8R8A8_TYPELESS\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_B8G8R8A8_UNORM_SRGB\n    { VK_FORMAT_B8G8R8A8_SRGB,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_B8G8R8X8_TYPELESS\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_B8G8R8X8_UNORM_SRGB\n    { VK_FORMAT_B8G8R8A8_SRGB,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT, 0,\n      { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,\n        VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }},\n    // DXGI_FORMAT_BC6H_TYPELESS\n    { VK_FORMAT_BC6H_UFLOAT_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_BC6H_UF16\n    { VK_FORMAT_BC6H_UFLOAT_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC6H_SF16\n    { VK_FORMAT_BC6H_SFLOAT_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC7_TYPELESS\n    { VK_FORMAT_BC7_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED },\n    // DXGI_FORMAT_BC7_UNORM\n    { VK_FORMAT_BC7_UNORM_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_BC7_UNORM_SRGB\n    { VK_FORMAT_BC7_SRGB_BLOCK,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_AYUV\n    { VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_R8G8B8A8_UINT,\n      VK_IMAGE_ASPECT_COLOR_BIT, 0,\n      { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_B,\n        VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_A } },\n    // DXGI_FORMAT_Y410\n    { }, // Unsupported\n    // DXGI_FORMAT_Y416\n    { }, // Unsupported\n    // DXGI_FORMAT_NV12\n    { VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT },\n    // DXGI_FORMAT_P010\n    { VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT },\n    // DXGI_FORMAT_P016\n    { VK_FORMAT_G16_B16R16_2PLANE_420_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT },\n    // DXGI_FORMAT_420_OPAQUE\n    { VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT },\n    // DXGI_FORMAT_YUY2\n    { VK_FORMAT_G8B8G8R8_422_UNORM,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_Y210\n    { }, // Unsupported\n    // DXGI_FORMAT_Y216\n    { }, // Unsupported\n    // DXGI_FORMAT_NV11\n    { }, // Unsupported\n    // DXGI_FORMAT_AI44\n    { }, // Unsupported\n    // DXGI_FORMAT_IA44\n    { }, // Unsupported\n    // DXGI_FORMAT_P8\n    { }, // Unsupported\n    // DXGI_FORMAT_A8P8\n    { }, // Unsupported\n    // DXGI_FORMAT_B4G4R4A4_UNORM\n    { VK_FORMAT_A4R4G4B4_UNORM_PACK16,\n      VK_FORMAT_UNDEFINED,\n      VK_FORMAT_UNDEFINED,\n      VK_IMAGE_ASPECT_COLOR_BIT },\n    // DXGI_FORMAT_P208\n    { }, // Unsupported\n    // DXGI_FORMAT_V208\n    { }, // Unsupported\n    // DXGI_FORMAT_V408\n    { }, // Unsupported\n  }};\n\n\n  const std::array<DXGI_VK_FORMAT_FAMILY, 133> g_dxgiFamilies = {{\n    // DXGI_FORMAT_UNKNOWN\n    { },\n    // DXGI_FORMAT_R32G32B32A32_TYPELESS\n    { VK_FORMAT_R32G32B32A32_UINT,\n      VK_FORMAT_R32G32B32A32_SINT,\n      VK_FORMAT_R32G32B32A32_SFLOAT },\n    // DXGI_FORMAT_R32G32B32A32_FLOAT\n    { },\n    // DXGI_FORMAT_R32G32B32A32_UINT\n    { },\n    // DXGI_FORMAT_R32G32B32A32_SINT\n    { },\n    // DXGI_FORMAT_R32G32B32_TYPELESS\n    { VK_FORMAT_R32G32B32_UINT,\n      VK_FORMAT_R32G32B32_SINT,\n      VK_FORMAT_R32G32B32_SFLOAT },\n    // DXGI_FORMAT_R32G32B32_FLOAT\n    { },\n    // DXGI_FORMAT_R32G32B32_UINT\n    { },\n    // DXGI_FORMAT_R32G32B32_SINT\n    { },\n    // DXGI_FORMAT_R16G16B16A16_TYPELESS\n    { VK_FORMAT_R16G16B16A16_UNORM,\n      VK_FORMAT_R16G16B16A16_SNORM,\n      VK_FORMAT_R16G16B16A16_UINT,\n      VK_FORMAT_R16G16B16A16_SINT,\n      VK_FORMAT_R16G16B16A16_SFLOAT },\n    // DXGI_FORMAT_R16G16B16A16_FLOAT\n    { },\n    // DXGI_FORMAT_R16G16B16A16_UNORM\n    { },\n    // DXGI_FORMAT_R16G16B16A16_UINT\n    { },\n    // DXGI_FORMAT_R16G16B16A16_SNORM\n    { },\n    // DXGI_FORMAT_R16G16B16A16_SINT\n    { },\n    // DXGI_FORMAT_R32G32_TYPELESS\n    { VK_FORMAT_R32G32_UINT,\n      VK_FORMAT_R32G32_SINT,\n      VK_FORMAT_R32G32_SFLOAT },\n    // DXGI_FORMAT_R32G32_FLOAT\n    { },\n    // DXGI_FORMAT_R32G32_UINT\n    { },\n    // DXGI_FORMAT_R32G32_SINT\n    { },\n    // DXGI_FORMAT_R32G8X24_TYPELESS\n    { },\n    // DXGI_FORMAT_D32_FLOAT_S8X24_UINT\n    { },\n    // DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS\n    { },\n    // DXGI_FORMAT_X32_TYPELESS_G8X24_UINT\n    { },\n    // DXGI_FORMAT_R10G10B10A2_TYPELESS\n    { VK_FORMAT_A2B10G10R10_UNORM_PACK32,\n      VK_FORMAT_A2B10G10R10_UINT_PACK32 },\n    // DXGI_FORMAT_R10G10B10A2_UNORM\n    { },\n    // DXGI_FORMAT_R10G10B10A2_UINT\n    { },\n    // DXGI_FORMAT_R11G11B10_FLOAT\n    { },\n    // DXGI_FORMAT_R8G8B8A8_TYPELESS\n    { VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_R8G8B8A8_SNORM,\n      VK_FORMAT_R8G8B8A8_SRGB,\n      VK_FORMAT_R8G8B8A8_UINT,\n      VK_FORMAT_R8G8B8A8_SINT },\n    // DXGI_FORMAT_R8G8B8A8_UNORM\n    { VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_R8G8B8A8_SRGB },\n    // DXGI_FORMAT_R8G8B8A8_UNORM_SRGB\n    { VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_R8G8B8A8_SRGB },\n    // DXGI_FORMAT_R8G8B8A8_UINT\n    { },\n    // DXGI_FORMAT_R8G8B8A8_SNORM\n    { },\n    // DXGI_FORMAT_R8G8B8A8_SINT\n    { },\n    // DXGI_FORMAT_R16G16_TYPELESS\n    { VK_FORMAT_R16G16_UNORM,\n      VK_FORMAT_R16G16_SNORM,\n      VK_FORMAT_R16G16_UINT,\n      VK_FORMAT_R16G16_SINT,\n      VK_FORMAT_R16G16_SFLOAT },\n    // DXGI_FORMAT_R16G16_FLOAT\n    { },\n    // DXGI_FORMAT_R16G16_UNORM\n    { },\n    // DXGI_FORMAT_R16G16_UINT\n    { },\n    // DXGI_FORMAT_R16G16_SNORM\n    { },\n    // DXGI_FORMAT_R16G16_SINT\n    { },\n    // DXGI_FORMAT_R32_TYPELESS\n    { VK_FORMAT_R32_UINT,\n      VK_FORMAT_R32_SINT,\n      VK_FORMAT_R32_SFLOAT },\n    // DXGI_FORMAT_D32_FLOAT\n    { },\n    // DXGI_FORMAT_R32_FLOAT\n    { },\n    // DXGI_FORMAT_R32_UINT\n    { },\n    // DXGI_FORMAT_R32_SINT\n    { },\n    // DXGI_FORMAT_R24G8_TYPELESS\n    { },\n    // DXGI_FORMAT_D24_UNORM_S8_UINT\n    { },\n    // DXGI_FORMAT_R24_UNORM_X8_TYPELESS\n    { },\n    // DXGI_FORMAT_X24_TYPELESS_G8_UINT\n    { },\n    // DXGI_FORMAT_R8G8_TYPELESS\n    { VK_FORMAT_R8G8_UNORM,\n      VK_FORMAT_R8G8_SNORM,\n      VK_FORMAT_R8G8_UINT,\n      VK_FORMAT_R8G8_SINT },\n    // DXGI_FORMAT_R8G8_UNORM\n    { },\n    // DXGI_FORMAT_R8G8_UINT\n    { },\n    // DXGI_FORMAT_R8G8_SNORM\n    { },\n    // DXGI_FORMAT_R8G8_SINT\n    { },\n    // DXGI_FORMAT_R16_TYPELESS\n    { VK_FORMAT_R16_UNORM,\n      VK_FORMAT_R16_SNORM,\n      VK_FORMAT_R16_UINT,\n      VK_FORMAT_R16_SINT,\n      VK_FORMAT_R16_SFLOAT },\n    // DXGI_FORMAT_R16_FLOAT\n    { },\n    // DXGI_FORMAT_D16_UNORM\n    { },\n    // DXGI_FORMAT_R16_UNORM\n    { },\n    // DXGI_FORMAT_R16_UINT\n    { },\n    // DXGI_FORMAT_R16_SNORM\n    { },\n    // DXGI_FORMAT_R16_SINT\n    { },\n    // DXGI_FORMAT_R8_TYPELESS\n    { VK_FORMAT_R8_UNORM,\n      VK_FORMAT_R8_SNORM,\n      VK_FORMAT_R8_UINT,\n      VK_FORMAT_R8_SINT },\n    // DXGI_FORMAT_R8_UNORM\n    { },\n    // DXGI_FORMAT_R8_UINT\n    { },\n    // DXGI_FORMAT_R8_SNORM\n    { },\n    // DXGI_FORMAT_R8_SINT\n    { },\n    // DXGI_FORMAT_A8_UNORM\n    { },\n    // DXGI_FORMAT_R1_UNORM\n    { }, // Unsupported\n    // DXGI_FORMAT_R9G9B9E5_SHAREDEXP\n    { },\n    // DXGI_FORMAT_R8G8_B8G8_UNORM\n    { },\n    // DXGI_FORMAT_G8R8_G8B8_UNORM\n    { },\n    // DXGI_FORMAT_BC1_TYPELESS\n    { VK_FORMAT_BC1_RGBA_UNORM_BLOCK,\n      VK_FORMAT_BC1_RGBA_SRGB_BLOCK },\n    // DXGI_FORMAT_BC1_UNORM\n    { VK_FORMAT_BC1_RGBA_UNORM_BLOCK,\n      VK_FORMAT_BC1_RGBA_SRGB_BLOCK },\n    // DXGI_FORMAT_BC1_UNORM_SRGB\n    { VK_FORMAT_BC1_RGBA_UNORM_BLOCK,\n      VK_FORMAT_BC1_RGBA_SRGB_BLOCK },\n    // DXGI_FORMAT_BC2_TYPELESS\n    { VK_FORMAT_BC2_UNORM_BLOCK,\n      VK_FORMAT_BC2_SRGB_BLOCK },\n    // DXGI_FORMAT_BC2_UNORM\n    { VK_FORMAT_BC2_UNORM_BLOCK,\n      VK_FORMAT_BC2_SRGB_BLOCK },\n    // DXGI_FORMAT_BC2_UNORM_SRGB\n    { VK_FORMAT_BC2_UNORM_BLOCK,\n      VK_FORMAT_BC2_SRGB_BLOCK },\n    // DXGI_FORMAT_BC3_TYPELESS\n    { VK_FORMAT_BC3_UNORM_BLOCK,\n      VK_FORMAT_BC3_SRGB_BLOCK },\n    // DXGI_FORMAT_BC3_UNORM\n    { VK_FORMAT_BC3_UNORM_BLOCK,\n      VK_FORMAT_BC3_SRGB_BLOCK },\n    // DXGI_FORMAT_BC3_UNORM_SRGB\n    { VK_FORMAT_BC3_UNORM_BLOCK,\n      VK_FORMAT_BC3_SRGB_BLOCK },\n    // DXGI_FORMAT_BC4_TYPELESS\n    { VK_FORMAT_BC4_UNORM_BLOCK,\n      VK_FORMAT_BC4_SNORM_BLOCK },\n    // DXGI_FORMAT_BC4_UNORM\n    { },\n    // DXGI_FORMAT_BC4_SNORM\n    { },\n    // DXGI_FORMAT_BC5_TYPELESS\n    { VK_FORMAT_BC5_UNORM_BLOCK,\n      VK_FORMAT_BC5_SNORM_BLOCK },\n    // DXGI_FORMAT_BC5_UNORM\n    { },\n    // DXGI_FORMAT_BC5_SNORM\n    { },\n    // DXGI_FORMAT_B5G6R5_UNORM\n    { },\n    // DXGI_FORMAT_B5G5R5A1_UNORM\n    { },\n    // DXGI_FORMAT_B8G8R8A8_UNORM\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_B8G8R8A8_SRGB },\n    // DXGI_FORMAT_B8G8R8X8_UNORM\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_B8G8R8A8_SRGB },\n    // DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM\n    { }, // Unsupported\n    // DXGI_FORMAT_B8G8R8A8_TYPELESS\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_B8G8R8A8_SRGB },\n    // DXGI_FORMAT_B8G8R8A8_UNORM_SRGB\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_B8G8R8A8_SRGB },\n    // DXGI_FORMAT_B8G8R8X8_TYPELESS\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_B8G8R8A8_SRGB },\n    // DXGI_FORMAT_B8G8R8X8_UNORM_SRGB\n    { VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_B8G8R8A8_SRGB },\n    // DXGI_FORMAT_BC6H_TYPELESS\n    { VK_FORMAT_BC6H_UFLOAT_BLOCK,\n      VK_FORMAT_BC6H_SFLOAT_BLOCK },\n    // DXGI_FORMAT_BC6H_UF16\n    { },\n    // DXGI_FORMAT_BC6H_SF16\n    { },\n    // DXGI_FORMAT_BC7_TYPELESS\n    { VK_FORMAT_BC7_UNORM_BLOCK,\n      VK_FORMAT_BC7_SRGB_BLOCK },\n    // DXGI_FORMAT_BC7_UNORM\n    { VK_FORMAT_BC7_UNORM_BLOCK,\n      VK_FORMAT_BC7_SRGB_BLOCK },\n    // DXGI_FORMAT_BC7_UNORM_SRGB\n    { VK_FORMAT_BC7_UNORM_BLOCK,\n      VK_FORMAT_BC7_SRGB_BLOCK },\n    // DXGI_FORMAT_AYUV\n    { VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_R8G8B8A8_UINT },\n    // DXGI_FORMAT_Y410\n    { }, // Unsupported\n    // DXGI_FORMAT_Y416\n    { }, // Unsupported\n    // DXGI_FORMAT_NV12\n    { VK_FORMAT_R8_UNORM,\n      VK_FORMAT_R8G8_UNORM,\n      VK_FORMAT_R8_UINT,\n      VK_FORMAT_R8G8_UINT },\n    // DXGI_FORMAT_P010\n    { VK_FORMAT_R16_UNORM,\n      VK_FORMAT_R16G16_UNORM,\n      VK_FORMAT_R16_UINT,\n      VK_FORMAT_R16G16_UINT },\n    // DXGI_FORMAT_P016\n    { VK_FORMAT_R16_UNORM,\n      VK_FORMAT_R16G16_UNORM,\n      VK_FORMAT_R16_UINT,\n      VK_FORMAT_R16G16_UINT },\n    // DXGI_FORMAT_420_OPAQUE\n    { VK_FORMAT_R8_UNORM,\n      VK_FORMAT_R8G8_UNORM,\n      VK_FORMAT_R8_UINT,\n      VK_FORMAT_R8G8_UINT },\n    // DXGI_FORMAT_YUY2\n    { VK_FORMAT_G8B8G8R8_422_UNORM,\n      VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_R8G8B8A8_UINT },\n    // DXGI_FORMAT_Y210\n    { }, // Unsupported\n    // DXGI_FORMAT_Y216\n    { }, // Unsupported\n    // DXGI_FORMAT_NV11\n    { }, // Unsupported\n    // DXGI_FORMAT_AI44\n    { }, // Unsupported\n    // DXGI_FORMAT_IA44\n    { }, // Unsupported\n    // DXGI_FORMAT_P8\n    { }, // Unsupported\n    // DXGI_FORMAT_A8P8\n    { }, // Unsupported\n    // DXGI_FORMAT_B4G4R4A4_UNORM\n    { }, // Unsupported\n    // DXGI_FORMAT_P208\n    { }, // Unsupported\n    // DXGI_FORMAT_V208\n    { }, // Unsupported\n    // DXGI_FORMAT_V408\n    { }, // Unsupported\n  }};\n  \n  \n  DXGIVkFormatTable::DXGIVkFormatTable(const Rc<DxvkDevice>& device)\n  : m_dxgiFormats (g_dxgiFormats), m_dxgiFamilies(g_dxgiFamilies) {\n    // AMD do not support 24-bit depth buffers on Vulkan,\n    // so we have to fall back to a 32-bit depth format.\n    if (!CheckImageFormatSupport(device, VK_FORMAT_D24_UNORM_S8_UINT,\n          VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT |\n          VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT)) {\n      Logger::info(\"DXGI: VK_FORMAT_D24_UNORM_S8_UINT -> VK_FORMAT_D32_SFLOAT_S8_UINT\");\n      RemapDepthFormat(DXGI_FORMAT_R24G8_TYPELESS,        VK_FORMAT_D32_SFLOAT_S8_UINT);\n      RemapDepthFormat(DXGI_FORMAT_R24_UNORM_X8_TYPELESS, VK_FORMAT_D32_SFLOAT_S8_UINT);\n      RemapDepthFormat(DXGI_FORMAT_X24_TYPELESS_G8_UINT,  VK_FORMAT_D32_SFLOAT_S8_UINT);\n      RemapDepthFormat(DXGI_FORMAT_D24_UNORM_S8_UINT,     VK_FORMAT_D32_SFLOAT_S8_UINT);\n    }\n\n    if (!CheckImageFormatSupport(device, VK_FORMAT_A8_UNORM_KHR,\n          VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT |\n          VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT |\n          VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) {\n      Logger::info(\"DXGI: VK_FORMAT_A8_UNORM_KHR -> VK_FORMAT_R8_UNORM\");\n      RemapColorFormat(DXGI_FORMAT_A8_UNORM, VK_FORMAT_R8_UNORM, {\n          VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO,\n          VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R });\n    }\n  }\n  \n  \n  DXGIVkFormatTable::~DXGIVkFormatTable() {\n    \n  }\n  \n  \n  DXGI_VK_FORMAT_INFO DXGIVkFormatTable::GetFormatInfo(\n          DXGI_FORMAT         Format,\n          DXGI_VK_FORMAT_MODE Mode) const {\n    return GetFormatInfoFromMapping(\n      GetFormatMapping(Format), Mode);\n  }\n\n\n  DXGI_VK_FORMAT_INFO DXGIVkFormatTable::GetPackedFormatInfo(\n          DXGI_FORMAT         Format,\n          DXGI_VK_FORMAT_MODE Mode) const {\n    return GetFormatInfoFromMapping(\n      GetPackedFormatMapping(Format), Mode);\n  }\n  \n  \n  DXGI_VK_FORMAT_FAMILY DXGIVkFormatTable::GetFormatFamily(\n          DXGI_FORMAT         Format,\n          DXGI_VK_FORMAT_MODE Mode) const {\n    if (Mode == DXGI_VK_FORMAT_MODE_DEPTH)\n      return DXGI_VK_FORMAT_FAMILY();\n    \n    const size_t formatId = size_t(Format);\n\n    return formatId < m_dxgiFamilies.size()\n      ? m_dxgiFamilies[formatId]\n      : m_dxgiFamilies[0];\n  }\n\n\n  DXGI_VK_FORMAT_INFO DXGIVkFormatTable::GetFormatInfoFromMapping(\n    const DXGI_VK_FORMAT_MAPPING* pMapping,\n          DXGI_VK_FORMAT_MODE   Mode) const {\n    switch (Mode) {\n      case DXGI_VK_FORMAT_MODE_ANY:\n        return pMapping->FormatColor != VK_FORMAT_UNDEFINED\n          ? DXGI_VK_FORMAT_INFO { pMapping->FormatColor, pMapping->AspectColor, pMapping->Swizzle }\n          : DXGI_VK_FORMAT_INFO { pMapping->FormatDepth, pMapping->AspectDepth };\n      \n      case DXGI_VK_FORMAT_MODE_COLOR:\n        return { pMapping->FormatColor, pMapping->AspectColor, pMapping->Swizzle };\n      \n      case DXGI_VK_FORMAT_MODE_DEPTH:\n        return { pMapping->FormatDepth, pMapping->AspectDepth };\n      \n      case DXGI_VK_FORMAT_MODE_RAW:\n        return { pMapping->FormatRaw, pMapping->AspectColor };\n    }\n    \n    Logger::err(\"DXGI: GetFormatInfoFromMapping: Internal error\");\n    return DXGI_VK_FORMAT_INFO();\n  }\n\n\n  const DXGI_VK_FORMAT_MAPPING* DXGIVkFormatTable::GetFormatMapping(\n          DXGI_FORMAT         Format) const {\n    const size_t formatId = size_t(Format);\n    \n    return formatId < m_dxgiFormats.size()\n      ? &m_dxgiFormats[formatId]\n      : &m_dxgiFormats[0];\n  }\n  \n\n  const DXGI_VK_FORMAT_MAPPING* DXGIVkFormatTable::GetPackedFormatMapping(\n          DXGI_FORMAT         Format) const {\n    const size_t formatId = size_t(Format);\n    \n    return formatId < g_dxgiFormats.size()\n      ? &g_dxgiFormats[formatId]\n      : &g_dxgiFormats[0];\n  }\n  \n\n  bool DXGIVkFormatTable::CheckImageFormatSupport(\n    const Rc<DxvkDevice>&       Device,\n          VkFormat              Format,\n          VkFormatFeatureFlags2 Features) const {\n    DxvkFormatFeatures supported = Device->getFormatFeatures(Format);\n    \n    return (supported.linear  & Features) == Features\n        || (supported.optimal & Features) == Features;\n  }\n  \n  \n  void DXGIVkFormatTable::RemapDepthFormat(\n          DXGI_FORMAT         Format,\n          VkFormat            Target) {\n    m_dxgiFormats[uint32_t(Format)].FormatDepth = Target;\n  }\n  \n\n  void DXGIVkFormatTable::RemapColorFormat(\n          DXGI_FORMAT         Format,\n          VkFormat            Target,\n          VkComponentMapping  Swizzle) {\n    m_dxgiFormats[uint32_t(Format)].FormatColor = Target;\n    m_dxgiFormats[uint32_t(Format)].Swizzle = Swizzle;\n  }\n  \n}"
  },
  {
    "path": "src/dxgi/dxgi_format.h",
    "content": "#pragma once\n\n#include \"dxgi_include.h\"\n\n#include \"../dxvk/dxvk_device.h\"\n#include \"../dxvk/dxvk_format.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Format mapping\n   * \n   * Maps a DXGI format to a set of Vulkan formats.\n   */\n  struct DXGI_VK_FORMAT_MAPPING {\n    VkFormat              FormatColor   = VK_FORMAT_UNDEFINED;  ///< Corresponding color format\n    VkFormat              FormatDepth   = VK_FORMAT_UNDEFINED;  ///< Corresponding depth format\n    VkFormat              FormatRaw     = VK_FORMAT_UNDEFINED;  ///< Bit-compatible integer format\n    VkImageAspectFlags    AspectColor   = 0;                    ///< Defined aspects for the color format\n    VkImageAspectFlags    AspectDepth   = 0;                    ///< Defined aspects for the depth format\n    VkComponentMapping    Swizzle       = {                     ///< Color component swizzle\n      VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,\n      VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };\n  };\n  \n  /**\n   * \\brief Format info\n   * \n   * Stores a Vulkan image format for a given\n   * DXGI format and some additional information\n   * on how resources with the particular format\n   * are supposed to be used.\n   */\n  struct DXGI_VK_FORMAT_INFO {\n    VkFormat              Format      = VK_FORMAT_UNDEFINED;  ///< Corresponding color format\n    VkImageAspectFlags    Aspect      = 0;                    ///< Defined image aspect mask\n    VkComponentMapping    Swizzle     = {                     ///< Component swizzle\n      VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,\n      VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };\n  };\n  \n  /**\n   * \\brief Format lookup mode\n   * \n   * When looking up an image format, additional information\n   * might be needed on how the image is going to be used.\n   * This is used to properly map typeless formats and color\n   * formats to depth formats if they are used on depth images.\n   */\n  enum DXGI_VK_FORMAT_MODE {\n    DXGI_VK_FORMAT_MODE_ANY   = 0,  ///< Color first, then depth\n    DXGI_VK_FORMAT_MODE_COLOR = 1,  ///< Color only\n    DXGI_VK_FORMAT_MODE_DEPTH = 2,  ///< Depth only\n    DXGI_VK_FORMAT_MODE_RAW   = 3,  ///< Unsigned integer format\n  };\n  \n  \n  /**\n   * \\brief Format family\n   * \n   * Stores a set of compatible formats. This can\n   * be used to aggregate formats for the image\n   * format list extension.\n   */\n  struct DXGI_VK_FORMAT_FAMILY {\n    constexpr static size_t MaxSize = 8;\n\n    DXGI_VK_FORMAT_FAMILY() { }\n    DXGI_VK_FORMAT_FAMILY(const std::initializer_list<VkFormat>& FormatList) {\n      for (VkFormat f : FormatList)\n        Add(f);\n    }\n\n    BOOL Add(VkFormat Format) {\n      for (UINT i = 0; i < FormatCount; i++) {\n        if (Formats[i] == Format)\n          return TRUE;\n      }\n\n      if (FormatCount < MaxSize) {\n        Formats[FormatCount++] = Format;\n        return TRUE;\n      } return FALSE;\n    }\n\n    UINT     FormatCount = 0;\n    VkFormat Formats[MaxSize];\n  };\n  \n  \n  /**\n   * \\brief Format table\n   * \n   * Initializes a format table for a specific\n   * device and provides methods to look up\n   * formats.\n   */\n  class DXGIVkFormatTable {\n    \n  public:\n    \n    DXGIVkFormatTable(\n      const Rc<DxvkDevice>& device);\n    ~DXGIVkFormatTable();\n    \n    /**\n     * \\brief Retrieves info for a given DXGI format\n     * \n     * \\param [in] Format The DXGI format to look up\n     * \\param [in] Mode the format lookup mode\n     * \\returns Format info\n     */\n    DXGI_VK_FORMAT_INFO GetFormatInfo(\n            DXGI_FORMAT         Format,\n            DXGI_VK_FORMAT_MODE Mode) const;\n    \n    /**\n     * \\brief Retrieves original info for a given DXGI format\n     * \n     * Doesn't perform any format adjustment, so this\n     * can be used to determine the packed data format\n     * of a DXGI format for things like data uploads.\n     * \\param [in] Format The DXGI format to look up\n     * \\param [in] Mode the format lookup mode\n     * \\returns Format info\n     */\n    DXGI_VK_FORMAT_INFO GetPackedFormatInfo(\n            DXGI_FORMAT         Format,\n            DXGI_VK_FORMAT_MODE Mode) const;\n    \n    /**\n     * \\brief Retrieves a format family\n     * \n     * \\param [in] Format The format to query\n     * \\param [in] Mode Image format mode\n     * \\returns Image format family\n     */\n    DXGI_VK_FORMAT_FAMILY GetFormatFamily(\n            DXGI_FORMAT         Format,\n            DXGI_VK_FORMAT_MODE Mode) const;\n    \n  private:\n    \n    std::array<DXGI_VK_FORMAT_MAPPING, 133> m_dxgiFormats;\n    std::array<DXGI_VK_FORMAT_FAMILY,  133> m_dxgiFamilies;\n\n    DXGI_VK_FORMAT_INFO GetFormatInfoFromMapping(\n      const DXGI_VK_FORMAT_MAPPING* pMapping,\n            DXGI_VK_FORMAT_MODE   Mode) const;\n    \n    const DXGI_VK_FORMAT_MAPPING* GetFormatMapping(\n            DXGI_FORMAT           Format) const;\n\n    const DXGI_VK_FORMAT_MAPPING* GetPackedFormatMapping(\n            DXGI_FORMAT           Format) const;\n\n    bool CheckImageFormatSupport(\n      const Rc<DxvkDevice>&       Device,\n            VkFormat              Format,\n            VkFormatFeatureFlags2 Features) const;\n    \n    void RemapDepthFormat(\n            DXGI_FORMAT           Format,\n            VkFormat              Target);\n        \n    void RemapColorFormat(\n            DXGI_FORMAT           Format,\n            VkFormat              Target,\n            VkComponentMapping    Swizzle);\n\n  };\n  \n};"
  },
  {
    "path": "src/dxgi/dxgi_include.h",
    "content": "#pragma once\n\n//for some reason we need to specify __declspec(dllexport) for MinGW\n#if defined(__WINE__) || !defined(_WIN32)\n  #define DLLEXPORT __attribute__((visibility(\"default\")))\n#else\n  #define DLLEXPORT\n#endif\n\n#include \"../util/com/com_destruction_notifier.h\"\n#include \"../util/com/com_guid.h\"\n#include \"../util/com/com_object.h\"\n#include \"../util/com/com_pointer.h\"\n\n#include \"../util/log/log.h\"\n#include \"../util/log/log_debug.h\"\n\n#include \"../util/rc/util_rc.h\"\n#include \"../util/rc/util_rc_ptr.h\"\n\n#include \"../util/util_env.h\"\n#include \"../util/util_enum.h\"\n#include \"../util/util_error.h\"\n#include \"../util/util_flags.h\"\n#include \"../util/util_likely.h\"\n#include \"../util/util_math.h\"\n#include \"../util/util_string.h\"\n\n#include <dxgi1_6.h>\n"
  },
  {
    "path": "src/dxgi/dxgi_interfaces.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_include.h\"\n\n#include \"../wsi/wsi_edid.h\"\n\n#include \"../util/util_time.h\"\n\n#include \"dxgi_format.h\"\n#include \"dxgi_include.h\"\n\nnamespace dxvk {\n  class DxgiAdapter;\n  class DxgiSwapChain;\n  class DxvkAdapter;\n  class DxvkBuffer;\n  class DxvkDevice;\n  class DxvkImage;\n}\n\nstruct IDXGIVkInteropDevice;\n\n\n/**\n * \\brief Per-monitor data\n */\nstruct DXGI_VK_MONITOR_DATA {\n  dxvk::DxgiSwapChain*  pSwapChain;\n  DXGI_FRAME_STATISTICS FrameStats;\n  DXGI_GAMMA_CONTROL    GammaCurve;\n  DXGI_MODE_DESC1       LastMode;\n  dxvk::wsi::WsiDisplayMetadata DisplayMetadata;\n};\n\n\n/**\n * \\brief HDR metadata struct\n */\nstruct DXGI_VK_HDR_METADATA {\n  DXGI_HDR_METADATA_TYPE    Type;\n  union {\n    DXGI_HDR_METADATA_HDR10 HDR10;\n  };\n};\n\n\n/**\n * \\brief Frame statistics\n */\nstruct DXGI_VK_FRAME_STATISTICS {\n  UINT64 PresentCount;\n  UINT64 PresentQPCTime;\n};\n\n\n/**\n * \\brief Private DXGI surface factory\n */\nMIDL_INTERFACE(\"1e7895a1-1bc3-4f9c-a670-290a4bc9581a\")\nIDXGIVkSurfaceFactory : public IUnknown {\n  virtual VkResult STDMETHODCALLTYPE CreateSurface(\n          VkInstance                Instance,\n          VkPhysicalDevice          Adapter,\n          VkSurfaceKHR*             pSurface) = 0;\n};\n\n\n/**\n * \\brief Private DXGI presenter\n * \n * Presenter interface that allows the DXGI swap\n * chain implementation to remain API-agnostic,\n * so that common code can stay in one class.\n */\nMIDL_INTERFACE(\"e4a9059e-b569-46ab-8de7-501bd2bc7f7a\")\nIDXGIVkSwapChain : public IUnknown {\n  virtual HRESULT STDMETHODCALLTYPE GetDesc(\n          DXGI_SWAP_CHAIN_DESC1*    pDesc) = 0;\n  \n  virtual HRESULT STDMETHODCALLTYPE GetAdapter(\n          REFIID                    riid,\n          void**                    ppvObject) = 0;\n  \n  virtual HRESULT STDMETHODCALLTYPE GetDevice(\n          REFIID                    riid,\n          void**                    ppDevice) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE GetImage(\n          UINT                      BufferId,\n          REFIID                    riid,\n          void**                    ppBuffer) = 0;\n\n  virtual UINT STDMETHODCALLTYPE GetImageIndex() = 0;\n\n  virtual UINT STDMETHODCALLTYPE GetFrameLatency() = 0;\n\n  virtual HANDLE STDMETHODCALLTYPE GetFrameLatencyEvent() = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE ChangeProperties(\n    const DXGI_SWAP_CHAIN_DESC1*    pDesc,\n    const UINT*                     pNodeMasks,\n          IUnknown* const*          ppPresentQueues) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetPresentRegion(\n    const RECT*                     pRegion) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetGammaControl(\n          UINT                      NumControlPoints,\n    const DXGI_RGB*                 pControlPoints) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetFrameLatency(\n          UINT                      MaxLatency) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE Present(\n          UINT                      SyncInterval,\n          UINT                      PresentFlags,\n    const DXGI_PRESENT_PARAMETERS*  pPresentParameters) = 0;\n\n  virtual UINT STDMETHODCALLTYPE CheckColorSpaceSupport(\n          DXGI_COLOR_SPACE_TYPE     ColorSpace) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetColorSpace(\n          DXGI_COLOR_SPACE_TYPE     ColorSpace) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetHDRMetaData(\n    const DXGI_VK_HDR_METADATA*     pMetaData) = 0;\n};\n\n\nMIDL_INTERFACE(\"785326d4-b77b-4826-ae70-8d08308ee6d1\")\nIDXGIVkSwapChain1 : public IDXGIVkSwapChain {\n  virtual void STDMETHODCALLTYPE GetLastPresentCount(\n          UINT64*                   pLastPresentCount) = 0;\n\n  virtual void STDMETHODCALLTYPE GetFrameStatistics(\n          DXGI_VK_FRAME_STATISTICS* pFrameStatistics) = 0;\n};\n\n\nMIDL_INTERFACE(\"aed91093-e02e-458c-bdef-a97da1a7e6d2\")\nIDXGIVkSwapChain2 : public IDXGIVkSwapChain1 {\n  virtual void STDMETHODCALLTYPE SetTargetFrameRate(\n          double                    FrameRate) = 0;\n};\n\n\n/**\n * \\brief Private DXGI presenter factory\n */\nMIDL_INTERFACE(\"e7d6c3ca-23a0-4e08-9f2f-ea5231df6633\")\nIDXGIVkSwapChainFactory : public IUnknown {\n  virtual HRESULT STDMETHODCALLTYPE CreateSwapChain(\n          IDXGIVkSurfaceFactory*    pSurfaceFactory,\n    const DXGI_SWAP_CHAIN_DESC1*    pDesc,\n          IDXGIVkSwapChain**        ppSwapChain) = 0;\n};\n\n\n/**\n * \\brief Private DXGI adapter interface\n * \n * The implementation of \\c IDXGIAdapter holds a\n * \\ref DxvkAdapter which can be retrieved using\n * this interface.\n */\nMIDL_INTERFACE(\"907bf281-ea3c-43b4-a8e4-9f231107b4ff\")\nIDXGIDXVKAdapter : public IDXGIAdapter4 {\n  virtual dxvk::Rc<dxvk::DxvkAdapter> STDMETHODCALLTYPE GetDXVKAdapter() = 0;\n\n  virtual dxvk::Rc<dxvk::DxvkInstance> STDMETHODCALLTYPE GetDXVKInstance() = 0;\n\n};\n\n\n/**\n * \\brief Private DXGI device interface\n */\nMIDL_INTERFACE(\"92a5d77b-b6e1-420a-b260-fdd701272827\")\nIDXGIDXVKDevice : public IUnknown {\n  virtual void STDMETHODCALLTYPE SetAPIVersion(\n            UINT                    Version) = 0;\n\n  virtual UINT STDMETHODCALLTYPE GetAPIVersion() = 0;\n\n};\n\n\n/**\n * \\brief Private DXGI monitor info interface\n * \n * Can be queried from the DXGI factory to store monitor\n * info globally, with a lifetime that exceeds that of\n * the \\c IDXGIOutput or \\c IDXGIAdapter objects.\n */\nMIDL_INTERFACE(\"c06a236f-5be3-448a-8943-89c611c0c2c1\")\nIDXGIVkMonitorInfo : public IUnknown {\n  /**\n   * \\brief Initializes monitor data\n   * \n   * Fails if data for the given monitor already exists.\n   * \\param [in] hMonitor The monitor handle\n   * \\param [in] pData Initial data\n   */\n  virtual HRESULT STDMETHODCALLTYPE InitMonitorData(\n          HMONITOR                hMonitor,\n    const DXGI_VK_MONITOR_DATA*   pData) = 0;\n\n  /**\n   * \\brief Retrieves and locks monitor data\n   * \n   * Fails if no data for the given monitor exists.\n   * \\param [in] hMonitor The monitor handle\n   * \\param [out] Pointer to monitor data\n   * \\returns S_OK on success\n   */\n  virtual HRESULT STDMETHODCALLTYPE AcquireMonitorData(\n          HMONITOR                hMonitor,\n          DXGI_VK_MONITOR_DATA**  ppData) = 0;\n  \n  /**\n   * \\brief Unlocks monitor data\n   * \n   * Must be called after each successful\n   * call to \\ref AcquireMonitorData.\n   * \\param [in] hMonitor The monitor handle\n   */\n  virtual void STDMETHODCALLTYPE ReleaseMonitorData() = 0;\n\n  /**\n   * \\brief Punt global colorspace\n   *\n   * This exists to satiate a requirement for\n   * IDXGISwapChain::SetColorSpace1 punting Windows into\n   * the global \"HDR mode\".\n   *\n   * This operation is atomic and does not require\n   * owning any monitor data.\n   *\n   * \\param [in] ColorSpace The colorspace\n   */\n  virtual void STDMETHODCALLTYPE PuntColorSpace(DXGI_COLOR_SPACE_TYPE ColorSpace) = 0;\n\n  /**\n   * \\brief Get current global colorspace\n   *\n   * This operation is atomic and does not require\n   * owning any monitor data.\n   *\n   * \\returns Current global colorspace\n   */\n  virtual DXGI_COLOR_SPACE_TYPE STDMETHODCALLTYPE CurrentColorSpace() const = 0;\n\n};\n\n\n/**\n * \\brief DXGI surface interface for Vulkan interop\n * \n * Provides access to the backing resource of a\n * DXGI surface, which is typically a D3D texture.\n */\nMIDL_INTERFACE(\"5546cf8c-77e7-4341-b05d-8d4d5000e77d\")\nIDXGIVkInteropSurface : public IUnknown {\n  /**\n   * \\brief Retrieves device interop interfaceSlots\n   * \n   * Queries the device that owns the surface for\n   * the \\ref IDXGIVkInteropDevice interface.\n   * \\param [out] ppDevice The device interface\n   * \\returns \\c S_OK on success\n   */\n  virtual HRESULT STDMETHODCALLTYPE GetDevice(\n          IDXGIVkInteropDevice**  ppDevice) = 0;\n  \n  /**\n   * \\brief Retrieves Vulkan image info\n   * \n   * Retrieves both the image handle as well as the image's\n   * properties. Any of the given pointers may be \\c nullptr.\n   * \n   * If \\c pInfo is not \\c nullptr, the following rules apply:\n   * - \\c pInfo->sType \\e must be \\c VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO\n   * - \\c pInfo->pNext \\e must be \\c nullptr or point to a supported\n   *   extension-specific structure (currently none)\n   * - \\c pInfo->queueFamilyIndexCount must be the length of the\n   *   \\c pInfo->pQueueFamilyIndices array, in \\c uint32_t units.\n   * - \\c pInfo->pQueueFamilyIndices must point to a pre-allocated\n   *   array of \\c uint32_t of size \\c pInfo->pQueueFamilyIndices.\n   * \n   * \\note As of now, the sharing mode will always be\n   *       \\c VK_SHARING_MODE_EXCLUSIVE and no queue\n   *       family indices will be written to the array.\n   * \n   * After the call, the structure pointed to by \\c pInfo can\n   * be used to create an image with identical properties.\n   * \n   * If \\c pLayout is not \\c nullptr, it will receive the\n   * layout that the image will be in after flushing any\n   * outstanding commands on the device.\n   * \\param [out] pHandle The image handle\n   * \\param [out] pLayout Image layout\n   * \\param [out] pInfo Image properties\n   * \\returns \\c S_OK on success, or \\c E_INVALIDARG\n   */\n  virtual HRESULT STDMETHODCALLTYPE GetVulkanImageInfo(\n          VkImage*              pHandle,\n          VkImageLayout*        pLayout,\n          VkImageCreateInfo*    pInfo) = 0;\n};\n\n\n/**\n * \\brief DXGI device interface for Vulkan interop\n * \n * Provides access to the device and instance handles\n * as well as the queue that is used for rendering.\n */\nMIDL_INTERFACE(\"e2ef5fa5-dc21-4af7-90c4-f67ef6a09323\")\nIDXGIVkInteropDevice : public IUnknown {\n  /**\n   * \\brief Queries Vulkan handles used by DXVK\n   * \n   * \\param [out] pInstance The Vulkan instance\n   * \\param [out] pPhysDev The physical device\n   * \\param [out] pDevide The device handle\n   */\n  virtual void STDMETHODCALLTYPE GetVulkanHandles(\n          VkInstance*           pInstance,\n          VkPhysicalDevice*     pPhysDev,\n          VkDevice*             pDevice) = 0;\n  \n  /**\n   * \\brief Queries the rendering queue used by DXVK\n   * \n   * \\param [out] pQueue The Vulkan queue handle\n   * \\param [out] pQueueFamilyIndex Queue family index\n   */\n  virtual void STDMETHODCALLTYPE GetSubmissionQueue(\n          VkQueue*              pQueue,\n          uint32_t*             pQueueFamilyIndex) = 0;\n  \n  /**\n   * \\brief Transitions a surface to a given layout\n   * \n   * Executes an explicit image layout transition on the\n   * D3D device. Note that the image subresources \\e must\n   * be transitioned back to its original layout before\n   * using it again from D3D11.\n   * \\param [in] pSurface The image to transform\n   * \\param [in] pSubresources Subresources to transform\n   * \\param [in] OldLayout Current image layout\n   * \\param [in] NewLayout Desired image layout\n   */\n  virtual void STDMETHODCALLTYPE TransitionSurfaceLayout(\n          IDXGIVkInteropSurface*    pSurface,\n    const VkImageSubresourceRange*  pSubresources,\n          VkImageLayout             OldLayout,\n          VkImageLayout             NewLayout) = 0;\n  \n  /**\n   * \\brief Flushes outstanding D3D rendering commands\n   * \n   * Must be called before submitting Vulkan commands\n   * to the rendering queue if those commands use the\n   * backing resource of a D3D11 object.\n   */\n  virtual void STDMETHODCALLTYPE FlushRenderingCommands() = 0;\n  \n  /**\n   * \\brief Locks submission queue\n   * \n   * Should be called immediately before submitting\n   * Vulkan commands to the rendering queue in order\n   * to prevent DXVK from using the queue.\n   * \n   * While the submission queue is locked, no D3D11\n   * methods must be called from the locking thread,\n   * or otherwise a deadlock might occur.\n   */\n  virtual void STDMETHODCALLTYPE LockSubmissionQueue() = 0;\n  \n  /**\n   * \\brief Releases submission queue\n   * \n   * Should be called immediately after submitting\n   * Vulkan commands to the rendering queue in order\n   * to allow DXVK to submit new commands.\n   */\n  virtual void STDMETHODCALLTYPE ReleaseSubmissionQueue() = 0;\n};\n\nstruct D3D11_TEXTURE2D_DESC1;\nstruct ID3D11Texture2D;\n\n/**\n * \\brief See IDXGIVkInteropDevice.\n */\nMIDL_INTERFACE(\"e2ef5fa5-dc21-4af7-90c4-f67ef6a09324\")\nIDXGIVkInteropDevice1 : public IDXGIVkInteropDevice {\n  /**\n   * \\brief Queries the rendering queue used by DXVK\n   * \n   * \\param [out] pQueue The Vulkan queue handle\n   * \\param [out] pQueueIndex Queue index\n   * \\param [out] pQueueFamilyIndex Queue family index\n   */\n  virtual void STDMETHODCALLTYPE GetSubmissionQueue1(\n          VkQueue*              pQueue,\n          uint32_t*             pQueueIndex,\n          uint32_t*             pQueueFamilyIndex) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE CreateTexture2DFromVkImage(\n    const D3D11_TEXTURE2D_DESC1* pDesc,\n          VkImage               vkImage,\n          ID3D11Texture2D**     ppTexture2D) = 0;\n};\n\n/**\n * \\brief DXGI adapter interface for Vulkan interop\n *\n * Provides access to the physical device and\n * instance handles for the given DXGI adapter.\n */\nMIDL_INTERFACE(\"3a6d8f2c-b0e8-4ab4-b4dc-4fd24891bfa5\")\nIDXGIVkInteropAdapter : public IUnknown {\n  /**\n   * \\brief Queries Vulkan handles used by DXVK\n   *\n   * \\param [out] pInstance The Vulkan instance\n   * \\param [out] pPhysDev The physical device\n   */\n  virtual void STDMETHODCALLTYPE GetVulkanHandles(\n          VkInstance*           pInstance,\n          VkPhysicalDevice*     pPhysDev) = 0;\n};\n\n/**\n * \\brief DXGI factory interface for Vulkan interop\n */\nMIDL_INTERFACE(\"4c5e1b0d-b0c8-4131-bfd8-9b2476f7f408\")\nIDXGIVkInteropFactory : public IUnknown {\n  /**\n   * \\brief Queries Vulkan instance used by DXVK\n   *\n   * \\param [out] pInstance The Vulkan instance\n   * \\param [out] ppfnVkGetInstanceProcAddr Vulkan entry point\n   */\n  virtual void STDMETHODCALLTYPE GetVulkanInstance(\n          VkInstance*           pInstance,\n          PFN_vkGetInstanceProcAddr* ppfnVkGetInstanceProcAddr) = 0;\n};\n\n/**\n * \\brief DXGI factory interface for Vulkan interop\n */\nMIDL_INTERFACE(\"2a289dbd-2d0a-4a51-89f7-f2adce465cd6\")\nIDXGIVkInteropFactory1 : public IDXGIVkInteropFactory {\n  virtual HRESULT STDMETHODCALLTYPE GetGlobalHDRState(\n          DXGI_COLOR_SPACE_TYPE   *pOutColorSpace,\n          DXGI_HDR_METADATA_HDR10 *ppOutMetadata) = 0;\n\n  virtual HRESULT STDMETHODCALLTYPE SetGlobalHDRState(\n          DXGI_COLOR_SPACE_TYPE    ColorSpace,\n    const DXGI_HDR_METADATA_HDR10 *pMetadata) = 0;\n};\n\n\n#ifndef _MSC_VER\n__CRT_UUID_DECL(IDXGIDXVKAdapter,          0x907bf281,0xea3c,0x43b4,0xa8,0xe4,0x9f,0x23,0x11,0x07,0xb4,0xff);\n__CRT_UUID_DECL(IDXGIDXVKDevice,           0x92a5d77b,0xb6e1,0x420a,0xb2,0x60,0xfd,0xf7,0x01,0x27,0x28,0x27);\n__CRT_UUID_DECL(IDXGIVkMonitorInfo,        0xc06a236f,0x5be3,0x448a,0x89,0x43,0x89,0xc6,0x11,0xc0,0xc2,0xc1);\n__CRT_UUID_DECL(IDXGIVkInteropFactory,     0x4c5e1b0d,0xb0c8,0x4131,0xbf,0xd8,0x9b,0x24,0x76,0xf7,0xf4,0x08);\n__CRT_UUID_DECL(IDXGIVkInteropFactory1,    0x2a289dbd,0x2d0a,0x4a51,0x89,0xf7,0xf2,0xad,0xce,0x46,0x5c,0xd6);\n__CRT_UUID_DECL(IDXGIVkInteropAdapter,     0x3a6d8f2c,0xb0e8,0x4ab4,0xb4,0xdc,0x4f,0xd2,0x48,0x91,0xbf,0xa5);\n__CRT_UUID_DECL(IDXGIVkInteropDevice,      0xe2ef5fa5,0xdc21,0x4af7,0x90,0xc4,0xf6,0x7e,0xf6,0xa0,0x93,0x23);\n__CRT_UUID_DECL(IDXGIVkInteropDevice1,     0xe2ef5fa5,0xdc21,0x4af7,0x90,0xc4,0xf6,0x7e,0xf6,0xa0,0x93,0x24);\n__CRT_UUID_DECL(IDXGIVkInteropSurface,     0x5546cf8c,0x77e7,0x4341,0xb0,0x5d,0x8d,0x4d,0x50,0x00,0xe7,0x7d);\n__CRT_UUID_DECL(IDXGIVkSurfaceFactory,     0x1e7895a1,0x1bc3,0x4f9c,0xa6,0x70,0x29,0x0a,0x4b,0xc9,0x58,0x1a);\n__CRT_UUID_DECL(IDXGIVkSwapChain,          0xe4a9059e,0xb569,0x46ab,0x8d,0xe7,0x50,0x1b,0xd2,0xbc,0x7f,0x7a);\n__CRT_UUID_DECL(IDXGIVkSwapChain1,         0x785326d4,0xb77b,0x4826,0xae,0x70,0x8d,0x08,0x30,0x8e,0xe6,0xd1);\n__CRT_UUID_DECL(IDXGIVkSwapChain2,         0xaed91093,0xe02e,0x458c,0xbd,0xef,0xa9,0x7d,0xa1,0xa7,0xe6,0xd2);\n__CRT_UUID_DECL(IDXGIVkSwapChainFactory,   0xe7d6c3ca,0x23a0,0x4e08,0x9f,0x2f,0xea,0x52,0x31,0xdf,0x66,0x33);\n#endif\n"
  },
  {
    "path": "src/dxgi/dxgi_main.cpp",
    "content": "#include \"dxgi_factory.h\"\n#include \"dxgi_include.h\"\n\nnamespace dxvk {\n  \n  Logger Logger::s_instance(\"dxgi.log\");\n  \n  HRESULT createDxgiFactory(UINT Flags, REFIID riid, void **ppFactory) {\n    try {\n      Com<DxgiFactory> factory = new DxgiFactory(Flags);\n      HRESULT hr = factory->QueryInterface(riid, ppFactory);\n\n      if (FAILED(hr))\n        return hr;\n      \n      return S_OK;\n    } catch (const DxvkError& e) {\n      Logger::err(e.message());\n      return E_FAIL;\n    }\n  }\n}\n\nextern \"C\" {\n  DLLEXPORT HRESULT __stdcall CreateDXGIFactory2(UINT Flags, REFIID riid, void **ppFactory) {\n    dxvk::Logger::warn(\"CreateDXGIFactory2: Ignoring flags\");\n    return dxvk::createDxgiFactory(Flags, riid, ppFactory);\n  }\n\n  DLLEXPORT HRESULT __stdcall CreateDXGIFactory1(REFIID riid, void **ppFactory) {\n    return dxvk::createDxgiFactory(0, riid, ppFactory);\n  }\n  \n  DLLEXPORT HRESULT __stdcall CreateDXGIFactory(REFIID riid, void **ppFactory) {\n    return dxvk::createDxgiFactory(0, riid, ppFactory);\n  }\n\n  DLLEXPORT HRESULT __stdcall DXGIDeclareAdapterRemovalSupport() {\n    static bool enabled = false;\n\n    if (std::exchange(enabled, true))\n      return DXGI_ERROR_ALREADY_EXISTS;\n\n    dxvk::Logger::warn(\"DXGIDeclareAdapterRemovalSupport: Stub\");\n    return S_OK;\n  }\n\n  DLLEXPORT HRESULT __stdcall DXGIGetDebugInterface1(UINT Flags, REFIID riid, void **ppDebug) {\n    static bool errorShown = false;\n\n    if (!std::exchange(errorShown, true))\n      dxvk::Logger::warn(\"DXGIGetDebugInterface1: Stub\");\n\n    return E_NOINTERFACE;\n  }\n\n}"
  },
  {
    "path": "src/dxgi/dxgi_monitor.cpp",
    "content": "#include \"dxgi_monitor.h\"\n\nnamespace dxvk {\n\n  DxgiMonitorInfo::DxgiMonitorInfo(IUnknown* pParent, const DxgiOptions& options)\n  : m_parent(pParent)\n  , m_options(options)\n  , m_globalColorSpace(DefaultColorSpace()) {\n\n  }\n\n\n  DxgiMonitorInfo::~DxgiMonitorInfo() {\n\n  }\n\n\n  ULONG STDMETHODCALLTYPE DxgiMonitorInfo::AddRef() {\n    return m_parent->AddRef();\n  }\n\n  \n  ULONG STDMETHODCALLTYPE DxgiMonitorInfo::Release() {\n    return m_parent->Release();\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE DxgiMonitorInfo::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    return m_parent->QueryInterface(riid, ppvObject);\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE DxgiMonitorInfo::InitMonitorData(\n          HMONITOR                hMonitor,\n    const DXGI_VK_MONITOR_DATA*   pData) {\n    if (!hMonitor || !pData)\n      return E_INVALIDARG;\n    \n    std::lock_guard<dxvk::mutex> lock(m_monitorMutex);\n    auto result = m_monitorData.insert({ hMonitor, *pData });\n\n    return result.second ? S_OK : E_INVALIDARG;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiMonitorInfo::AcquireMonitorData(\n          HMONITOR                hMonitor,\n          DXGI_VK_MONITOR_DATA**  ppData) {\n    InitReturnPtr(ppData);\n\n    if (!hMonitor || !ppData)\n      return E_INVALIDARG;\n    \n    m_monitorMutex.lock();\n\n    auto entry = m_monitorData.find(hMonitor);\n    if (entry == m_monitorData.end()) {\n      m_monitorMutex.unlock();\n      return DXGI_ERROR_NOT_FOUND;\n    }\n\n    *ppData = &entry->second;\n    return S_OK;\n  }\n\n\n  void STDMETHODCALLTYPE DxgiMonitorInfo::ReleaseMonitorData() {\n    m_monitorMutex.unlock();\n  }\n\n\n  void STDMETHODCALLTYPE DxgiMonitorInfo::PuntColorSpace(DXGI_COLOR_SPACE_TYPE ColorSpace) {\n    // Only allow punting if we started from sRGB.\n    // That way we can go from sRGB -> HDR10 or HDR10 -> sRGB if we started in sRGB.\n    // But if we started off by advertising HDR10 to the game, don't allow us to go back.\n    // This mirrors the behaviour of the global Windows HDR toggle more closely.\n    if (DefaultColorSpace() != DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709)\n      return;\n\n    m_globalColorSpace = ColorSpace;\n  }\n\n\n  DXGI_COLOR_SPACE_TYPE STDMETHODCALLTYPE DxgiMonitorInfo::CurrentColorSpace() const {\n    return m_globalColorSpace;\n  }\n\n\n  DXGI_COLOR_SPACE_TYPE DxgiMonitorInfo::DefaultColorSpace() const {\n    return m_options.enableHDR\n      ? DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020\n      : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;\n  }\n\n\n  uint32_t GetMonitorFormatBpp(DXGI_FORMAT Format) {\n    switch (Format) {\n      case DXGI_FORMAT_R8G8B8A8_UNORM:\n      case DXGI_FORMAT_B8G8R8A8_UNORM:\n      case DXGI_FORMAT_B8G8R8X8_UNORM:\n      case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:\n      case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:\n      case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:\n      case DXGI_FORMAT_R10G10B10A2_UNORM:\n        return 32;\n      \n      case DXGI_FORMAT_R16G16B16A16_FLOAT:\n        // Floating point output doesn't really make sense.\n        // This seemingly works on Windows, and based on FindClosestMode etc documentaton,\n        // this seems required to work for any format that scanout it supported for.\n        // Treat as 10-bit -> 32.\n        return 32;\n      \n      default:\n        Logger::warn(str::format(\n          \"GetMonitorFormatBpp: Unknown format: \",\n          Format));\n        return 32;\n    }\n  }\n  \n}"
  },
  {
    "path": "src/dxgi/dxgi_monitor.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <unordered_map>\n\n#include \"dxgi_interfaces.h\"\n#include \"dxgi_options.h\"\n\n#include \"../wsi/wsi_monitor.h\"\n\nnamespace dxvk {\n\n  class DxgiSwapChain;\n\n  class DxgiMonitorInfo : public IDXGIVkMonitorInfo {\n\n  public:\n\n    DxgiMonitorInfo(IUnknown* pParent, const DxgiOptions& options);\n\n    ~DxgiMonitorInfo();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n    \n    ULONG STDMETHODCALLTYPE Release();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                  riid,\n            void**                  ppvObject);\n    \n    HRESULT STDMETHODCALLTYPE InitMonitorData(\n            HMONITOR                hMonitor,\n      const DXGI_VK_MONITOR_DATA*   pData);\n\n    HRESULT STDMETHODCALLTYPE AcquireMonitorData(\n            HMONITOR                hMonitor,\n            DXGI_VK_MONITOR_DATA**  ppData);\n\n    void STDMETHODCALLTYPE ReleaseMonitorData();\n\n    void STDMETHODCALLTYPE PuntColorSpace(DXGI_COLOR_SPACE_TYPE ColorSpace);\n\n    DXGI_COLOR_SPACE_TYPE STDMETHODCALLTYPE CurrentColorSpace() const;\n\n    DXGI_COLOR_SPACE_TYPE DefaultColorSpace() const;\n\n  private:\n\n    IUnknown* m_parent;\n    const DxgiOptions& m_options;\n\n    dxvk::mutex                                        m_monitorMutex;\n    std::unordered_map<HMONITOR, DXGI_VK_MONITOR_DATA> m_monitorData;\n\n    std::atomic<DXGI_COLOR_SPACE_TYPE> m_globalColorSpace;\n\n  };\n\n\n  /**\n   * \\brief Queries bits per pixel for a format\n   * \n   * The format must be a valid swap chain format.\n   * \\param [in] Format The DXGI format to query\n   * \\returns Bits per pixel for this format\n   */\n  uint32_t GetMonitorFormatBpp(\n          DXGI_FORMAT             Format);\n\n  /**\n   * \\brief Converts a DXVK WSI display mode to a DXGI display mode\n   */\n  inline DXGI_MODE_DESC1 ConvertDisplayMode(const wsi::WsiMode& WsiMode) {\n    DXGI_MODE_DESC1 dxgiMode  = { };\n    dxgiMode.Width            = WsiMode.width;\n    dxgiMode.Height           = WsiMode.height;\n    dxgiMode.RefreshRate      = DXGI_RATIONAL{ WsiMode.refreshRate.numerator, WsiMode.refreshRate.denominator };\n    dxgiMode.Format           = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; // FIXME\n    dxgiMode.ScanlineOrdering = WsiMode.interlaced ? DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST : DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;\n    dxgiMode.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;\n    dxgiMode.Stereo           = FALSE;\n    return dxgiMode;\n  }\n\n  /**\n   * \\brief Converts a DXGI display mode to a DXVK WSI display mode\n   */\n  inline wsi::WsiMode ConvertDisplayMode(const DXGI_MODE_DESC1& DxgiMode) {\n    wsi::WsiMode wsiMode = { };\n    wsiMode.width        = DxgiMode.Width;\n    wsiMode.height       = DxgiMode.Height;\n    wsiMode.refreshRate  = wsi::WsiRational{ DxgiMode.RefreshRate.Numerator, DxgiMode.RefreshRate.Denominator };\n    wsiMode.bitsPerPixel = GetMonitorFormatBpp(DxgiMode.Format);\n    wsiMode.interlaced   = DxgiMode.ScanlineOrdering == DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST\n                        || DxgiMode.ScanlineOrdering == DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST;\n    return wsiMode;\n  }\n\n}"
  },
  {
    "path": "src/dxgi/dxgi_object.h",
    "content": "#pragma once\n\n#include \"dxgi_include.h\"\n\n#include \"../util/com/com_private_data.h\"\n\nnamespace dxvk {\n  \n  template<typename Base>\n  class DxgiObject : public ComObject<Base> {\n    \n  public:\n    \n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID       Name,\n            UINT*         pDataSize,\n            void*         pData) final {\n      return m_privateData.getData(\n        Name, pDataSize, pData);\n    }\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID       Name,\n            UINT          DataSize,\n      const void*         pData) final {\n      return m_privateData.setData(\n        Name, DataSize, pData);\n    }\n    \n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID       Name,\n      const IUnknown*     pUnknown) final {\n      return m_privateData.setInterface(\n        Name, pUnknown);\n    }\n    \n  private:\n    \n    ComPrivateData m_privateData;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/dxgi/dxgi_options.cpp",
    "content": "#include \"dxgi_options.h\"\n\n#include <unordered_map>\n\nnamespace dxvk {\n\n  static int32_t parsePciId(const std::string& str) {\n    if (str.size() != 4)\n      return -1;\n    \n    int32_t id = 0;\n\n    for (size_t i = 0; i < str.size(); i++) {\n      id *= 16;\n\n      if (str[i] >= '0' && str[i] <= '9')\n        id += str[i] - '0';\n      else if (str[i] >= 'A' && str[i] <= 'F')\n        id += str[i] - 'A' + 10;\n      else if (str[i] >= 'a' && str[i] <= 'f')\n        id += str[i] - 'a' + 10;\n      else\n        return -1;\n    }\n\n    return id;\n  }\n\n  /* First generation XeSS causes crash on proton for Intel due to missing\n   * Intel interface. Avoid crash by pretending to be non-Intel if the\n   * libxess.dll module is loaded by an application.\n   */\n  static bool isXessUsed() {\n#ifdef _WIN32\n      if (GetModuleHandleA(\"libxess\") != nullptr ||\n          GetModuleHandleA(\"libxess_dx11\") != nullptr)\n        return true;\n      else\n        return false;\n#else\n      return false;\n#endif\n  }\n\n  static bool isNvapiEnabled() {\n    return env::getEnvVar(\"DXVK_ENABLE_NVAPI\") == \"1\";\n  }\n\n\n  static bool isHDRDisallowed(bool enableUe4Workarounds) {\n#ifdef _WIN32\n    // Unreal Engine 4 titles use AGS/NVAPI to try and enable\n    // HDR globally.\n    // The game checks IDXGIOutput::GetDesc1's ColorSpace\n    // being HDR10 to see if it should enable HDR.\n    // Many of these UE4 games statically link against AGS.\n    //\n    // This is a problem as when UE4 tries to enable HDR via AGS,\n    // it does not check if AGSContext, and the display info etc\n    // are nullptr unlike the rest of the code using AGS.\n    // So we need to special-case UE4 titles to disable reporting a HDR\n    // when they are in DX11 mode.\n    //\n    // The simplest way to do this is to key off the fact that all\n    // UE4 titles have an executable ending with \"-Win64-Shipping\".\n    //\n    // We check if d3d12.dll is present, to determine what path in\n    // UE4 we are on, as there are some games that ship both and support HDR.\n    // (eg. The Dark Pictures: House of Ashes, 1281590)\n    // Luckily for us, they only load d3d12.dll on the D3D12 render path\n    // so we can key off that to force disable HDR only in D3D11.\n    std::string exeName = env::getExeName();\n    bool isUE4 = enableUe4Workarounds || exeName.find(\"-Win64-Shipping\") != std::string::npos;\n    bool hasD3D12 = GetModuleHandleA(\"d3d12\") != nullptr;\n\n    if (isUE4 && !hasD3D12 && !isNvapiEnabled())\n      return true;\n#endif\n    return false;\n  }\n\n  \n  DxgiOptions::DxgiOptions(const Config& config) {\n    // Fetch these as a string representing a hexadecimal number and parse it.\n    this->customVendorId = parsePciId(config.getOption<std::string>(\"dxgi.customVendorId\"));\n    this->customDeviceId = parsePciId(config.getOption<std::string>(\"dxgi.customDeviceId\"));\n    this->customDeviceDesc = config.getOption<std::string>(\"dxgi.customDeviceDesc\", \"\");\n    \n    // Interpret the memory limits as Megabytes\n    this->maxDeviceMemory = VkDeviceSize(config.getOption<int32_t>(\"dxgi.maxDeviceMemory\", 0)) << 20;\n    this->maxSharedMemory = VkDeviceSize(config.getOption<int32_t>(\"dxgi.maxSharedMemory\", 0)) << 20;\n\n    this->maxFrameRate     = config.getOption<int32_t>(\"dxgi.maxFrameRate\", 0);\n    this->syncInterval     = config.getOption<int32_t>(\"dxgi.syncInterval\", -1);\n    this->forceRefreshRate = config.getOption<int32_t>(\"dxgi.forceRefreshRate\", 0u);\n\n    // We don't support dcomp swapchains and some games may rely on them failing on creation\n    this->enableDummyCompositionSwapchain = config.getOption<bool>(\"dxgi.enableDummyCompositionSwapchain\", false);\n\n    // Expose Nvidia GPUs properly if NvAPI is enabled in environment\n    this->hideNvidiaGpu = !isNvapiEnabled();\n    applyTristate(this->hideNvidiaGpu, config.getOption<Tristate>(\"dxgi.hideNvidiaGpu\", Tristate::Auto));\n\n    // Treat NVK adapters the same as Nvidia cards on the proprietary by\n    // default, but provide an override in case something isn't working.\n    this->hideNvkGpu = this->hideNvidiaGpu;\n    applyTristate(this->hideNvkGpu, config.getOption<Tristate>(\"dxgi.hideNvkGpu\", Tristate::Auto));\n\n    // Expose AMD and Intel GPU by default, unless a config override is active.\n    // Implement as a tristate so that we have the option to introduce similar\n    // logic to Nvidia later, if necessary.\n    this->hideAmdGpu = config.getOption<Tristate>(\"dxgi.hideAmdGpu\", Tristate::Auto) == Tristate::True;\n    this->hideIntelGpu = config.getOption<Tristate>(\"dxgi.hideIntelGpu\", Tristate::Auto) == Tristate::True;\n\n    /* Force vendor ID to non-Intel ID when XeSS is in use */\n    if (isXessUsed()) {\n      Logger::info(str::format(\"Detected XeSS usage, hiding Intel GPU Vendor\"));\n      this->hideIntelGpu = true;\n    }\n\n    this->enableHDR = config.getOption<bool>(\"dxgi.enableHDR\", env::getEnvVar(\"DXVK_HDR\") == \"1\");\n\n    bool enableUe4Workarounds = config.getOption<bool>(\"dxgi.enableUe4Workarounds\", false);\n\n    if (this->enableHDR && isHDRDisallowed(enableUe4Workarounds)) {\n      Logger::info(\"HDR was configured to be enabled, but has been force disabled as a UE4 DX11 game was detected.\");\n      this->enableHDR = false;\n    }\n  }\n  \n}\n"
  },
  {
    "path": "src/dxgi/dxgi_options.h",
    "content": "#pragma once\n\n#include \"../util/config/config.h\"\n\n#include \"../dxvk/dxvk_include.h\"\n\n#include \"dxgi_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief DXGI options\n   * \n   * Per-app options that control the\n   * behaviour of some DXGI classes.\n   */\n  struct DxgiOptions {\n    DxgiOptions(const Config& config);\n\n    /// Override PCI vendor and device IDs reported to the\n    /// application. This may make apps think they are running\n    /// on a different GPU than they do and behave differently.\n    int32_t customVendorId;\n    int32_t customDeviceId;\n    std::string customDeviceDesc;\n    \n    /// Override maximum reported VRAM size. This may be\n    /// useful for some 64-bit games which do not support\n    /// more than 4 GiB of VRAM.\n    VkDeviceSize maxDeviceMemory;\n    VkDeviceSize maxSharedMemory;\n\n    /// Reports Nvidia GPUs running on the proprietary driver as a different\n    /// vendor (usually AMD). Proton will generally disable this option.\n    bool hideNvidiaGpu;\n\n    /// Reports Nvidia GPUs running on NVK as a different vendor (usually AMD)\n    bool hideNvkGpu;\n\n    /// Reports AMD GPUs as a different vendor (usually Nvidia)\n    bool hideAmdGpu;\n\n    /// Reports Intel GPUs as a different vendor (usually AMD)\n    bool hideIntelGpu;\n\n    /// Enable HDR\n    bool enableHDR;\n\n    /// Enable support for dummy composition swapchains\n    bool enableDummyCompositionSwapchain;\n\n    /// Limit frame rate\n    int32_t maxFrameRate;\n\n    /// Sync interval. Overrides the value\n    /// passed to IDXGISwapChain::Present.\n    int32_t syncInterval;\n\n    /// Forced refresh rate, disable other modes\n    uint32_t forceRefreshRate;\n  };\n  \n}\n"
  },
  {
    "path": "src/dxgi/dxgi_output.cpp",
    "content": "#include <algorithm>\n#include <numeric>\n\n#include <cstdlib>\n#include <cstring>\n\n#include <sstream>\n#include <string>\n\n#include \"dxgi_adapter.h\"\n#include \"dxgi_factory.h\"\n#include \"dxgi_output.h\"\n#include \"dxgi_swapchain.h\"\n\n#include \"../dxvk/dxvk_format.h\"\n\n#include \"../util/util_misc.h\"\n#include \"../util/util_sleep.h\"\n#include \"../util/util_time.h\"\n\nnamespace dxvk {\n\n  DxgiOutput::DxgiOutput(\n    const Com<DxgiFactory>& factory,\n    const Com<DxgiAdapter>& adapter,\n              HMONITOR      monitor)\n  : m_factory     ( factory ),\n    m_adapter     ( adapter ),\n    m_monitorInfo ( factory->GetMonitorInfo() ),\n    m_monitor     ( monitor ),\n    m_destructionNotifier(this) {\n    CacheMonitorData();\n  }\n  \n  \n  DxgiOutput::~DxgiOutput() {\n    \n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDXGIObject)\n     || riid == __uuidof(IDXGIOutput)\n     || riid == __uuidof(IDXGIOutput1)\n     || riid == __uuidof(IDXGIOutput2)\n     || riid == __uuidof(IDXGIOutput3)\n     || riid == __uuidof(IDXGIOutput4)\n     || riid == __uuidof(IDXGIOutput5)\n     || riid == __uuidof(IDXGIOutput6)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n    \n    if (logQueryInterfaceError(__uuidof(IDXGIOutput), riid)) {\n      Logger::warn(\"DxgiOutput::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetParent(REFIID riid, void **ppParent) {\n    return m_adapter->QueryInterface(riid, ppParent);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::FindClosestMatchingMode(\n    const DXGI_MODE_DESC *pModeToMatch,\n          DXGI_MODE_DESC *pClosestMatch,\n          IUnknown       *pConcernedDevice) {\n    if (!pModeToMatch || !pClosestMatch)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    DXGI_MODE_DESC1 modeToMatch;\n    modeToMatch.Width            = pModeToMatch->Width;\n    modeToMatch.Height           = pModeToMatch->Height;\n    modeToMatch.RefreshRate      = pModeToMatch->RefreshRate;\n    modeToMatch.Format           = pModeToMatch->Format;\n    modeToMatch.ScanlineOrdering = pModeToMatch->ScanlineOrdering;\n    modeToMatch.Scaling          = pModeToMatch->Scaling;\n    modeToMatch.Stereo           = FALSE;\n\n    DXGI_MODE_DESC1 closestMatch = { };\n\n    HRESULT hr = FindClosestMatchingMode1(\n      &modeToMatch, &closestMatch, pConcernedDevice);\n    \n    if (FAILED(hr))\n      return hr;\n    \n    pClosestMatch->Width            = closestMatch.Width;\n    pClosestMatch->Height           = closestMatch.Height;\n    pClosestMatch->RefreshRate      = closestMatch.RefreshRate;\n    pClosestMatch->Format           = closestMatch.Format;\n    pClosestMatch->ScanlineOrdering = closestMatch.ScanlineOrdering;\n    pClosestMatch->Scaling          = closestMatch.Scaling;\n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::FindClosestMatchingMode1(\n    const DXGI_MODE_DESC1*      pModeToMatch,\n          DXGI_MODE_DESC1*      pClosestMatch,\n          IUnknown*             pConcernedDevice) {\n    if (!pModeToMatch || !pClosestMatch)\n      return DXGI_ERROR_INVALID_CALL;\n\n    if (pModeToMatch->Format == DXGI_FORMAT_UNKNOWN && !pConcernedDevice)\n      return DXGI_ERROR_INVALID_CALL;\n\n    // Both or neither must be zero\n    if ((pModeToMatch->Width == 0) ^ (pModeToMatch->Height == 0))\n      return DXGI_ERROR_INVALID_CALL;\n\n    wsi::WsiMode activeWsiMode = { };\n    wsi::getCurrentDisplayMode(m_monitor, &activeWsiMode);\n\n    DXGI_MODE_DESC1 activeMode = ConvertDisplayMode(activeWsiMode);\n\n    DXGI_MODE_DESC1 defaultMode;\n    defaultMode.Width            = 0;\n    defaultMode.Height           = 0;\n    defaultMode.RefreshRate      = { 0, 0 };\n    defaultMode.Format           = DXGI_FORMAT_UNKNOWN;\n    defaultMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;\n    defaultMode.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;\n    defaultMode.Stereo           = pModeToMatch->Stereo;\n\n    if (pModeToMatch->ScanlineOrdering == DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED)\n      defaultMode.ScanlineOrdering = activeMode.ScanlineOrdering;\n\n    if (pModeToMatch->Scaling == DXGI_MODE_SCALING_UNSPECIFIED)\n      defaultMode.Scaling = activeMode.Scaling;\n\n    DXGI_FORMAT targetFormat = pModeToMatch->Format;\n\n    if (pModeToMatch->Format == DXGI_FORMAT_UNKNOWN) {\n      defaultMode.Format = activeMode.Format;\n      targetFormat       = activeMode.Format;\n    }\n\n    if (!pModeToMatch->Width) {\n      defaultMode.Width  = activeMode.Width;\n      defaultMode.Height = activeMode.Height;\n    }\n\n    if (!pModeToMatch->RefreshRate.Numerator || !pModeToMatch->RefreshRate.Denominator) {\n      defaultMode.RefreshRate.Numerator   = activeMode.RefreshRate.Numerator;\n      defaultMode.RefreshRate.Denominator = activeMode.RefreshRate.Denominator;\n    }\n\n    UINT modeCount = 0;\n    GetDisplayModeList1(targetFormat, DXGI_ENUM_MODES_SCALING, &modeCount, nullptr);\n    \n    if (modeCount == 0) {\n      Logger::err(\"DXGI: FindClosestMatchingMode: No modes found\");\n      return DXGI_ERROR_NOT_FOUND;\n    }\n\n    std::vector<DXGI_MODE_DESC1> modes(modeCount);\n    GetDisplayModeList1(targetFormat, DXGI_ENUM_MODES_SCALING, &modeCount, modes.data());\n\n    const DxgiOptions* options = m_factory->GetOptions();\n\n    DXGI_MODE_DESC1 modeToMatch = *pModeToMatch;\n    if (options->forceRefreshRate)\n      modeToMatch.RefreshRate = {options->forceRefreshRate, 1u};\n\n    FilterModesByDesc(modes, modeToMatch);\n    FilterModesByDesc(modes, defaultMode);\n\n    if (modes.empty())\n      return DXGI_ERROR_NOT_FOUND;\n\n    *pClosestMatch = modes[0];\n\n    Logger::debug(str::format(\n      \"DXGI: For mode \",\n        modeToMatch.Width, \"x\", modeToMatch.Height, \"@\",\n        modeToMatch.RefreshRate.Denominator ? (modeToMatch.RefreshRate.Numerator / modeToMatch.RefreshRate.Denominator) : 0,\n      \" found closest mode \",\n        pClosestMatch->Width, \"x\", pClosestMatch->Height, \"@\",\n        pClosestMatch->RefreshRate.Denominator ? (pClosestMatch->RefreshRate.Numerator / pClosestMatch->RefreshRate.Denominator) : 0));\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDesc(DXGI_OUTPUT_DESC *pDesc) {\n    if (pDesc == nullptr)\n      return DXGI_ERROR_INVALID_CALL;\n\n    DXGI_OUTPUT_DESC1 desc;\n    HRESULT hr = GetDesc1(&desc);\n\n    if (SUCCEEDED(hr)) {\n      std::memcpy(pDesc->DeviceName, desc.DeviceName, sizeof(pDesc->DeviceName));\n      pDesc->DesktopCoordinates = desc.DesktopCoordinates;\n      pDesc->AttachedToDesktop  = desc.AttachedToDesktop;\n      pDesc->Rotation           = desc.Rotation;\n      pDesc->Monitor            = desc.Monitor;\n    }\n\n    return hr;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDesc1(\n          DXGI_OUTPUT_DESC1*    pDesc) {\n    if (pDesc == nullptr)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    if (!wsi::getDesktopCoordinates(m_monitor, &pDesc->DesktopCoordinates)) {\n      Logger::err(\"DXGI: Failed to query monitor coords\");\n      return E_FAIL;\n    }\n    \n    if (!wsi::getDisplayName(m_monitor, pDesc->DeviceName)) {\n      Logger::err(\"DXGI: Failed to query monitor name\");\n      return E_FAIL;\n    }\n\n    pDesc->AttachedToDesktop     = 1;\n    pDesc->Rotation              = DXGI_MODE_ROTATION_UNSPECIFIED;\n    pDesc->Monitor               = m_monitor;\n    pDesc->BitsPerColor          = 10;\n    // This should only return DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020\n    // (HDR) if the user has the HDR setting enabled in Windows.\n    // Games can still punt into HDR mode by using CheckColorSpaceSupport\n    // and SetColorSpace1.\n    //\n    // We have no way of checking the actual Windows colorspace as the\n    // only public method for this *is* DXGI which we are re-implementing.\n    // So we just pick our color space based on the DXVK_HDR env var\n    // and the punting from SetColorSpace1.\n    pDesc->ColorSpace            = m_monitorInfo->CurrentColorSpace();\n    pDesc->RedPrimary[0]         = m_metadata.redPrimary[0];\n    pDesc->RedPrimary[1]         = m_metadata.redPrimary[1];\n    pDesc->GreenPrimary[0]       = m_metadata.greenPrimary[0];\n    pDesc->GreenPrimary[1]       = m_metadata.greenPrimary[1];\n    pDesc->BluePrimary[0]        = m_metadata.bluePrimary[0];\n    pDesc->BluePrimary[1]        = m_metadata.bluePrimary[1];\n    pDesc->WhitePoint[0]         = m_metadata.whitePoint[0];\n    pDesc->WhitePoint[1]         = m_metadata.whitePoint[1];\n    pDesc->MinLuminance          = m_metadata.minLuminance;\n    pDesc->MaxLuminance          = m_metadata.maxLuminance;\n    pDesc->MaxFullFrameLuminance = m_metadata.maxFullFrameLuminance;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplayModeList(\n          DXGI_FORMAT    EnumFormat,\n          UINT           Flags,\n          UINT*          pNumModes,\n          DXGI_MODE_DESC* pDesc) {\n    if (pNumModes == nullptr)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    std::vector<DXGI_MODE_DESC1> modes;\n\n    if (pDesc)\n      modes.resize(std::max(1u, *pNumModes));\n    \n    HRESULT hr = GetDisplayModeList1(\n      EnumFormat, Flags, pNumModes,\n      pDesc ? modes.data() : nullptr);\n    \n    for (uint32_t i = 0; i < *pNumModes && i < modes.size(); i++) {\n      pDesc[i].Width            = modes[i].Width;\n      pDesc[i].Height           = modes[i].Height;\n      pDesc[i].RefreshRate      = modes[i].RefreshRate;\n      pDesc[i].Format           = modes[i].Format;\n      pDesc[i].ScanlineOrdering = modes[i].ScanlineOrdering;\n      pDesc[i].Scaling          = modes[i].Scaling;\n    }\n    \n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplayModeList1(\n          DXGI_FORMAT           EnumFormat,\n          UINT                  Flags,\n          UINT*                 pNumModes,\n          DXGI_MODE_DESC1*      pDesc) {\n    if (pNumModes == nullptr)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    // Special case, just return zero modes\n    if (EnumFormat == DXGI_FORMAT_UNKNOWN) {\n      *pNumModes = 0;\n      return S_OK;\n    }\n\n    // Walk over all modes that the display supports and\n    // return those that match the requested format etc.\n    wsi::WsiMode devMode = { };\n    \n    uint32_t srcModeId = 0;\n    uint32_t dstModeId = 0;\n    \n    std::vector<DXGI_MODE_DESC1> modeList;\n    \n    while (wsi::getDisplayMode(m_monitor, srcModeId++, &devMode)) {\n      // Only enumerate interlaced modes if requested.\n      if (devMode.interlaced && !(Flags & DXGI_ENUM_MODES_INTERLACED))\n        continue;\n      \n      // Skip modes with incompatible formats\n      if (devMode.bitsPerPixel != GetMonitorFormatBpp(EnumFormat))\n        continue;\n      \n      if (pDesc != nullptr) {\n        DXGI_MODE_DESC1 mode = ConvertDisplayMode(devMode);\n        // Fix up the DXGI_FORMAT to match what we were enumerating.\n        mode.Format = EnumFormat;\n\n        modeList.push_back(mode);\n      }\n      \n      dstModeId += 1;\n    }\n    \n    // Sort display modes by width, height and refresh rate,\n    // in that order. Some games rely on correct ordering.\n    std::sort(modeList.begin(), modeList.end(),\n      [] (const DXGI_MODE_DESC1& a, const DXGI_MODE_DESC1& b) {\n        if (a.Width < b.Width) return true;\n        if (a.Width > b.Width) return false;\n        \n        if (a.Height < b.Height) return true;\n        if (a.Height > b.Height) return false;\n        \n        return (a.RefreshRate.Numerator / a.RefreshRate.Denominator)\n             < (b.RefreshRate.Numerator / b.RefreshRate.Denominator);\n      });\n    \n    // If requested, write out the first set of display\n    // modes to the destination array.\n    if (pDesc != nullptr) {\n      for (uint32_t i = 0; i < *pNumModes && i < dstModeId; i++)\n        pDesc[i] = modeList[i];\n      \n      if (dstModeId > *pNumModes)\n        return DXGI_ERROR_MORE_DATA;\n    }\n    \n    *pNumModes = dstModeId;\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplaySurfaceData(IDXGISurface* pDestination) {\n    Logger::err(\"DxgiOutput::GetDisplaySurfaceData: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) {\n    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;\n    HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);\n\n    if (FAILED(hr))\n      return hr;\n\n    // Need to acquire swap chain and unlock monitor data, since querying\n    // frame statistics from the swap chain will also access monitor data.\n    Com<IDXGISwapChain> swapChain = monitorInfo->pSwapChain;\n    m_monitorInfo->ReleaseMonitorData();\n\n    // This API only works if there is a full-screen swap chain active.\n    if (swapChain == nullptr) {\n      *pStats = DXGI_FRAME_STATISTICS();\n      return S_OK;\n    }\n\n    return swapChain->GetFrameStatistics(pStats);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControl(DXGI_GAMMA_CONTROL* pArray) {\n    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;\n    HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);\n\n    if (FAILED(hr))\n      return hr;\n    \n    *pArray = monitorInfo->GammaCurve;\n    m_monitorInfo->ReleaseMonitorData();\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControlCapabilities(DXGI_GAMMA_CONTROL_CAPABILITIES* pGammaCaps) {\n    pGammaCaps->ScaleAndOffsetSupported = FALSE;\n    pGammaCaps->MaxConvertedValue       = 1.0f;\n    pGammaCaps->MinConvertedValue       = 0.0f;\n    pGammaCaps->NumGammaControlPoints   = DXGI_VK_GAMMA_CP_COUNT;\n    \n    for (uint32_t i = 0; i < pGammaCaps->NumGammaControlPoints; i++)\n      pGammaCaps->ControlPointPositions[i] = GammaControlPointLocation(i);\n    return S_OK;\n  }\n  \n  \n  void STDMETHODCALLTYPE DxgiOutput::ReleaseOwnership() {\n    Logger::warn(\"DxgiOutput::ReleaseOwnership: Stub\");\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::SetDisplaySurface(IDXGISurface* pScanoutSurface) {\n    Logger::err(\"DxgiOutput::SetDisplaySurface: Not implemented\");\n    return E_NOTIMPL;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplaySurfaceData1(IDXGIResource* pDestination) {\n    Logger::err(\"DxgiOutput::SetDisplaySurface1: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::SetGammaControl(const DXGI_GAMMA_CONTROL* pArray) {\n    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;\n    HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);\n\n    if (FAILED(hr))\n      return hr;\n    \n    monitorInfo->GammaCurve = *pArray;\n\n    if (monitorInfo->pSwapChain) {\n      hr = monitorInfo->pSwapChain->SetGammaControl(\n        DXGI_VK_GAMMA_CP_COUNT, pArray->GammaCurve);\n    }\n\n    m_monitorInfo->ReleaseMonitorData();\n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::TakeOwnership(\n          IUnknown *pDevice,\n          BOOL     Exclusive) {\n    Logger::warn(\"DxgiOutput::TakeOwnership: Stub\");\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::WaitForVBlank() {\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"DxgiOutput::WaitForVBlank: Inaccurate\");\n\n    // Get monitor data to compute the sleep duration\n    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;\n    HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);\n\n    if (FAILED(hr))\n      return hr;\n\n    // Estimate number of vblanks since last mode\n    // change, then wait for one more refresh period\n    auto refreshPeriod = computeRefreshPeriod(\n      monitorInfo->LastMode.RefreshRate.Numerator,\n      monitorInfo->LastMode.RefreshRate.Denominator);\n\n    auto t0 = dxvk::high_resolution_clock::get_time_from_counter(monitorInfo->FrameStats.SyncQPCTime.QuadPart);\n    auto t1 = dxvk::high_resolution_clock::now();\n\n    uint64_t vblankCount = computeRefreshCount(t0, t1, refreshPeriod);\n    auto t2 = t0 + (vblankCount + 1) * refreshPeriod;\n\n    m_monitorInfo->ReleaseMonitorData();\n\n    // Sleep until the given time point\n    Sleep::sleepUntil(t1, t2);\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiOutput::DuplicateOutput(\n          IUnknown*                 pDevice,\n          IDXGIOutputDuplication**  ppOutputDuplication) {\n    return DuplicateOutput1(pDevice, 0, 0, nullptr, ppOutputDuplication);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiOutput::DuplicateOutput1(\n          IUnknown*                 pDevice,\n          UINT                      Flags,\n          UINT                      SupportedFormatsCount,\n    const DXGI_FORMAT*              pSupportedFormats,\n          IDXGIOutputDuplication**  ppOutputDuplication) {\n    InitReturnPtr(ppOutputDuplication);\n\n    if (!pDevice)\n      return E_INVALIDARG;\n    \n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::err(\"DxgiOutput::DuplicateOutput1: Not implemented\");\n    \n    // At least return a valid error code\n    return DXGI_ERROR_UNSUPPORTED;\n  }\n\n\n  BOOL DxgiOutput::SupportsOverlays() {\n    return FALSE;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiOutput::CheckOverlaySupport(\n          DXGI_FORMAT EnumFormat,\n          IUnknown*   pConcernedDevice,\n          UINT*       pFlags) {\n    Logger::warn(\"DxgiOutput: CheckOverlaySupport: Stub\");\n    return DXGI_ERROR_UNSUPPORTED;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiOutput::CheckOverlayColorSpaceSupport(\n          DXGI_FORMAT           Format,\n          DXGI_COLOR_SPACE_TYPE ColorSpace,\n          IUnknown*             pConcernedDevice,\n          UINT*                 pFlags) {\n    Logger::warn(\"DxgiOutput: CheckOverlayColorSpaceSupport: Stub\");\n    return DXGI_ERROR_UNSUPPORTED;\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE DxgiOutput::CheckHardwareCompositionSupport(\n          UINT*                 pFlags) {\n    Logger::warn(\"DxgiOutput: CheckHardwareCompositionSupport: Stub\");\n\n    *pFlags = 0;\n    return S_OK;\n  }\n\n\n  void DxgiOutput::FilterModesByDesc(\n          std::vector<DXGI_MODE_DESC1>& Modes,\n    const DXGI_MODE_DESC1&              TargetMode) {\n    // Filter modes based on format properties\n    bool testScanlineOrder = false;\n    bool testScaling       = false;\n    bool testFormat        = false;\n\n    for (const auto& mode : Modes) {\n      testScanlineOrder |= TargetMode.ScanlineOrdering != DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED\n                        && TargetMode.ScanlineOrdering == mode.ScanlineOrdering;\n      testScaling       |= TargetMode.Scaling != DXGI_MODE_SCALING_UNSPECIFIED\n                        && TargetMode.Scaling == mode.Scaling;\n      testFormat        |= TargetMode.Format != DXGI_FORMAT_UNKNOWN\n                        && TargetMode.Format == mode.Format;\n    }\n\n    for (auto it = Modes.begin(); it != Modes.end(); ) {\n      bool skipMode = it->Stereo != TargetMode.Stereo;\n\n      if (testScanlineOrder)\n        skipMode |= it->ScanlineOrdering != TargetMode.ScanlineOrdering;\n\n      if (testScaling)\n        skipMode |= it->Scaling != TargetMode.Scaling;\n\n      if (testFormat)\n        skipMode |= it->Format != TargetMode.Format;\n\n      it = skipMode ? Modes.erase(it) : ++it;\n    }\n\n    // Filter by closest resolution\n    uint32_t minDiffResolution  = 0;\n\n    if (TargetMode.Width) {\n      minDiffResolution = std::accumulate(\n        Modes.begin(), Modes.end(), std::numeric_limits<uint32_t>::max(),\n        [&TargetMode] (uint32_t current, const DXGI_MODE_DESC1& mode) {\n          uint32_t diff = std::abs(int32_t(TargetMode.Width  - mode.Width))\n                        + std::abs(int32_t(TargetMode.Height - mode.Height));\n          return std::min(current, diff);\n        });\n\n      for (auto it = Modes.begin(); it != Modes.end(); ) {\n        uint32_t diff = std::abs(int32_t(TargetMode.Width  - it->Width))\n                      + std::abs(int32_t(TargetMode.Height - it->Height));\n\n        bool skipMode = diff != minDiffResolution;\n        it = skipMode ? Modes.erase(it) : ++it;\n      }\n    }\n\n    // Filter by closest refresh rate\n    uint32_t minDiffRefreshRate = 0;\n\n    if (TargetMode.RefreshRate.Numerator && TargetMode.RefreshRate.Denominator) {\n      minDiffRefreshRate = std::accumulate(\n        Modes.begin(), Modes.end(), std::numeric_limits<uint64_t>::max(),\n        [&TargetMode] (uint64_t current, const DXGI_MODE_DESC1& mode) {\n          uint64_t rate = uint64_t(mode.RefreshRate.Numerator)\n                        * uint64_t(TargetMode.RefreshRate.Denominator)\n                        / uint64_t(mode.RefreshRate.Denominator);\n          uint64_t diff = std::abs(int64_t(rate - uint64_t(TargetMode.RefreshRate.Numerator)));\n          return std::min(current, diff);\n        });\n\n      for (auto it = Modes.begin(); it != Modes.end(); ) {\n        uint64_t rate = uint64_t(it->RefreshRate.Numerator)\n                      * uint64_t(TargetMode.RefreshRate.Denominator)\n                      / uint64_t(it->RefreshRate.Denominator);\n        uint64_t diff = std::abs(int64_t(rate - uint64_t(TargetMode.RefreshRate.Numerator)));\n\n        bool skipMode = diff != minDiffRefreshRate;\n        it = skipMode ? Modes.erase(it) : ++it;\n      }\n    }\n  }\n\n\n  void DxgiOutput::CacheMonitorData() {\n    // Try and find an existing monitor info.\n    DXGI_VK_MONITOR_DATA* pMonitorData;\n    if (SUCCEEDED(m_monitorInfo->AcquireMonitorData(m_monitor, &pMonitorData))) {\n      m_metadata = pMonitorData->DisplayMetadata;\n      m_monitorInfo->ReleaseMonitorData();\n      return;\n    }\n\n    // Init monitor info ourselves.\n    // \n    // If some other thread ends up beating us to it\n    // by another InitMonitorData, it doesn't really matter.\n    // \n    // The only thing we cache from this is the m_metadata which\n    // should be exactly the same.\n    // We don't store any pointers from the DXGI_VK_MONITOR_DATA\n    // sturcture, etc.\n    DXGI_VK_MONITOR_DATA monitorData = {};\n\n    // Query current display mode\n    wsi::WsiMode activeWsiMode = { };\n    wsi::getCurrentDisplayMode(m_monitor, &activeWsiMode);\n\n    // Get the display metadata + colorimetry\n    wsi::WsiEdidData edidData = wsi::getMonitorEdid(m_monitor);\n    std::optional<wsi::WsiDisplayMetadata> metadata = std::nullopt;\n    if (!edidData.empty())\n      metadata = wsi::parseColorimetryInfo(edidData);\n\n    if (metadata)\n      m_metadata = metadata.value();\n    else\n      Logger::err(\"DXGI: Failed to parse display metadata + colorimetry info, using blank.\");\n\n    // Normalize either the display metadata we got back, or our\n    // blank one to get something sane here.\n    NormalizeDisplayMetadata(m_monitorInfo->DefaultColorSpace() != DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, m_metadata);\n\n    auto refreshPeriod = computeRefreshPeriod(\n      activeWsiMode.refreshRate.numerator,\n      activeWsiMode.refreshRate.denominator);\n\n    monitorData.FrameStats.SyncQPCTime.QuadPart = dxvk::high_resolution_clock::get_counter();\n    monitorData.FrameStats.SyncRefreshCount = computeRefreshCount(\n      dxvk::high_resolution_clock::time_point(),\n      dxvk::high_resolution_clock::get_time_from_counter(monitorData.FrameStats.SyncQPCTime.QuadPart),\n      refreshPeriod);\n\n    monitorData.FrameStats.PresentRefreshCount = monitorData.FrameStats.SyncRefreshCount;\n    monitorData.GammaCurve.Scale = { 1.0f, 1.0f, 1.0f };\n    monitorData.GammaCurve.Offset = { 0.0f, 0.0f, 0.0f };\n    monitorData.LastMode = ConvertDisplayMode(activeWsiMode);\n    monitorData.DisplayMetadata = m_metadata;\n\n    for (uint32_t i = 0; i < DXGI_VK_GAMMA_CP_COUNT; i++) {\n      const float value = GammaControlPointLocation(i);\n      monitorData.GammaCurve.GammaCurve[i] = { value, value, value };\n    }\n\n    m_monitorInfo->InitMonitorData(m_monitor, &monitorData);\n  }\n\n}\n"
  },
  {
    "path": "src/dxgi/dxgi_output.h",
    "content": "#pragma once\n\n#include \"dxgi_monitor.h\"\n#include \"dxgi_object.h\"\n\nnamespace dxvk {\n  \n  class DxgiAdapter;\n  class DxgiFactory;\n  \n  /**\n   * \\brief Number of gamma control points\n   */\n  constexpr uint32_t DXGI_VK_GAMMA_CP_COUNT = 1024;\n  \n  /**\n   * \\brief Computes gamma control point location\n   * \n   * \\param [in] CpIndex Control point ID\n   * \\returns Location of the control point\n   */\n  inline float GammaControlPointLocation(uint32_t CpIndex) {\n    return float(CpIndex) / float(DXGI_VK_GAMMA_CP_COUNT - 1);\n  }\n  \n  \n  class DxgiOutput : public DxgiObject<IDXGIOutput6> {\n    \n  public:\n    \n    DxgiOutput(\n      const Com<DxgiFactory>& factory,\n      const Com<DxgiAdapter>& adapter,\n            HMONITOR          monitor);\n    \n    ~DxgiOutput();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                riid,\n            void**                ppvObject) final;\n    \n    HRESULT STDMETHODCALLTYPE GetParent(\n            REFIID                riid,\n            void**                ppParent) final;\n    \n    HRESULT STDMETHODCALLTYPE FindClosestMatchingMode(\n      const DXGI_MODE_DESC*       pModeToMatch,\n            DXGI_MODE_DESC*       pClosestMatch,\n            IUnknown*             pConcernedDevice) final;\n\n    HRESULT STDMETHODCALLTYPE FindClosestMatchingMode1(\n      const DXGI_MODE_DESC1*      pModeToMatch,\n            DXGI_MODE_DESC1*      pClosestMatch,\n            IUnknown*             pConcernedDevice) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDesc(\n            DXGI_OUTPUT_DESC*     pDesc) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDesc1(\n            DXGI_OUTPUT_DESC1*    pDesc) final;\n\n    HRESULT STDMETHODCALLTYPE GetDisplayModeList(\n            DXGI_FORMAT           EnumFormat,\n            UINT                  Flags,\n            UINT*                 pNumModes,\n            DXGI_MODE_DESC*       pDesc) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDisplayModeList1(\n            DXGI_FORMAT           EnumFormat,\n            UINT                  Flags,\n            UINT*                 pNumModes,\n            DXGI_MODE_DESC1*      pDesc) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDisplaySurfaceData(\n            IDXGISurface*         pDestination) final;\n\n    HRESULT STDMETHODCALLTYPE GetDisplaySurfaceData1(\n            IDXGIResource*        pDestination) final;\n    \n    HRESULT STDMETHODCALLTYPE GetFrameStatistics(\n            DXGI_FRAME_STATISTICS* pStats) final;\n    \n    HRESULT STDMETHODCALLTYPE GetGammaControl(\n            DXGI_GAMMA_CONTROL*   pArray) final;    \n    \n    HRESULT STDMETHODCALLTYPE GetGammaControlCapabilities(\n            DXGI_GAMMA_CONTROL_CAPABILITIES *pGammaCaps) final;\n    \n    void STDMETHODCALLTYPE ReleaseOwnership() final;\n    \n    HRESULT STDMETHODCALLTYPE SetDisplaySurface(\n            IDXGISurface*         pScanoutSurface) final;\n    \n    HRESULT STDMETHODCALLTYPE SetGammaControl(\n      const DXGI_GAMMA_CONTROL*   pArray) final;\n    \n    HRESULT STDMETHODCALLTYPE TakeOwnership(\n            IUnknown*             pDevice,\n            BOOL                  Exclusive) final;\n    \n    HRESULT STDMETHODCALLTYPE WaitForVBlank() final;\n\n    HRESULT STDMETHODCALLTYPE DuplicateOutput(\n            IUnknown*                 pDevice,\n            IDXGIOutputDuplication**  ppOutputDuplication) final;\n    \n    HRESULT STDMETHODCALLTYPE DuplicateOutput1(\n            IUnknown*                 pDevice,\n            UINT                      Flags,\n            UINT                      SupportedFormatsCount,\n      const DXGI_FORMAT*              pSupportedFormats,\n            IDXGIOutputDuplication**  ppOutputDuplication) final;\n    \n    BOOL STDMETHODCALLTYPE SupportsOverlays() final;\n\n    HRESULT STDMETHODCALLTYPE CheckOverlaySupport(\n            DXGI_FORMAT           EnumFormat,\n            IUnknown*             pConcernedDevice,\n            UINT*                 pFlags) final;\n    \n    HRESULT STDMETHODCALLTYPE CheckOverlayColorSpaceSupport(\n            DXGI_FORMAT           Format,\n            DXGI_COLOR_SPACE_TYPE ColorSpace,\n            IUnknown*             pConcernedDevice,\n            UINT*                 pFlags) final;\n    \n    HRESULT STDMETHODCALLTYPE CheckHardwareCompositionSupport(\n            UINT*                 pFlags) final;\n\n  private:\n    \n    Com<DxgiFactory> m_factory;\n    Com<DxgiAdapter> m_adapter;\n    DxgiMonitorInfo* m_monitorInfo = nullptr;\n    HMONITOR         m_monitor     = nullptr;\n\n    wsi::WsiDisplayMetadata m_metadata = {};\n\n    D3DDestructionNotifier m_destructionNotifier;\n\n    static void FilterModesByDesc(\n            std::vector<DXGI_MODE_DESC1>& Modes,\n      const DXGI_MODE_DESC1&              TargetMode);\n    \n    void CacheMonitorData();\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxgi/dxgi_surface.cpp",
    "content": "#include \"dxgi_surface.h\"\n\n#include \"../wsi/wsi_window.h\"\n\nnamespace dxvk {\n\n  DxgiSurfaceFactory::DxgiSurfaceFactory(PFN_vkGetInstanceProcAddr vulkanLoaderProc, HWND hWnd)\n  : m_vkGetInstanceProcAddr(vulkanLoaderProc), m_window(hWnd), m_ownsWindow(!hWnd) {\n    if (!m_window)\n      m_window = CreateDummyWindow();\n  }\n\n\n  DxgiSurfaceFactory::~DxgiSurfaceFactory() {\n    if (m_ownsWindow)\n      DestroyDummyWindow();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiSurfaceFactory::QueryInterface(\n          REFIID                  riid,\n          void**                  ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n\n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDXGIVkSurfaceFactory)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (logQueryInterfaceError(__uuidof(IDXGIVkSurfaceFactory), riid)) {\n      Logger::warn(\"DxgiSurfaceFactory::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n\n\n  VkResult STDMETHODCALLTYPE DxgiSurfaceFactory::CreateSurface(\n          VkInstance                Instance,\n          VkPhysicalDevice          Adapter,\n          VkSurfaceKHR*             pSurface) {\n    return wsi::createSurface(m_window, m_vkGetInstanceProcAddr, Instance, pSurface);\n  }\n\n\n  HWND DxgiSurfaceFactory::CreateDummyWindow() {\n#ifdef _WIN32\n    static std::atomic<bool> s_wndClassRegistered = { false };\n\n    HINSTANCE hInstance = ::GetModuleHandle(nullptr);\n\n    if (!s_wndClassRegistered.load(std::memory_order_acquire)) {\n      WNDCLASSEXW wndClass = { };\n      wndClass.cbSize = sizeof(wndClass);\n      wndClass.style = CS_HREDRAW | CS_VREDRAW;\n      wndClass.lpfnWndProc = &::DefWindowProcW;\n      wndClass.hInstance = hInstance;\n      wndClass.lpszClassName = L\"DXVKDUMMYWNDCLASS\";\n\n      ATOM atom = ::RegisterClassExW(&wndClass);\n\n      if (!atom)\n        Logger::warn(\"DxgiSurfaceFactory: Failed to register dummy window class\");\n\n      s_wndClassRegistered.store(!!atom, std::memory_order_release);\n    }\n\n    HWND hWnd = ::CreateWindowW(L\"DXVKDUMMYWNDCLASS\", L\"DXVKDUMMYWINDOW\",\n      WS_OVERLAPPEDWINDOW, 0, 0, 320, 240, nullptr, nullptr, hInstance, nullptr);\n\n    if (!hWnd)\n      Logger::err(\"DxgiSurfaceFactory: Failed to create dummy window\");\n\n    return hWnd;\n#else\n    return nullptr;\n#endif\n  }\n\n\n  void DxgiSurfaceFactory::DestroyDummyWindow() {\n#ifdef _WIN32\n    DestroyWindow(m_window);\n#endif\n  }\n\n}\n"
  },
  {
    "path": "src/dxgi/dxgi_surface.h",
    "content": "#pragma once\n\n#include \"../util/com/com_object.h\"\n\n#include \"../vulkan/vulkan_loader.h\"\n\n#include \"dxgi_interfaces.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Surface factory\n   *\n   * Provides a way to transparently create a\n   * Vulkan surface for a given platform window.\n   */\n  class DxgiSurfaceFactory : public ComObject<IDXGIVkSurfaceFactory> {\n\n  public:\n\n    DxgiSurfaceFactory(\n            PFN_vkGetInstanceProcAddr vulkanLoaderProc,\n            HWND                      hWnd);\n\n    ~DxgiSurfaceFactory();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject);\n\n    VkResult STDMETHODCALLTYPE CreateSurface(\n            VkInstance                Instance,\n            VkPhysicalDevice          Adapter,\n            VkSurfaceKHR*             pSurface);\n\n  private:\n\n    PFN_vkGetInstanceProcAddr m_vkGetInstanceProcAddr = nullptr;\n    HWND                      m_window = nullptr;\n    bool                      m_ownsWindow = false;\n\n    HWND CreateDummyWindow();\n\n    void DestroyDummyWindow();\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxgi/dxgi_swapchain.cpp",
    "content": "#include \"dxgi_factory.h\"\n#include \"dxgi_output.h\"\n#include \"dxgi_swapchain.h\"\n\n#include \"../util/util_misc.h\"\n\n#include <d3d12.h>\n\nnamespace dxvk {\n  \n  DxgiSwapChain::DxgiSwapChain(\n          DxgiFactory*                pFactory,\n          IDXGIVkSwapChain*           pPresenter,\n          HWND                        hWnd,\n    const DXGI_SWAP_CHAIN_DESC1*      pDesc,\n    const DXGI_SWAP_CHAIN_FULLSCREEN_DESC*  pFullscreenDesc,\n          IUnknown*                   pDevice)\n  : m_factory   (pFactory),\n    m_window    (hWnd),\n    m_desc      (*pDesc),\n    m_descFs    (*pFullscreenDesc),\n    m_presentId (0u),\n    m_presenter (pPresenter),\n    m_monitor   (wsi::getWindowMonitor(m_window)),\n    m_is_d3d12(SUCCEEDED(pDevice->QueryInterface(__uuidof(ID3D12CommandQueue), reinterpret_cast<void**>(&Com<ID3D12CommandQueue>())))),\n    m_destructionNotifier(this) {\n\n    if (FAILED(m_presenter->GetAdapter(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&m_adapter))))\n      throw DxvkError(\"DXGI: Failed to get adapter for present device\");\n\n    // Query updated interface versions from presenter, this\n    // may fail e.g. with older vkd3d-proton builds.\n    m_presenter->QueryInterface(__uuidof(IDXGIVkSwapChain1), reinterpret_cast<void**>(&m_presenter1));\n    m_presenter->QueryInterface(__uuidof(IDXGIVkSwapChain2), reinterpret_cast<void**>(&m_presenter2));\n\n    m_frameRateOption = m_factory->GetOptions()->maxFrameRate;\n\n    // Query monitor info form DXVK's DXGI factory, if available\n    m_factory->QueryInterface(__uuidof(IDXGIVkMonitorInfo), reinterpret_cast<void**>(&m_monitorInfo));\n    \n    // Apply initial window mode and fullscreen state\n    if (!m_descFs.Windowed && FAILED(EnterFullscreenMode(nullptr)))\n      throw DxvkError(\"DXGI: Failed to set initial fullscreen state\");\n\n    // Ensure that RGBA16 swap chains are scRGB if supported\n    UpdateColorSpace(m_desc.Format, m_colorSpace);\n\n    // Somewhat hacky way to determine whether to forward the\n    // display refresh rate in windowed mode even with a sync\n    // interval of 1.\n    if (!m_is_d3d12) {\n      auto instance = pFactory->GetDXVKInstance();\n      m_hasLatencyControl = instance->options().latencySleep == Tristate::True;\n    }\n  }\n  \n  \n  DxgiSwapChain::~DxgiSwapChain() {\n    if (!m_descFs.Windowed)\n      RestoreDisplayMode(m_monitor);\n\n    // Decouple swap chain from monitor if necessary\n    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;\n    \n    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorInfo))) {\n      if (monitorInfo->pSwapChain == this)\n        monitorInfo->pSwapChain = nullptr;\n      \n      ReleaseMonitorData();\n    }\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::QueryInterface(REFIID riid, void** ppvObject) {\n    if (ppvObject == nullptr)\n      return E_POINTER;\n\n    *ppvObject = nullptr;\n    \n    if (riid == __uuidof(IUnknown)\n     || riid == __uuidof(IDXGIObject)\n     || riid == __uuidof(IDXGIDeviceSubObject)\n     || riid == __uuidof(IDXGISwapChain)\n     || riid == __uuidof(IDXGISwapChain1)\n     || riid == __uuidof(IDXGISwapChain2)\n     || riid == __uuidof(IDXGISwapChain3)\n     || riid == __uuidof(IDXGISwapChain4)) {\n      *ppvObject = ref(this);\n      return S_OK;\n    }\n\n    if (riid == __uuidof(ID3DDestructionNotifier)) {\n      *ppvObject = ref(&m_destructionNotifier);\n      return S_OK;\n    }\n    \n    if (logQueryInterfaceError(__uuidof(IDXGISwapChain), riid)) {\n      Logger::warn(\"DxgiSwapChain::QueryInterface: Unknown interface query\");\n      Logger::warn(str::format(riid));\n    }\n\n    return E_NOINTERFACE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetParent(REFIID riid, void** ppParent) {\n    return m_factory->QueryInterface(riid, ppParent);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDevice(REFIID riid, void** ppDevice) {\n    return m_presenter->GetDevice(riid, ppDevice);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetBuffer(UINT Buffer, REFIID riid, void** ppSurface) {\n    return m_presenter->GetImage(Buffer, riid, ppSurface);\n  }\n\n\n  UINT STDMETHODCALLTYPE DxgiSwapChain::GetCurrentBackBufferIndex() {\n    return m_presenter->GetImageIndex();\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetContainingOutput(IDXGIOutput** ppOutput) {\n    InitReturnPtr(ppOutput);\n    \n    if (!wsi::isWindow(m_window))\n      return DXGI_ERROR_INVALID_CALL;\n    \n    Com<IDXGIOutput1> output;\n\n    if (m_target == nullptr) {\n      HRESULT hr = GetOutputFromMonitor(wsi::getWindowMonitor(m_window), &output);\n\n      if (FAILED(hr))\n        return hr;\n    } else {\n      output = m_target;\n    }\n\n    *ppOutput = output.ref();\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc) {\n    if (!pDesc)\n      return E_INVALIDARG;\n    \n    pDesc->BufferDesc.Width     = m_desc.Width;\n    pDesc->BufferDesc.Height    = m_desc.Height;\n    pDesc->BufferDesc.RefreshRate = m_descFs.RefreshRate;\n    pDesc->BufferDesc.Format    = m_desc.Format;\n    pDesc->BufferDesc.ScanlineOrdering = m_descFs.ScanlineOrdering;\n    pDesc->BufferDesc.Scaling   = m_descFs.Scaling;\n    pDesc->SampleDesc           = m_desc.SampleDesc;\n    pDesc->BufferUsage          = m_desc.BufferUsage;\n    pDesc->BufferCount          = m_desc.BufferCount;\n    pDesc->OutputWindow         = m_window;\n    pDesc->Windowed             = m_descFs.Windowed;\n    pDesc->SwapEffect           = m_desc.SwapEffect;\n    pDesc->Flags                = m_desc.Flags;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc1(DXGI_SWAP_CHAIN_DESC1* pDesc) {\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n    \n    *pDesc = m_desc;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetBackgroundColor(\n          DXGI_RGBA*                pColor) {\n    Logger::err(\"DxgiSwapChain::GetBackgroundColor: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetRotation(\n          DXGI_MODE_ROTATION*       pRotation) {\n    Logger::err(\"DxgiSwapChain::GetRotation: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetRestrictToOutput(\n          IDXGIOutput**             ppRestrictToOutput) {\n    InitReturnPtr(ppRestrictToOutput);\n    \n    Logger::err(\"DxgiSwapChain::GetRestrictToOutput: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) {\n    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);\n\n    if (!pStats)\n      return E_INVALIDARG;\n\n    static bool s_errorShown = false;\n\n    if (!std::exchange(s_errorShown, true))\n      Logger::warn(\"DxgiSwapChain::GetFrameStatistics: Frame statistics may be inaccurate\");\n\n    // Populate frame statistics with local present count and current time\n    auto t1Counter = dxvk::high_resolution_clock::get_counter();\n\n    DXGI_VK_FRAME_STATISTICS frameStatistics = { };\n    frameStatistics.PresentCount = m_presentId;\n    frameStatistics.PresentQPCTime = t1Counter;\n\n    if (m_presenter1 != nullptr)\n      m_presenter1->GetFrameStatistics(&frameStatistics);\n\n    // Fill in actual DXGI statistics, using monitor data to help compute\n    // vblank counts if possible. This is not fully accurate, especially on\n    // displays with variable refresh rates, but it's the best we can do.\n    DXGI_VK_MONITOR_DATA* monitorData = nullptr;\n\n    pStats->PresentCount          = frameStatistics.PresentCount;\n    pStats->PresentRefreshCount   = 0;\n    pStats->SyncRefreshCount      = 0;\n    pStats->SyncQPCTime.QuadPart  = frameStatistics.PresentQPCTime;\n    pStats->SyncGPUTime.QuadPart  = 0;\n\n    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorData))) {\n      auto refreshPeriod = computeRefreshPeriod(\n        monitorData->LastMode.RefreshRate.Numerator,\n        monitorData->LastMode.RefreshRate.Denominator);\n\n      auto t0 = dxvk::high_resolution_clock::get_time_from_counter(monitorData->FrameStats.SyncQPCTime.QuadPart);\n      auto t1 = dxvk::high_resolution_clock::get_time_from_counter(t1Counter);\n      auto t2 = dxvk::high_resolution_clock::get_time_from_counter(frameStatistics.PresentQPCTime);\n\n      pStats->PresentRefreshCount = m_presenter1 != nullptr\n        ? monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t2, refreshPeriod)\n        : monitorData->FrameStats.PresentRefreshCount;\n      pStats->SyncRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);\n\n      ReleaseMonitorData();\n    }\n\n    // Docs say that DISJOINT is returned on the first call and around\n    // mode changes. Just make this swap chain state for now.\n    HRESULT hr = S_OK;\n\n    if (std::exchange(m_frameStatisticsDisjoint, false))\n      hr = DXGI_ERROR_FRAME_STATISTICS_DISJOINT;\n\n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenState(\n          BOOL*         pFullscreen,\n          IDXGIOutput** ppTarget) {\n    HRESULT hr = S_OK;\n\n    if (!m_is_d3d12 && !m_descFs.Windowed && wsi::isOccluded(m_window))\n      SetFullscreenState(FALSE, nullptr);\n    if (pFullscreen != nullptr)\n      *pFullscreen = !m_descFs.Windowed;\n    \n    if (ppTarget != nullptr)\n      *ppTarget = m_target.ref();\n\n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenDesc(\n          DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) {\n    if (pDesc == nullptr)\n      return E_INVALIDARG;\n    \n    *pDesc = m_descFs;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetHwnd(\n          HWND*                     pHwnd) {\n    if (pHwnd == nullptr)\n      return E_INVALIDARG;\n    \n    *pHwnd = m_window;\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetCoreWindow(\n          REFIID                    refiid,\n          void**                    ppUnk) {\n    InitReturnPtr(ppUnk);\n    \n    Logger::err(\"DxgiSwapChain::GetCoreWindow: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetLastPresentCount(UINT* pLastPresentCount) {\n    if (pLastPresentCount == nullptr)\n      return E_INVALIDARG;\n\n    UINT64 presentId = m_presentId;\n\n    if (m_presenter1 != nullptr)\n      m_presenter1->GetLastPresentCount(&presentId);\n\n    *pLastPresentCount = UINT(presentId);\n    return S_OK;\n  }\n  \n  \n  BOOL STDMETHODCALLTYPE DxgiSwapChain::IsTemporaryMonoSupported() {\n    // This seems to be related to stereo 3D display\n    // modes, which we don't support at the moment\n    return FALSE;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) {\n    return PresentBase(SyncInterval, Flags, nullptr);\n  }\n\n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present1(\n          UINT                      SyncInterval,\n          UINT                      PresentFlags,\n    const DXGI_PRESENT_PARAMETERS*  pPresentParameters) {\n\n    return PresentBase(SyncInterval, PresentFlags, pPresentParameters);\n  }\n\n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::PresentBase(\n          UINT                      SyncInterval,\n          UINT                      PresentFlags,\n    const DXGI_PRESENT_PARAMETERS*  pPresentParameters) {\n\n    if (SyncInterval > 4)\n      return DXGI_ERROR_INVALID_CALL;\n\n    if ((m_desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD || m_desc.SwapEffect == DXGI_SWAP_EFFECT_SEQUENTIAL) && wsi::isMinimized(m_window))\n      return DXGI_STATUS_OCCLUDED;\n    bool occluded = !m_descFs.Windowed && wsi::isOccluded(m_window) && !wsi::isMinimized(m_window);\n\n    auto options = m_factory->GetOptions();\n\n    if (options->syncInterval >= 0)\n      SyncInterval = options->syncInterval;\n\n    UpdateGlobalHDRState();\n\n    if (!(PresentFlags & DXGI_PRESENT_TEST))\n      UpdateTargetFrameRate(SyncInterval);\n\n    std::lock_guard<dxvk::recursive_mutex> lockWin(m_lockWindow);\n    HRESULT hr = S_OK;\n\n    if (wsi::isWindow(m_window) || !m_window) {\n      std::lock_guard<dxvk::mutex> lockBuf(m_lockBuffer);\n      hr = m_presenter->Present(SyncInterval, PresentFlags, nullptr);\n    }\n\n    if (PresentFlags & DXGI_PRESENT_TEST)\n      return hr == S_OK && occluded ? DXGI_STATUS_OCCLUDED : hr;\n\n    if (hr == S_OK) {\n\n      m_presentId += 1;\n\n      // Update monitor frame statistics. This is not consistent with swap chain\n      // frame statistics at all, but we want to ensure that all presents become\n      // visible to the IDXGIOutput in case applications rely on that behaviour.\n      DXGI_VK_MONITOR_DATA* monitorData = nullptr;\n\n      if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorData))) {\n        auto refreshPeriod = computeRefreshPeriod(\n          monitorData->LastMode.RefreshRate.Numerator,\n          monitorData->LastMode.RefreshRate.Denominator);\n\n        auto t0 = dxvk::high_resolution_clock::get_time_from_counter(monitorData->FrameStats.SyncQPCTime.QuadPart);\n        auto t1 = dxvk::high_resolution_clock::now();\n\n        monitorData->FrameStats.PresentCount += 1;\n        monitorData->FrameStats.PresentRefreshCount = monitorData->FrameStats.SyncRefreshCount + computeRefreshCount(t0, t1, refreshPeriod);\n        ReleaseMonitorData();\n      }\n      if (occluded) {\n        if (!(PresentFlags & DXGI_PRESENT_TEST))\n          SetFullscreenState(FALSE, nullptr);\n        hr = DXGI_STATUS_OCCLUDED;\n      }\n    }\n\n    return hr;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeBuffers(\n          UINT                      BufferCount,\n          UINT                      Width,\n          UINT                      Height,\n          DXGI_FORMAT               NewFormat,\n          UINT                      SwapChainFlags) {\n    return ResizeBuffers1(BufferCount, Width, Height,\n      NewFormat, SwapChainFlags, nullptr, nullptr);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeBuffers1(\n          UINT                      BufferCount,\n          UINT                      Width,\n          UINT                      Height,\n          DXGI_FORMAT               Format,\n          UINT                      SwapChainFlags,\n    const UINT*                     pCreationNodeMask,\n          IUnknown* const*          ppPresentQueue) {\n    if (m_window && !wsi::isWindow(m_window))\n      return DXGI_ERROR_INVALID_CALL;\n\n    constexpr UINT PreserveFlags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;\n\n    if ((m_desc.Flags & PreserveFlags) != (SwapChainFlags & PreserveFlags))\n      return DXGI_ERROR_INVALID_CALL;\n    \n    std::lock_guard<dxvk::mutex> lock(m_lockBuffer);\n    m_desc.Width  = Width;\n    m_desc.Height = Height;\n    \n    if (m_window) {\n      wsi::getWindowSize(m_window,\n        m_desc.Width  ? nullptr : &m_desc.Width,\n        m_desc.Height ? nullptr : &m_desc.Height);\n    }\n    \n    if (BufferCount != 0)\n      m_desc.BufferCount = BufferCount;\n    \n    if (Format != DXGI_FORMAT_UNKNOWN)\n      m_desc.Format = Format;\n    \n    HRESULT hr = m_presenter->ChangeProperties(&m_desc, pCreationNodeMask, ppPresentQueue);\n\n    if (FAILED(hr))\n      return hr;\n\n    UpdateColorSpace(m_desc.Format, m_colorSpace);\n    return hr;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeTarget(const DXGI_MODE_DESC* pNewTargetParameters) {\n    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);\n\n    if (!pNewTargetParameters)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    if (!wsi::isWindow(m_window))\n      return DXGI_ERROR_INVALID_CALL;\n\n    // Promote display mode\n    DXGI_MODE_DESC1 newDisplayMode = { };\n    newDisplayMode.Width = pNewTargetParameters->Width;\n    newDisplayMode.Height = pNewTargetParameters->Height;\n    newDisplayMode.RefreshRate = pNewTargetParameters->RefreshRate;\n    newDisplayMode.Format = pNewTargetParameters->Format;\n    newDisplayMode.ScanlineOrdering = pNewTargetParameters->ScanlineOrdering;\n    newDisplayMode.Scaling = pNewTargetParameters->Scaling;\n\n    // Update the swap chain description\n    if (newDisplayMode.RefreshRate.Numerator != 0)\n      m_descFs.RefreshRate = newDisplayMode.RefreshRate;\n    \n    m_descFs.ScanlineOrdering = newDisplayMode.ScanlineOrdering;\n    m_descFs.Scaling          = newDisplayMode.Scaling;\n    \n    if (m_descFs.Windowed) {\n      wsi::resizeWindow(\n        m_window, &m_windowState,\n        newDisplayMode.Width,\n        newDisplayMode.Height);\n    } else {\n      Com<IDXGIOutput1> output;\n      \n      if (FAILED(GetOutputFromMonitor(m_monitor, &output))) {\n        Logger::err(\"DXGI: ResizeTarget: Failed to query containing output\");\n        return E_FAIL;\n      }\n\n      RECT bounds = { };\n      wsi::getDesktopCoordinates(m_monitor, &bounds);\n\n      uint32_t width = 0u;\n      uint32_t height = 0u;\n\n      wsi::getWindowSize(m_window, &width, &height);\n\n      // Window bounds were changed behind our back, update saved state\n      if (uint32_t(bounds.right - bounds.left) != width || uint32_t(bounds.bottom - bounds.top) != height)\n        wsi::saveWindowState(m_window, &m_windowState, false);\n\n      ChangeDisplayMode(output.ptr(), &newDisplayMode);\n      wsi::updateFullscreenWindow(m_monitor, m_window, false);\n    }\n\n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetFullscreenState(\n          BOOL          Fullscreen,\n          IDXGIOutput*  pTarget) {\n    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);\n\n    if (!Fullscreen && pTarget)\n      return DXGI_ERROR_INVALID_CALL;\n\n    Com<IDXGIOutput1> target;\n\n    if (pTarget) {\n      DXGI_OUTPUT_DESC desc;\n\n      pTarget->QueryInterface(IID_PPV_ARGS(&target));\n      target->GetDesc(&desc);\n\n      if (!m_descFs.Windowed && Fullscreen && m_monitor != desc.Monitor) {\n        HRESULT hr = this->LeaveFullscreenMode();\n        if (FAILED(hr))\n          return hr;\n      }\n    }\n\n    if (m_descFs.Windowed && Fullscreen)\n      return this->EnterFullscreenMode(target.ptr());\n    else if (!m_descFs.Windowed && !Fullscreen)\n      return this->LeaveFullscreenMode();\n    \n    return S_OK;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetBackgroundColor(\n    const DXGI_RGBA*                pColor) {\n    Logger::err(\"DxgiSwapChain::SetBackgroundColor: Not implemented\");\n    return E_NOTIMPL;\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetRotation(\n          DXGI_MODE_ROTATION        Rotation) {\n\n    if (Rotation == DXGI_MODE_ROTATION_IDENTITY)\n      return S_OK;\n\n    Logger::err(str::format(\"DxgiSwapChain::SetRotation(\", Rotation,\"): Not implemented\"));\n    return E_NOTIMPL;\n  }\n  \n  \n  HANDLE STDMETHODCALLTYPE DxgiSwapChain::GetFrameLatencyWaitableObject() {\n    if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))\n      return nullptr;\n\n    return m_presenter->GetFrameLatencyEvent();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetMatrixTransform(\n          DXGI_MATRIX_3X2_F*        pMatrix) {\n    // We don't support composition swap chains\n    Logger::err(\"DxgiSwapChain::GetMatrixTransform: Not supported\");\n    return DXGI_ERROR_INVALID_CALL;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetMaximumFrameLatency(\n          UINT*                     pMaxLatency) {\n    if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))\n      return DXGI_ERROR_INVALID_CALL;\n\n    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);\n    *pMaxLatency = m_presenter->GetFrameLatency();\n    return S_OK;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetSourceSize(\n          UINT*                     pWidth,\n          UINT*                     pHeight) {\n    // TODO implement properly once supported\n    if (pWidth)  *pWidth  = m_desc.Width;\n    if (pHeight) *pHeight = m_desc.Height;\n    return S_OK;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetMatrixTransform(\n    const DXGI_MATRIX_3X2_F*        pMatrix) {\n    // We don't support composition swap chains\n    Logger::err(\"DxgiSwapChain::SetMatrixTransform: Not supported\");\n    return DXGI_ERROR_INVALID_CALL;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetMaximumFrameLatency(\n          UINT                      MaxLatency) {\n    if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))\n      return DXGI_ERROR_INVALID_CALL;\n\n    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);\n    return m_presenter->SetFrameLatency(MaxLatency);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetSourceSize(\n          UINT                      Width,\n          UINT                      Height) {\n    if (Width  == 0 || Width  > m_desc.Width\n     || Height == 0 || Height > m_desc.Height)\n      return E_INVALIDARG;\n\n    std::lock_guard<dxvk::mutex> lock(m_lockBuffer);\n\n    RECT region = { 0, 0, LONG(Width), LONG(Height) };\n    return m_presenter->SetPresentRegion(&region);\n  }\n  \n\n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::CheckColorSpaceSupport(\n          DXGI_COLOR_SPACE_TYPE           ColorSpace,\n          UINT*                           pColorSpaceSupport) {\n    if (!pColorSpaceSupport)\n      return E_INVALIDARG;\n\n    std::lock_guard<dxvk::mutex> lock(m_lockBuffer);\n\n    if (ValidateColorSpaceSupport(m_desc.Format, ColorSpace))\n      *pColorSpaceSupport = DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT;\n    else\n      *pColorSpaceSupport = 0;\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetColorSpace1(DXGI_COLOR_SPACE_TYPE ColorSpace) {\n    std::lock_guard<dxvk::mutex> lock(m_lockBuffer);\n\n    if (!ValidateColorSpaceSupport(m_desc.Format, ColorSpace))\n      return E_INVALIDARG;\n\n    // Write back color space if setting it up succeeded. This way, we preserve\n    // the current color space even if the swap chain temporarily switches to a\n    // back buffer format which does not support it.\n    HRESULT hr = UpdateColorSpace(m_desc.Format, ColorSpace);\n\n    if (SUCCEEDED(hr))\n      m_colorSpace = ColorSpace;\n\n    return hr;\n  }\n\n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetHDRMetaData(\n          DXGI_HDR_METADATA_TYPE    Type,\n          UINT                      Size,\n          void*                     pMetaData) {\n    if (Size && !pMetaData)\n      return E_INVALIDARG;\n\n    DXGI_VK_HDR_METADATA metadata = { Type };\n\n    switch (Type) {\n      case DXGI_HDR_METADATA_TYPE_NONE:\n        break;\n\n      case DXGI_HDR_METADATA_TYPE_HDR10:\n        if (Size != sizeof(DXGI_HDR_METADATA_HDR10))\n          return E_INVALIDARG;\n\n        metadata.HDR10 = *static_cast<const DXGI_HDR_METADATA_HDR10*>(pMetaData);\n        break;\n\n      default:\n        Logger::err(str::format(\"DXGI: Unsupported HDR metadata type: \", Type));\n        return E_INVALIDARG;\n    }\n\n    std::lock_guard<dxvk::mutex> lock(m_lockBuffer);\n    return m_presenter->SetHDRMetaData(&metadata);\n  }\n  \n  \n  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetGammaControl(\n          UINT                      NumPoints,\n    const DXGI_RGB*                 pGammaCurve) {\n    std::lock_guard<dxvk::mutex> lockBuf(m_lockBuffer);\n    return m_presenter->SetGammaControl(NumPoints, pGammaCurve);\n  }\n\n\n  HRESULT DxgiSwapChain::EnterFullscreenMode(IDXGIOutput1* pTarget) {\n    if (m_ModeChangeInProgress) {\n      Logger::warn(\"Nested EnterFullscreenMode\");\n      return DXGI_STATUS_MODE_CHANGE_IN_PROGRESS;\n    }\n    scoped_bool in_progress(m_ModeChangeInProgress);\n\n    Com<IDXGIOutput1> output = pTarget;\n\n    if (!wsi::isWindow(m_window))\n      return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;\n    \n    if (output == nullptr) {\n      if (FAILED(GetOutputFromMonitor(wsi::getWindowMonitor(m_window), &output))) {\n        Logger::err(\"DXGI: EnterFullscreenMode: Cannot query containing output\");\n        return E_FAIL;\n      }\n    }\n\n    DXGI_MODE_DESC1 displayMode = { };\n    displayMode.Width            = m_desc.Width;\n    displayMode.Height           = m_desc.Height;\n    displayMode.RefreshRate      = m_descFs.RefreshRate;\n    displayMode.Format           = m_desc.Format;\n    // Ignore these two, games usually use them wrong and we don't\n    // support any scaling modes except UNSPECIFIED anyway.\n    displayMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;\n    displayMode.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;\n    \n    if (FAILED(ChangeDisplayMode(output.ptr(), &displayMode))) {\n      Logger::err(\"DXGI: EnterFullscreenMode: Failed to change display mode\");\n      return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;\n    }\n    \n    // Update swap chain description\n    m_descFs.Windowed = FALSE;\n    \n    // Move the window so that it covers the entire output\n    bool modeSwitch = (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) != 0u;\n\n    DXGI_OUTPUT_DESC desc;\n    output->GetDesc(&desc);\n\n    wsi::saveWindowState(m_window, &m_windowState, true);\n\n    if (!wsi::enterFullscreenMode(desc.Monitor, m_window, &m_windowState, modeSwitch)) {\n      Logger::err(\"DXGI: EnterFullscreenMode: Failed to enter fullscreen mode\");\n      return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;\n    }\n    \n    m_monitor = desc.Monitor;\n    m_target  = std::move(output);\n\n    // Apply current gamma curve of the output\n    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;\n\n    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorInfo))) {\n      if (!monitorInfo->pSwapChain)\n        monitorInfo->pSwapChain = this;\n      \n      SetGammaControl(DXGI_VK_GAMMA_CP_COUNT, monitorInfo->GammaCurve.GammaCurve);\n      ReleaseMonitorData();\n    }\n\n    return S_OK;\n  }\n  \n  \n  HRESULT DxgiSwapChain::LeaveFullscreenMode() {\n    if (m_ModeChangeInProgress) {\n      Logger::warn(\"Nested LeaveFullscreenMode\");\n      return DXGI_STATUS_MODE_CHANGE_IN_PROGRESS;\n    }\n    scoped_bool in_progress(m_ModeChangeInProgress);\n\n    if (FAILED(RestoreDisplayMode(m_monitor)))\n      Logger::warn(\"DXGI: LeaveFullscreenMode: Failed to restore display mode\");\n    \n    // Reset gamma control and decouple swap chain from monitor\n    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;\n\n    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorInfo))) {\n      if (monitorInfo->pSwapChain == this)\n        monitorInfo->pSwapChain = nullptr;\n      \n      SetGammaControl(0, nullptr);\n      ReleaseMonitorData();\n    }\n\n    // Restore internal state\n    m_descFs.Windowed = TRUE;\n    m_target  = nullptr;\n    m_monitor = wsi::getWindowMonitor(m_window);\n    \n    if (!wsi::isWindow(m_window))\n      return S_OK;\n    \n    if (!wsi::leaveFullscreenMode(m_window, &m_windowState)) {\n      Logger::err(\"DXGI: LeaveFullscreenMode: Failed to exit fullscreen mode\");\n      return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;\n    }\n    wsi::restoreWindowState(m_window, &m_windowState, true);\n    \n    return S_OK;\n  }\n  \n  \n  HRESULT DxgiSwapChain::ChangeDisplayMode(\n          IDXGIOutput1*           pOutput,\n    const DXGI_MODE_DESC1*        pDisplayMode) {\n    if (!pOutput)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    // Find a mode that the output supports\n    DXGI_OUTPUT_DESC outputDesc;\n    pOutput->GetDesc(&outputDesc);\n    \n    DXGI_MODE_DESC1 preferredMode = *pDisplayMode;\n    DXGI_MODE_DESC1 selectedMode = { };\n\n    if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH)) {\n      preferredMode.Width = 0;\n      preferredMode.Height = 0;\n    }\n\n    if (preferredMode.Format == DXGI_FORMAT_UNKNOWN)\n      preferredMode.Format = m_desc.Format;\n    \n    HRESULT hr = pOutput->FindClosestMatchingMode1(\n      &preferredMode, &selectedMode, nullptr);\n\n    if (FAILED(hr)) {\n      Logger::err(str::format(\n        \"DXGI: Failed to query closest mode:\",\n        \"\\n  Format: \", preferredMode.Format,\n        \"\\n  Mode:   \", preferredMode.Width, \"x\", preferredMode.Height,\n          \"@\", preferredMode.RefreshRate.Numerator / std::max(preferredMode.RefreshRate.Denominator, 1u)));\n      return hr;\n    }\n\n    if (!selectedMode.RefreshRate.Denominator)\n      selectedMode.RefreshRate.Denominator = 1;\n\n    if (!wsi::setWindowMode(outputDesc.Monitor, m_window, &m_windowState, ConvertDisplayMode(selectedMode)))\n      return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;\n\n    DXGI_VK_MONITOR_DATA* monitorData = nullptr;\n\n    if (SUCCEEDED(AcquireMonitorData(outputDesc.Monitor, &monitorData))) {\n      auto refreshPeriod = computeRefreshPeriod(\n        monitorData->LastMode.RefreshRate.Numerator,\n        monitorData->LastMode.RefreshRate.Denominator);\n\n      auto t1Counter = dxvk::high_resolution_clock::get_counter();\n\n      auto t0 = dxvk::high_resolution_clock::get_time_from_counter(monitorData->FrameStats.SyncQPCTime.QuadPart);\n      auto t1 = dxvk::high_resolution_clock::get_time_from_counter(t1Counter);\n\n      monitorData->FrameStats.SyncRefreshCount += computeRefreshCount(t0, t1, refreshPeriod);\n      monitorData->FrameStats.SyncQPCTime.QuadPart = t1Counter;\n      monitorData->LastMode = selectedMode;\n      ReleaseMonitorData();\n    }\n\n    m_frameRateRefresh = double(selectedMode.RefreshRate.Numerator)\n                       / double(selectedMode.RefreshRate.Denominator);\n    return S_OK;\n  }\n  \n  \n  HRESULT DxgiSwapChain::RestoreDisplayMode(HMONITOR hMonitor) {\n    if (!hMonitor)\n      return DXGI_ERROR_INVALID_CALL;\n    \n    if (!wsi::restoreDisplayMode())\n      return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;\n\n    m_frameRateRefresh = 0.0;\n    return S_OK;\n  }\n  \n  \n  HRESULT DxgiSwapChain::GetSampleCount(UINT Count, VkSampleCountFlagBits* pCount) const {\n    switch (Count) {\n      case  1: *pCount = VK_SAMPLE_COUNT_1_BIT;  return S_OK;\n      case  2: *pCount = VK_SAMPLE_COUNT_2_BIT;  return S_OK;\n      case  4: *pCount = VK_SAMPLE_COUNT_4_BIT;  return S_OK;\n      case  8: *pCount = VK_SAMPLE_COUNT_8_BIT;  return S_OK;\n      case 16: *pCount = VK_SAMPLE_COUNT_16_BIT; return S_OK;\n    }\n    \n    return E_INVALIDARG;\n  }\n\n\n  HRESULT DxgiSwapChain::GetOutputFromMonitor(\n          HMONITOR                  Monitor,\n          IDXGIOutput1**            ppOutput) {\n    if (!ppOutput)\n      return DXGI_ERROR_INVALID_CALL;\n\n    Com<IDXGIOutput> output;\n\n    for (uint32_t i = 0; SUCCEEDED(m_adapter->EnumOutputs(i, &output)); i++) {\n      DXGI_OUTPUT_DESC outputDesc;\n      output->GetDesc(&outputDesc);\n      \n      if (outputDesc.Monitor == Monitor)\n        return output->QueryInterface(IID_PPV_ARGS(ppOutput));\n      \n      output = nullptr;\n    }\n    \n    return DXGI_ERROR_NOT_FOUND;\n  }\n\n\n  HRESULT DxgiSwapChain::AcquireMonitorData(\n          HMONITOR                hMonitor,\n          DXGI_VK_MONITOR_DATA**  ppData) {\n    if (m_monitorInfo == nullptr || !hMonitor)\n      return E_NOINTERFACE;\n\n    HRESULT hr = m_monitorInfo->AcquireMonitorData(hMonitor, ppData);\n\n    if (FAILED(hr) && HasLiveReferences()) {\n      // We may need to initialize a DXGI output to populate monitor data.\n      // If acquiring monitor data has failed previously, do not try again.\n      if (hMonitor == m_monitor && !m_monitorHasOutput)\n        return E_NOINTERFACE;\n\n      Com<IDXGIOutput1> output;\n\n      if (SUCCEEDED(GetOutputFromMonitor(hMonitor, &output)))\n        hr = m_monitorInfo->AcquireMonitorData(hMonitor, ppData);\n    }\n\n    if (hMonitor == m_monitor)\n      m_monitorHasOutput = SUCCEEDED(hr);\n\n    return hr;\n  }\n\n  \n  void DxgiSwapChain::ReleaseMonitorData() {\n    if (m_monitorInfo != nullptr)\n      m_monitorInfo->ReleaseMonitorData();\n  }\n\n\n  void DxgiSwapChain::UpdateGlobalHDRState() {\n    // Update the global HDR state if called from the legacy NVAPI\n    // interfaces, etc.\n\n    auto state = m_factory->GlobalHDRState();\n    if (m_globalHDRStateSerial != state.Serial) {\n      SetColorSpace1(state.ColorSpace);\n\n      switch (state.Metadata.Type) {\n        case DXGI_HDR_METADATA_TYPE_NONE:\n          SetHDRMetaData(DXGI_HDR_METADATA_TYPE_NONE, 0, nullptr);\n          break;\n        case DXGI_HDR_METADATA_TYPE_HDR10:\n          SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(state.Metadata.HDR10), reinterpret_cast<void*>(&state.Metadata.HDR10));\n          break;\n        default:\n          Logger::err(str::format(\"DXGI: Unsupported HDR metadata type (global): \", state.Metadata.Type));\n          break;\n      }\n\n      m_globalHDRStateSerial = state.Serial;\n    }\n  }\n\n\n  bool DxgiSwapChain::ValidateColorSpaceSupport(\n          DXGI_FORMAT             Format,\n          DXGI_COLOR_SPACE_TYPE   ColorSpace) {\n    // RGBA16 swap chains are treated as scRGB even on SDR displays,\n    // and regular sRGB is not exposed when this format is used.\n    if (Format == DXGI_FORMAT_R16G16B16A16_FLOAT)\n      return ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;\n\n    // For everything else, we will always expose plain sRGB\n    if (ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709)\n      return true;\n\n    // Only expose HDR10 color space if HDR option is enabled\n    if (ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)\n      return m_factory->GetOptions()->enableHDR && m_presenter->CheckColorSpaceSupport(ColorSpace);\n\n    return false;\n  }\n\n\n  HRESULT DxgiSwapChain::UpdateColorSpace(\n          DXGI_FORMAT             Format,\n          DXGI_COLOR_SPACE_TYPE   ColorSpace) {\n    // Don't do anything if the explicitly sepected color space\n    // is compatible with the back buffer format already\n    if (!ValidateColorSpaceSupport(Format, ColorSpace)) {\n      ColorSpace = Format == DXGI_FORMAT_R16G16B16A16_FLOAT\n        ? DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709\n        : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;\n    }\n\n    // Ensure that we pick a supported color space. This is relevant for\n    // mapping scRGB to sRGB on SDR setups, matching Windows behaviour.\n    if (!m_presenter->CheckColorSpaceSupport(ColorSpace))\n      ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;\n\n    HRESULT hr = m_presenter->SetColorSpace(ColorSpace);\n\n    // If this was a colorspace other than our current one,\n    // punt us into that one on the DXGI output.\n    if (SUCCEEDED(hr))\n      m_monitorInfo->PuntColorSpace(ColorSpace);\n\n    return hr;\n  }\n\n\n  void DxgiSwapChain::UpdateTargetFrameRate(\n          UINT                    SyncInterval) {\n    if (m_presenter2 == nullptr)\n      return;\n\n    // Engage the frame limiter with large sync intervals even in windowed\n    // mode since we want to avoid double-presenting to the swap chain.\n    if (SyncInterval != m_frameRateSyncInterval && m_descFs.Windowed) {\n      bool engageLimiter = (SyncInterval > 1u) || (SyncInterval && m_hasLatencyControl);\n\n      m_frameRateSyncInterval = SyncInterval;\n      m_frameRateRefresh = 0.0f;\n\n      if (engageLimiter && wsi::isWindow(m_window)) {\n        wsi::WsiMode mode = { };\n\n        if (wsi::getCurrentDisplayMode(wsi::getWindowMonitor(m_window), &mode)) {\n          if (mode.refreshRate.numerator && mode.refreshRate.denominator) {\n            m_frameRateRefresh = double(mode.refreshRate.numerator)\n                               / double(mode.refreshRate.denominator);\n          }\n        }\n      }\n    } else if (!m_descFs.Windowed) {\n      // Reset tracking when in fullscreen mode\n      m_frameRateSyncInterval = 0;\n    }\n\n    // Use a negative number to indicate that the limiter should only\n    // be engaged if the target frame rate is actually exceeded\n    double frameRate = m_frameRateOption;\n\n    if (frameRate != -1.0) {\n      if (SyncInterval && frameRate == 0.0)\n        frameRate = -m_frameRateRefresh / double(SyncInterval);\n\n      if (m_frameRateLimit != frameRate) {\n        m_frameRateLimit = frameRate;\n        m_presenter2->SetTargetFrameRate(frameRate);\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxgi/dxgi_swapchain.h",
    "content": "#pragma once\n\n#include <memory>\n#include <mutex>\n\n#include \"dxgi_interfaces.h\"\n#include \"dxgi_monitor.h\"\n#include \"dxgi_object.h\"\n\n#include \"../d3d11/d3d11_interfaces.h\"\n\n#include \"../spirv/spirv_module.h\"\n\n#include \"../util/util_time.h\"\n\n#include \"../wsi/wsi_window.h\"\n#include \"../wsi/wsi_monitor.h\"\n\nnamespace dxvk {\n  \n  class DxgiDevice;\n  class DxgiFactory;\n  class DxgiOutput;\n  \n  class DxgiSwapChain : public DxgiObject<IDXGISwapChain4> {\n    \n  public:\n    \n    DxgiSwapChain(\n            DxgiFactory*                pFactory,\n            IDXGIVkSwapChain*           pPresenter,\n            HWND                        hWnd,\n      const DXGI_SWAP_CHAIN_DESC1*      pDesc,\n      const DXGI_SWAP_CHAIN_FULLSCREEN_DESC*  pFullscreenDesc,\n            IUnknown*                   pDevice);\n    \n    ~DxgiSwapChain();\n    \n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject) final;\n            \n    HRESULT STDMETHODCALLTYPE GetParent(\n            REFIID                    riid,\n            void**                    ppParent) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDevice(\n            REFIID                    riid,\n            void**                    ppDevice) final;\n    \n    HRESULT STDMETHODCALLTYPE GetBuffer(\n            UINT                      Buffer,\n            REFIID                    riid,\n            void**                    ppSurface) final;\n    \n    UINT STDMETHODCALLTYPE GetCurrentBackBufferIndex() final;\n\n    HRESULT STDMETHODCALLTYPE GetContainingOutput(\n            IDXGIOutput**             ppOutput) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDesc(\n            DXGI_SWAP_CHAIN_DESC*     pDesc) final;\n    \n    HRESULT STDMETHODCALLTYPE GetDesc1(\n            DXGI_SWAP_CHAIN_DESC1*    pDesc) final;\n    \n    HRESULT STDMETHODCALLTYPE GetFullscreenState(\n            BOOL*                     pFullscreen,\n            IDXGIOutput**             ppTarget) final;\n    \n    HRESULT STDMETHODCALLTYPE GetFullscreenDesc(\n            DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) final;\n\n    HRESULT STDMETHODCALLTYPE GetHwnd(\n            HWND*                     pHwnd) final;\n\n    HRESULT STDMETHODCALLTYPE GetCoreWindow(\n            REFIID                    refiid,\n            void**                    ppUnk) final;\n\n    HRESULT STDMETHODCALLTYPE GetBackgroundColor(\n            DXGI_RGBA*                pColor) final;\n    \n    HRESULT STDMETHODCALLTYPE GetRotation(\n            DXGI_MODE_ROTATION*       pRotation) final;\n    \n    HRESULT STDMETHODCALLTYPE GetRestrictToOutput(\n            IDXGIOutput**             ppRestrictToOutput) final;\n    \n    HRESULT STDMETHODCALLTYPE GetFrameStatistics(\n            DXGI_FRAME_STATISTICS*    pStats) final;\n    \n    HRESULT STDMETHODCALLTYPE GetLastPresentCount(\n            UINT*                     pLastPresentCount) final;\n    \n    BOOL STDMETHODCALLTYPE IsTemporaryMonoSupported() final;\n\n    HRESULT STDMETHODCALLTYPE Present(\n            UINT                      SyncInterval,\n            UINT                      Flags) final;\n    \n    HRESULT STDMETHODCALLTYPE Present1(\n            UINT                      SyncInterval,\n            UINT                      PresentFlags,\n      const DXGI_PRESENT_PARAMETERS*  pPresentParameters) final;\n\n    HRESULT STDMETHODCALLTYPE ResizeBuffers(\n            UINT                      BufferCount,\n            UINT                      Width,\n            UINT                      Height,\n            DXGI_FORMAT               NewFormat,\n            UINT                      SwapChainFlags) final;\n    \n    HRESULT STDMETHODCALLTYPE ResizeBuffers1(\n            UINT                      BufferCount,\n            UINT                      Width,\n            UINT                      Height,\n            DXGI_FORMAT               Format,\n            UINT                      SwapChainFlags,\n      const UINT*                     pCreationNodeMask,\n            IUnknown* const*          ppPresentQueue) final;\n\n    HRESULT STDMETHODCALLTYPE ResizeTarget(\n      const DXGI_MODE_DESC*           pNewTargetParameters) final;\n    \n    HRESULT STDMETHODCALLTYPE SetFullscreenState(\n            BOOL                      Fullscreen,\n            IDXGIOutput*              pTarget) final;\n    \n    HRESULT STDMETHODCALLTYPE SetBackgroundColor(\n      const DXGI_RGBA*                pColor) final;\n\n    HRESULT STDMETHODCALLTYPE SetRotation(\n            DXGI_MODE_ROTATION        Rotation) final;\n    \n    HANDLE STDMETHODCALLTYPE GetFrameLatencyWaitableObject() final;\n\n    HRESULT STDMETHODCALLTYPE GetMatrixTransform(\n            DXGI_MATRIX_3X2_F*        pMatrix) final;\n    \n    HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(\n            UINT*                     pMaxLatency) final;\n    \n    HRESULT STDMETHODCALLTYPE GetSourceSize(\n            UINT*                     pWidth,\n            UINT*                     pHeight) final;\n    \n    HRESULT STDMETHODCALLTYPE SetMatrixTransform(\n      const DXGI_MATRIX_3X2_F*        pMatrix) final;\n    \n    HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(\n            UINT                      MaxLatency) final;\n\n    HRESULT STDMETHODCALLTYPE SetSourceSize(\n            UINT                      Width,\n            UINT                      Height) final;\n    \n    HRESULT STDMETHODCALLTYPE CheckColorSpaceSupport(\n            DXGI_COLOR_SPACE_TYPE     ColorSpace,\n            UINT*                     pColorSpaceSupport) final;\n\n    HRESULT STDMETHODCALLTYPE SetColorSpace1(\n            DXGI_COLOR_SPACE_TYPE     ColorSpace) final;\n    \n    HRESULT STDMETHODCALLTYPE SetHDRMetaData(\n            DXGI_HDR_METADATA_TYPE    Type,\n            UINT                      Size,\n            void*                     pMetaData) final;\n\n    HRESULT STDMETHODCALLTYPE SetGammaControl(\n            UINT                      NumPoints,\n      const DXGI_RGB*                 pGammaCurve);\n    \n  private:\n    \n    dxvk::recursive_mutex           m_lockWindow;\n    dxvk::mutex                     m_lockBuffer;\n\n    Com<DxgiFactory>                m_factory;\n    Com<IDXGIAdapter>               m_adapter;\n    Com<IDXGIOutput1>               m_target;\n    Com<IDXGIVkMonitorInfo>         m_monitorInfo;\n    \n    HWND                            m_window;\n    DXGI_SWAP_CHAIN_DESC1           m_desc;\n    DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_descFs;\n    UINT                            m_presentId;\n    bool                            m_ModeChangeInProgress = false;\n\n    Com<IDXGIVkSwapChain>           m_presenter;\n    Com<IDXGIVkSwapChain1>          m_presenter1;\n    Com<IDXGIVkSwapChain2>          m_presenter2;\n    \n    HMONITOR                        m_monitor;\n    bool                            m_monitorHasOutput = true;\n    bool                            m_frameStatisticsDisjoint = true;\n    wsi::DxvkWindowState            m_windowState;\n\n    double                          m_frameRateOption = 0.0;\n    double                          m_frameRateRefresh = 0.0;\n    double                          m_frameRateLimit = 0.0;\n    uint32_t                        m_frameRateSyncInterval = 0u;\n    bool                            m_is_d3d12;\n\n    DXGI_COLOR_SPACE_TYPE           m_colorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;\n\n    uint32_t                        m_globalHDRStateSerial = 0;\n    bool                            m_hasLatencyControl = false;\n\n    D3DDestructionNotifier          m_destructionNotifier;\n    \n    HRESULT EnterFullscreenMode(\n            IDXGIOutput1            *pTarget);\n    \n    HRESULT LeaveFullscreenMode();\n    \n    HRESULT ChangeDisplayMode(\n            IDXGIOutput1*           pOutput,\n      const DXGI_MODE_DESC1*        pDisplayMode);\n    \n    HRESULT RestoreDisplayMode(\n            HMONITOR                hMonitor);\n    \n    HRESULT GetSampleCount(\n            UINT                    Count,\n            VkSampleCountFlagBits*  pCount) const;\n    \n    HRESULT GetOutputFromMonitor(\n            HMONITOR                Monitor,\n            IDXGIOutput1**          ppOutput);\n    \n    HRESULT AcquireMonitorData(\n            HMONITOR                hMonitor,\n            DXGI_VK_MONITOR_DATA**  ppData);\n    \n    void ReleaseMonitorData();\n\n    void UpdateGlobalHDRState();\n\n    bool ValidateColorSpaceSupport(\n            DXGI_FORMAT             Format,\n            DXGI_COLOR_SPACE_TYPE   ColorSpace);\n\n    HRESULT UpdateColorSpace(\n            DXGI_FORMAT             Format,\n            DXGI_COLOR_SPACE_TYPE   ColorSpace);\n\n    void UpdateTargetFrameRate(\n            UINT                    SyncInterval);\n\n    HRESULT STDMETHODCALLTYPE PresentBase(\n            UINT                      SyncInterval,\n            UINT                      PresentFlags,\n      const DXGI_PRESENT_PARAMETERS*  pPresentParameters);\n  };\n  \n}\n"
  },
  {
    "path": "src/dxgi/dxgi_swapchain_dispatcher.h",
    "content": "#pragma once\n\n#include \"dxgi_swapchain.h\"\n\nnamespace dxvk {\n\n  class DxgiSwapChainDispatcher : public IDXGISwapChain4 {\n\n  public:\n\n    DxgiSwapChainDispatcher(IDXGISwapChain4* dispatch, IUnknown* device)\n      : m_device(device),\n        m_dispatch(dispatch) {\n    }\n\n    virtual ~DxgiSwapChainDispatcher() {\n    }\n\n    ULONG STDMETHODCALLTYPE AddRef() {\n      if (likely(m_dispatch != nullptr))\n        return m_dispatch->AddRef();\n\n      return 0;\n    }\n\n    ULONG STDMETHODCALLTYPE Release() {\n      if (likely(m_dispatch != nullptr)) {\n        ULONG refCount = m_dispatch->Release();\n\n        if (unlikely(!refCount)) {\n          // Clear the underlying swap chain pointer. Helps in\n          // use-after-free situations encountered in Divinity\n          // Original Sin Enhanced Edition.\n          m_dispatch = nullptr;\n          delete this;\n        }\n\n        return refCount;\n      }\n\n      return ~0u;\n    }\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    riid,\n            void**                    ppvObject) final {\n      if (ppvObject == nullptr)\n        return E_POINTER;\n\n      *ppvObject = nullptr;\n\n      if (riid == __uuidof(IUnknown)\n       || riid == __uuidof(IDXGIObject)\n       || riid == __uuidof(IDXGIDeviceSubObject)\n       || riid == __uuidof(IDXGISwapChain)\n       || riid == __uuidof(IDXGISwapChain1)\n       || riid == __uuidof(IDXGISwapChain2)\n       || riid == __uuidof(IDXGISwapChain3)\n       || riid == __uuidof(IDXGISwapChain4)) {\n        *ppvObject = ref(this);\n        return S_OK;\n      }\n\n      if (logQueryInterfaceError(__uuidof(IDXGISwapChain), riid)) {\n        Logger::warn(\"DxgiSwapChainDispatcher::QueryInterface: Unknown interface query\");\n        Logger::warn(str::format(riid));\n      }\n\n      return m_dispatch->QueryInterface(riid, ppvObject);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetPrivateData(\n            REFGUID       Name,\n            UINT*         pDataSize,\n            void*         pData) final {\n      return m_dispatch->GetPrivateData(Name, pDataSize, pData);\n    }\n\n    HRESULT STDMETHODCALLTYPE SetPrivateData(\n            REFGUID       Name,\n            UINT          DataSize,\n      const void*         pData) final {\n      return m_dispatch->SetPrivateData(Name, DataSize, pData);\n    }\n\n    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(\n            REFGUID       Name,\n      const IUnknown*     pUnknown) final {\n      return m_dispatch->SetPrivateDataInterface(Name, pUnknown);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetParent(\n            REFIID                    riid,\n            void**                    ppParent) final {\n      return m_dispatch->GetParent(riid, ppParent);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetDevice(\n            REFIID                    riid,\n            void**                    ppDevice) final {\n      return m_dispatch->GetDevice(riid, ppDevice);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetBuffer(\n            UINT                      Buffer,\n            REFIID                    riid,\n            void**                    ppSurface) final {\n      return m_dispatch->GetBuffer(Buffer, riid, ppSurface);\n    }\n\n    UINT STDMETHODCALLTYPE GetCurrentBackBufferIndex() final {\n      return m_dispatch->GetCurrentBackBufferIndex();\n    }\n\n    HRESULT STDMETHODCALLTYPE GetContainingOutput(\n            IDXGIOutput**             ppOutput) final {\n      return m_dispatch->GetContainingOutput(ppOutput);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetDesc(\n            DXGI_SWAP_CHAIN_DESC*     pDesc) final {\n      return m_dispatch->GetDesc(pDesc);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetDesc1(\n            DXGI_SWAP_CHAIN_DESC1*    pDesc) final {\n      return m_dispatch->GetDesc1(pDesc);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetFullscreenState(\n            BOOL*                     pFullscreen,\n            IDXGIOutput**             ppTarget) final {\n      if (likely(m_dispatch != nullptr))\n        return m_dispatch->GetFullscreenState(pFullscreen, ppTarget);\n\n      if (pFullscreen)\n        *pFullscreen = FALSE;\n\n      if (ppTarget)\n        *ppTarget = nullptr;\n\n      return S_OK;\n    }\n\n    HRESULT STDMETHODCALLTYPE GetFullscreenDesc(\n            DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) final {\n      return m_dispatch->GetFullscreenDesc(pDesc);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetHwnd(\n            HWND*                     pHwnd) final {\n      return m_dispatch->GetHwnd(pHwnd);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetCoreWindow(\n            REFIID                    refiid,\n            void**                    ppUnk) final {\n      return m_dispatch->GetCoreWindow(refiid, ppUnk);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetBackgroundColor(\n            DXGI_RGBA*                pColor) final {\n      return m_dispatch->GetBackgroundColor(pColor);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetRotation(\n            DXGI_MODE_ROTATION*       pRotation) final {\n      return m_dispatch->GetRotation(pRotation);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetRestrictToOutput(\n            IDXGIOutput**             ppRestrictToOutput) final {\n      return m_dispatch->GetRestrictToOutput(ppRestrictToOutput);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetFrameStatistics(\n            DXGI_FRAME_STATISTICS*    pStats) final {\n      return m_dispatch->GetFrameStatistics(pStats);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetLastPresentCount(\n            UINT*                     pLastPresentCount) final {\n      return m_dispatch->GetLastPresentCount(pLastPresentCount);\n    }\n\n    BOOL STDMETHODCALLTYPE IsTemporaryMonoSupported() final {\n      return m_dispatch->IsTemporaryMonoSupported();\n    }\n\n    HRESULT STDMETHODCALLTYPE Present(\n            UINT                      SyncInterval,\n            UINT                      Flags) final {\n      return m_dispatch->Present(SyncInterval, Flags);\n    }\n\n    HRESULT STDMETHODCALLTYPE Present1(\n            UINT                      SyncInterval,\n            UINT                      PresentFlags,\n      const DXGI_PRESENT_PARAMETERS*  pPresentParameters) final {\n      return m_dispatch->Present1(SyncInterval, PresentFlags, pPresentParameters);\n    }\n\n    HRESULT STDMETHODCALLTYPE ResizeBuffers(\n            UINT                      BufferCount,\n            UINT                      Width,\n            UINT                      Height,\n            DXGI_FORMAT               NewFormat,\n            UINT                      SwapChainFlags) final {\n      return m_dispatch->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags);\n    }\n\n    HRESULT STDMETHODCALLTYPE ResizeBuffers1(\n            UINT                      BufferCount,\n            UINT                      Width,\n            UINT                      Height,\n            DXGI_FORMAT               Format,\n            UINT                      SwapChainFlags,\n      const UINT*                     pCreationNodeMask,\n            IUnknown* const*          ppPresentQueue) final {\n      return m_dispatch->ResizeBuffers1(BufferCount, Width, Height, Format, SwapChainFlags, pCreationNodeMask, ppPresentQueue);\n    }\n\n    HRESULT STDMETHODCALLTYPE ResizeTarget(\n      const DXGI_MODE_DESC*           pNewTargetParameters) final {\n      return m_dispatch->ResizeTarget(pNewTargetParameters);\n    }\n\n    HRESULT STDMETHODCALLTYPE SetFullscreenState(\n            BOOL                      Fullscreen,\n            IDXGIOutput*              pTarget) final {\n      if (likely(m_dispatch != nullptr))\n        return m_dispatch->SetFullscreenState(Fullscreen, pTarget);\n      return S_OK;\n    }\n\n\n    HRESULT STDMETHODCALLTYPE SetBackgroundColor(\n      const DXGI_RGBA*                pColor) final {\n      return m_dispatch->SetBackgroundColor(pColor);\n    }\n\n    HRESULT STDMETHODCALLTYPE SetRotation(\n            DXGI_MODE_ROTATION        Rotation) final {\n      return m_dispatch->SetRotation(Rotation);\n    }\n\n    HANDLE STDMETHODCALLTYPE GetFrameLatencyWaitableObject() final {\n      return m_dispatch->GetFrameLatencyWaitableObject();\n    }\n\n    HRESULT STDMETHODCALLTYPE GetMatrixTransform(\n            DXGI_MATRIX_3X2_F*        pMatrix) final {\n      return m_dispatch->GetMatrixTransform(pMatrix);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(\n            UINT*                     pMaxLatency) final {\n      return m_dispatch->GetMaximumFrameLatency(pMaxLatency);\n    }\n\n    HRESULT STDMETHODCALLTYPE GetSourceSize(\n            UINT*                     pWidth,\n            UINT*                     pHeight) final {\n      return m_dispatch->GetSourceSize(pWidth, pHeight);\n    }\n\n    HRESULT STDMETHODCALLTYPE SetMatrixTransform(\n      const DXGI_MATRIX_3X2_F*        pMatrix) final {\n      return m_dispatch->SetMatrixTransform(pMatrix);\n    }\n\n    HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(\n            UINT                      MaxLatency) final {\n      return m_dispatch->SetMaximumFrameLatency(MaxLatency);\n    }\n\n    HRESULT STDMETHODCALLTYPE SetSourceSize(\n            UINT                      Width,\n            UINT                      Height) final {\n      return m_dispatch->SetSourceSize(Width, Height);\n    }\n\n    HRESULT STDMETHODCALLTYPE CheckColorSpaceSupport(\n            DXGI_COLOR_SPACE_TYPE     ColorSpace,\n            UINT*                     pColorSpaceSupport) final {\n      return m_dispatch->CheckColorSpaceSupport(ColorSpace, pColorSpaceSupport);\n    }\n\n    HRESULT STDMETHODCALLTYPE SetColorSpace1(\n            DXGI_COLOR_SPACE_TYPE     ColorSpace) final {\n      return m_dispatch->SetColorSpace1(ColorSpace);\n    }\n\n    HRESULT STDMETHODCALLTYPE SetHDRMetaData(\n            DXGI_HDR_METADATA_TYPE    Type,\n            UINT                      Size,\n            void*                     pMetaData) final {\n      return m_dispatch->SetHDRMetaData(Type, Size, pMetaData);\n    }\n\n  private:\n\n    // m_device is not used or reference counted, provided for compatibility with mod engines\n    // which expect to find device pointer in the memory after swapchain interface.\n    IUnknown* m_device;\n\n    IDXGISwapChain4* m_dispatch;\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxgi/meson.build",
    "content": "dxgi_res = wrc_generator.process('version.rc')\n\ndxgi_src = [\n  'dxgi_adapter.cpp',\n  'dxgi_enums.cpp',\n  'dxgi_factory.cpp',\n  'dxgi_format.cpp',\n  'dxgi_main.cpp',\n  'dxgi_monitor.cpp',\n  'dxgi_options.cpp',\n  'dxgi_output.cpp',\n  'dxgi_surface.cpp',\n  'dxgi_swapchain.cpp',\n]\n\ndxgi_ld_args      = []\ndxgi_link_depends = []\n\nif platform != 'windows'\n  dxgi_ld_args      += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'dxgi.sym') ]\n  dxgi_link_depends += files('dxgi.sym')\nendif\n\ndxgi_dll = shared_library(dxvk_name_prefix+'dxgi', dxgi_src, dxgi_res,\n  dependencies        : [ dxvk_dep ],\n  include_directories : dxvk_include_path,\n  install             : true,\n  vs_module_defs      : 'dxgi'+def_spec_ext,\n  link_args           : dxgi_ld_args,\n  link_depends        : [ dxgi_link_depends ],\n  kwargs              : dxvk_so_version,\n)\n\ndxgi_dep = declare_dependency(\n  link_with           : [ dxgi_dll ],\n  include_directories : [ dxvk_include_path ],\n)\n\nif platform != 'windows'\n  pkg.generate(dxgi_dll,\n    filebase: dxvk_pkg_prefix + 'dxgi',\n    subdirs:  'dxvk',\n  )\nendif\n"
  },
  {
    "path": "src/dxgi/version.rc",
    "content": "#include <windows.h>\n\n// DLL version information.\nVS_VERSION_INFO    VERSIONINFO\nFILEVERSION        10,0,17763,1\nPRODUCTVERSION     10,0,17763,1\nFILEFLAGSMASK      VS_FFI_FILEFLAGSMASK\nFILEFLAGS          0\nFILEOS VOS_NT_WINDOWS32\nFILETYPE VFT_DLL\nFILESUBTYPE VFT2_UNKNOWN\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"080904b0\"\n    BEGIN\n      VALUE \"CompanyName\",      \"DXVK\"\n      VALUE \"FileDescription\",  \"DirectX Graphics Infrastructure\"\n      VALUE \"FileVersion\",      \"10.0.17763.1 (WinBuild.160101.0800)\"\n      VALUE \"InternalName\",     \"dxgi.dll\"\n      VALUE \"LegalCopyright\",   \"zlib/libpng license\"\n      VALUE \"OriginalFilename\", \"dxgi.dll\"\n      VALUE \"ProductName\",      \"DXVK\"\n      VALUE \"ProductVersion\",   \"10.0.17763.1\"\n    END\n  END\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0809, 1200\n  END\nEND\n\n"
  },
  {
    "path": "src/dxso/dxso_analysis.cpp",
    "content": "#include \"dxso_analysis.h\"\n\nnamespace dxvk {\n\n  DxsoAnalyzer::DxsoAnalyzer(\n    DxsoAnalysisInfo& analysis)\n    : m_analysis(&analysis) { }\n\n  void DxsoAnalyzer::processInstruction(\n    const DxsoInstructionContext& ctx) {\n    DxsoOpcode opcode = ctx.instruction.opcode;\n\n    // Co-issued CNDs are issued before their parents,\n    // except when the parent is a CND.\n    if (opcode == DxsoOpcode::Cnd &&\n        m_parentOpcode != DxsoOpcode::Cnd &&\n        ctx.instruction.coissue) {\n      m_analysis->coissues.push_back(ctx);\n    }\n\n    if (opcode == DxsoOpcode::TexKill)\n      m_analysis->usesKill = true;\n\n    if (opcode == DxsoOpcode::DsX\n     || opcode == DxsoOpcode::DsY\n\n     || opcode == DxsoOpcode::Tex\n     || opcode == DxsoOpcode::TexCoord\n     || opcode == DxsoOpcode::TexBem\n     || opcode == DxsoOpcode::TexBemL\n     || opcode == DxsoOpcode::TexReg2Ar\n     || opcode == DxsoOpcode::TexReg2Gb\n     || opcode == DxsoOpcode::TexM3x2Pad\n     || opcode == DxsoOpcode::TexM3x2Tex\n     || opcode == DxsoOpcode::TexM3x3Pad\n     || opcode == DxsoOpcode::TexM3x3Tex\n     || opcode == DxsoOpcode::TexM3x3Spec\n     || opcode == DxsoOpcode::TexM3x3VSpec\n     || opcode == DxsoOpcode::TexReg2Rgb\n     || opcode == DxsoOpcode::TexDp3Tex\n     || opcode == DxsoOpcode::TexM3x2Depth\n     || opcode == DxsoOpcode::TexDp3\n     || opcode == DxsoOpcode::TexM3x3\n     //  Explicit LOD.\n     //|| opcode == DxsoOpcode::TexLdd\n     //|| opcode == DxsoOpcode::TexLdl\n     || opcode == DxsoOpcode::TexDepth)\n      m_analysis->usesDerivatives = true;\n\n    m_parentOpcode = ctx.instruction.opcode;\n  }\n\n  void DxsoAnalyzer::finalize(size_t tokenCount) {\n    m_analysis->bytecodeByteLength = tokenCount * sizeof(uint32_t);\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_analysis.h",
    "content": "#pragma once\n\n#include \"dxso_modinfo.h\"\n#include \"dxso_decoder.h\"\n\nnamespace dxvk {\n\n  struct DxsoAnalysisInfo {\n    uint32_t bytecodeByteLength;\n\n    bool usesDerivatives = false;\n    bool usesKill        = false;\n\n    std::vector<DxsoInstructionContext> coissues;\n  };\n\n  class DxsoAnalyzer {\n\n  public:\n\n    DxsoAnalyzer(\n            DxsoAnalysisInfo& analysis);\n\n    /**\n     * \\brief Processes a single instruction\n     * \\param [in] ins The instruction\n     */\n    void processInstruction(\n      const DxsoInstructionContext& ctx);\n\n    void finalize(size_t tokenCount);\n\n  private:\n\n    DxsoAnalysisInfo* m_analysis = nullptr;\n\n    DxsoOpcode m_parentOpcode    = DxsoOpcode::Nop;\n\n  };\n\n}"
  },
  {
    "path": "src/dxso/dxso_code.cpp",
    "content": "#include \"dxso_code.h\"\n\nnamespace dxvk {\n\n  DxsoCode::DxsoCode(DxsoReader& reader) {\n    m_code =\n      reinterpret_cast<const uint32_t*>(reader.currentPtr());\n  }\n\n  const uint32_t* DxsoCodeIter::ptrAt(uint32_t id) const {\n    return m_ptr + id;\n  }\n\n\n  uint32_t DxsoCodeIter::at(uint32_t id) const {\n    return m_ptr[id];\n  }\n\n\n  uint32_t DxsoCodeIter::read() {\n    return *(m_ptr++);\n  }\n\n  DxsoCodeIter DxsoCodeIter::skip(uint32_t n) const {\n    return DxsoCodeIter(m_ptr + n);\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_code.h",
    "content": "#pragma once\n\n#include \"dxso_include.h\"\n#include \"dxso_reader.h\"\n\n#include <vector>\n#include <cstdint>\n\nnamespace dxvk {\n\n  /**\n   * \\brief DXBC code iterator\n   * \n   * Convenient pointer wrapper that allows\n   * reading the code token stream.\n   */\n  class DxsoCodeIter {\n    \n  public:\n    \n    DxsoCodeIter(\n      const uint32_t* ptr)\n    : m_ptr(ptr) { }\n    \n    const uint32_t* ptrAt(uint32_t id) const;\n    \n    uint32_t at(uint32_t id) const;\n    uint32_t read();\n    \n    DxsoCodeIter skip(uint32_t n) const;\n    \n  private:\n    \n    const uint32_t* m_ptr = nullptr;\n    \n  };\n\n  class DxsoCode {\n\n  public:\n\n    DxsoCode(DxsoReader& reader);\n\n    DxsoCodeIter iter() const {\n      return DxsoCodeIter(m_code);\n    }\n\n  private:\n\n    const uint32_t* m_code;\n\n  };\n\n}"
  },
  {
    "path": "src/dxso/dxso_common.cpp",
    "content": "#include \"dxso_common.h\"\n\nnamespace dxvk {\n\n  VkShaderStageFlagBits DxsoProgramInfo::shaderStage() const {\n    switch (m_type) {\n      case DxsoProgramTypes::PixelShader:  return VK_SHADER_STAGE_FRAGMENT_BIT;\n      case DxsoProgramTypes::VertexShader: return VK_SHADER_STAGE_VERTEX_BIT;\n      default:                             break;\n    }\n\n    throw DxvkError(\"DxsoProgramInfo::shaderStage: Unsupported program type\");\n  }\n\n\n  spv::ExecutionModel DxsoProgramInfo::executionModel() const {\n    switch (m_type) {\n      case DxsoProgramTypes::PixelShader:  return spv::ExecutionModelFragment;\n      case DxsoProgramTypes::VertexShader: return spv::ExecutionModelVertex;\n      default:                             break;\n    }\n\n    throw DxvkError(\"DxsoProgramInfo::executionModel: Unsupported program type\");\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_common.h",
    "content": "#pragma once\n\n#include \"dxso_include.h\"\n\n#include \"../d3d9/d3d9_include.h\"\n\n#include <cstdint>\n\nnamespace dxvk {\n\n /**\n * \\brief DXSO Program type\n *\n * Defines the shader stage that a DXSO\n * module has been compiled for.\n */\n  using DxsoProgramType = D3D9ShaderType;\n  using DxsoProgramTypes = D3D9ShaderType;\n\n  class DxsoProgramInfo {\n\n  public:\n\n    DxsoProgramInfo() { }\n    DxsoProgramInfo(\n            DxsoProgramType type,\n            uint32_t        minorVersion,\n            uint32_t        majorVersion)\n      : m_type{ type }\n      , m_minorVersion{ minorVersion }\n      , m_majorVersion{ majorVersion } {}\n\n    /**\n     * \\brief Program type\n     * \\returns Program type\n     */\n    DxsoProgramType type() const {\n      return m_type;\n    }\n\n    /**\n     * \\brief Vulkan shader stage\n     *\n     * The \\c VkShaderStageFlagBits constant\n     * that corresponds to the program type.\n     * \\returns Vulkan shader stage\n     */\n    VkShaderStageFlagBits shaderStage() const;\n\n    /**\n     * \\brief SPIR-V execution model\n     *\n     * The execution model that corresponds\n     * to the Vulkan shader stage.\n     * \\returns SPIR-V execution model\n     */\n    spv::ExecutionModel executionModel() const;\n\n    /**\n     * \\brief Minor version\n     * \\returns The minor version of the shader model.\n     */\n    uint32_t minorVersion() const {\n      return m_minorVersion;\n    }\n\n    /**\n     * \\brief Major version\n     * \\returns The major version of the shader model.\n     */\n    uint32_t majorVersion() const {\n      return m_majorVersion;\n    }\n\n  private:\n\n    DxsoProgramType m_type;\n\n    uint32_t        m_minorVersion;\n    uint32_t        m_majorVersion;\n\n  };\n\n}"
  },
  {
    "path": "src/dxso/dxso_compiler.cpp",
    "content": "#include \"dxso_analysis.h\"\n#include \"dxso_compiler.h\"\n#include \"dxso_util.h\"\n\n#include \"../d3d9/d3d9_caps.h\"\n#include \"../d3d9/d3d9_constant_set.h\"\n#include \"../d3d9/d3d9_state.h\"\n#include \"../d3d9/d3d9_spec_constants.h\"\n#include \"../d3d9/d3d9_fixed_function.h\"\n\n#include \"../dxvk/dxvk_shader_spirv.h\"\n\n#include <cfloat>\n\nnamespace dxvk {\n\n  DxsoCompiler::DxsoCompiler(\n    const std::string&        fileName,\n    const DxsoModuleInfo&     moduleInfo,\n    const DxsoProgramInfo&    programInfo,\n    const DxsoAnalysisInfo&   analysis,\n    const D3D9ConstantLayout& layout)\n    : m_moduleInfo ( moduleInfo )\n    , m_programInfo( programInfo )\n    , m_analysis   ( &analysis )\n    , m_layout     ( &layout )\n    , m_module     ( spvVersion(1, 3) ) {\n    // Declare an entry point ID. We'll need it during the\n    // initialization phase where the execution mode is set.\n    m_entryPointId = m_module.allocateId();\n\n    // Set the shader name so that we recognize it in renderdoc\n    m_module.setDebugSource(\n      spv::SourceLanguageUnknown, 0,\n      m_module.addDebugString(fileName.c_str()),\n      nullptr);\n\n    // Set the memory model. This is the same for all shaders.\n    m_module.setMemoryModel(\n      spv::AddressingModelLogical,\n      spv::MemoryModelGLSL450);\n\n    m_usedSamplers = 0;\n    m_usedRTs      = 0;\n    m_textureTypes = 0;\n    m_rRegs.reserve(DxsoMaxTempRegs);\n\n    for (uint32_t i = 0; i < m_rRegs.size(); i++)\n      m_rRegs.at(i)  = DxsoRegisterPointer{ };\n\n    for (uint32_t i = 0; i < m_cFloat.size(); i++)\n      m_cFloat.at(i) = 0;\n\n    for (uint32_t i = 0; i < m_cInt.size(); i++)\n      m_cInt.at(i)   = 0;\n\n    for (uint32_t i = 0; i < m_cBool.size(); i++)\n      m_cBool.at(i)  = 0;\n\n    m_vs.addr        = DxsoRegisterPointer{ };\n    m_vs.oPos        = DxsoRegisterPointer{ };\n    m_fog            = DxsoRegisterPointer{ };\n    m_vs.oPSize      = DxsoRegisterPointer{ };\n\n    for (uint32_t i = 0; i < m_ps.oColor.size(); i++)\n      m_ps.oColor.at(i) = DxsoRegisterPointer{ };\n    m_ps.oDepth      = DxsoRegisterPointer{ };\n    m_ps.vFace       = DxsoRegisterPointer{ };\n    m_ps.vPos        = DxsoRegisterPointer{ };\n\n    m_loopCounter = DxsoRegisterPointer{ };\n\n    this->emitInit();\n  }\n\n\n  void DxsoCompiler::processInstruction(\n    const DxsoInstructionContext& ctx,\n          uint32_t                currentCoissueIdx) {\n    const DxsoOpcode opcode = ctx.instruction.opcode;\n\n    for (const auto& coissue : m_analysis->coissues) {\n      if (coissue.instructionIdx == ctx.instructionIdx &&\n          coissue.instructionIdx != currentCoissueIdx)\n        return;\n\n      if (coissue.instructionIdx == ctx.instructionIdx + 1)\n        processInstruction(coissue, coissue.instructionIdx);\n    }\n\n    switch (opcode) {\n    case DxsoOpcode::Nop:\n      return;\n\n    case DxsoOpcode::Dcl:\n      return this->emitDcl(ctx);\n\n    case DxsoOpcode::Def:\n    case DxsoOpcode::DefI:\n    case DxsoOpcode::DefB:\n      return this->emitDef(ctx);\n\n    case DxsoOpcode::Mov:\n    case DxsoOpcode::Mova:\n      return this->emitMov(ctx);\n\n    case DxsoOpcode::Add:\n    case DxsoOpcode::Sub:\n    case DxsoOpcode::Mad:\n    case DxsoOpcode::Mul:\n    case DxsoOpcode::Rcp:\n    case DxsoOpcode::Rsq:\n    case DxsoOpcode::Dp3:\n    case DxsoOpcode::Dp4:\n    case DxsoOpcode::Slt:\n    case DxsoOpcode::Sge:\n    case DxsoOpcode::Min:\n    case DxsoOpcode::ExpP:\n    case DxsoOpcode::Exp:\n    case DxsoOpcode::Max:\n    case DxsoOpcode::Pow:\n    case DxsoOpcode::Crs:\n    case DxsoOpcode::Abs:\n    case DxsoOpcode::Sgn:\n    case DxsoOpcode::Nrm:\n    case DxsoOpcode::SinCos:\n    case DxsoOpcode::Lit:\n    case DxsoOpcode::Dst:\n    case DxsoOpcode::LogP:\n    case DxsoOpcode::Log:\n    case DxsoOpcode::Lrp:\n    case DxsoOpcode::Frc:\n    case DxsoOpcode::Cmp:\n    case DxsoOpcode::Bem:\n    case DxsoOpcode::Cnd:\n    case DxsoOpcode::Dp2Add:\n    case DxsoOpcode::DsX:\n    case DxsoOpcode::DsY:\n      return this->emitVectorAlu(ctx);\n\n    case DxsoOpcode::SetP:\n      return this->emitPredicateOp(ctx);\n\n    case DxsoOpcode::M3x2:\n    case DxsoOpcode::M3x3:\n    case DxsoOpcode::M3x4:\n    case DxsoOpcode::M4x3:\n    case DxsoOpcode::M4x4:\n      return this->emitMatrixAlu(ctx);\n\n    case DxsoOpcode::Loop:\n      return this->emitControlFlowLoop(ctx);\n    case DxsoOpcode::EndLoop:\n      return this->emitControlFlowEndLoop(ctx);\n\n    case DxsoOpcode::Rep:\n      return this->emitControlFlowRep(ctx);\n    case DxsoOpcode::EndRep:\n      return this->emitControlFlowEndRep(ctx);\n\n    case DxsoOpcode::Break:\n      return this->emitControlFlowBreak(ctx);\n    case DxsoOpcode::BreakC:\n      return this->emitControlFlowBreakC(ctx);\n\n    case DxsoOpcode::If:\n    case DxsoOpcode::Ifc:\n      return this->emitControlFlowIf(ctx);\n    case DxsoOpcode::Else:\n      return this->emitControlFlowElse(ctx);\n    case DxsoOpcode::EndIf:\n      return this->emitControlFlowEndIf(ctx);\n\n    case DxsoOpcode::TexCoord:\n      return this->emitTexCoord(ctx);\n\n    case DxsoOpcode::Tex:\n    case DxsoOpcode::TexLdl:\n    case DxsoOpcode::TexLdd:\n    case DxsoOpcode::TexDp3Tex:\n    case DxsoOpcode::TexReg2Ar:\n    case DxsoOpcode::TexReg2Gb:\n    case DxsoOpcode::TexReg2Rgb:\n    case DxsoOpcode::TexBem:\n    case DxsoOpcode::TexBemL:\n    case DxsoOpcode::TexM3x2Tex:\n    case DxsoOpcode::TexM3x3Tex:\n    case DxsoOpcode::TexM3x3Spec:\n    case DxsoOpcode::TexM3x3VSpec:\n      return this->emitTextureSample(ctx);\n    case DxsoOpcode::TexKill:\n      return this->emitTextureKill(ctx);\n    case DxsoOpcode::TexDepth:\n      return this->emitTextureDepth(ctx);\n\n    case DxsoOpcode::TexM3x3Pad:\n    case DxsoOpcode::TexM3x2Pad:\n      // We don't need to do anything here, these are just padding instructions\n      break;\n\n    case DxsoOpcode::End:\n    case DxsoOpcode::Comment:\n    case DxsoOpcode::Phase:\n      break;\n\n    default:\n      Logger::warn(str::format(\"DxsoCompiler::processInstruction: unhandled opcode: \", opcode));\n      break;\n    }\n  }\n\n  void DxsoCompiler::finalize() {\n    if (m_programInfo.type() == DxsoProgramTypes::VertexShader)\n      this->emitVsFinalize();\n    else\n      this->emitPsFinalize();\n\n    // Declare the entry point, we now have all the\n    // information we need, including the interfaces\n    m_module.addEntryPoint(m_entryPointId,\n      m_programInfo.executionModel(), \"main\");\n    m_module.setDebugName(m_entryPointId, \"main\");\n  }\n\n\n  Rc<DxvkShader> DxsoCompiler::compile() {\n    DxvkSpirvShaderCreateInfo info;\n    info.bindingCount = m_bindings.size();\n    info.bindings = m_bindings.data();\n    info.sharedPushData = DxvkPushDataBlock(0u, sizeof(D3D9RenderStateInfo), 4u, 0u);\n    info.localPushData = m_samplerPushData;\n    info.samplerHeap = DxvkShaderBinding(VK_SHADER_STAGE_ALL, GetGlobalSamplerSetIndex(), 0u);\n\n    if (m_programInfo.type() == DxsoProgramTypes::PixelShader)\n      info.flatShadingInputs = m_ps.flatShadingMask;\n\n    return new DxvkSpirvShader(info, m_module.compile());\n  }\n\n  void DxsoCompiler::emitInit() {\n    // Set up common capabilities for all shaders\n    m_module.enableCapability(spv::CapabilityShader);\n    m_module.enableCapability(spv::CapabilityImageQuery);\n\n    if (isSwvp()) {\n      m_cFloatBuffer = this->emitDclSwvpConstantBuffer<DxsoConstantBufferType::Float>();\n      m_cIntBuffer = this->emitDclSwvpConstantBuffer<DxsoConstantBufferType::Int>();\n      m_cBoolBuffer = this->emitDclSwvpConstantBuffer<DxsoConstantBufferType::Bool>();\n    } else {\n      this->emitDclConstantBuffer();\n    }\n\n    this->emitDclInputArray();\n\n    // Initialize the shader module with capabilities\n    // etc. Each shader type has its own peculiarities.\n    switch (m_programInfo.type()) {\n      case DxsoProgramTypes::VertexShader: return this->emitVsInit();\n      case DxsoProgramTypes::PixelShader:  return this->emitPsInit();\n      default: break;\n    }\n  }\n\n  void DxsoCompiler::emitDclConstantBuffer() {\n    std::array<uint32_t, 2> members = {\n      // int i[16 or 2048]\n      m_module.defArrayTypeUnique(\n        getVectorTypeId({ DxsoScalarType::Sint32, 4 }),\n        m_module.constu32(m_layout->intCount)),\n\n      // float f[256 or 224 or 8192]\n      m_module.defArrayTypeUnique(\n        getVectorTypeId({ DxsoScalarType::Float32, 4 }),\n        m_module.constu32(m_layout->floatCount))\n    };\n\n    // Decorate array strides, this is required.\n    m_module.decorateArrayStride(members[0], 16);\n    m_module.decorateArrayStride(members[1], 16);\n\n    const uint32_t structType =\n      m_module.defStructType(members.size(), members.data());\n\n    m_module.decorate(structType, false\n      ? spv::DecorationBufferBlock\n      : spv::DecorationBlock);\n\n    m_module.memberDecorateOffset(structType, 0, m_layout->intOffset());\n    m_module.memberDecorateOffset(structType, 1, m_layout->floatOffset());\n\n    m_module.setDebugName(structType, \"cbuffer_t\");\n    m_module.setDebugMemberName(structType, 0, \"i\");\n    m_module.setDebugMemberName(structType, 1, \"f\");\n\n    m_cBuffer = m_module.newVar(\n      m_module.defPointerType(structType, spv::StorageClassUniform),\n      spv::StorageClassUniform);\n\n    m_module.setDebugName(m_cBuffer, \"c\");\n\n    const uint32_t bindingId = computeResourceSlotId(\n      m_programInfo.type(), DxsoBindingType::ConstantBuffer,\n      0);\n\n    m_module.decorateDescriptorSet(m_cBuffer, 0);\n    m_module.decorateBinding(m_cBuffer, bindingId);\n\n    auto& binding = m_bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = bindingId;\n    binding.resourceIndex   = bindingId;\n    binding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n    binding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n  }\n\n  template<DxsoConstantBufferType ConstantBufferType>\n  int DxsoCompiler::emitDclSwvpConstantBuffer() {\n    uint32_t member;\n    bool asSsbo;\n    if constexpr (ConstantBufferType == DxsoConstantBufferType::Float) {\n      asSsbo = m_moduleInfo.options.vertexFloatConstantBufferAsSSBO;\n\n      // float f[8192]\n      member =  m_module.defArrayTypeUnique(\n        getVectorTypeId({ DxsoScalarType::Float32, 4 }),\n        m_module.constu32(m_layout->floatCount));\n      m_module.decorateArrayStride(member, 16);\n    } else if constexpr (ConstantBufferType == DxsoConstantBufferType::Int) {\n      asSsbo = false;\n\n      // int i[2048]\n      member = m_module.defArrayTypeUnique(\n          getVectorTypeId({ DxsoScalarType::Sint32, 4 }),\n          m_module.constu32(m_layout->intCount));\n      m_module.decorateArrayStride(member, 16);\n    } else {\n      asSsbo = false;\n\n      // int i[256] (bitmasks for 2048 bools)\n      member = m_module.defArrayTypeUnique(\n          getVectorTypeId({ DxsoScalarType::Uint32, 4 }),\n          m_module.constu32(m_layout->bitmaskCount / 4));\n      // Must be a multiple of 4 otherwise.\n      m_module.decorateArrayStride(member, 16);\n    }\n\n    const uint32_t structType =\n      m_module.defStructType(1, &member);\n\n    m_module.decorate(structType, asSsbo\n      ? spv::DecorationBufferBlock\n      : spv::DecorationBlock);\n\n    m_module.memberDecorateOffset(structType, 0, 0);\n\n    if constexpr (ConstantBufferType == DxsoConstantBufferType::Float) {\n      m_module.setDebugName(structType, \"cbuffer_float_t\");\n      m_module.setDebugMemberName(structType, 0, \"f\");\n    } else if constexpr (ConstantBufferType == DxsoConstantBufferType::Int) {\n      m_module.setDebugName(structType, \"cbuffer_int_t\");\n      m_module.setDebugMemberName(structType, 0, \"i\");\n    } else {\n      m_module.setDebugName(structType, \"cbuffer_bool_t\");\n      m_module.setDebugMemberName(structType, 0, \"b\");\n    }\n\n    uint32_t constantBufferId = m_module.newVar(\n      m_module.defPointerType(structType, spv::StorageClassUniform),\n      spv::StorageClassUniform);\n\n    uint32_t bindingId;\n    if constexpr (ConstantBufferType == DxsoConstantBufferType::Float) {\n      m_module.setDebugName(constantBufferId, \"cF\");\n      bindingId = computeResourceSlotId(\n        m_programInfo.type(), DxsoBindingType::ConstantBuffer,\n        0);\n    } else if constexpr (ConstantBufferType == DxsoConstantBufferType::Int) {\n      m_module.setDebugName(constantBufferId, \"cI\");\n      bindingId = computeResourceSlotId(\n        m_programInfo.type(), DxsoBindingType::ConstantBuffer,\n        1);\n    } else {\n      m_module.setDebugName(constantBufferId, \"cB\");\n      bindingId = computeResourceSlotId(\n        m_programInfo.type(), DxsoBindingType::ConstantBuffer,\n        2);\n    }\n\n    m_module.decorateDescriptorSet(constantBufferId, 0);\n    m_module.decorateBinding(constantBufferId, bindingId);\n\n    if (asSsbo)\n      m_module.decorate(constantBufferId, spv::DecorationNonWritable);\n\n    auto& binding = m_bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = bindingId;\n    binding.resourceIndex   = bindingId;\n    binding.descriptorType  = asSsbo\n      ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER\n      : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n    binding.access          = asSsbo\n      ? VK_ACCESS_SHADER_READ_BIT\n      : VK_ACCESS_UNIFORM_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n    return constantBufferId;\n  }\n\n\n  void DxsoCompiler::emitDclInputArray() {\n    DxsoArrayType info;\n    info.ctype   = DxsoScalarType::Float32;\n    info.ccount  = 4;\n    info.alength = DxsoMaxInterfaceRegs;\n\n    uint32_t arrayTypeId = getArrayTypeId(info);\n\n    // Define the actual variable. Note that this is private\n    // because we will copy input registers\n    // to the array during the setup phase.\n    const uint32_t ptrTypeId = m_module.defPointerType(\n      arrayTypeId, spv::StorageClassPrivate);\n\n    m_vArray = m_module.newVar(\n      ptrTypeId, spv::StorageClassPrivate);\n    m_module.setDebugName(m_vArray, \"v\");\n  }\n\n  void DxsoCompiler::emitDclOutputArray() {\n    DxsoArrayType info;\n    info.ctype   = DxsoScalarType::Float32;\n    info.ccount  = 4;\n    info.alength = m_programInfo.type() == DxsoProgramTypes::VertexShader\n      ? DxsoMaxInterfaceRegs\n      : caps::MaxSimultaneousRenderTargets;\n\n    uint32_t arrayTypeId = getArrayTypeId(info);\n\n    // Define the actual variable. Note that this is private\n    // because we will copy input registers\n    // to the array during the setup phase.\n    const uint32_t ptrTypeId = m_module.defPointerType(\n      arrayTypeId, spv::StorageClassPrivate);\n\n    m_oArray = m_module.newVar(\n      ptrTypeId, spv::StorageClassPrivate);\n    m_module.setDebugName(m_oArray, \"o\");\n  }\n\n\n  void DxsoCompiler::emitVsInit() {\n    m_module.enableCapability(spv::CapabilityClipDistance);\n\n    // Only VS needs this, because PS has\n    // non-indexable specialized output regs\n    this->emitDclOutputArray();\n\n    // Main function of the vertex shader\n    m_vs.functionId = m_module.allocateId();\n    m_module.setDebugName(m_vs.functionId, \"vs_main\");\n\n    this->setupRenderStateInfo(caps::MaxTexturesVS + 1u);\n\n    m_specUbo = SetupSpecUBO(m_module, m_bindings);\n\n    this->emitFunctionBegin(\n      m_vs.functionId,\n      m_module.defVoidType(),\n      m_module.defFunctionType(\n        m_module.defVoidType(), 0, nullptr));\n    this->emitFunctionLabel();\n  }\n\n\n  void DxsoCompiler::emitPsSharedConstants() {\n    m_ps.sharedState = GetSharedConstants(m_module);\n\n    const uint32_t bindingId = computeResourceSlotId(\n      m_programInfo.type(), DxsoBindingType::ConstantBuffer,\n      PSShared);\n\n    m_module.decorateDescriptorSet(m_ps.sharedState, 0);\n    m_module.decorateBinding(m_ps.sharedState, bindingId);\n\n    auto& binding = m_bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = bindingId;\n    binding.resourceIndex   = bindingId;\n    binding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n    binding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n  }\n\n\n  void DxsoCompiler::emitPsInit() {\n    m_module.enableExtension(\"SPV_EXT_demote_to_helper_invocation\");\n    m_module.enableCapability(spv::CapabilityDemoteToHelperInvocationEXT);\n    m_module.enableCapability(spv::CapabilityDerivativeControl);\n\n    m_module.setExecutionMode(m_entryPointId,\n      spv::ExecutionModeOriginUpperLeft);\n\n\n    // Main function of the pixel shader\n    m_ps.functionId = m_module.allocateId();\n    m_module.setDebugName(m_ps.functionId, \"ps_main\");\n\n    this->setupRenderStateInfo(caps::MaxTexturesPS);\n    this->emitPsSharedConstants();\n\n    m_specUbo = SetupSpecUBO(m_module, m_bindings);\n\n    this->emitFunctionBegin(\n      m_ps.functionId,\n      m_module.defVoidType(),\n      m_module.defFunctionType(\n        m_module.defVoidType(), 0, nullptr));\n    this->emitFunctionLabel();\n  }\n\n\n  void DxsoCompiler::emitFunctionBegin(\n          uint32_t                entryPoint,\n          uint32_t                returnType,\n          uint32_t                funcType) {\n    this->emitFunctionEnd();\n\n    m_module.functionBegin(\n      returnType, entryPoint, funcType,\n      spv::FunctionControlMaskNone);\n\n    m_insideFunction = true;\n  }\n\n\n  void DxsoCompiler::emitFunctionEnd() {\n    if (m_insideFunction) {\n      m_module.opReturn();\n      m_module.functionEnd();\n    }\n\n    m_insideFunction = false;\n  }\n\n\n  uint32_t DxsoCompiler::emitFunctionLabel() {\n    uint32_t labelId = m_module.allocateId();\n    m_module.opLabel(labelId);\n    return labelId;\n  }\n\n\n  void DxsoCompiler::emitMainFunctionBegin() {\n    this->emitFunctionBegin(\n      m_entryPointId,\n      m_module.defVoidType(),\n      m_module.defFunctionType(\n        m_module.defVoidType(), 0, nullptr));\n    m_mainFuncLabel = this->emitFunctionLabel();\n  }\n\n\n  uint32_t DxsoCompiler::emitNewVariable(const DxsoRegisterInfo& info) {\n    const uint32_t ptrTypeId = this->getPointerTypeId(info);\n    return m_module.newVar(ptrTypeId, info.sclass);\n  }\n\n\n  uint32_t DxsoCompiler::emitNewVariableDefault(\n    const DxsoRegisterInfo& info,\n          uint32_t          value) {\n    const uint32_t ptrTypeId = this->getPointerTypeId(info);\n    if (value == 0)\n      return m_module.newVar(ptrTypeId, info.sclass);\n    else\n      return m_module.newVarInit(ptrTypeId, info.sclass, value);\n  }\n\n\n  uint32_t DxsoCompiler::emitNewBuiltinVariable(\n    const DxsoRegisterInfo& info,\n          spv::BuiltIn      builtIn,\n    const char*             name,\n          uint32_t          value) {\n    const uint32_t varId = emitNewVariableDefault(info, value);\n\n    m_module.setDebugName(varId, name);\n    m_module.decorateBuiltIn(varId, builtIn);\n\n    if (m_programInfo.type() == DxsoProgramTypes::PixelShader\n     && info.type.ctype != DxsoScalarType::Float32\n     && info.type.ctype != DxsoScalarType::Bool\n     && info.sclass == spv::StorageClassInput)\n      m_module.decorate(varId, spv::DecorationFlat);\n\n    return varId;\n  }\n\n  DxsoCfgBlock* DxsoCompiler::cfgFindBlock(\n    const std::initializer_list<DxsoCfgBlockType>& types) {\n    for (auto cur =  m_controlFlowBlocks.rbegin();\n              cur != m_controlFlowBlocks.rend(); cur++) {\n      for (auto type : types) {\n        if (cur->type == type)\n          return &(*cur);\n      }\n    }\n\n    return nullptr;\n  }\n\n  spv::BuiltIn semanticToBuiltIn(bool input, DxsoSemantic semantic) {\n    if (input)\n      return spv::BuiltInMax;\n\n    if (semantic == DxsoSemantic{ DxsoUsage::Position, 0 })\n      return spv::BuiltInPosition;\n\n    if (semantic == DxsoSemantic{ DxsoUsage::PointSize, 0 })\n      return spv::BuiltInPointSize;\n\n    return spv::BuiltInMax;\n  }\n\n  void DxsoCompiler::emitDclInterface(\n            bool         input,\n            uint32_t     regNumber,\n            DxsoSemantic semantic,\n            DxsoRegMask  mask,\n            bool         centroid) {\n    auto& sgn = input\n      ? m_isgn : m_osgn;\n\n    const bool pixel  = m_programInfo.type() == DxsoProgramTypes::PixelShader;\n    const bool vertex = !pixel;\n\n    if (pixel && input && semantic.usage == DxsoUsage::Color)\n      centroid = true;\n\n    uint32_t slot = 0;\n\n    uint32_t& slots = input\n      ? m_inputMask\n      : m_outputMask;\n\n    uint16_t& explicits = input\n      ? m_explicitInputs\n      : m_explicitOutputs;\n\n    // Some things we consider builtins could be packed in an output reg.\n    bool builtin = semanticToBuiltIn(input, semantic) != spv::BuiltInMax;\n\n    uint32_t i = sgn.elemCount++;\n\n    if (input && vertex) {\n      // Any slot will do! Let's chose the next one\n      slot = i;\n    }\n    else if ( (!input && vertex)\n           || (input  && pixel ) ) {\n      // Don't register the slot if it belongs to a builtin\n      if (!builtin)\n        slot = RegisterLinkerSlot(semantic);\n    }\n    else { //if (!input && pixel)\n      // We want to make the output slot the same as the\n      // output register for pixel shaders so they go to\n      // the right render target.\n      slot = regNumber;\n    }\n\n    // Don't want to mark down any of these builtins.\n    if (!builtin)\n      slots   |= 1u << slot;\n    explicits |= 1u << regNumber;\n\n    auto& elem = sgn.elems[i];\n    elem.slot      = slot;\n    elem.regNumber = regNumber;\n    elem.semantic  = semantic;\n    elem.mask      = mask;\n    elem.centroid  = centroid;\n  }\n\n  void DxsoCompiler::emitDclSampler(\n          uint32_t        idx,\n          DxsoTextureType type) {\n    m_usedSamplers |= (1u << idx);\n\n    if (!m_samplerArray)\n      m_samplerArray = SetupSamplerArray(m_module);\n\n    VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n\n    auto DclSampler = [this, &viewType](\n      uint32_t        idx,\n      uint32_t        bindingId,\n      DxsoSamplerType type,\n      bool            depth,\n      bool            implicit) {\n      // Setup our combines sampler.\n      DxsoSamplerInfo& sampler = !depth\n        ? m_samplers[idx].color[type]\n        : m_samplers[idx].depth[type];\n\n      spv::Dim dimensionality;\n\n      const char* suffix = \"_2d\";\n\n      switch (type) {\n        default:\n        case SamplerTypeTexture2D:\n          sampler.dimensions = 2;\n          dimensionality = spv::Dim2D;\n          viewType = VK_IMAGE_VIEW_TYPE_2D;\n          break;\n\n        case SamplerTypeTextureCube:\n          suffix = \"_cube\";\n          sampler.dimensions = 3;\n          dimensionality = spv::DimCube;\n          viewType = VK_IMAGE_VIEW_TYPE_CUBE;\n          break;\n\n        case SamplerTypeTexture3D:\n          suffix = \"_3d\";\n          sampler.dimensions = 3;\n          dimensionality = spv::Dim3D;\n          viewType = VK_IMAGE_VIEW_TYPE_3D;\n          break;\n      }\n\n      sampler.imageTypeId = m_module.defImageType(\n        m_module.defFloatType(32),\n        dimensionality, depth ? 1 : 0, 0, 0, 1,\n        spv::ImageFormatUnknown);\n\n      sampler.imageVarId = m_module.newVar(\n        m_module.defPointerType(\n          sampler.imageTypeId, spv::StorageClassUniformConstant),\n        spv::StorageClassUniformConstant);\n\n      sampler.sampledTypeId = m_module.defSampledImageType(sampler.imageTypeId);\n      sampler.samplerIndex = idx;\n\n      std::string name = str::format(\"t\", idx, suffix, depth ? \"_shadow\" : \"\");\n      m_module.setDebugName(sampler.imageVarId, name.c_str());\n\n      m_module.decorateDescriptorSet(sampler.imageVarId, 0);\n      m_module.decorateBinding      (sampler.imageVarId, bindingId);\n    };\n\n    const uint32_t binding = computeResourceSlotId(m_programInfo.type(),\n      DxsoBindingType::Image,\n      idx);\n\n    const bool implicit = m_programInfo.majorVersion() < 2 || m_moduleInfo.options.forceSamplerTypeSpecConstants;\n\n    if (!implicit) {\n      DxsoSamplerType samplerType = \n        SamplerTypeFromTextureType(type);\n\n      DclSampler(idx, binding, samplerType, false, implicit);\n\n      if (samplerType != SamplerTypeTexture3D) {\n        // We could also be depth compared!\n        DclSampler(idx, binding, samplerType, true, implicit);\n      }\n\n      const uint32_t offset = idx * 2;\n      uint32_t textureBits = uint32_t(viewType);\n      m_textureTypes |= textureBits << offset;\n    }\n    else {\n      // Could be any of these!\n      // We will check with the spec constant at sample time.\n      for (uint32_t i = 0; i < SamplerTypeCount; i++) {\n        auto samplerType = static_cast<DxsoSamplerType>(i);\n\n        DclSampler(idx, binding, samplerType, false, implicit);\n\n        if (samplerType != SamplerTypeTexture3D)\n          DclSampler(idx, binding, samplerType, true, implicit);\n      }\n    }\n\n    m_samplers[idx].type = type;\n\n    auto& imageBinding = m_bindings.emplace_back();\n    imageBinding.set             = 0u;\n    imageBinding.binding         = binding;\n    imageBinding.resourceIndex   = binding;\n    imageBinding.descriptorType  = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n    imageBinding.viewType        = implicit ? VK_IMAGE_VIEW_TYPE_MAX_ENUM : viewType;\n    imageBinding.access          = VK_ACCESS_SHADER_READ_BIT;\n\n    auto& samplerBinding = m_bindings.emplace_back();\n    samplerBinding.resourceIndex  = binding;\n    samplerBinding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;\n    samplerBinding.blockOffset    = GetPushSamplerOffset(idx);\n    samplerBinding.flags.set(DxvkDescriptorFlag::PushData);\n  }\n\n\n  uint32_t DxsoCompiler::emitArrayIndex(\n            uint32_t          idx,\n      const DxsoBaseRegister* relative) {\n    uint32_t result = m_module.consti32(idx);\n\n    if (relative != nullptr) {\n      DxsoRegisterValue offset = emitRegisterLoad(*relative, DxsoRegMask(true, false, false, false), nullptr);\n\n      result = m_module.opIAdd(\n        getVectorTypeId(offset.type),\n        result, offset.id);\n    }\n\n    return result;\n  }\n\n\n  DxsoRegisterPointer DxsoCompiler::emitInputPtr(\n            bool              texture,\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative) {\n    uint32_t idx = reg.id.num;\n\n    // Account for the two color regs.\n    if (texture)\n      idx += 2;\n\n    DxsoRegisterPointer input;\n\n    input.type = DxsoVectorType{ DxsoScalarType::Float32, 4 };\n\n    uint32_t index = this->emitArrayIndex(idx, relative);\n\n    const uint32_t typeId = getVectorTypeId(input.type);\n    input.id = m_module.opAccessChain(\n      m_module.defPointerType(typeId, spv::StorageClassPrivate),\n      m_vArray,\n      1, &index);\n\n    return input;\n  }\n\n  DxsoRegisterPointer DxsoCompiler::emitRegisterPtr(\n      const char*             name,\n            DxsoScalarType    ctype,\n            uint32_t          ccount,\n            uint32_t          defaultVal,\n            spv::StorageClass storageClass,\n            spv::BuiltIn      builtIn) {\n    DxsoRegisterPointer result;\n\n    DxsoRegisterInfo info;\n    info.type.ctype    = ctype;\n    info.type.ccount   = ccount;\n    info.type.alength  = 1;\n    info.sclass        = storageClass;\n\n    result.type = DxsoVectorType{ ctype, ccount };\n    if (builtIn == spv::BuiltInMax) {\n      result.id = this->emitNewVariableDefault(info, defaultVal);\n      m_module.setDebugName(result.id, name);\n    }\n    else {\n      result.id = this->emitNewBuiltinVariable(\n        info, builtIn, name, defaultVal);\n    }\n\n    return result;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitLoadConstant(\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative) {\n\n    // SWVP cbuffers:           Member    Binding index\n    // float                    f[8192];  0\n    // int32_t                  i[2048];  1\n    // bool (uint32_t bitmask)  i[[256]]; 2\n\n    // HWVP cbuffer:            Member         Member index\n    // int32_t                  i[16];         0\n    // float                    f[256 or 224]; 1\n    //\n    // bools as spec constant bitmasks\n    DxsoRegisterValue result = { };\n\n    switch (reg.id.type) {\n      case DxsoRegisterType::Const:\n        result.type = { DxsoScalarType::Float32, 4 };\n\n        if (!relative)\n          result.id = m_cFloat.at(reg.id.num);\n        break;\n      \n      case DxsoRegisterType::ConstInt:\n        result.type = { DxsoScalarType::Sint32, 4 };\n        result.id = m_cInt.at(reg.id.num);\n        break;\n      \n      case DxsoRegisterType::ConstBool:\n        result.type = { DxsoScalarType::Bool, 1 };\n        result.id = m_cBool.at(reg.id.num);\n        break;\n      \n      default: break;\n    }\n\n    if (result.id)\n      return result;\n\n    switch (reg.id.type) {\n      case DxsoRegisterType::Const:\n        if (!relative) {\n          m_meta.maxConstIndexF = std::max(m_meta.maxConstIndexF, reg.id.num + 1);\n          m_meta.maxConstIndexF = std::min(m_meta.maxConstIndexF, m_layout->floatCount);\n        } else {\n          m_meta.maxConstIndexF = m_layout->floatCount;\n          m_meta.needsConstantCopies |= m_cFloat.at(reg.id.num) != 0;\n        }\n        break;\n      \n      case DxsoRegisterType::ConstInt:\n        m_meta.maxConstIndexI = std::max(m_meta.maxConstIndexI, reg.id.num + 1);\n        m_meta.maxConstIndexI = std::min(m_meta.maxConstIndexI, m_layout->intCount);\n        break;\n      \n      case DxsoRegisterType::ConstBool:\n        m_meta.maxConstIndexB = std::max(m_meta.maxConstIndexB, reg.id.num + 1);\n        m_meta.maxConstIndexB = std::min(m_meta.maxConstIndexB, m_layout->boolCount);\n        m_meta.boolConstantMask |= 1 << reg.id.num;\n        break;\n      \n      default: break;\n    }\n\n    uint32_t relativeIdx = this->emitArrayIndex(reg.id.num, relative);\n\n    if (reg.id.type != DxsoRegisterType::ConstBool) {\n      uint32_t structIdx;\n      uint32_t cBufferId;\n\n      if (reg.id.type == DxsoRegisterType::Const) {\n        if (isSwvp()) {\n          structIdx = m_module.constu32(0);\n          cBufferId = m_cFloatBuffer;\n        } else {\n          structIdx = m_module.constu32(1);\n          cBufferId = m_cBuffer;\n        }\n      } else {\n        if (isSwvp()) {\n          structIdx = m_module.constu32(0);\n          cBufferId = m_cIntBuffer;\n        } else {\n          structIdx = m_module.constu32(0);\n          cBufferId = m_cBuffer;\n        }\n      }\n\n      std::array<uint32_t, 2> indices = { structIdx, relativeIdx };\n\n      uint32_t typeId = getVectorTypeId(result.type);\n      uint32_t ptrId = m_module.opAccessChain(\n        m_module.defPointerType(typeId, spv::StorageClassUniform),\n        cBufferId, indices.size(), indices.data());\n\n      result.id = m_module.opLoad(typeId, ptrId);\n    } else {\n      // Bool constants have no relative indexing, so we can do the bitfield\n      // magic for SWVP at compile time.\n\n      uint32_t uintType  = getScalarTypeId(DxsoScalarType::Uint32);\n      uint32_t uvec4Type = getVectorTypeId({ DxsoScalarType::Uint32, 4 });\n\n      // If not SWVP, spec const this\n      uint32_t bit;\n      if (m_layout->bitmaskCount != 1) {\n        std::array<uint32_t, 2> indices = { m_module.constu32(0), m_module.constu32(reg.id.num / 128) };\n\n        uint32_t indexCount = m_layout->bitmaskCount == 1 ? 1 : 2;\n        uint32_t accessType = m_layout->bitmaskCount == 1 ? uintType : uvec4Type;\n\n        uint32_t ptrId = m_module.opAccessChain(\n          m_module.defPointerType(accessType, spv::StorageClassUniform),\n          m_cBoolBuffer, indexCount, indices.data());\n\n        uint32_t bitfield = m_module.opLoad(accessType, ptrId);\n        uint32_t bitIdx = m_module.consti32(reg.id.num % 32);\n\n        uint32_t index = (reg.id.num % 128) / 32;\n        bitfield = m_module.opCompositeExtract(uintType, bitfield, 1, &index);\n\n        bit = m_module.opBitFieldUExtract(\n          uintType, bitfield, bitIdx, m_module.consti32(1));\n      }\n      else {\n        bit = m_spec.get(m_module, m_specUbo,\n          m_programInfo.type() == DxsoProgramType::VertexShader\n            ? SpecVertexShaderBools\n            : SpecPixelShaderBools,\n          reg.id.num, 1);\n      }\n\n      result.id = m_module.opINotEqual(\n        getVectorTypeId(result.type),\n        bit, m_module.constu32(0));\n    }\n\n    return result;\n  }\n\n\n  DxsoRegisterPointer DxsoCompiler::emitOutputPtr(\n            bool              texcrdOut,\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative) {\n    uint32_t idx = reg.id.num;\n\n    // Account for the two color regs.\n    if (texcrdOut)\n      idx += 2;\n\n    DxsoRegisterPointer input;\n\n    input.type = DxsoVectorType{ DxsoScalarType::Float32, 4 };\n\n    uint32_t index = this->emitArrayIndex(idx, relative);\n\n    const uint32_t typeId = getVectorTypeId(input.type);\n    input.id = m_module.opAccessChain(\n      m_module.defPointerType(typeId, spv::StorageClassPrivate),\n      m_oArray,\n      1, &index);\n\n    return input;\n  }\n\n\n  DxsoRegisterPointer DxsoCompiler::emitGetOperandPtr(\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative) {\n    switch (reg.id.type) {\n      case DxsoRegisterType::Temp: {\n        if (reg.id.num >= m_rRegs.size())\n          m_rRegs.resize( reg.id.num + 1, DxsoRegisterPointer { } );\n        DxsoRegisterPointer& ptr = m_rRegs.at(reg.id.num);\n        if (ptr.id == 0) {\n          std::string name = str::format(\"r\", reg.id.num);\n          ptr = this->emitRegisterPtr(\n            name.c_str(), DxsoScalarType::Float32, 4,\n            m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f));\n        }\n        return ptr;\n      }\n\n      case DxsoRegisterType::Input: {\n        if (!(m_explicitInputs & 1u << reg.id.num)) {\n          this->emitDclInterface(\n            true, reg.id.num,\n            DxsoSemantic{ DxsoUsage::Color, reg.id.num },\n            IdentityWriteMask, false);\n        }\n\n        return this->emitInputPtr(false, reg, relative);\n      }\n\n      case DxsoRegisterType::PixelTexcoord:\n      case DxsoRegisterType::Texture: {\n        if (m_programInfo.type() == DxsoProgramTypes::PixelShader) {\n          // Texture register\n\n          // SM2, or SM 1.4\n          if (reg.id.type == DxsoRegisterType::PixelTexcoord\n          ||  m_programInfo.majorVersion() >= 2\n          || (m_programInfo.majorVersion() == 1\n           && m_programInfo.minorVersion() == 4)) {\n            uint32_t adjustedNumber = reg.id.num + 2;\n            if (!(m_explicitInputs & 1u << adjustedNumber)) {\n              this->emitDclInterface(\n                true, adjustedNumber,\n                DxsoSemantic{ DxsoUsage::Texcoord, reg.id.num },\n                IdentityWriteMask, false);\n            }\n\n            return this->emitInputPtr(true, reg, relative);\n          }\n          else {\n            // User must use tex/texcoord to put data in this private register.\n            // We use the an oob id which fxc never generates for the texcoord data.\n            DxsoRegisterPointer& ptr = m_tRegs.at(reg.id.num);\n            if (ptr.id == 0) {\n              std::string name = str::format(\"t\", reg.id.num);\n              ptr = this->emitRegisterPtr(\n                name.c_str(), DxsoScalarType::Float32, 4,\n                m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f));\n            }\n            return ptr;\n          }\n        }\n        else {\n          // Address register\n          if (m_vs.addr.id == 0) {\n            m_vs.addr = this->emitRegisterPtr(\n              \"a0\", DxsoScalarType::Sint32, 4,\n              m_module.constvec4i32(0, 0, 0, 0));\n          }\n          return m_vs.addr;\n        }\n      }\n\n      case DxsoRegisterType::RasterizerOut:\n        switch (reg.id.num) {\n          case RasterOutPosition:\n            if (m_vs.oPos.id == 0) {\n              m_vs.oPos = this->emitRegisterPtr(\n                \"oPos\", DxsoScalarType::Float32, 4,\n                m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f),\n                spv::StorageClassOutput, spv::BuiltInPosition);\n            }\n            return m_vs.oPos;\n\n          case RasterOutFog:\n            if (m_fog.id == 0) {\n              bool input = m_programInfo.type() == DxsoProgramType::PixelShader;\n              DxsoSemantic semantic = DxsoSemantic{ DxsoUsage::Fog, 0 };\n\n              uint32_t slot = RegisterLinkerSlot(semantic);\n\n              uint32_t& slots = input\n                ? m_inputMask\n                : m_outputMask;\n\n              slots |= 1u << slot;\n\n              m_fog = this->emitRegisterPtr(\n                input ? \"vFog\" : \"oFog\",\n                DxsoScalarType::Float32, 1,\n                input ? 0 : m_module.constf32(1.0f),\n                input ? spv::StorageClassInput : spv::StorageClassOutput);\n\n              m_module.decorateLocation(m_fog.id, slot);\n            }\n            return m_fog;\n\n          case RasterOutPointSize:\n            if (m_vs.oPSize.id == 0) {\n              m_vs.oPSize = this->emitRegisterPtr(\n                \"oPSize\", DxsoScalarType::Float32, 1,\n                m_module.constf32(0.0f),\n                spv::StorageClassOutput, spv::BuiltInPointSize);\n            }\n            return m_vs.oPSize;\n\n          default: {\n            DxsoRegisterPointer nullPointer = { };\n            return nullPointer;\n          }\n        }\n\n      case DxsoRegisterType::ColorOut: {\n        uint32_t idx = std::min(reg.id.num, 4u);\n\n        if (m_ps.oColor[idx].id == 0) {\n          std::string name = str::format(\"oC\", idx);\n          m_ps.oColor[idx] = this->emitRegisterPtr(\n            name.c_str(), DxsoScalarType::Float32, 4,\n            m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f),\n            spv::StorageClassOutput);\n\n          m_outputMask |= 1u << idx;\n          m_module.decorateLocation(m_ps.oColor[idx].id, idx);\n          m_module.decorateIndex(m_ps.oColor[idx].id, 0);\n\n          m_usedRTs |= (1u << idx);\n        }\n        return m_ps.oColor[idx];\n      }\n\n      case DxsoRegisterType::AttributeOut: {\n        auto ptr = this->emitOutputPtr(false, reg, nullptr);\n\n        if (!(m_explicitOutputs & 1u << reg.id.num)) {\n          this->emitDclInterface(\n            false, reg.id.num,\n            DxsoSemantic{ DxsoUsage::Color, reg.id.num },\n            IdentityWriteMask, false);\n\n          m_module.opStore(ptr.id, m_module.constfReplicant(0, ptr.type.ccount));\n        }\n\n        return ptr;\n      }\n\n      case DxsoRegisterType::Output: {\n        bool texcrdOut = m_programInfo.type() == DxsoProgramTypes::VertexShader\n                      && m_programInfo.majorVersion() != 3;\n\n        auto ptr = this->emitOutputPtr(texcrdOut, reg, !texcrdOut ? relative : nullptr);\n\n        if (texcrdOut) {\n          uint32_t adjustedNumber = reg.id.num + 2;\n          if (!(m_explicitOutputs & 1u << adjustedNumber)) {\n            this->emitDclInterface(\n              false, adjustedNumber,\n              DxsoSemantic{ DxsoUsage::Texcoord, reg.id.num },\n              IdentityWriteMask, false);\n\n            m_module.opStore(ptr.id, m_module.constfReplicant(0, ptr.type.ccount));\n          }\n        }\n\n        return ptr;\n      }\n\n      case DxsoRegisterType::DepthOut:\n        if (m_ps.oDepth.id == 0) {\n          m_module.setExecutionMode(m_entryPointId,\n            spv::ExecutionModeDepthReplacing);\n\n          m_ps.oDepth = this->emitRegisterPtr(\n            \"oDepth\", DxsoScalarType::Float32, 1,\n            m_module.constf32(0.0f),\n            spv::StorageClassOutput, spv::BuiltInFragDepth);\n        }\n        return m_ps.oDepth;\n\n      case DxsoRegisterType::Loop:\n        if (m_loopCounter.id == 0) {\n          m_loopCounter = this->emitRegisterPtr(\n            \"aL\", DxsoScalarType::Sint32, 1,\n            m_module.consti32(0));\n        }\n        return m_loopCounter;\n\n      case DxsoRegisterType::MiscType:\n        if (reg.id.num == MiscTypePosition) {\n          if (m_ps.vPos.id == 0) {\n            m_ps.vPos = this->emitRegisterPtr(\n              \"vPos\", DxsoScalarType::Float32, 4, 0);\n          }\n          return m_ps.vPos;\n        }\n        else { // MiscTypeFace\n          if (m_ps.vFace.id == 0) {\n            m_ps.vFace = this->emitRegisterPtr(\n              \"vFace\", DxsoScalarType::Float32, 4, 0);\n          }\n          return m_ps.vFace;\n        }\n\n      case DxsoRegisterType::Predicate: {\n        DxsoRegisterPointer& ptr = m_pRegs.at(reg.id.num);\n        if (ptr.id == 0) {\n          std::string name = str::format(\"p\", reg.id.num);\n          ptr = this->emitRegisterPtr(\n            name.c_str(), DxsoScalarType::Bool, 4,\n            m_module.constvec4b32(false, false, false, false));\n        }\n        return ptr;\n      }\n\n      default: {\n        //Logger::warn(str::format(\"emitGetOperandPtr: unhandled reg type: \", reg.id.type));\n\n        DxsoRegisterPointer nullPointer = { };\n        return nullPointer;\n      }\n    }\n  }\n\n\n  uint32_t DxsoCompiler::emitBoolComparison(DxsoVectorType type, DxsoComparison cmp, uint32_t a, uint32_t b) {\n    const uint32_t typeId = getVectorTypeId(type);\n    switch (cmp) {\n      default:\n      case DxsoComparison::Never:        return m_module.constbReplicant(false, type.ccount);  break;\n      case DxsoComparison::GreaterThan:  return m_module.opFOrdGreaterThan     (typeId, a, b); break;\n      case DxsoComparison::Equal:        return m_module.opFOrdEqual           (typeId, a, b); break;\n      case DxsoComparison::GreaterEqual: return m_module.opFOrdGreaterThanEqual(typeId, a, b); break;\n      case DxsoComparison::LessThan:     return m_module.opFOrdLessThan        (typeId, a, b); break;\n      case DxsoComparison::NotEqual:     return m_module.opFUnordNotEqual      (typeId, a, b); break;\n      case DxsoComparison::LessEqual:    return m_module.opFOrdLessThanEqual   (typeId, a, b); break;\n      case DxsoComparison::Always:       return m_module.constbReplicant(true, type.ccount);   break;\n    }\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitValueLoad(\n            DxsoRegisterPointer ptr) {\n    DxsoRegisterValue result;\n    result.type = ptr.type;\n    result.id   = m_module.opLoad(\n      getVectorTypeId(result.type),\n      ptr.id);\n    return result;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::applyPredicate(DxsoRegisterValue pred, DxsoRegisterValue dst, DxsoRegisterValue src) {\n    if (dst.type.ccount != pred.type.ccount) {\n      DxsoRegMask mask = DxsoRegMask(\n        pred.type.ccount > 0,\n        pred.type.ccount > 1,\n        pred.type.ccount > 2,\n        pred.type.ccount > 3);\n\n      pred = emitRegisterSwizzle(pred, IdentitySwizzle, mask);\n    }\n\n    dst.id = m_module.opSelect(\n      getVectorTypeId(dst.type),\n      pred.id,\n      src.id, dst.id);\n\n    return dst;\n  }\n\n\n  void DxsoCompiler::emitValueStore(\n          DxsoRegisterPointer     ptr,\n          DxsoRegisterValue       value,\n          DxsoRegMask             writeMask,\n          DxsoRegisterValue       predicate) {\n    // If the source value consists of only one component,\n    // it is stored in all components of the destination.\n    if (value.type.ccount == 1)\n      value = emitRegisterExtend(value, writeMask.popCount());\n\n    if (ptr.type.ccount == writeMask.popCount()) {\n      if (predicate.id)\n        value = applyPredicate(predicate, emitValueLoad(ptr), value);\n\n      // Simple case: We write to the entire register\n      m_module.opStore(ptr.id, value.id);\n    } else {\n      // We only write to part of the destination\n      // register, so we need to load and modify it\n      DxsoRegisterValue tmp = emitValueLoad(ptr);\n      tmp = emitRegisterInsert(tmp, value, writeMask);\n\n      if (predicate.id)\n        value = applyPredicate(predicate, emitValueLoad(ptr), tmp);\n\n      m_module.opStore(ptr.id, tmp.id);\n    }\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitClampBoundReplicant(\n            DxsoRegisterValue       srcValue,\n            float                   lb,\n            float                   ub) {\n    srcValue.id = m_module.opFClamp(getVectorTypeId(srcValue.type), srcValue.id,\n      m_module.constfReplicant(lb, srcValue.type.ccount),\n      m_module.constfReplicant(ub, srcValue.type.ccount));\n\n    return srcValue;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitSaturate(\n            DxsoRegisterValue       srcValue) {\n    return emitClampBoundReplicant(srcValue, 0.0f, 1.0f);\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitMulOperand(\n          DxsoRegisterValue       operand,\n          DxsoRegisterValue       other) {\n    if (m_moduleInfo.options.d3d9FloatEmulation != D3D9FloatEmulation::Strict || operand.id == other.id)\n      return operand;\n\n    uint32_t boolId = getVectorTypeId({ DxsoScalarType::Bool, other.type.ccount });\n    uint32_t zeroId = m_module.constfReplicant(0.0f, other.type.ccount);\n\n    DxsoRegisterValue result;\n    result.type = operand.type;\n    result.id = m_module.opSelect(getVectorTypeId(result.type),\n      m_module.opFOrdEqual(boolId, other.id, zeroId), zeroId, operand.id);\n    return result;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitMul(\n          DxsoRegisterValue       a,\n          DxsoRegisterValue       b) {\n    auto az = emitMulOperand(a, b);\n    auto bz = emitMulOperand(b, a);\n\n    DxsoRegisterValue result;\n    result.type = a.type;\n    result.id = m_module.opFMul(getVectorTypeId(result.type), az.id, bz.id);\n    return result;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitMad(\n          DxsoRegisterValue       a,\n          DxsoRegisterValue       b,\n          DxsoRegisterValue       c) {\n    DxsoRegisterValue result = emitMul(a, b);\n    result.id = m_module.opFAdd(getVectorTypeId(result.type), result.id, c.id);\n    return result;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitDot(\n            DxsoRegisterValue       a,\n            DxsoRegisterValue       b) {\n    auto az = emitMulOperand(a, b);\n    auto bz = emitMulOperand(b, a);\n\n    DxsoRegisterValue dot;\n    dot.type.ctype  = a.type.ctype;\n    dot.type.ccount = 1;\n    dot.id          = 0;\n\n    uint32_t componentType = getVectorTypeId(dot.type);\n\n    for (uint32_t i = 0; i < a.type.ccount; i++) {\n      uint32_t product = m_module.opFMul(componentType,\n        m_module.opCompositeExtract(componentType, az.id, 1, &i),\n        m_module.opCompositeExtract(componentType, bz.id, 1, &i));\n\n      dot.id = dot.id ? m_module.opFAdd(componentType, dot.id, product) : product;\n    }\n\n    return dot;\n  }\n\n  DxsoRegisterValue DxsoCompiler::emitMix(\n            DxsoRegisterValue       x,\n            DxsoRegisterValue       y,\n            DxsoRegisterValue       a) {\n    uint32_t typeId = getVectorTypeId(x.type);\n\n    DxsoRegisterValue ySubx;\n    ySubx.type = x.type;\n    ySubx.id   = m_module.opFSub(typeId, y.id, x.id);\n\n    return emitMad(a, ySubx, x);\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitCross(\n          DxsoRegisterValue       a,\n          DxsoRegisterValue       b) {\n    uint32_t typeId = getVectorTypeId(a.type);\n\n    const std::array<uint32_t, 4> shiftIndices = { 1, 2, 0, 1 };\n\n    DxsoRegisterValue result;\n    result.type = { DxsoScalarType::Float32, 3 };\n\n    std::array<DxsoRegisterValue, 2> products;\n\n    for (uint32_t i = 0; i < 2; i++) {\n      DxsoRegisterValue ashift;\n      ashift.type = result.type;\n      ashift.id = m_module.opVectorShuffle(typeId,\n        a.id, a.id, 3, &shiftIndices[i]);\n\n      DxsoRegisterValue bshift;\n      bshift.type = result.type;\n      bshift.id = m_module.opVectorShuffle(typeId,\n        b.id, b.id, 3, &shiftIndices[1 - i]);\n\n      products[i] = emitMul(ashift, bshift);\n    }\n\n    result.id = m_module.opFSub(typeId, products[0].id, products[1].id);\n    return result;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitRegisterInsert(\n            DxsoRegisterValue       dstValue,\n            DxsoRegisterValue       srcValue,\n            DxsoRegMask             srcMask) {\n    DxsoRegisterValue result;\n    result.type = dstValue.type;\n\n    const uint32_t typeId = getVectorTypeId(result.type);\n\n    if (srcMask.popCount() == 0) {\n      // Nothing to do if the insertion mask is empty\n      result.id = dstValue.id;\n    } else if (dstValue.type.ccount == 1) {\n      // Both values are scalar, so the first component\n      // of the write mask decides which one to take.\n      result.id = srcMask[0] ? srcValue.id : dstValue.id;\n    } else if (srcValue.type.ccount == 1) {\n      // The source value is scalar. Since OpVectorShuffle\n      // requires both arguments to be vectors, we have to\n      // use OpCompositeInsert to modify the vector instead.\n      const uint32_t componentId = srcMask.firstSet();\n\n      result.id = m_module.opCompositeInsert(typeId,\n        srcValue.id, dstValue.id, 1, &componentId);\n    } else {\n      // Both arguments are vectors. We can determine which\n      // components to take from which vector and use the\n      // OpVectorShuffle instruction.\n      std::array<uint32_t, 4> components;\n      uint32_t srcComponentId = dstValue.type.ccount;\n\n      for (uint32_t i = 0; i < dstValue.type.ccount; i++)\n        components.at(i) = srcMask[i] ? srcComponentId++ : i;\n\n      result.id = m_module.opVectorShuffle(\n        typeId, dstValue.id, srcValue.id,\n        dstValue.type.ccount, components.data());\n    }\n\n    return result;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitRegisterLoadRaw(\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative) {\n    switch (reg.id.type) {\n      case DxsoRegisterType::Const:\n      case DxsoRegisterType::ConstInt:\n      case DxsoRegisterType::ConstBool:\n        return emitLoadConstant(reg, relative);\n      \n      default:\n        return emitValueLoad(emitGetOperandPtr(reg, relative));\n    }\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitRegisterExtend(\n            DxsoRegisterValue       value,\n            uint32_t                size) {\n    if (size == 1)\n      return value;\n\n    std::array<uint32_t, 4> ids = {{\n      value.id, value.id,\n      value.id, value.id,\n    }};\n\n    DxsoRegisterValue result;\n    result.type.ctype  = value.type.ctype;\n    result.type.ccount = size;\n    result.id = m_module.opCompositeConstruct(\n      getVectorTypeId(result.type),\n      size, ids.data());\n    return result;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitRegisterSwizzle(\n            DxsoRegisterValue       value,\n            DxsoRegSwizzle          swizzle,\n            DxsoRegMask             writeMask) {\n    if (value.type.ccount == 1)\n      return emitRegisterExtend(value, writeMask.popCount());\n\n    std::array<uint32_t, 4> indices;\n\n    uint32_t dstIndex = 0;\n\n    for (uint32_t i = 0; i < 4; i++) {\n      if (writeMask[i])\n        indices[dstIndex++] = swizzle[i];\n    }\n\n    // If the swizzle combined with the mask can be reduced\n    // to a no-op, we don't need to insert any instructions.\n    bool isIdentitySwizzle = dstIndex == value.type.ccount;\n\n    for (uint32_t i = 0; i < dstIndex && isIdentitySwizzle; i++)\n      isIdentitySwizzle &= indices[i] == i;\n\n    if (isIdentitySwizzle)\n      return value;\n\n    // Use OpCompositeExtract if the resulting vector contains\n    // only one component, and OpVectorShuffle if it is a vector.\n    DxsoRegisterValue result;\n    result.type.ctype  = value.type.ctype;\n    result.type.ccount = dstIndex;\n\n    const uint32_t typeId = getVectorTypeId(result.type);\n\n    if (dstIndex == 1) {\n      result.id = m_module.opCompositeExtract(\n        typeId, value.id, 1, indices.data());\n    } else {\n      result.id = m_module.opVectorShuffle(\n        typeId, value.id, value.id,\n        dstIndex, indices.data());\n    }\n\n    return result;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitSrcOperandPreSwizzleModifiers(\n            DxsoRegisterValue       value,\n            DxsoRegModifier         modifier) {\n    // r / r.z\n    // r / r.w\n    if (modifier == DxsoRegModifier::Dz\n     || modifier == DxsoRegModifier::Dw) {\n      const uint32_t index = modifier == DxsoRegModifier::Dz ? 2 : 3;\n\n      std::array<uint32_t, 4> indices = { index, index, index, index };\n\n      uint32_t component = m_module.opVectorShuffle(\n        getVectorTypeId(value.type), value.id, value.id, value.type.ccount, indices.data());\n\n      value.id = m_module.opFDiv(\n        getVectorTypeId(value.type), value.id, component);\n    }\n\n    return value;\n  }\n\n\n  DxsoRegisterValue DxsoCompiler::emitSrcOperandPostSwizzleModifiers(\n            DxsoRegisterValue       value,\n            DxsoRegModifier         modifier) {\n    // r - 0.5\n    if (modifier == DxsoRegModifier::Bias\n     || modifier == DxsoRegModifier::BiasNeg) {\n      uint32_t halfVec = m_module.constfReplicant(\n        0.5f, value.type.ccount);\n\n      value.id = m_module.opFSub(\n        getVectorTypeId(value.type), value.id, halfVec);\n    }\n\n    // fma(r, 2.0f, -1.0f)\n    if (modifier == DxsoRegModifier::Sign\n     || modifier == DxsoRegModifier::SignNeg) {\n      uint32_t twoVec = m_module.constfReplicant(\n        2.0f, value.type.ccount);\n\n      uint32_t minusOneVec = m_module.constfReplicant(\n        -1.0f, value.type.ccount);\n\n      value.id = m_module.opFFma(\n        getVectorTypeId(value.type), value.id, twoVec, minusOneVec);\n    }\n\n    // 1 - r\n    if (modifier == DxsoRegModifier::Comp) {\n      uint32_t oneVec = m_module.constfReplicant(\n        1.0f, value.type.ccount);\n\n      value.id = m_module.opFSub(\n        getVectorTypeId(value.type), oneVec, value.id);\n    }\n\n    // r * 2\n    if (modifier == DxsoRegModifier::X2\n     || modifier == DxsoRegModifier::X2Neg) {\n      uint32_t twoVec = m_module.constfReplicant(\n        2.0f, value.type.ccount);\n\n      value.id = m_module.opFMul(\n        getVectorTypeId(value.type), value.id, twoVec);\n    }\n\n    // abs( r )\n    if (modifier == DxsoRegModifier::Abs\n     || modifier == DxsoRegModifier::AbsNeg) {\n      value.id = m_module.opFAbs(\n        getVectorTypeId(value.type), value.id);\n    }\n\n    // !r\n    if (modifier == DxsoRegModifier::Not) {\n      value.id =\n        m_module.opLogicalNot(getVectorTypeId(value.type), value.id);\n    }\n\n    // -r\n    // Treating as -r\n    // Treating as -r\n    // -r * 2\n    // -abs(r)\n    if (modifier == DxsoRegModifier::Neg\n     || modifier == DxsoRegModifier::BiasNeg\n     || modifier == DxsoRegModifier::SignNeg\n     || modifier == DxsoRegModifier::X2Neg\n     || modifier == DxsoRegModifier::AbsNeg) {\n      value.id = m_module.opFNegate(\n        getVectorTypeId(value.type), value.id);\n    }\n\n    return value;\n  }\n\n  DxsoRegisterValue DxsoCompiler::emitRegisterLoad(\n      const DxsoBaseRegister& reg,\n            DxsoRegMask       writeMask,\n      const DxsoBaseRegister* relative) {\n    // Load operand from the operand pointer\n    DxsoRegisterValue result = emitRegisterLoadRaw(reg, relative);\n\n    // PS 1.x clamps float constants\n    if (m_programInfo.type() == DxsoProgramType::PixelShader && m_programInfo.majorVersion() == 1\n      && reg.id.type == DxsoRegisterType::Const)\n      result = emitClampBoundReplicant(result, -1.0f, 1.0f);\n\n    // Apply operand modifiers\n    result = emitSrcOperandPreSwizzleModifiers(result, reg.modifier);\n\n    // Apply operand swizzle to the operand value\n    result = emitRegisterSwizzle(result, reg.swizzle, writeMask);\n\n    // Apply operand modifiers\n    result = emitSrcOperandPostSwizzleModifiers(result, reg.modifier);\n    return result;\n  }\n\n  void DxsoCompiler::emitDcl(const DxsoInstructionContext& ctx) {\n    auto id = ctx.dst.id;\n\n    if (id.type == DxsoRegisterType::Sampler) {\n      this->emitDclSampler(\n        ctx.dst.id.num,\n        ctx.dcl.textureType);\n    }\n    else if (id.type == DxsoRegisterType::Input\n          || id.type == DxsoRegisterType::Texture\n          || id.type == DxsoRegisterType::Output) {\n      DxsoSemantic semantic = ctx.dcl.semantic;\n\n      uint32_t vIndex = id.num;\n\n      if (m_programInfo.type() == DxsoProgramTypes::PixelShader) {\n        // Semantic in PS < 3 is based upon id.\n        if (m_programInfo.majorVersion() < 3) {\n          // Account for the two color registers.\n          if (id.type == DxsoRegisterType::Texture)\n            vIndex += 2;\n\n          semantic = DxsoSemantic{\n            id.type == DxsoRegisterType::Texture ? DxsoUsage::Texcoord : DxsoUsage::Color,\n            id.num };\n        }\n      }\n\n      this->emitDclInterface(\n        id.type != DxsoRegisterType::Output,\n        vIndex,\n        semantic,\n        ctx.dst.mask,\n        ctx.dst.centroid);\n    }\n    else {\n      //Logger::warn(str::format(\"DxsoCompiler::emitDcl: unhandled register type \", id.type));\n    }\n  }\n\n  void DxsoCompiler::emitDef(const DxsoInstructionContext& ctx) {\n    switch (ctx.instruction.opcode) {\n      case DxsoOpcode::Def:  emitDefF(ctx); break;\n      case DxsoOpcode::DefI: emitDefI(ctx); break;\n      case DxsoOpcode::DefB: emitDefB(ctx); break;\n      default:\n        throw DxvkError(\"DxsoCompiler::emitDef: Invalid definition opcode\");\n        break;\n    }\n  }\n\n  void DxsoCompiler::emitDefF(const DxsoInstructionContext& ctx) {\n    const float* data = ctx.def.float32;\n\n    uint32_t constId = m_module.constvec4f32(data[0], data[1], data[2], data[3]);\n    m_cFloat.at(ctx.dst.id.num) = constId;\n\n    std::string name = str::format(\"cF\", ctx.dst.id.num, \"_def\");\n    m_module.setDebugName(constId, name.c_str());\n\n    DxsoDefinedConstant constant;\n    constant.uboIdx = ctx.dst.id.num;\n    for (uint32_t i = 0; i < 4; i++)\n      constant.float32[i] = data[i];\n    m_constants.push_back(constant);\n    m_maxDefinedFloatConstant = std::max(static_cast<int32_t>(constant.uboIdx), m_maxDefinedFloatConstant);\n  }\n\n  void DxsoCompiler::emitDefI(const DxsoInstructionContext& ctx) {\n    const int32_t* data = ctx.def.int32;\n\n    uint32_t constId = m_module.constvec4i32(data[0], data[1], data[2], data[3]);\n    m_cInt.at(ctx.dst.id.num) = constId;\n\n    std::string name = str::format(\"cI\", ctx.dst.id.num, \"_def\");\n    m_module.setDebugName(constId, name.c_str());\n\n    m_maxDefinedIntConstant = std::max(static_cast<int32_t>(ctx.dst.id.num), m_maxDefinedIntConstant);\n  }\n\n  void DxsoCompiler::emitDefB(const DxsoInstructionContext& ctx) {\n    const int32_t* data = ctx.def.int32;\n\n    uint32_t constId = m_module.constBool(data[0] != 0);\n    m_cBool.at(ctx.dst.id.num) = constId;\n\n    std::string name = str::format(\"cB\", ctx.dst.id.num, \"_def\");\n    m_module.setDebugName(constId, name.c_str());\n\n    m_maxDefinedBoolConstant = std::max(static_cast<int32_t>(ctx.dst.id.num), m_maxDefinedBoolConstant);\n  }\n\n\n  bool DxsoCompiler::isScalarRegister(DxsoRegisterId id) {\n    return id == DxsoRegisterId{DxsoRegisterType::DepthOut, 0}\n        || id == DxsoRegisterId{DxsoRegisterType::RasterizerOut, RasterOutPointSize}\n        || id == DxsoRegisterId{DxsoRegisterType::RasterizerOut, RasterOutFog};\n  }\n\n\n  void DxsoCompiler::emitMov(const DxsoInstructionContext& ctx) {\n    DxsoRegisterPointer dst = emitGetOperandPtr(ctx.dst);\n\n    DxsoRegMask mask = ctx.dst.mask;\n\n    if (isScalarRegister(ctx.dst.id))\n      mask = DxsoRegMask(true, false, false, false);\n\n    DxsoRegisterValue src0 = emitRegisterLoad(ctx.src[0], mask);\n\n    DxsoRegisterValue result;\n    result.type.ctype  = dst.type.ctype;\n    result.type.ccount = mask.popCount();\n\n    const uint32_t typeId = getVectorTypeId(result.type);\n\n    if (dst.type.ctype != src0.type.ctype) {\n      // We have Mova for this... but it turns out Mov has the same behaviour in d3d9!\n\n      // Convert float -> int32_t\n      // and vice versa\n      if (dst.type.ctype == DxsoScalarType::Sint32) {\n        // We need to floor for VS 1.1 and below, the documentation is a dirty stinking liar.\n        if (m_programInfo.majorVersion() < 2 && m_programInfo.minorVersion() < 2)\n          result.id = m_module.opFloor(getVectorTypeId(src0.type), src0.id);\n        else\n          result.id = m_module.opRound(getVectorTypeId(src0.type), src0.id);\n\n        result.id = m_module.opConvertFtoS(typeId, result.id);\n      }\n      else // Float32\n        result.id = m_module.opConvertStoF(typeId, src0.id);\n    }\n    else // No special stuff needed!\n      result.id = src0.id;\n\n    this->emitDstStore(dst, result, mask, ctx.dst.saturate, emitPredicateLoad(ctx), ctx.dst.shift, ctx.dst.id);\n  }\n\n  std::array<uint32_t, 2> DxsoCompiler::emitBem(\n      const DxsoInstructionContext& ctx,\n      const DxsoRegisterValue& src0,\n      const DxsoRegisterValue& src1) {\n\n    // For texbem:\n    //  src0 = tc(m), src1 = t(n), dst.x = u', dst.y = v'\n\n    // dst.x = src0.x + [bm00(m) * src1.x + bm10(m) * src1.y]\n    // dst.y = src0.y + [bm01(m) * src1.x + bm11(m) * src1.y]\n\n    // But we flipped the bm indices so we can use dot here...\n\n    // dst.x = src0.x + dot(bm0, src1)\n    // dst.y = src0.y + dot(bm1, src1)\n\n    std::array<uint32_t, 2> values = { m_module.constf32(0.0f), m_module.constf32(0.0f) };\n\n    for (uint32_t i = 0; i < 2; i++) {\n      uint32_t fl_t   = getScalarTypeId(DxsoScalarType::Float32);\n      uint32_t vec2_t = getVectorTypeId({ DxsoScalarType::Float32, 2 });\n      std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };\n\n      uint32_t tc_m_n = m_module.opCompositeExtract(fl_t, src0.id, 1, &i);\n\n      uint32_t offset = m_module.constu32(D3D9SharedPSStages_Count * ctx.dst.id.num + D3D9SharedPSStages_BumpEnvMat0 + i);\n      uint32_t bm     = m_module.opAccessChain(m_module.defPointerType(vec2_t, spv::StorageClassUniform),\n                                              m_ps.sharedState, 1, &offset);\n                bm    = m_module.opLoad(vec2_t, bm);\n\n      uint32_t t      = m_module.opVectorShuffle(vec2_t, src1.id, src1.id, 2, indices.data());\n\n      uint32_t dot    = m_module.opDot(fl_t, bm, t);\n\n      values[i]       = m_module.opFAdd(fl_t, tc_m_n, dot);\n    }\n    return values;\n  }\n\n\n  void DxsoCompiler::emitVectorAlu(const DxsoInstructionContext& ctx) {\n    const auto& src = ctx.src;\n\n    DxsoRegMask mask = ctx.dst.mask;\n\n    DxsoRegisterPointer dst = emitGetOperandPtr(ctx.dst);\n\n    if (isScalarRegister(ctx.dst.id))\n      mask = DxsoRegMask(true, false, false, false);\n\n    DxsoRegisterValue result;\n    result.type.ctype  = dst.type.ctype;\n    result.type.ccount = mask.popCount();\n\n    DxsoVectorType scalarType = result.type;\n    scalarType.ccount = 1;\n\n    const uint32_t typeId       = getVectorTypeId(result.type);\n    const uint32_t scalarTypeId = getVectorTypeId(scalarType);\n\n    const DxsoOpcode opcode = ctx.instruction.opcode;\n    switch (opcode) {\n      case DxsoOpcode::Add:\n        result.id = m_module.opFAdd(typeId,\n          emitRegisterLoad(src[0], mask).id,\n          emitRegisterLoad(src[1], mask).id);\n        break;\n      case DxsoOpcode::Sub:\n        result.id = m_module.opFSub(typeId,\n          emitRegisterLoad(src[0], mask).id,\n          emitRegisterLoad(src[1], mask).id);\n        break;\n      case DxsoOpcode::Mad:\n        result.id = emitMad(\n          emitRegisterLoad(src[0], mask),\n          emitRegisterLoad(src[1], mask),\n          emitRegisterLoad(src[2], mask)).id;\n        break;\n      case DxsoOpcode::Mul:\n        result.id = emitMul(\n          emitRegisterLoad(src[0], mask),\n          emitRegisterLoad(src[1], mask)).id;\n        break;\n      case DxsoOpcode::Rcp:\n        result.id = m_module.opFDiv(typeId,\n          m_module.constfReplicant(1.0f, result.type.ccount),\n          emitRegisterLoad(src[0], mask).id);\n\n        if (m_moduleInfo.options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled) {\n          result.id = m_module.opNMin(typeId, result.id,\n            m_module.constfReplicant(std::numeric_limits<float>::max(), result.type.ccount));\n        }\n        break;\n      case DxsoOpcode::Rsq: \n        result.id = m_module.opFAbs(typeId,\n          emitRegisterLoad(src[0], mask).id);\n\n        result.id = m_module.opInverseSqrt(typeId,\n          result.id);\n\n        if (m_moduleInfo.options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled) {\n          result.id = m_module.opNMin(typeId, result.id,\n            m_module.constfReplicant(std::numeric_limits<float>::max(), result.type.ccount));\n        }\n        break;\n      case DxsoOpcode::Dp3: {\n        DxsoRegMask srcMask(true, true, true, false);\n        result = emitDot(\n          emitRegisterLoad(src[0], srcMask),\n          emitRegisterLoad(src[1], srcMask));\n        break;\n      }\n      case DxsoOpcode::Dp4:\n        result = emitDot(\n          emitRegisterLoad(src[0], IdentityWriteMask),\n          emitRegisterLoad(src[1], IdentityWriteMask));\n        break;\n      case DxsoOpcode::Slt:\n      case DxsoOpcode::Sge: {\n        const uint32_t boolTypeId =\n          getVectorTypeId({ DxsoScalarType::Bool, result.type.ccount });\n\n        uint32_t cmpResult = opcode == DxsoOpcode::Slt\n          ? m_module.opFOrdLessThan        (boolTypeId, emitRegisterLoad(src[0], mask).id, emitRegisterLoad(src[1], mask).id)\n          : m_module.opFOrdGreaterThanEqual(boolTypeId, emitRegisterLoad(src[0], mask).id, emitRegisterLoad(src[1], mask).id);\n\n        result.id = m_module.opSelect(typeId, cmpResult,\n          m_module.constfReplicant(1.0f, result.type.ccount),\n          m_module.constfReplicant(0.0f, result.type.ccount));\n        break;\n      }\n      case DxsoOpcode::Min:\n        result.id = m_module.opFMin(typeId,\n          emitRegisterLoad(src[0], mask).id,\n          emitRegisterLoad(src[1], mask).id);\n        break;\n      case DxsoOpcode::Max:\n        result.id = m_module.opFMax(typeId,\n          emitRegisterLoad(src[0], mask).id,\n          emitRegisterLoad(src[1], mask).id);\n        break;\n      case DxsoOpcode::ExpP:\n        if (m_programInfo.majorVersion() < 2) {\n          DxsoRegMask srcMask(true, false, false, false);\n          uint32_t src0 = emitRegisterLoad(src[0], srcMask).id;\n\n          uint32_t index = 0;\n\n          std::array<uint32_t, 4> resultIndices;\n\n          if (mask[0]) resultIndices[index++] = m_module.opExp2(scalarTypeId, m_module.opFloor(scalarTypeId, src0));\n          if (mask[1]) resultIndices[index++] = m_module.opFSub(scalarTypeId, src0, m_module.opFloor(scalarTypeId, src0));\n          if (mask[2]) resultIndices[index++] = m_module.opExp2(scalarTypeId, src0);\n          if (mask[3]) resultIndices[index++] = m_module.constf32(1.0f);\n\n          if (result.type.ccount == 1)\n            result.id = resultIndices[0];\n          else\n            result.id = m_module.opCompositeConstruct(typeId, result.type.ccount, resultIndices.data());\n\n        if (m_moduleInfo.options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled) {\n          result.id = m_module.opNMin(typeId, result.id,\n            m_module.constfReplicant(std::numeric_limits<float>::max(), result.type.ccount));\n        }\n          break;\n        }\n        [[fallthrough]];\n      case DxsoOpcode::Exp:\n        result.id = m_module.opExp2(typeId,\n          emitRegisterLoad(src[0], mask).id);\n\n        if (m_moduleInfo.options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled) {\n          result.id = m_module.opNMin(typeId, result.id,\n            m_module.constfReplicant(std::numeric_limits<float>::max(), result.type.ccount));\n        }\n        break;\n      case DxsoOpcode::Pow: {\n        uint32_t base = emitRegisterLoad(src[0], mask).id;\n        base = m_module.opFAbs(typeId, base);\n\n        uint32_t exponent = emitRegisterLoad(src[1], mask).id;\n\n        result.id = m_module.opPow(typeId, base, exponent);\n\n        if (m_moduleInfo.options.d3d9FloatEmulation != D3D9FloatEmulation::Disabled) {\n          DxsoRegisterValue cmp;\n          cmp.type  = { DxsoScalarType::Bool, result.type.ccount };\n          cmp.id    = m_module.opFOrdEqual(getVectorTypeId(cmp.type),\n            exponent, m_module.constfReplicant(0.0f, cmp.type.ccount));\n\n          result.id = m_module.opSelect(typeId, cmp.id,\n            m_module.constfReplicant(1.0f, cmp.type.ccount), result.id);\n        }\n        break;\n      }\n      case DxsoOpcode::Crs: {\n        DxsoRegMask vec3Mask(true, true, true, false);\n        \n        DxsoRegisterValue crossValue = emitCross(\n          emitRegisterLoad(src[0], vec3Mask),\n          emitRegisterLoad(src[1], vec3Mask));\n\n        std::array<uint32_t, 3> indices = { 0, 0, 0 };\n\n        uint32_t index = 0;\n        for (uint32_t i = 0; i < indices.size(); i++) {\n          if (mask[i])\n            indices[index++] = m_module.opCompositeExtract(m_module.defFloatType(32), crossValue.id, 1, &i);\n        }\n\n        if (result.type.ccount == 1)\n          result.id = indices[0];\n        else\n          result.id = m_module.opCompositeConstruct(typeId, result.type.ccount, indices.data());\n\n        break;\n      }\n      case DxsoOpcode::Abs:\n        result.id = m_module.opFAbs(typeId,\n          emitRegisterLoad(src[0], mask).id);\n        break;\n      case DxsoOpcode::Sgn:\n        result.id = m_module.opFSign(typeId,\n          emitRegisterLoad(src[0], mask).id);\n        break;\n      case DxsoOpcode::Nrm: {\n        // Nrm is 3D...\n        DxsoRegMask srcMask(true, true, true, false);\n        auto vec3 = emitRegisterLoad(src[0], srcMask);\n        auto dot = emitDot(vec3, vec3);\n\n        DxsoRegisterValue rcpLength;\n        rcpLength.type = scalarType;\n        rcpLength.id = m_module.opInverseSqrt(scalarTypeId, dot.id);\n        if (m_moduleInfo.options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled) {\n          rcpLength.id = m_module.opNMin(scalarTypeId, rcpLength.id, m_module.constf32(std::numeric_limits<float>::max()));\n        }\n\n        // r * rsq(r . r)\n        result.id = emitMul(emitRegisterLoad(src[0], mask), emitRegisterExtend(rcpLength, mask.popCount())).id;\n        break;\n      }\n      case DxsoOpcode::SinCos: {\n        DxsoRegMask srcMask(true, false, false, false);\n        uint32_t src0 = emitRegisterLoad(src[0], srcMask).id;\n\n        std::array<uint32_t, 4> sincosVectorIndices = { 0, 0, 0, 0 };\n\n        uint32_t index = 0;\n        uint32_t type = m_module.defFloatType(32);\n        uint32_t sincos = m_module.opSinCos(src0, !m_moduleInfo.options.sincosEmulation);\n\n        uint32_t sinIndex = 0u;\n        uint32_t cosIndex = 1u;\n\n        if (mask[0])\n          sincosVectorIndices[index++] = m_module.opCompositeExtract(type, sincos, 1u, &cosIndex);\n\n        if (mask[1])\n          sincosVectorIndices[index++] = m_module.opCompositeExtract(type, sincos, 1u, &sinIndex);\n\n        for (; index < result.type.ccount; index++) {\n          if (sincosVectorIndices[index] == 0)\n            sincosVectorIndices[index] = m_module.constf32(0.0f);\n        }\n            \n        if (result.type.ccount == 1)\n          result.id = sincosVectorIndices[0];\n        else\n          result.id = m_module.opCompositeConstruct(typeId, result.type.ccount, sincosVectorIndices.data());\n\n        break;\n      }\n      case DxsoOpcode::Lit: {\n        DxsoRegMask srcMask(true, true, true, true);\n        uint32_t srcOp = emitRegisterLoad(src[0], srcMask).id;\n\n        const uint32_t x = 0;\n        const uint32_t y = 1;\n        const uint32_t w = 3;\n\n        uint32_t srcX = m_module.opCompositeExtract(scalarTypeId, srcOp, 1, &x);\n        uint32_t srcY = m_module.opCompositeExtract(scalarTypeId, srcOp, 1, &y);\n        uint32_t srcW = m_module.opCompositeExtract(scalarTypeId, srcOp, 1, &w);\n\n        uint32_t power = m_module.opFClamp(\n          scalarTypeId, srcW,\n          m_module.constf32(-127.9961f), m_module.constf32(127.9961f));\n\n        std::array<uint32_t, 4> resultIndices;\n\n        uint32_t index = 0;\n\n        if (mask[0]) resultIndices[index++] = m_module.constf32(1.0f);\n        if (mask[1]) resultIndices[index++] = m_module.opFMax(scalarTypeId, srcX, m_module.constf32(0));\n        if (mask[2]) resultIndices[index++] = m_module.opPow (scalarTypeId, m_module.opFMax(scalarTypeId, srcY, m_module.constf32(0)), power);\n        if (mask[3]) resultIndices[index++] = m_module.constf32(1.0f);\n\n        const uint32_t boolType = m_module.defBoolType();\n        uint32_t zTestX = m_module.opFOrdGreaterThanEqual(boolType, srcX, m_module.constf32(0));\n        uint32_t zTestY = m_module.opFOrdGreaterThanEqual(boolType, srcY, m_module.constf32(0));\n        uint32_t zTest  = m_module.opLogicalAnd(boolType, zTestX, zTestY);\n\n        if (result.type.ccount > 2)\n          resultIndices[2] = m_module.opSelect(\n            scalarTypeId,\n            zTest,\n            resultIndices[2],\n            m_module.constf32(0.0f));\n\n        if (result.type.ccount == 1)\n          result.id = resultIndices[0];\n        else\n          result.id = m_module.opCompositeConstruct(typeId, result.type.ccount, resultIndices.data());\n        break;\n      }\n      case DxsoOpcode::Dst: {\n        //dest.x = 1;\n        //dest.y = src0.y * src1.y;\n        //dest.z = src0.z;\n        //dest.w = src1.w;\n\n        DxsoRegMask srcMask(true, true, true, true);\n\n        uint32_t src0 = emitRegisterLoad(src[0], srcMask).id;\n        uint32_t src1 = emitRegisterLoad(src[1], srcMask).id;\n\n        const uint32_t y = 1;\n        const uint32_t z = 2;\n        const uint32_t w = 3;\n\n        DxsoRegisterValue src0Y = { scalarType, m_module.opCompositeExtract(scalarTypeId, src0, 1, &y) };\n        DxsoRegisterValue src1Y = { scalarType, m_module.opCompositeExtract(scalarTypeId, src1, 1, &y) };\n\n        uint32_t src0Z = m_module.opCompositeExtract(scalarTypeId, src0, 1, &z);\n        uint32_t src1W = m_module.opCompositeExtract(scalarTypeId, src1, 1, &w);\n\n        std::array<uint32_t, 4> resultIndices;\n        resultIndices[0] = m_module.constf32(1.0f);\n        resultIndices[1] = emitMul(src0Y, src1Y).id;\n        resultIndices[2] = src0Z;\n        resultIndices[3] = src1W;\n\n        if (result.type.ccount == 1)\n          result.id = resultIndices[0];\n        else\n          result.id = m_module.opCompositeConstruct(typeId, result.type.ccount, resultIndices.data());\n        break;\n      }\n      case DxsoOpcode::LogP:\n      case DxsoOpcode::Log:\n        result.id = m_module.opFAbs(typeId, emitRegisterLoad(src[0], mask).id);\n        result.id = m_module.opLog2(typeId, result.id);\n        if (m_moduleInfo.options.d3d9FloatEmulation == D3D9FloatEmulation::Enabled) {\n          result.id = m_module.opNMax(typeId, result.id,\n            m_module.constfReplicant(-std::numeric_limits<float>::max(), result.type.ccount));\n        }\n        break;\n      case DxsoOpcode::Lrp:\n        result.id = emitMix(\n          emitRegisterLoad(src[2], mask),\n          emitRegisterLoad(src[1], mask),\n          emitRegisterLoad(src[0], mask)).id;\n        break;\n      case DxsoOpcode::Frc:\n        result.id = m_module.opFract(typeId,\n          emitRegisterLoad(src[0], mask).id);\n        break;\n      case DxsoOpcode::Cmp: {\n        const uint32_t boolTypeId =\n          getVectorTypeId({ DxsoScalarType::Bool, result.type.ccount });\n\n        uint32_t cmp = m_module.opFOrdGreaterThanEqual(\n          boolTypeId,\n          emitRegisterLoad(src[0], mask).id,\n          m_module.constfReplicant(0.0f, result.type.ccount));\n\n        result.id = m_module.opSelect(\n          typeId, cmp,\n          emitRegisterLoad(src[1], mask).id,\n          emitRegisterLoad(src[2], mask).id);\n        break;\n      }\n      case DxsoOpcode::Bem: {\n        DxsoRegisterValue src0 = emitRegisterLoad(src[0], mask);\n        DxsoRegisterValue src1 = emitRegisterLoad(src[1], mask);\n\n        auto values = emitBem(ctx, src0, src1);\n        result.id   = m_module.opCompositeConstruct(typeId, values.size(), values.data());\n        break;\n      }\n      case DxsoOpcode::Cnd: {\n        const uint32_t boolTypeId =\n          getVectorTypeId({ DxsoScalarType::Bool, result.type.ccount });\n\n        uint32_t cmp = m_module.opFOrdGreaterThan(\n          boolTypeId,\n          emitRegisterLoad(src[0], mask).id,\n          m_module.constfReplicant(0.5f, result.type.ccount));\n\n        result.id = m_module.opSelect(\n          typeId, cmp,\n          emitRegisterLoad(src[1], mask).id,\n          emitRegisterLoad(src[2], mask).id);\n        break;\n      }\n      case DxsoOpcode::Dp2Add: {\n        DxsoRegMask dotSrcMask(true, true, false, false);\n        DxsoRegMask addSrcMask(true, false, false, false);\n\n        DxsoRegisterValue dot = emitDot(\n          emitRegisterLoad(src[0], dotSrcMask),\n          emitRegisterLoad(src[1], dotSrcMask));\n\n        dot.id = m_module.opFAdd(scalarTypeId,\n          dot.id, emitRegisterLoad(src[2], addSrcMask).id);\n\n        result.id   = dot.id;\n        result.type = scalarType;\n        break;\n      }\n      case DxsoOpcode::DsX:\n        result.id = m_module.opDpdx(\n          typeId, emitRegisterLoad(src[0], mask).id);\n        break;\n      case DxsoOpcode::DsY:\n        result.id = m_module.opDpdy(\n          typeId, emitRegisterLoad(src[0], mask).id);\n        break;\n      default:\n        Logger::warn(str::format(\"DxsoCompiler::emitVectorAlu: unimplemented op \", opcode));\n        return;\n    }\n\n    this->emitDstStore(dst, result, mask, ctx.dst.saturate, emitPredicateLoad(ctx), ctx.dst.shift, ctx.dst.id);\n  }\n\n\n  void DxsoCompiler::emitPredicateOp(const DxsoInstructionContext& ctx) {\n    const auto& src = ctx.src;\n\n    DxsoRegMask mask = ctx.dst.mask;\n\n    DxsoRegisterPointer dst = emitGetOperandPtr(ctx.dst);\n\n    DxsoRegisterValue result;\n    result.type.ctype  = dst.type.ctype;\n    result.type.ccount = mask.popCount();\n\n    result.id = emitBoolComparison(\n      result.type, ctx.instruction.specificData.comparison,\n      emitRegisterLoad(src[0], mask).id, emitRegisterLoad(src[1], mask).id);\n\n    this->emitValueStore(dst, result, mask, emitPredicateLoad(ctx));\n  }\n\n\n  void DxsoCompiler::emitMatrixAlu(const DxsoInstructionContext& ctx) {\n    const DxsoOpcode opcode = ctx.instruction.opcode;\n\n    uint32_t dotCount;\n    uint32_t componentCount;\n\n    switch (opcode) {\n      case DxsoOpcode::M3x2:\n        dotCount       = 3;\n        componentCount = 2;\n        break;\n      case DxsoOpcode::M3x3:\n        dotCount       = 3;\n        componentCount = 3;\n        break;\n      case DxsoOpcode::M3x4:\n        dotCount       = 3;\n        componentCount = 4;\n        break;\n      case DxsoOpcode::M4x3:\n        dotCount       = 4;\n        componentCount = 3;\n        break;\n      case DxsoOpcode::M4x4:\n        dotCount       = 4;\n        componentCount = 4;\n        break;\n      default:\n        Logger::warn(str::format(\"DxsoCompiler::emitMatrixAlu: unimplemented op \", opcode));\n        return;\n    }\n\n    DxsoRegisterPointer dst = emitGetOperandPtr(ctx.dst);\n\n    // Fix the dst mask if componentCount != maskCount\n    // ie. M4x3 on .xyzw.\n    uint32_t maskCnt = 0;\n    uint8_t mask = 0;\n    for (uint32_t i = 0; i < 4 && maskCnt < componentCount; i++) {\n      if (ctx.dst.mask[i]) {\n        mask |= 1 << i;\n        maskCnt++;\n      }\n    }\n    DxsoRegMask dstMask = DxsoRegMask(mask);\n\n    DxsoRegisterValue result;\n    result.type.ctype  = dst.type.ctype;\n    result.type.ccount = componentCount;\n\n    const uint32_t typeId = getVectorTypeId(result.type);\n\n    DxsoRegMask srcMask(true, true, true, dotCount == 4);\n    std::array<uint32_t, 4> indices;\n\n    DxsoRegister src0 = ctx.src[0];\n    DxsoRegister src1 = ctx.src[1];\n\n    for (uint32_t i = 0; i < componentCount; i++) {\n      indices[i] = emitDot(\n        emitRegisterLoad(src0, srcMask),\n        emitRegisterLoad(src1, srcMask)).id;\n\n      src1.id.num++;\n    }\n\n    result.id = m_module.opCompositeConstruct(\n      typeId, componentCount, indices.data());\n\n    this->emitDstStore(dst, result, dstMask, ctx.dst.saturate, emitPredicateLoad(ctx), ctx.dst.shift, ctx.dst.id);\n  }\n\n\nvoid DxsoCompiler::emitControlFlowGenericLoop(\n          bool     count,\n          uint32_t initialVar,\n          uint32_t strideVar,\n          uint32_t iterationCountVar) {\n    const uint32_t itType = m_module.defIntType(32, 1);\n\n    DxsoCfgBlock block;\n    block.type = DxsoCfgBlockType::Loop;\n    block.b_loop.labelHeader   = m_module.allocateId();\n    block.b_loop.labelBegin    = m_module.allocateId();\n    block.b_loop.labelContinue = m_module.allocateId();\n    block.b_loop.labelBreak    = m_module.allocateId();\n    block.b_loop.iteratorPtr   = m_module.newVar(\n      m_module.defPointerType(itType, spv::StorageClassPrivate), spv::StorageClassPrivate);\n    block.b_loop.strideVar     = strideVar;\n    block.b_loop.countBackup   = 0;\n\n    if (count) {\n      DxsoBaseRegister loop;\n      loop.id = { DxsoRegisterType::Loop, 0 };\n\n      DxsoRegisterPointer loopPtr = emitGetOperandPtr(loop, nullptr);\n      uint32_t loopVal = m_module.opLoad(\n        getVectorTypeId(loopPtr.type), loopPtr.id);\n\n      block.b_loop.countBackup = loopVal;\n\n      m_module.opStore(loopPtr.id, initialVar);\n    }\n\n    m_module.setDebugName(block.b_loop.iteratorPtr, \"iter\");\n\n    m_module.opStore(block.b_loop.iteratorPtr, iterationCountVar);\n\n    m_module.opBranch(block.b_loop.labelHeader);\n    m_module.opLabel (block.b_loop.labelHeader);\n\n    m_module.opLoopMerge(\n      block.b_loop.labelBreak,\n      block.b_loop.labelContinue,\n      spv::LoopControlMaskNone);\n\n    m_module.opBranch(block.b_loop.labelBegin);\n    m_module.opLabel (block.b_loop.labelBegin);\n\n    uint32_t iterator = m_module.opLoad(itType, block.b_loop.iteratorPtr);\n    uint32_t complete = m_module.opIEqual(m_module.defBoolType(), iterator, m_module.consti32(0));\n\n    const uint32_t breakBlock = m_module.allocateId();\n    const uint32_t mergeBlock = m_module.allocateId();\n\n    m_module.opSelectionMerge(mergeBlock,\n      spv::SelectionControlMaskNone);\n\n    m_module.opBranchConditional(\n      complete, breakBlock, mergeBlock);\n\n    m_module.opLabel(breakBlock);\n\n    m_module.opBranch(block.b_loop.labelBreak);\n\n    m_module.opLabel(mergeBlock);\n\n    iterator = m_module.opISub(itType, iterator, m_module.consti32(1));\n    m_module.opStore(block.b_loop.iteratorPtr, iterator);\n\n    m_controlFlowBlocks.push_back(block);\n  }\n\n  void DxsoCompiler::emitControlFlowGenericLoopEnd() {\n    if (m_controlFlowBlocks.size() == 0\n      || m_controlFlowBlocks.back().type != DxsoCfgBlockType::Loop)\n      throw DxvkError(\"DxsoCompiler: 'EndRep' without 'Rep' or 'Loop' found\");\n\n    // Remove the block from the stack, it's closed\n    const DxsoCfgBlock block = m_controlFlowBlocks.back();\n    m_controlFlowBlocks.pop_back();\n\n    if (block.b_loop.strideVar) {\n      DxsoBaseRegister loop;\n      loop.id = { DxsoRegisterType::Loop, 0 };\n\n      DxsoRegisterPointer loopPtr = emitGetOperandPtr(loop, nullptr);\n      uint32_t val = m_module.opLoad(\n        getVectorTypeId(loopPtr.type), loopPtr.id);\n\n      val = m_module.opIAdd(\n        getVectorTypeId(loopPtr.type),\n        val, block.b_loop.strideVar);\n\n      m_module.opStore(loopPtr.id, val);\n    }\n\n    // Declare the continue block\n    m_module.opBranch(block.b_loop.labelContinue);\n    m_module.opLabel(block.b_loop.labelContinue);\n\n    // Declare the merge block\n    m_module.opBranch(block.b_loop.labelHeader);\n    m_module.opLabel(block.b_loop.labelBreak);\n\n    if (block.b_loop.countBackup) {\n      DxsoBaseRegister loop;\n      loop.id = { DxsoRegisterType::Loop, 0 };\n\n      DxsoRegisterPointer loopPtr = emitGetOperandPtr(loop, nullptr);\n\n      m_module.opStore(loopPtr.id, block.b_loop.countBackup);\n    }\n  }\n\n  void DxsoCompiler::emitControlFlowRep(const DxsoInstructionContext& ctx) {\n    DxsoRegMask srcMask(true, false, false, false);\n    this->emitControlFlowGenericLoop(\n      false, 0, 0,\n      emitRegisterLoad(ctx.src[0], srcMask).id);\n  }\n\n  void DxsoCompiler::emitControlFlowEndRep(const DxsoInstructionContext& ctx) {\n    emitControlFlowGenericLoopEnd();\n  }\n\n  void DxsoCompiler::emitControlFlowLoop(const DxsoInstructionContext& ctx) {\n    const uint32_t itType = m_module.defIntType(32, 1);\n\n    DxsoRegMask srcMask(true, true, true, false);\n    uint32_t integerRegister = emitRegisterLoad(ctx.src[1], srcMask).id;\n    uint32_t x = 0;\n    uint32_t y = 1;\n    uint32_t z = 2;\n\n    uint32_t iterCount    = m_module.opCompositeExtract(itType, integerRegister, 1, &x);\n    uint32_t initialValue = m_module.opCompositeExtract(itType, integerRegister, 1, &y);\n    uint32_t strideSize   = m_module.opCompositeExtract(itType, integerRegister, 1, &z);\n\n    this->emitControlFlowGenericLoop(\n      true,\n      initialValue,\n      strideSize,\n      iterCount);\n  }\n\n  void DxsoCompiler::emitControlFlowEndLoop(const DxsoInstructionContext& ctx) {\n    this->emitControlFlowGenericLoopEnd();\n  }\n\n  void DxsoCompiler::emitControlFlowBreak(const DxsoInstructionContext& ctx) {\n    DxsoCfgBlock* cfgBlock =\n      cfgFindBlock({ DxsoCfgBlockType::Loop });\n\n    if (cfgBlock == nullptr)\n      throw DxvkError(\"DxbcCompiler: 'Break' outside 'Rep' or 'Loop' found\");\n\n    m_module.opBranch(cfgBlock->b_loop.labelBreak);\n\n    // Subsequent instructions assume that there is an open block\n    const uint32_t labelId = m_module.allocateId();\n    m_module.opLabel(labelId);\n  }\n\n  void DxsoCompiler::emitControlFlowBreakC(const DxsoInstructionContext& ctx) {\n    DxsoCfgBlock* cfgBlock =\n      cfgFindBlock({ DxsoCfgBlockType::Loop });\n\n    if (cfgBlock == nullptr)\n      throw DxvkError(\"DxbcCompiler: 'BreakC' outside 'Rep' or 'Loop' found\");\n\n    DxsoRegMask srcMask(true, false, false, false);\n    auto a = emitRegisterLoad(ctx.src[0], srcMask);\n    auto b = emitRegisterLoad(ctx.src[1], srcMask);\n\n    uint32_t result = this->emitBoolComparison(\n      { DxsoScalarType::Bool, a.type.ccount },\n      ctx.instruction.specificData.comparison,\n      a.id, b.id);\n\n    // We basically have to wrap this into an 'if' block\n    const uint32_t breakBlock = m_module.allocateId();\n    const uint32_t mergeBlock = m_module.allocateId();\n\n    m_module.opSelectionMerge(mergeBlock,\n      spv::SelectionControlMaskNone);\n\n    m_module.opBranchConditional(\n      result, breakBlock, mergeBlock);\n\n    m_module.opLabel(breakBlock);\n\n    m_module.opBranch(cfgBlock->b_loop.labelBreak);\n\n    m_module.opLabel(mergeBlock);\n  }\n\n  void DxsoCompiler::emitControlFlowIf(const DxsoInstructionContext& ctx) {\n    const auto opcode = ctx.instruction.opcode;\n\n    uint32_t result;\n\n    DxsoRegMask srcMask(true, false, false, false);\n    if (opcode == DxsoOpcode::Ifc) {\n      auto a = emitRegisterLoad(ctx.src[0], srcMask);\n      auto b = emitRegisterLoad(ctx.src[1], srcMask);\n\n      result = this->emitBoolComparison(\n        { DxsoScalarType::Bool, a.type.ccount },\n        ctx.instruction.specificData.comparison,\n        a.id, b.id);\n    } else\n      result = emitRegisterLoad(ctx.src[0], srcMask).id;\n\n    // Declare the 'if' block. We do not know if there\n    // will be an 'else' block or not, so we'll assume\n    // that there is one and leave it empty otherwise.\n    DxsoCfgBlock block;\n    block.type = DxsoCfgBlockType::If;\n    block.b_if.ztestId   = result;\n    block.b_if.labelIf   = m_module.allocateId();\n    block.b_if.labelElse = 0;\n    block.b_if.labelEnd  = m_module.allocateId();\n    block.b_if.headerPtr = m_module.getInsertionPtr();\n    m_controlFlowBlocks.push_back(block);\n\n    // We'll insert the branch instruction when closing\n    // the block, since we don't know whether or not an\n    // else block is needed right now.\n    m_module.opLabel(block.b_if.labelIf);\n  }\n\n  void DxsoCompiler::emitControlFlowElse(const DxsoInstructionContext& ctx) {\n    if (m_controlFlowBlocks.size() == 0\n     || m_controlFlowBlocks.back().type != DxsoCfgBlockType::If\n     || m_controlFlowBlocks.back().b_if.labelElse != 0)\n      throw DxvkError(\"DxsoCompiler: 'Else' without 'If' found\");\n    \n    // Set the 'Else' flag so that we do\n    // not insert a dummy block on 'EndIf'\n    DxsoCfgBlock& block = m_controlFlowBlocks.back();\n    block.b_if.labelElse = m_module.allocateId();\n    \n    // Close the 'If' block by branching to\n    // the merge block we declared earlier\n    m_module.opBranch(block.b_if.labelEnd);\n    m_module.opLabel (block.b_if.labelElse);\n  }\n\n  void DxsoCompiler::emitControlFlowEndIf(const DxsoInstructionContext& ctx) {\n    if (m_controlFlowBlocks.size() == 0\n     || m_controlFlowBlocks.back().type != DxsoCfgBlockType::If)\n      throw DxvkError(\"DxsoCompiler: 'EndIf' without 'If' found\");\n    \n    // Remove the block from the stack, it's closed\n    DxsoCfgBlock block = m_controlFlowBlocks.back();\n    m_controlFlowBlocks.pop_back();\n    \n    // Write out the 'if' header\n    m_module.beginInsertion(block.b_if.headerPtr);\n    \n    m_module.opSelectionMerge(\n      block.b_if.labelEnd,\n      spv::SelectionControlMaskNone);\n    \n    m_module.opBranchConditional(\n      block.b_if.ztestId,\n      block.b_if.labelIf,\n      block.b_if.labelElse != 0\n        ? block.b_if.labelElse\n        : block.b_if.labelEnd);\n    \n    m_module.endInsertion();\n    \n    // End the active 'if' or 'else' block\n    m_module.opBranch(block.b_if.labelEnd);\n    m_module.opLabel (block.b_if.labelEnd);\n  }\n\n\n  void DxsoCompiler::emitTexCoord(const DxsoInstructionContext& ctx) {\n    DxsoRegisterValue result;\n\n    if (m_programInfo.majorVersion() == 1 && m_programInfo.minorVersion() == 4) {\n      // TexCrd Op (PS 1.4)\n      result = emitRegisterLoad(ctx.src[0], ctx.dst.mask);\n    } else {\n      // TexCoord Op (PS 1.0 - PS 1.3)\n      DxsoRegister texcoord;\n      texcoord.id.type = DxsoRegisterType::PixelTexcoord;\n      texcoord.id.num  = ctx.dst.id.num;\n\n      result = emitRegisterLoadRaw(texcoord, nullptr);\n      // Saturate\n      result = emitSaturate(result);\n      // w = 1.0f\n      uint32_t wIndex = 3;\n      result.id = m_module.opCompositeInsert(getVectorTypeId(result.type),\n        m_module.constf32(1.0f),\n        result.id,\n        1, &wIndex);\n    }\n\n    DxsoRegisterPointer dst = emitGetOperandPtr(ctx.dst);\n\n    this->emitDstStore(dst, result, ctx.dst.mask, ctx.dst.saturate, emitPredicateLoad(ctx), ctx.dst.shift, ctx.dst.id);\n  }\n\n  void DxsoCompiler::emitTextureSample(const DxsoInstructionContext& ctx) {\n    DxsoRegisterPointer dst = emitGetOperandPtr(ctx.dst);\n\n    const DxsoOpcode opcode = ctx.instruction.opcode;\n\n    DxsoRegisterValue texcoordVar;\n    uint32_t samplerIdx = 0u;\n\n    DxsoRegMask vec3Mask(true, true, true,  false);\n    DxsoRegMask srcMask (true, true, true,  true);\n\n    auto DoProjection = [&](DxsoRegisterValue coord, bool switchProjRes) {\n      uint32_t bool_t = m_module.defBoolType();\n      uint32_t texcoord_t = getVectorTypeId(coord.type);\n\n      uint32_t w = 3;\n\n      uint32_t projScalar = m_module.opCompositeExtract(\n        m_module.defFloatType(32), coord.id, 1, &w);\n\n      projScalar = m_module.opFDiv(m_module.defFloatType(32), m_module.constf32(1.0), projScalar);\n      uint32_t projResult = m_module.opVectorTimesScalar(texcoord_t, coord.id, projScalar);\n\n      if (switchProjRes) {\n        uint32_t shouldProj = m_spec.get(m_module, m_specUbo, SpecSamplerProjected, samplerIdx, 1);\n        shouldProj = m_module.opINotEqual(bool_t, shouldProj, m_module.constu32(0));\n\n        uint32_t bvec4_t = m_module.defVectorType(bool_t, 4);\n        std::array<uint32_t, 4> indices = { shouldProj, shouldProj, shouldProj, shouldProj };\n        shouldProj = m_module.opCompositeConstruct(bvec4_t, indices.size(), indices.data());\n\n        return m_module.opSelect(texcoord_t, shouldProj, projResult, coord.id);\n      } else {\n        return projResult;\n      }\n    };\n\n    if (opcode == DxsoOpcode::TexM3x2Tex || opcode == DxsoOpcode::TexM3x3Tex || opcode == DxsoOpcode::TexM3x3Spec || opcode == DxsoOpcode::TexM3x3VSpec) {\n      const uint32_t count = opcode == DxsoOpcode::TexM3x2Tex ? 2 : 3;\n\n      auto n = emitRegisterLoad(ctx.src[0], vec3Mask);\n\n      std::array<uint32_t, 4> indices = { 0, 0, m_module.constf32(0.0f), m_module.constf32(0.0f) };\n      for (uint32_t i = 0; i < count; i++) {\n        auto reg = ctx.dst;\n        reg.id.num -= (count - 1) - i;\n        auto m = emitRegisterLoadTexcoord(reg, vec3Mask);\n\n        indices[i] = emitDot(m, n).id;\n      }\n\n      if (opcode == DxsoOpcode::TexM3x3Spec || opcode == DxsoOpcode::TexM3x3VSpec) {\n        uint32_t vec3Type = getVectorTypeId({ DxsoScalarType::Float32, 3 });\n        uint32_t normal = m_module.opCompositeConstruct(vec3Type, 3, indices.data());\n\n        uint32_t eyeRay;\n        // VSpec -> Create eye ray from .w of last 3 tex coords (m, m-1, m-2)\n        // Spec -> Get eye ray from src[1]\n        if (opcode == DxsoOpcode::TexM3x3VSpec) {\n          DxsoRegMask wMask(false, false, false, true);\n\n          std::array<uint32_t, 3> eyeRayIndices;\n          for (uint32_t i = 0; i < 3; i++) {\n            auto reg = ctx.dst;\n            reg.id.num -= (count - 1) - i;\n            eyeRayIndices[i] = emitRegisterLoadTexcoord(reg, wMask).id;\n          }\n\n          eyeRay = m_module.opCompositeConstruct(vec3Type, eyeRayIndices.size(), eyeRayIndices.data());\n        }\n        else\n          eyeRay = emitRegisterLoad(ctx.src[1], vec3Mask).id;\n\n        eyeRay = m_module.opNormalize(vec3Type, eyeRay);\n        normal = m_module.opNormalize(vec3Type, normal);\n        uint32_t reflection = m_module.opReflect(vec3Type, eyeRay, normal);\n        reflection = m_module.opFNegate(vec3Type, reflection);\n\n        for (uint32_t i = 0; i < 3; i++)\n          indices[i] = m_module.opCompositeExtract(m_module.defFloatType(32), reflection, 1, &i);\n      }\n\n      texcoordVar.type = { DxsoScalarType::Float32, 4 };\n      texcoordVar.id   = m_module.opCompositeConstruct(getVectorTypeId(texcoordVar.type), indices.size(), indices.data());\n      \n      samplerIdx = ctx.dst.id.num;\n    }\n    else if (opcode == DxsoOpcode::TexBem || opcode == DxsoOpcode::TexBemL) {\n      auto m = emitRegisterLoadTexcoord(ctx.dst, srcMask);\n      auto n = emitRegisterLoad(ctx.src[0], srcMask);\n\n      texcoordVar = m;\n      samplerIdx = ctx.dst.id.num;\n\n      // The projection (/.w) happens before this...\n      // Of course it does...\n      // TexBem/TexBemL only exist in PS<=1.3\n      texcoordVar.id  = DoProjection(texcoordVar, true);\n      auto values     = emitBem(ctx, texcoordVar, n);\n      for (uint32_t i = 0; i < 2; i++)\n        texcoordVar.id = m_module.opCompositeInsert(getVectorTypeId(texcoordVar.type), values[i], texcoordVar.id, 1, &i);\n    }\n    else if (opcode == DxsoOpcode::TexReg2Ar) {\n      texcoordVar = emitRegisterLoad(ctx.src[0], srcMask);\n      texcoordVar = emitRegisterSwizzle(texcoordVar, DxsoRegSwizzle(3, 0, 0, 0), srcMask);\n\n      samplerIdx = ctx.dst.id.num;\n    }\n    else if (opcode == DxsoOpcode::TexReg2Gb) {\n      texcoordVar = emitRegisterLoad(ctx.src[0], srcMask);\n      texcoordVar = emitRegisterSwizzle(texcoordVar, DxsoRegSwizzle(1, 2, 2, 2), srcMask);\n\n      samplerIdx = ctx.dst.id.num;\n    }\n    else if (opcode == DxsoOpcode::TexReg2Rgb) {\n      texcoordVar = emitRegisterLoad(ctx.src[0], srcMask);\n      texcoordVar = emitRegisterSwizzle(texcoordVar, DxsoRegSwizzle(0, 1, 2, 2), srcMask);\n\n      samplerIdx = ctx.dst.id.num;\n    }\n    else if (opcode == DxsoOpcode::TexDp3Tex) {\n      auto m = emitRegisterLoadTexcoord(ctx.dst,    vec3Mask);\n      auto n = emitRegisterLoad(ctx.src[0], vec3Mask);\n\n      auto dot = emitDot(m, n);\n\n      std::array<uint32_t, 4> indices = { dot.id, m_module.constf32(0.0f), m_module.constf32(0.0f), m_module.constf32(0.0f) };\n\n      texcoordVar.type = { DxsoScalarType::Float32, 4 };\n      texcoordVar.id   = m_module.opCompositeConstruct(getVectorTypeId(texcoordVar.type),\n        indices.size(), indices.data());\n\n      samplerIdx  = ctx.dst.id.num;\n    }\n    else {\n      if (m_programInfo.majorVersion() >= 2) { // SM 2.0+\n        texcoordVar = emitRegisterLoad(ctx.src[0], srcMask);\n        samplerIdx  = ctx.src[1].id.num;\n      } else if (\n        m_programInfo.majorVersion() == 1\n     && m_programInfo.minorVersion() == 4) { // SM 1.4\n        texcoordVar = emitRegisterLoad(ctx.src[0], srcMask);\n        samplerIdx  = ctx.dst.id.num;\n      }\n      else { // SM 1.0-1.3\n        texcoordVar = emitRegisterLoadTexcoord(ctx.dst, srcMask);\n        samplerIdx  = ctx.dst.id.num;\n      }\n    }\n\n    // SM < 1.x does not have dcl sampler type.\n    if (m_programInfo.majorVersion() < 2 && !m_samplers[samplerIdx].color[SamplerTypeTexture2D].imageVarId)\n      emitDclSampler(samplerIdx, DxsoTextureType::Texture2D);\n\n    DxsoSampler sampler = m_samplers.at(samplerIdx);\n\n    auto SampleImage = [this, opcode, dst, ctx, samplerIdx, DoProjection](DxsoRegisterValue texcoordVar, DxsoSamplerInfo& sampler, bool depth, DxsoSamplerType samplerType, uint32_t isNull) {\n      DxsoRegisterValue result;\n      result.type.ctype  = dst.type.ctype;\n      result.type.ccount = depth ? 1 : 4;\n\n      const uint32_t typeId = getVectorTypeId(result.type);\n\n      SpirvImageOperands imageOperands;\n      if (m_programInfo.type() == DxsoProgramTypes::VertexShader) {\n        imageOperands.sLod = m_module.constf32(0.0f);\n        imageOperands.flags |= spv::ImageOperandsLodMask;\n      }\n\n      if (opcode == DxsoOpcode::TexLdl) {\n        uint32_t w = 3;\n        imageOperands.sLod = m_module.opCompositeExtract(\n          m_module.defFloatType(32), texcoordVar.id, 1, &w);\n        imageOperands.flags |= spv::ImageOperandsLodMask;\n      }\n\n      if (opcode == DxsoOpcode::TexLdd) {\n        DxsoRegMask gradMask(true, true, sampler.dimensions == 3, false);\n        imageOperands.flags |= spv::ImageOperandsGradMask;\n        imageOperands.sGradX = emitRegisterLoad(ctx.src[2], gradMask).id;\n        imageOperands.sGradY = emitRegisterLoad(ctx.src[3], gradMask).id;\n      }\n\n      if (opcode == DxsoOpcode::Tex\n        && m_programInfo.majorVersion() >= 2) {\n        if (ctx.instruction.specificData.texld == DxsoTexLdMode::Project) {\n          texcoordVar.id = DoProjection(texcoordVar, false);\n        }\n        else if (ctx.instruction.specificData.texld == DxsoTexLdMode::Bias) {\n          uint32_t w = 3;\n          imageOperands.sLodBias = m_module.opCompositeExtract(\n            m_module.defFloatType(32), texcoordVar.id, 1, &w);\n          imageOperands.flags |= spv::ImageOperandsBiasMask;\n        }\n      }\n\n      // We already handled this for TexBem(L)\n      if (m_programInfo.majorVersion() < 2 && m_programInfo.minorVersion() < 4 && samplerType != SamplerTypeTextureCube && opcode != DxsoOpcode::TexBem && opcode != DxsoOpcode::TexBemL) {\n        texcoordVar.id = DoProjection(texcoordVar, true);\n      }\n\n      uint32_t bool_t = m_module.defBoolType();\n\n      uint32_t reference = 0;\n      if (depth) {\n        uint32_t uiType = m_module.defIntType(32, false);\n        uint32_t fType = m_module.defFloatType(32);\n        uint32_t component = sampler.dimensions;\n        reference = m_module.opCompositeExtract(\n          fType, texcoordVar.id, 1, &component);\n\n        // [D3D8] Scale Dref from [0..(2^N - 1)] for D24S8 and D16 if Dref scaling is enabled\n        uint32_t drefScaleShift = m_spec.get(m_module, m_specUbo, SpecDrefScaling);\n        uint32_t drefScale      = m_module.opShiftLeftLogical(uiType, m_module.constu32(1), drefScaleShift);\n        drefScale               = m_module.opConvertUtoF(fType, drefScale);\n        drefScale               = m_module.opFSub(fType, drefScale, m_module.constf32(1.0f));\n        drefScale               = m_module.opFDiv(fType, m_module.constf32(1.0f), drefScale);\n        reference               = m_module.opSelect(fType,\n          m_module.opINotEqual(bool_t, drefScaleShift, m_module.constu32(0)),\n          m_module.opFMul(fType, reference, drefScale),\n          reference\n        );\n\n        // Clamp Dref to [0..1] for D32F emulating UNORM textures \n        uint32_t clampDref = m_spec.get(m_module, m_specUbo, SpecSamplerDrefClamp, samplerIdx, 1);\n        clampDref = m_module.opINotEqual(bool_t, clampDref, m_module.constu32(0));\n        uint32_t clampedDref = m_module.opFClamp(fType, reference, m_module.constf32(0.0f), m_module.constf32(1.0f));\n        reference = m_module.opSelect(fType, clampDref, clampedDref, reference);\n      }\n\n      uint32_t fetch4 = 0;\n      if (m_programInfo.type() == DxsoProgramType::PixelShader && samplerType != SamplerTypeTexture3D) {\n        fetch4 = m_spec.get(m_module, m_specUbo, SpecSamplerFetch4, samplerIdx, 1);\n\n        fetch4 = m_module.opINotEqual(bool_t, fetch4, m_module.constu32(0));\n\n        uint32_t bvec4_t = m_module.defVectorType(bool_t, 4);\n        std::array<uint32_t, 4> indices = { fetch4, fetch4, fetch4, fetch4 };\n        fetch4 = m_module.opCompositeConstruct(bvec4_t, indices.size(), indices.data());\n      }\n\n      result.id = this->emitSample(\n        typeId,\n        sampler,\n        texcoordVar,\n        reference,\n        fetch4,\n        imageOperands);\n\n      // If we are sampling depth we've already specc'ed this!\n      // This path is always size 4 because it only hits on color.\n      if (isNull != 0) {\n        uint32_t bool_t = m_module.defBoolType();\n        uint32_t bvec4_t = m_module.defVectorType(bool_t, 4);\n        std::array<uint32_t, 4> indices = { isNull, isNull, isNull, isNull };\n        isNull = m_module.opCompositeConstruct(bvec4_t, indices.size(), indices.data());\n        result.id = m_module.opSelect(typeId, isNull, m_module.constvec4f32(0.0f, 0.0f, 0.0f, 1.0f), result.id);\n      }\n\n      // Apply operand swizzle to the operand value\n      if (m_programInfo.majorVersion() >= 2) // SM 2.0+\n        result = emitRegisterSwizzle(result, ctx.src[1].swizzle, ctx.dst.mask);\n      else\n        result = emitRegisterSwizzle(result, IdentitySwizzle, ctx.dst.mask);\n\n      if (opcode == DxsoOpcode::TexBemL) {\n        uint32_t float_t = m_module.defFloatType(32);\n\n        uint32_t index = m_module.constu32(D3D9SharedPSStages_Count * ctx.dst.id.num + D3D9SharedPSStages_BumpEnvLScale);\n        uint32_t lScale = m_module.opAccessChain(m_module.defPointerType(float_t, spv::StorageClassUniform),\n                                                 m_ps.sharedState, 1, &index);\n                 lScale = m_module.opLoad(float_t, lScale);\n\n                 index = m_module.constu32(D3D9SharedPSStages_Count * ctx.dst.id.num + D3D9SharedPSStages_BumpEnvLOffset);\n        uint32_t lOffset = m_module.opAccessChain(m_module.defPointerType(float_t, spv::StorageClassUniform),\n                                                  m_ps.sharedState, 1, &index);\n                 lOffset = m_module.opLoad(float_t, lOffset);\n\n        uint32_t zIndex = 2;\n        uint32_t scale = m_module.opCompositeExtract(float_t, result.id, 1, &zIndex);\n                 scale = m_module.opFMul(float_t, scale, lScale);\n                 scale = m_module.opFAdd(float_t, scale, lOffset);\n                 scale = m_module.opFClamp(float_t, scale, m_module.constf32(0.0f), m_module.constf32(1.0));\n\n        result.id = m_module.opVectorTimesScalar(getVectorTypeId(result.type), result.id, scale);\n      }\n\n      this->emitDstStore(dst, result, ctx.dst.mask, ctx.dst.saturate, emitPredicateLoad(ctx), ctx.dst.shift, ctx.dst.id);\n    };\n\n    auto SampleType = [&](DxsoSamplerType samplerType) {\n      uint32_t bitOffset = m_programInfo.type() == DxsoProgramTypes::VertexShader\n        ? samplerIdx + FirstVSSamplerSlot\n        : samplerIdx;\n\n      uint32_t isNull = m_spec.get(m_module, m_specUbo, SpecSamplerNull, bitOffset, 1);\n      isNull = m_module.opINotEqual(m_module.defBoolType(), isNull, m_module.constu32(0));\n\n      // Only do the check for depth comp. samplers\n      // if we aren't a 3D texture\n      if (samplerType != SamplerTypeTexture3D) {\n        uint32_t colorLabel  = m_module.allocateId();\n        uint32_t depthLabel  = m_module.allocateId();\n        uint32_t endLabel    = m_module.allocateId();\n\n        uint32_t isDepth = m_spec.get(m_module, m_specUbo, SpecSamplerDepthMode, bitOffset, 1);\n        isDepth = m_module.opINotEqual(m_module.defBoolType(), isDepth, m_module.constu32(0));\n\n        m_module.opSelectionMerge(endLabel, spv::SelectionControlMaskNone);\n        m_module.opBranchConditional(isDepth, depthLabel, colorLabel);\n\n        m_module.opLabel(colorLabel);\n        SampleImage(texcoordVar, sampler.color[samplerType], false, samplerType, isNull);\n        m_module.opBranch(endLabel);\n\n        m_module.opLabel(depthLabel);\n        // No spec constant as if we are unbound we always fall down the color path.\n        SampleImage(texcoordVar, sampler.depth[samplerType], true, samplerType, 0);\n        m_module.opBranch(endLabel);\n\n        m_module.opLabel(endLabel);\n      }\n      else\n        SampleImage(texcoordVar, sampler.color[samplerType], false, samplerType, isNull);\n    };\n\n    if (m_programInfo.majorVersion() >= 2 && !m_moduleInfo.options.forceSamplerTypeSpecConstants) {\n      DxsoSamplerType samplerType =\n        SamplerTypeFromTextureType(sampler.type);\n\n      SampleType(samplerType);\n    }\n    else {\n      std::array<SpirvSwitchCaseLabel, 3> typeCaseLabels = {{\n        { uint32_t(SamplerTypeTexture2D),           m_module.allocateId() },\n        { uint32_t(SamplerTypeTexture3D),           m_module.allocateId() },\n        { uint32_t(SamplerTypeTextureCube),         m_module.allocateId() },\n      }};\n\n      uint32_t switchEndLabel = m_module.allocateId();\n      uint32_t type = m_spec.get(m_module, m_specUbo, SpecSamplerType, samplerIdx * 2, 2);\n\n      m_module.opSelectionMerge(switchEndLabel, spv::SelectionControlMaskNone);\n      m_module.opSwitch(type,\n        typeCaseLabels[uint32_t(SamplerTypeTexture2D)].labelId,\n        typeCaseLabels.size(),\n        typeCaseLabels.data());\n\n      for (const auto& label : typeCaseLabels) {\n        m_module.opLabel(label.labelId);\n\n        SampleType(DxsoSamplerType(label.literal));\n\n        m_module.opBranch(switchEndLabel);\n      }\n\n      m_module.opLabel(switchEndLabel);\n    }\n  }\n\n  void DxsoCompiler::emitTextureKill(const DxsoInstructionContext& ctx) {\n    DxsoRegisterValue texReg;\n\n    if (m_programInfo.majorVersion() >= 2 ||\n       (m_programInfo.majorVersion() == 1\n     && m_programInfo.minorVersion() == 4)) // SM 2.0+ or 1.4\n      texReg = emitRegisterLoadRaw(ctx.dst, ctx.dst.hasRelative ? &ctx.dst.relative : nullptr);\n    else { // SM 1.0-1.3\n      DxsoRegister texcoord;\n      texcoord.id = { DxsoRegisterType::PixelTexcoord, ctx.dst.id.num };\n\n      texReg = emitRegisterLoadRaw(texcoord, nullptr);\n    }\n\n    std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };\n\n    // On SM1 it only works on the first \n    if (m_programInfo.majorVersion() < 2) {\n      texReg.type.ccount = 3;\n\n      texReg.id = m_module.opVectorShuffle(\n        getVectorTypeId(texReg.type),\n        texReg.id, texReg.id,\n        texReg.type.ccount, indices.data());\n    }\n    else {\n      // The writemask actually applies and works here...\n      // (FXC doesn't generate this but it fixes broken ENB shaders)\n      texReg = emitRegisterSwizzle(texReg, IdentitySwizzle, ctx.dst.mask);\n    }\n\n    const uint32_t boolVecTypeId =\n      getVectorTypeId({ DxsoScalarType::Bool, texReg.type.ccount });\n\n    uint32_t result = m_module.opFOrdLessThan(\n      boolVecTypeId, texReg.id,\n      m_module.constfReplicant(0.0f, texReg.type.ccount));\n\n    if (texReg.type.ccount != 1)\n      result = m_module.opAny(m_module.defBoolType(), result);\n\n    uint32_t labelIf = m_module.allocateId();\n    uint32_t labelEnd = m_module.allocateId();\n\n    m_module.opSelectionMerge(labelEnd, spv::SelectionControlMaskNone);\n    m_module.opBranchConditional(result, labelIf, labelEnd);\n\n    m_module.opLabel(labelIf);\n    m_module.opDemoteToHelperInvocation();\n    m_module.opBranch(labelEnd);\n\n    m_module.opLabel(labelEnd);\n  }\n\n  void DxsoCompiler::emitTextureDepth(const DxsoInstructionContext& ctx) {\n    const uint32_t fType = m_module.defFloatType(32);\n\n    DxsoRegMask srcMask(true, true, false, false);\n    uint32_t r5 = emitRegisterLoad(ctx.src[0], srcMask).id;\n    uint32_t x = 0;\n    uint32_t y = 1;\n\n    uint32_t xValue = m_module.opCompositeExtract(fType, r5, 1, &x);\n    uint32_t yValue = m_module.opCompositeExtract(fType, r5, 1, &y);\n\n    // The docs say if yValue is 0 the result is 1.0 but native drivers return\n    // 0 for xValue <= 0. So we don't have to do anything special since -INF and\n    // NAN get clamped to 0 at the end of the shader.\n    uint32_t result = m_module.opFDiv(fType, xValue, yValue);\n\n    DxsoBaseRegister depth;\n    depth.id = { DxsoRegisterType::DepthOut, 0 };\n\n    DxsoRegisterPointer depthPtr = emitGetOperandPtr(depth, nullptr);\n\n    m_module.opStore(depthPtr.id, result);\n  }\n\n\n  uint32_t DxsoCompiler::emitSample(\n          uint32_t                resultType,\n          DxsoSamplerInfo&        samplerInfo,\n          DxsoRegisterValue       coordinates,\n          uint32_t                reference,\n          uint32_t                fetch4,\n    const SpirvImageOperands&     operands) {\n    const bool depthCompare = reference != 0;\n    const bool explicitLod  =\n       (operands.flags & spv::ImageOperandsLodMask)\n    || (operands.flags & spv::ImageOperandsGradMask);\n\n    uint32_t image = m_module.opLoad(samplerInfo.imageTypeId, samplerInfo.imageVarId);\n    uint32_t sampler = LoadSampler(m_module, m_samplerArray, m_rsBlock, m_rsFirstSampler, samplerInfo.samplerIndex);\n\n    uint32_t sampledImage = m_module.opSampledImage(samplerInfo.sampledTypeId, image, sampler);\n\n    uint32_t val;\n\n\n    if (depthCompare) {\n      if (explicitLod)\n        val = m_module.opImageSampleDrefExplicitLod(resultType, sampledImage, coordinates.id, reference, operands);\n      else\n        val = m_module.opImageSampleDrefImplicitLod(resultType, sampledImage, coordinates.id, reference, operands);\n    }\n    else {\n      if (explicitLod)\n        val = m_module.opImageSampleExplicitLod(resultType, sampledImage, coordinates.id, operands);\n      else\n        val = m_module.opImageSampleImplicitLod(resultType, sampledImage, coordinates.id, operands);\n    }\n\n\n\n    if (fetch4 && !depthCompare) {\n      SpirvImageOperands fetch4Operands = operands;\n      fetch4Operands.flags &= ~spv::ImageOperandsLodMask;\n      fetch4Operands.flags &= ~spv::ImageOperandsGradMask;\n      fetch4Operands.flags &= ~spv::ImageOperandsBiasMask;\n\n      // Doesn't really work for cubes...\n      // D3D9 does support gather on 3D but we cannot :<\n      // Nothing probably relies on that though.\n      // If we come back to this ever, make sure to handle cube/3d differences.\n      if (samplerInfo.dimensions == 2) {\n        uint32_t image = m_module.opImage(samplerInfo.imageTypeId, sampledImage);\n\n        // Account for half texel offset...\n        // textureSize = 1.0f / float(2 * textureSize(sampler, 0))\n        DxsoRegisterValue textureSize;\n        textureSize.type = { DxsoScalarType::Sint32, samplerInfo.dimensions };\n        textureSize.id = m_module.opImageQuerySizeLod(getVectorTypeId(textureSize.type), image, m_module.consti32(0));\n        textureSize.id = m_module.opIMul(getVectorTypeId(textureSize.type), textureSize.id, m_module.constiReplicant(2, samplerInfo.dimensions));\n\n        textureSize.type = { DxsoScalarType::Float32, samplerInfo.dimensions };\n        textureSize.id = m_module.opConvertStoF(getVectorTypeId(textureSize.type), textureSize.id);\n        // HACK: Bias fetch4 half-texel offset to avoid a \"grid\" effect.\n        // Technically we should only do that for non-powers of two\n        // as only then does the imprecision need to be biased\n        // towards infinity -- but that's not really worth doing...\n        float numerator = 1.0f - 1.0f / 256.0f;\n        textureSize.id = m_module.opFDiv(getVectorTypeId(textureSize.type), m_module.constfReplicant(numerator, samplerInfo.dimensions), textureSize.id);\n\n        // coord => same dimensions as texture size (no cube here !)\n        const std::array<uint32_t, 4> naturalIndices = { 0, 1, 2, 3 };\n        coordinates.type.ccount = samplerInfo.dimensions;\n        coordinates.id = m_module.opVectorShuffle(getVectorTypeId(coordinates.type), coordinates.id, coordinates.id, coordinates.type.ccount, naturalIndices.data());\n        // coord += textureSize;\n        coordinates.id = m_module.opFAdd(getVectorTypeId(coordinates.type), coordinates.id, textureSize.id);\n      }\n\n      uint32_t fetch4Val = m_module.opImageGather(resultType, sampledImage, coordinates.id, m_module.consti32(0), fetch4Operands);\n      // B R G A swizzle... Funny D3D9 order.\n      const std::array<uint32_t, 4> indices = { 2, 0, 1, 3 };\n      fetch4Val = m_module.opVectorShuffle(resultType, fetch4Val, fetch4Val, indices.size(), indices.data());\n\n      val = m_module.opSelect(resultType, fetch4, fetch4Val, val);\n    }\n\n    return val;\n  }\n\n\n  void DxsoCompiler::emitInputSetup() {\n    uint32_t pointCoord = 0;\n    D3D9PointSizeInfoPS pointInfo;\n\n    if (m_programInfo.type() == DxsoProgramType::PixelShader) {\n      pointCoord = GetPointCoord(m_module);\n      pointInfo  = GetPointSizeInfoPS(m_spec, m_module, m_rsBlock, m_specUbo);\n    }\n\n    for (uint32_t i = 0; i < m_isgn.elemCount; i++) {\n      const auto& elem = m_isgn.elems[i];\n      const uint32_t slot = elem.slot;\n      \n      DxsoRegisterInfo info;\n      info.type.ctype   = DxsoScalarType::Float32;\n      info.type.ccount  = 4;\n      info.type.alength = 1;\n      info.sclass       = spv::StorageClassInput;\n\n      DxsoRegisterPointer inputPtr;\n      inputPtr.id          = emitNewVariable(info);\n      inputPtr.type.ctype  = DxsoScalarType::Float32;\n      inputPtr.type.ccount = info.type.ccount;\n\n      m_module.decorateLocation(inputPtr.id, slot);\n\n      if (m_programInfo.type() == DxsoProgramType::PixelShader\n       && m_moduleInfo.options.forceSampleRateShading) {\n        m_module.enableCapability(spv::CapabilitySampleRateShading);\n        m_module.decorate(inputPtr.id, spv::DecorationSample);\n      }\n\n      std::string name =\n        str::format(\"in_\", elem.semantic.usage, elem.semantic.usageIndex);\n      m_module.setDebugName(inputPtr.id, name.c_str());\n\n      if (elem.centroid)\n        m_module.decorate(inputPtr.id, spv::DecorationCentroid);\n\n      uint32_t typeId    = this->getVectorTypeId({ DxsoScalarType::Float32, 4 });\n      uint32_t ptrTypeId = m_module.defPointerType(typeId, spv::StorageClassPrivate);\n\n      uint32_t regNumVar = m_module.constu32(elem.regNumber);\n\n      DxsoRegisterPointer indexPtr;\n      indexPtr.id   = m_module.opAccessChain(ptrTypeId, m_vArray, 1, &regNumVar);\n      indexPtr.type = inputPtr.type;\n      indexPtr.type.ccount = 4;\n\n      DxsoRegisterValue indexVal = this->emitValueLoad(inputPtr);\n\n      DxsoRegisterValue workingReg;\n      workingReg.type = indexVal.type;\n\n      workingReg.id = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f);\n\n      DxsoRegMask mask = elem.mask;\n      if (mask.popCount() == 0)\n        mask = DxsoRegMask(true, true, true, true);\n\n      // Pixel shaders seem to load every element (or every element exported by VS)\n      // regardless of the mask in the DCL used.\n      //\n      // I imagine this is because at one point D3D9 ASM was very literal to GPUs,\n      // and the input's mask from the PS didn't correspond to anything and the\n      // physical output register was filled with the extra data from the VS not\n      // included in the mask on the PS, so it just worked(tm).\n      //\n      // Fixes a bug in Test Drive Unlimited's shadow code where it outputs o8.xyz, but the PS\n      // has a decl for v8.xy and it tries to do 2D Shadow sampling (needs 3 elements .xyz) in the PS.\n      // This likely occured because the compiler was not aware of shadow sampling when generating\n      // the writemask for the PS.\n      if (m_programInfo.type() == DxsoProgramType::PixelShader)\n        mask = DxsoRegMask(true, true, true, true);\n\n      std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };\n      uint32_t count = 0;\n      for (uint32_t i = 0; i < 4; i++) {\n        if (mask[i]) {\n          indices[i] = i + 4;\n          count++;\n        }\n      }\n\n      workingReg.id = m_module.opVectorShuffle(getVectorTypeId(workingReg.type),\n        workingReg.id, indexVal.id, 4, indices.data());\n\n      // We need to replace TEXCOORD inputs with gl_PointCoord\n      // if D3DRS_POINTSPRITEENABLE is set.\n      if (m_programInfo.type() == DxsoProgramType::PixelShader && elem.semantic.usage == DxsoUsage::Texcoord)\n        workingReg.id = m_module.opSelect(getVectorTypeId(workingReg.type), pointInfo.isSprite, pointCoord, workingReg.id);\n\n      if (m_programInfo.type() == DxsoProgramType::PixelShader && elem.semantic.usage == DxsoUsage::Color) {\n        if (elem.semantic.usageIndex < 2)\n          m_ps.flatShadingMask |= 1u << slot;\n      }\n\n      m_module.opStore(indexPtr.id, workingReg.id);\n    }\n  }\n\n\n  void DxsoCompiler::emitLinkerOutputSetup() {\n    std::array<bool, 2> outputtedColor = {};\n    std::array<bool, 8> outputtedTexcoords = {};\n    bool outputtedNormals = false;\n\n    for (uint32_t i = 0; i < m_osgn.elemCount; i++) {\n      const auto& elem = m_osgn.elems[i];\n      const uint32_t slot = elem.slot;\n\n      switch (elem.semantic.usage) {\n        case DxsoUsage::Color:\n          if (elem.semantic.usageIndex < outputtedColor.size()) {\n            outputtedColor[elem.semantic.usageIndex] = true;\n          }\n          break;\n\n        case DxsoUsage::Normal:\n          outputtedNormals = true;\n          break;\n\n\n        case DxsoUsage::Texcoord:\n          if (elem.semantic.usageIndex < outputtedTexcoords.size()) {\n            outputtedTexcoords[elem.semantic.usageIndex] = true;\n          }\n          break;\n        default: break; // Silence GCC warnings\n      }\n      \n      DxsoRegisterInfo info;\n      info.type.ctype   = DxsoScalarType::Float32;\n      info.type.ccount  = 4;\n      info.type.alength = 1;\n      info.sclass       = spv::StorageClassOutput;\n\n      spv::BuiltIn builtIn =\n        semanticToBuiltIn(false, elem.semantic);\n\n      DxsoRegisterPointer outputPtr;\n      outputPtr.type.ctype  = DxsoScalarType::Float32;\n      outputPtr.type.ccount = 4;\n\n      DxsoRegMask mask = elem.mask;\n\n      bool scalar = false;\n\n      if (builtIn == spv::BuiltInMax) {\n        outputPtr.id = emitNewVariableDefault(info,\n          m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f));\n        m_module.decorateLocation(outputPtr.id, slot);\n\n        std::string name =\n          str::format(\"out_\", elem.semantic.usage, elem.semantic.usageIndex);\n        m_module.setDebugName(outputPtr.id, name.c_str());\n      }\n      else {\n        const char* name = \"unknown_builtin\";\n        if (builtIn == spv::BuiltInPosition)\n          name = \"oPos\";\n        else if (builtIn == spv::BuiltInPointSize) {\n          outputPtr.type.ccount = 1;\n          info.type.ccount = 1;\n          name = \"oPSize\";\n          bool maskValues[4];\n          for (uint32_t i = 0; i < 4; i++)\n            maskValues[i] = i == elem.mask.firstSet();\n          mask = DxsoRegMask(maskValues[0], maskValues[1], maskValues[2], maskValues[3]);\n        }\n\n        outputPtr.id = emitNewVariableDefault(info,\n          m_module.constfReplicant(0.0f, info.type.ccount));\n\n        m_module.setDebugName(outputPtr.id, name);\n        m_module.decorateBuiltIn(outputPtr.id, builtIn);\n\n        if (builtIn == spv::BuiltInPosition)\n          m_vs.oPos = outputPtr;\n        else if (builtIn == spv::BuiltInPointSize) {\n          scalar = true;\n          m_vs.oPSize = outputPtr;\n        }\n      }\n\n      uint32_t typeId    = this->getVectorTypeId({ DxsoScalarType::Float32, 4 });\n      uint32_t ptrTypeId = m_module.defPointerType(typeId, spv::StorageClassPrivate);\n\n      uint32_t regNumVar = m_module.constu32(elem.regNumber);\n\n      DxsoRegisterPointer indexPtr;\n      indexPtr.id   = m_module.opAccessChain(ptrTypeId, m_oArray, 1, &regNumVar);\n      indexPtr.type = outputPtr.type;\n      indexPtr.type.ccount = 4;\n\n      DxsoRegisterValue indexVal = this->emitValueLoad(indexPtr);\n\n      DxsoRegisterValue workingReg;\n      workingReg.type.ctype  = indexVal.type.ctype;\n      workingReg.type.ccount = scalar ? 1 : 4;\n\n      workingReg.id = scalar\n        ? m_module.constf32(0.0f)\n        : m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f);\n\n      std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };\n\n      if (scalar) {\n        workingReg.id = m_module.opCompositeExtract(getVectorTypeId(workingReg.type),\n          indexVal.id, 1, indices.data());\n      } else {\n        if (mask.popCount() == 0)\n          mask = DxsoRegMask(true, true, true, true);\n\n        uint32_t count = 0;\n        for (uint32_t i = 0; i < 4; i++) {\n          if (mask[i])\n            indices[count++] = i + 4;\n        }\n\n\n        workingReg.id = m_module.opVectorShuffle(getVectorTypeId(workingReg.type),\n          workingReg.id, indexVal.id, 4, indices.data());\n      }\n\n      // Ie. 0 or 1 for diffuse and specular color\n      // and for Shader Model 1 or 2\n      // (because those have dedicated color registers\n      // where this rule applies)\n      if (elem.semantic.usage == DxsoUsage::Color &&\n          elem.semantic.usageIndex < 2 &&\n          m_programInfo.majorVersion() < 3)\n        workingReg = emitSaturate(workingReg);\n\n      m_module.opStore(outputPtr.id, workingReg.id);\n    }\n\n    auto OutputDefault = [&](DxsoSemantic semantic) {\n      DxsoRegisterInfo info;\n      info.type.ctype   = DxsoScalarType::Float32;\n      info.type.ccount  = semantic.usage != DxsoUsage::Fog ? 4 : 1;\n      info.type.alength = 1;\n      info.sclass       = spv::StorageClassOutput;\n\n      uint32_t slot = RegisterLinkerSlot(semantic);\n\n      uint32_t value;\n\n      if (semantic == DxsoSemantic{ DxsoUsage::Color, 0}) {\n        value = m_module.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f);\n      } else if (semantic.usage == DxsoUsage::Color) {\n        value = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 1.0f);\n      } else if (semantic == DxsoSemantic{ DxsoUsage::Fog, 0}) {\n        value = m_module.constf32(0.0);\n      } else {\n        value = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f);\n      }\n      // TODO: If it's used with a SM3 PS, we need to export 0,0,0,0 as the default for color1.\n      //       Implement that using a spec constant.\n\n      uint32_t outputPtr = emitNewVariableDefault(info, value);\n\n      m_module.decorateLocation(outputPtr, slot);\n\n      std::string name =\n        str::format(\"out_\", semantic.usage, semantic.usageIndex, \"_default\");\n\n      m_module.setDebugName(outputPtr, name.c_str());\n\n      m_outputMask |= 1u << slot;\n    };\n\n    if (m_programInfo.majorVersion() == 3) {\n      // Assume that shader model 3 vertex shaders hardly ever get mixed with fixed function pixel processing\n      // If they do, the backend handles it. Color 0 is the exception because that needs a different value.\n      if (!outputtedColor[0]) {\n        OutputDefault(DxsoSemantic{ DxsoUsage::Color, 0 });\n      }\n    } else {\n      // Emit the outputs that fixed function expects but the shader didn't emit itself.\n      // This avoids SPIR-V patching in the backend which would disable fast linked pipelines.\n      for (uint32_t i = 0; i < outputtedColor.size(); i++) {\n        if (outputtedColor[i]) continue;\n        OutputDefault(DxsoSemantic{ DxsoUsage::Color, i });\n      }\n      for (uint32_t i = 0; i < outputtedTexcoords.size(); i++) {\n        if (outputtedTexcoords[i]) continue;\n        OutputDefault(DxsoSemantic{ DxsoUsage::Texcoord, i });\n      }\n      if (!outputtedNormals) {\n        OutputDefault(DxsoSemantic{ DxsoUsage::Normal, 0 });\n      }\n      if (m_fog.id == 0) {\n        OutputDefault(DxsoSemantic{ DxsoUsage::Fog, 0 });\n      }\n    }\n\n    auto pointInfo = GetPointSizeInfoVS(m_spec, m_module, m_vs.oPos.id, 0, 0, m_rsBlock, m_specUbo, false);\n\n    if (m_vs.oPSize.id == 0) {\n      m_vs.oPSize = this->emitRegisterPtr(\n        \"oPSize\", DxsoScalarType::Float32, 1, 0,\n        spv::StorageClassOutput, spv::BuiltInPointSize);\n\n      uint32_t pointSize = m_module.opFClamp(m_module.defFloatType(32), pointInfo.defaultValue, pointInfo.min, pointInfo.max);\n\n      m_module.opStore(m_vs.oPSize.id, pointSize);\n    }\n    else {\n      uint32_t float_t = m_module.defFloatType(32);\n      uint32_t pointSize = m_module.opFClamp(m_module.defFloatType(32), m_module.opLoad(float_t, m_vs.oPSize.id), pointInfo.min, pointInfo.max);\n      m_module.opStore(m_vs.oPSize.id, pointSize);\n    }\n  }\n\n\n  void DxsoCompiler::emitVsClipping() {\n    uint32_t clipPlaneCountId = m_module.constu32(caps::MaxClipPlanes);\n    \n    uint32_t floatType = m_module.defFloatType(32);\n    uint32_t vec4Type  = m_module.defVectorType(floatType, 4);\n    uint32_t boolType  = m_module.defBoolType();\n    \n    // Declare uniform buffer containing clip planes\n    uint32_t clipPlaneArray  = m_module.defArrayTypeUnique(vec4Type, clipPlaneCountId);\n    uint32_t clipPlaneStruct = m_module.defStructTypeUnique(1, &clipPlaneArray);\n    uint32_t clipPlaneBlock  = m_module.newVar(\n      m_module.defPointerType(clipPlaneStruct, spv::StorageClassUniform),\n      spv::StorageClassUniform);\n    \n    m_module.decorateArrayStride  (clipPlaneArray, 16);\n    \n    m_module.setDebugName         (clipPlaneStruct, \"clip_info_t\");\n    m_module.setDebugMemberName   (clipPlaneStruct, 0, \"clip_planes\");\n    m_module.decorate             (clipPlaneStruct, spv::DecorationBlock);\n    m_module.memberDecorateOffset (clipPlaneStruct, 0, 0);\n    \n    uint32_t bindingId = computeResourceSlotId(\n      m_programInfo.type(), DxsoBindingType::ConstantBuffer,\n      DxsoConstantBuffers::VSClipPlanes);\n    \n    m_module.setDebugName         (clipPlaneBlock, \"clip_info\");\n    m_module.decorateDescriptorSet(clipPlaneBlock, 0);\n    m_module.decorateBinding      (clipPlaneBlock, bindingId);\n    \n    auto& binding = m_bindings.emplace_back();\n    binding.set             = 0u;\n    binding.binding         = bindingId;\n    binding.resourceIndex   = bindingId;\n    binding.descriptorType  = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n    binding.access          = VK_ACCESS_UNIFORM_READ_BIT;\n    binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n    // Declare output array for clip distances\n    uint32_t clipDistArray = m_module.newVar(\n      m_module.defPointerType(\n        m_module.defArrayType(floatType, clipPlaneCountId),\n        spv::StorageClassOutput),\n      spv::StorageClassOutput);\n\n    m_module.decorateBuiltIn(clipDistArray, spv::BuiltInClipDistance);\n    m_module.decorate(m_vs.oPos.id, spv::DecorationInvariant);\n    \n    const uint32_t positionPtr = m_vs.oPos.id;\n\n    // We generated a bad shader, let's not make it even worse.\n    if (positionPtr == 0) {\n      Logger::warn(\"Shader without Position output. Something is likely wrong here.\");\n      return;\n    }\n\n    // Compute clip distances\n    DxsoRegisterValue position;\n    position.type = { DxsoScalarType::Float32, 4 };\n    position.id = m_module.opLoad(vec4Type, positionPtr);\n\n    // Always consider clip planes enabled when doing GPL by forcing 6 for the quick value.\n    uint32_t clipPlaneCount = m_spec.get(m_module, m_specUbo, SpecClipPlaneCount, 0, 32, m_module.constu32(caps::MaxClipPlanes));\n    \n    for (uint32_t i = 0; i < caps::MaxClipPlanes; i++) {\n      std::array<uint32_t, 2> blockMembers = {{\n        m_module.constu32(0),\n        m_module.constu32(i),\n      }};\n\n      DxsoRegisterValue plane;\n      plane.type = { DxsoScalarType::Float32, 4 };\n      plane.id = m_module.opLoad(vec4Type, m_module.opAccessChain(\n        m_module.defPointerType(vec4Type, spv::StorageClassUniform),\n        clipPlaneBlock, blockMembers.size(), blockMembers.data()));\n\n      DxsoRegisterValue dist = emitDot(position, plane);\n\n      uint32_t clipPlaneEnabled = m_module.opULessThan(boolType, m_module.constu32(i), clipPlaneCount);\n\n      uint32_t value = m_module.opSelect(floatType, clipPlaneEnabled, dist.id, m_module.constf32(0.0f));\n\n      m_module.opStore(m_module.opAccessChain(\n        m_module.defPointerType(floatType, spv::StorageClassOutput),\n        clipDistArray, 1, &blockMembers[1]), value);\n    }\n  }\n\n\n  void DxsoCompiler::setupRenderStateInfo(uint32_t samplerCount) {\n    auto blockInfo = SetupRenderStateBlock(m_module, (1u << samplerCount) - 1u);\n\n    m_rsBlock = blockInfo.first;\n    m_rsFirstSampler = blockInfo.second;\n\n    uint32_t samplerDwordCount = (samplerCount + 1u) / 2u;\n\n    m_samplerPushData = DxvkPushDataBlock(m_programInfo.shaderStage(), GetPushSamplerOffset(0u),\n      samplerDwordCount * sizeof(uint32_t), sizeof(uint32_t), (1u << samplerDwordCount) - 1u);\n  }\n\n\n  void DxsoCompiler::emitFog() {\n    DxsoRegister color0;\n    color0.id = DxsoRegisterId{ DxsoRegisterType::ColorOut, 0 };\n    auto oColor0Ptr = this->emitGetOperandPtr(color0);\n\n    DxsoRegister vFog;\n    vFog.id = DxsoRegisterId{ DxsoRegisterType::RasterizerOut, RasterOutFog };\n    auto vFogPtr = this->emitGetOperandPtr(vFog);\n\n    DxsoRegister vPos;\n    vPos.id = DxsoRegisterId{ DxsoRegisterType::MiscType, DxsoMiscTypeIndices::MiscTypePosition };\n    auto vPosPtr = this->emitGetOperandPtr(vPos);\n\n    D3D9FogContext fogCtx;\n    fogCtx.IsPixel     = true;\n    fogCtx.RangeFog    = false;\n    fogCtx.RenderState = m_rsBlock;\n    fogCtx.vPos        = m_module.opLoad(getVectorTypeId(vPosPtr.type),    vPosPtr.id);\n    fogCtx.vFog        = m_module.opLoad(getVectorTypeId(vFogPtr.type),    vFogPtr.id);\n    fogCtx.oColor      = m_module.opLoad(getVectorTypeId(oColor0Ptr.type), oColor0Ptr.id);\n    fogCtx.IsFixedFunction = false;\n    fogCtx.IsPositionT = false;\n    fogCtx.HasSpecular = false;\n    fogCtx.Specular    = 0;\n    fogCtx.SpecUBO     = m_specUbo;\n\n    m_module.opStore(oColor0Ptr.id, DoFixedFunctionFog(m_spec, m_module, fogCtx));\n  }\n\n  \n  void DxsoCompiler::emitPsProcessing() {\n    uint32_t floatType = m_module.defFloatType(32);\n    uint32_t uintType  = m_module.defIntType(32, 0);\n    uint32_t uintPtr   = m_module.defPointerType(uintType, spv::StorageClassPushConstant);\n    \n    // Implement alpha test and fog\n    DxsoRegister color0;\n    color0.id = DxsoRegisterId{ DxsoRegisterType::ColorOut, 0 };\n    auto oC0 = this->emitGetOperandPtr(color0);\n    \n    if (oC0.id) {\n      if (m_programInfo.majorVersion() < 3)\n        emitFog();\n\n      uint32_t alphaRefMember = m_module.constu32(uint32_t(D3D9RenderStateItem::AlphaRef));\n      uint32_t alphaComponentId = 3;\n\n      D3D9AlphaTestContext alphaTestContext;\n      alphaTestContext.alphaFuncId = m_spec.get(m_module, m_specUbo, SpecAlphaCompareOp);\n      alphaTestContext.alphaPrecisionId = m_spec.get(m_module, m_specUbo, SpecAlphaPrecisionBits);\n      alphaTestContext.alphaRefId = m_module.opLoad(uintType,\n        m_module.opAccessChain(uintPtr, m_rsBlock, 1, &alphaRefMember));\n      alphaTestContext.alphaId = m_module.opCompositeExtract(floatType,\n        m_module.opLoad(m_module.defVectorType(floatType, 4), oC0.id),\n        1, &alphaComponentId);\n\n      DoFixedFunctionAlphaTest(m_module, alphaTestContext);\n    }\n  }\n\n  void DxsoCompiler::emitOutputDepthClamp() {\n    // HACK: Some drivers do not clamp FragDepth to [minDepth..maxDepth]\n    // before writing to the depth attachment, but we do not have acccess\n    // to those. Clamp to [0..1] instead.\n\n    if (m_ps.oDepth.id != 0) {\n      auto result = emitValueLoad(m_ps.oDepth);\n\n      result = emitSaturate(result);\n\n      m_module.opStore(\n        m_ps.oDepth.id,\n        result.id);\n    }\n}\n\n\n  void DxsoCompiler::emitVsFinalize() {\n    this->emitMainFunctionBegin();\n\n    this->emitInputSetup();\n    m_module.opFunctionCall(\n      m_module.defVoidType(),\n      m_vs.functionId, 0, nullptr);\n    this->emitLinkerOutputSetup();\n\n    this->emitVsClipping();\n\n    this->emitFunctionEnd();\n  }\n\n  void DxsoCompiler::emitPsFinalize() {\n    this->emitMainFunctionBegin();\n\n    this->emitInputSetup();\n\n    bool canUsePixelFog = m_programInfo.majorVersion() < 3;\n\n    if (canUsePixelFog) {\n      // Look up vPos so it gets initted.\n      DxsoRegister vPos;\n      vPos.id = DxsoRegisterId{ DxsoRegisterType::MiscType, DxsoMiscTypeIndices::MiscTypePosition };\n      this->emitGetOperandPtr(vPos);\n    }\n\n    if (m_ps.vPos.id != 0) {\n      DxsoRegisterPointer fragCoord = this->emitRegisterPtr(\n        \"ps_frag_coord\", DxsoScalarType::Float32, 4, 0,\n        spv::StorageClassInput, spv::BuiltInFragCoord);\n\n      DxsoRegisterValue val = this->emitValueLoad(fragCoord);\n      val.id = m_module.opFSub(\n        getVectorTypeId(val.type), val.id,\n        m_module.constvec4f32(0.5f, 0.5f, 0.0f, 0.0f));\n\n      m_module.opStore(m_ps.vPos.id, val.id);\n    }\n\n    if (m_ps.vFace.id != 0) {\n      DxsoRegisterPointer faceBool = this->emitRegisterPtr(\n        \"ps_is_front_face\", DxsoScalarType::Bool, 1, 0,\n        spv::StorageClassInput, spv::BuiltInFrontFacing);\n\n      DxsoRegisterValue frontFace = emitValueLoad(faceBool);\n      DxsoRegisterValue selectOp = emitRegisterExtend(frontFace, 4);\n\n      m_module.opStore(\n        m_ps.vFace.id,\n        m_module.opSelect(getVectorTypeId(m_ps.vFace.type), selectOp.id,\n          m_module.constvec4f32( 1.0f,  1.0f,  1.0f,  1.0f),\n          m_module.constvec4f32(-1.0f, -1.0f, -1.0f, -1.0f)));\n    }\n\n    m_module.opFunctionCall(\n      m_module.defVoidType(),\n      m_ps.functionId, 0, nullptr);\n\n    // r0 in PS1 is the colour output register. Move r0 -> cO0 here.\n    if (m_programInfo.majorVersion() == 1\n    && m_programInfo.type() == DxsoProgramTypes::PixelShader) {\n      DxsoRegister r0;\n      r0.id = { DxsoRegisterType::Temp, 0 };\n\n      DxsoRegister c0;\n      c0.id = { DxsoRegisterType::ColorOut, 0 };\n\n      DxsoRegisterValue val   = emitRegisterLoadRaw(r0, nullptr);\n      DxsoRegisterPointer out = emitGetOperandPtr(c0);\n      m_module.opStore(out.id, val.id);\n    }\n\n    // No need to setup output here as it's non-indexable\n    // everything has already gone to the right place!\n\n    this->emitPsProcessing();\n    this->emitOutputDepthClamp();\n    this->emitFunctionEnd();\n  }\n\n\n\n  uint32_t DxsoCompiler::getScalarTypeId(DxsoScalarType type) {\n    switch (type) {\n      case DxsoScalarType::Uint32:  return m_module.defIntType(32, 0);\n      case DxsoScalarType::Sint32:  return m_module.defIntType(32, 1);\n      case DxsoScalarType::Float32: return m_module.defFloatType(32);\n      case DxsoScalarType::Bool:    return m_module.defBoolType();\n    }\n\n    throw DxvkError(\"DxsoCompiler: Invalid scalar type\");\n  }\n\n\n  uint32_t DxsoCompiler::getVectorTypeId(const DxsoVectorType& type) {\n    uint32_t typeId = this->getScalarTypeId(type.ctype);\n\n    if (type.ccount > 1)\n      typeId = m_module.defVectorType(typeId, type.ccount);\n\n    return typeId;\n  }\n\n\n  uint32_t DxsoCompiler::getArrayTypeId(const DxsoArrayType& type) {\n    DxsoVectorType vtype;\n    vtype.ctype  = type.ctype;\n    vtype.ccount = type.ccount;\n\n    uint32_t typeId = this->getVectorTypeId(vtype);\n\n    if (type.alength > 1) {\n      typeId = m_module.defArrayType(typeId,\n        m_module.constu32(type.alength));\n    }\n\n    return typeId;\n  }\n\n\n  uint32_t DxsoCompiler::getPointerTypeId(const DxsoRegisterInfo& type) {\n    return m_module.defPointerType(\n      this->getArrayTypeId(type.type),\n      type.sclass);\n  }\n\n}\n"
  },
  {
    "path": "src/dxso/dxso_compiler.h",
    "content": "#pragma once\n\n#include \"dxso_decoder.h\"\n#include \"dxso_header.h\"\n#include \"dxso_modinfo.h\"\n#include \"dxso_isgn.h\"\n#include \"dxso_util.h\"\n\n#include \"../d3d9/d3d9_constant_layout.h\"\n#include \"../d3d9/d3d9_spec_constants.h\"\n#include \"../spirv/spirv_module.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Scalar value type\n   * \n   * Enumerates possible register component\n   * types. Scalar types are represented as\n   * a one-component vector type.\n   */\n  enum class DxsoScalarType : uint32_t {\n    Uint32    = 0,\n    Sint32    = 1,\n    Float32   = 2,\n    Bool      = 3,\n  };\n\n  /**\n   * \\brief Vector type\n   * \n   * Convenience struct that stores a scalar\n   * type and a component count. The compiler\n   * can use this to generate SPIR-V types.\n   */\n  struct DxsoVectorType {\n    DxsoScalarType    ctype;\n    uint32_t          ccount;\n  };\n  \n  \n  /**\n   * \\brief Array type\n   * \n   * Convenience struct that stores a scalar type, a\n   * component count and an array size. An array of\n   * length 0 will be evaluated to a vector type. The\n   * compiler can use this to generate SPIR-V types.\n   */\n  struct DxsoArrayType {\n    DxsoScalarType    ctype;\n    uint32_t          ccount;\n    uint32_t          alength;\n  };\n  \n  \n  /**\n   * \\brief Register info\n   * \n   * Stores the array type of a register and\n   * its storage class. The compiler can use\n   * this to generate SPIR-V pointer types.\n   */\n  struct DxsoRegisterInfo {\n    DxsoArrayType     type;\n    spv::StorageClass sclass;\n  };\n  \n  \n  /**\n   * \\brief Register value\n   * \n   * Stores a vector type and a SPIR-V ID that\n   * represents an intermediate value. This is\n   * used to track the type of such values.\n   */\n  struct DxsoRegisterValue {\n    DxsoVectorType    type;\n    uint32_t          id;\n  };\n\n  \n  /**\n   * \\brief Register pointer\n   * \n   * Stores a vector type and a SPIR-V ID that\n   * represents a pointer to such a vector. This\n   * can be used to load registers conveniently.\n   */\n  struct DxsoRegisterPointer {\n    DxsoVectorType    type;\n    uint32_t          id   = 0;\n  };\n\n  /**\n   * \\brief Sampler info\n   *\n   * Stores a vector type and a SPIR-V ID that\n   * represents a pointer to such a vector. This\n   * can be used to load registers conveniently.\n   */\n  struct DxsoSamplerInfo {\n    uint32_t dimensions = 0;\n\n    uint32_t imageVarId = 0;\n    uint32_t imageTypeId = 0;\n\n    uint32_t sampledTypeId = 0u;\n    uint32_t samplerIndex = 0u;\n  };\n\n  enum DxsoSamplerType : uint32_t {\n    SamplerTypeTexture2D = 0,\n    SamplerTypeTexture3D = 1,\n    SamplerTypeTextureCube,\n\n    SamplerTypeCount\n  };\n\n  inline auto SamplerTypeFromTextureType(DxsoTextureType type) {\n    switch (type) {\n      default:\n      case DxsoTextureType::Texture2D:   return SamplerTypeTexture2D;   break;\n      case DxsoTextureType::Texture3D:   return SamplerTypeTexture3D;   break;\n      case DxsoTextureType::TextureCube: return SamplerTypeTextureCube; break;\n    }\n  }\n\n  struct DxsoSampler {\n    DxsoSamplerInfo color[SamplerTypeCount];\n    DxsoSamplerInfo depth[SamplerTypeCount];\n\n    DxsoTextureType type;\n  };\n\n  struct DxsoAnalysisInfo;\n\n  /**\n   * \\brief Vertex shader-specific structure\n   */\n  struct DxsoCompilerVsPart {\n    uint32_t functionId       = 0;\n\n    ////////////////////\n    // Address register\n    DxsoRegisterPointer addr;\n\n    //////////////////////////////\n    // Rasterizer output registers\n    DxsoRegisterPointer oPos;\n    DxsoRegisterPointer oPSize;\n  };\n\n  /**\n   * \\brief Pixel shader-specific structure\n   */\n  struct DxsoCompilerPsPart {\n    uint32_t functionId         = 0;\n\n    //////////////\n    // Misc Types\n    DxsoRegisterPointer vPos;\n    DxsoRegisterPointer vFace;\n\n    ///////////////////\n    // Colour Outputs\n    std::array<DxsoRegisterPointer, 4> oColor;\n\n    ////////////////\n    // Depth output\n    DxsoRegisterPointer oDepth;\n\n    ////////////////\n    // Shared State\n    uint32_t sharedState        = 0;\n\n    uint32_t flatShadingMask    = 0;\n  };\n\n  struct DxsoCfgBlockIf {\n    uint32_t ztestId;\n    uint32_t labelIf;\n    uint32_t labelElse;\n    uint32_t labelEnd;\n    size_t   headerPtr;\n  };\n\n  struct DxsoCfgBlockLoop {\n    uint32_t labelHeader;\n    uint32_t labelBegin;\n    uint32_t labelContinue;\n    uint32_t labelBreak;\n    uint32_t iteratorPtr;\n\n    uint32_t strideVar;\n    uint32_t countBackup;\n  };\n\n  enum class DxsoCfgBlockType : uint32_t {\n    If, Loop\n  };\n\n  struct DxsoCfgBlock {\n    DxsoCfgBlockType type;\n    \n    union {\n      DxsoCfgBlockIf     b_if;\n      DxsoCfgBlockLoop   b_loop;\n    };\n  };\n\n  using DxsoSrcArray = std::array<DxsoRegisterValue, DxsoMaxOperandCount>;\n\n  class DxsoCompiler {\n\n  public:\n\n    DxsoCompiler(\n      const std::string&        fileName,\n      const DxsoModuleInfo&     moduleInfo,\n      const DxsoProgramInfo&    programInfo,\n      const DxsoAnalysisInfo&   analysis,\n      const D3D9ConstantLayout& layout);\n\n    /**\n     * \\brief Processes a single instruction\n     * \\param [in] ins The instruction\n     */\n    void processInstruction(\n      const DxsoInstructionContext& ctx,\n            uint32_t                currentCoissueIdx = 0);\n\n    /**\n     * \\brief Finalizes the shader\n     */\n    void finalize();\n\n    /**\n     * \\brief Compiles the shader\n     * \\returns The final shader objects\n     */\n    Rc<DxvkShader> compile();\n\n    const DxsoIsgn& isgn() { return m_isgn; }\n    const DxsoIsgn& osgn() { return m_osgn; }\n\n    const DxsoShaderMetaInfo& meta() { return m_meta; }\n    const DxsoDefinedConstants& constants() { return m_constants; }\n    uint32_t usedSamplers() const { return m_usedSamplers; }\n    uint32_t usedRTs() const { return m_usedRTs; }\n    int32_t  maxDefinedFloatConstant() const { return m_maxDefinedFloatConstant; }\n    int32_t  maxDefinedIntConstant() const { return m_maxDefinedIntConstant; }\n    int32_t  maxDefinedBoolConstant() const { return m_maxDefinedBoolConstant; }\n    uint32_t textureTypes() const { return m_textureTypes; }\n\n  private:\n\n    DxsoModuleInfo             m_moduleInfo;\n    DxsoProgramInfo            m_programInfo;\n    const DxsoAnalysisInfo*    m_analysis;\n    const D3D9ConstantLayout*  m_layout;\n\n    DxsoShaderMetaInfo         m_meta;\n    DxsoDefinedConstants       m_constants;\n    int32_t                    m_maxDefinedFloatConstant = -1;\n    int32_t                    m_maxDefinedIntConstant = -1;\n    int32_t                    m_maxDefinedBoolConstant = -1;\n\n    SpirvModule                m_module;\n\n    D3D9ShaderSpecConstantManager m_spec;\n\n    ///////////////////////////////////////////////////////\n    // Resource slot description for the shader. This will\n    // be used to map D3D9 bindings to DXVK bindings.\n    std::vector<DxvkBindingInfo>  m_bindings;\n\n    ////////////////////////////////////////////////\n    // Temporary r# vector registers with immediate\n    // indexing, and x# vector array registers.\n    std::vector<DxsoRegisterPointer> m_rRegs;\n\n    ////////////////////////////////////////////////\n    // Predicate registers\n    std::array<\n      DxsoRegisterPointer,\n      1> m_pRegs = { };\n\n    //////////////////////////////////////////////////////////////////\n    // Array of input values. Since v# and o# registers are indexable\n    // in DXSO, we need to copy them into an array first.\n    uint32_t m_vArray = 0;\n    uint32_t m_oArray = 0;\n\n    ////////////////////////////////\n    // Input and output signatures\n    DxsoIsgn m_isgn;\n    DxsoIsgn m_osgn;\n\n    ////////////////////////////////////\n    // Ptr to the constant buffer array\n    uint32_t m_cBuffer = 0;\n    uint32_t m_cFloatBuffer = 0;\n    uint32_t m_cIntBuffer = 0;\n    uint32_t m_cBoolBuffer = 0;\n\n    ////////////////////////////////////////\n    // Constant buffer deffed mappings\n    std::array<uint32_t, caps::MaxFloatConstantsSoftware> m_cFloat;\n    std::array<uint32_t, caps::MaxOtherConstantsSoftware> m_cInt;\n    std::array<uint32_t, caps::MaxOtherConstantsSoftware> m_cBool;\n\n    //////////////////////\n    // Loop counter\n    DxsoRegisterPointer m_loopCounter;\n\n    ///////////////////////////////////\n    // Working tex/coord registers (PS)\n    std::array<\n      DxsoRegisterPointer,\n      DxsoMaxTextureRegs> m_tRegs = { };\n\n    ///////////////////////////////////////////////\n    // Control flow information. Stores labels for\n    // currently active if-else blocks and loops.\n    std::vector<DxsoCfgBlock> m_controlFlowBlocks;\n\n    //////////////////////////////////////////////\n    // Function state tracking. Required in order\n    // to properly end functions in some cases.\n    bool m_insideFunction = false;\n\n    ////////////\n    // Samplers\n    std::array<DxsoSampler, 17> m_samplers = { };\n\n    ////////////////////////////////////////////\n    // What io regswe need to\n    // NOT generate semantics for\n    uint16_t m_explicitInputs  = 0;\n    uint16_t m_explicitOutputs = 0;\n\n    ///////////////////////////////////////////////////\n    // Entry point description - we'll need to declare\n    // the function ID and all input/output variables.\n    uint32_t m_entryPointId = 0;\n\n    ////////////////////////////////////////////\n    // Inter-stage shader interface slots. Also\n    // covers vertex input and fragment output.\n    uint32_t m_inputMask = 0u;\n    uint32_t m_outputMask = 0u;\n\n    ///////////////////////////////////\n    // Shader-specific data structures\n    DxsoCompilerVsPart m_vs;\n    DxsoCompilerPsPart m_ps;\n\n    DxsoRegisterPointer m_fog;\n\n    //////////////////////////////////////////\n    // Bit masks containing used samplers\n    // and render targets for hazard tracking\n    uint32_t m_usedSamplers;\n    uint32_t m_usedRTs;\n\n    uint32_t m_textureTypes;\n\n    uint32_t m_specUbo = 0;\n\n    uint32_t m_rsBlock = 0;\n    uint32_t m_rsFirstSampler = 0u;\n\n    uint32_t m_samplerArray = 0u;\n\n    uint32_t m_mainFuncLabel = 0;\n\n    DxvkPushDataBlock m_samplerPushData;\n\n    //////////////////////////////////////\n    // Common function definition methods\n    void emitInit();\n\n    //////////////////////\n    // Common shader dcls\n    template<DxsoConstantBufferType ConstantBufferType>\n    int emitDclSwvpConstantBuffer();\n\n    void emitDclConstantBuffer();\n\n    void emitDclInputArray();\n    void emitDclOutputArray();\n\n    /////////////////////////////////\n    // Shader initialization methods\n    void emitVsInit();\n\n    void emitPsSharedConstants();\n    void emitPsInit();\n\n    void emitFunctionBegin(\n            uint32_t                entryPoint,\n            uint32_t                returnType,\n            uint32_t                funcType);\n\n    void emitFunctionEnd();\n\n    uint32_t emitFunctionLabel();\n\n    void emitMainFunctionBegin();\n\n    ///////////////////////////////\n    // Variable definition methods\n    uint32_t emitNewVariable(\n      const DxsoRegisterInfo& info);\n\n    uint32_t emitNewVariableDefault(\n      const DxsoRegisterInfo& info,\n            uint32_t          value);\n    \n    uint32_t emitNewBuiltinVariable(\n      const DxsoRegisterInfo& info,\n            spv::BuiltIn      builtIn,\n      const char*             name,\n            uint32_t          value);\n\n    DxsoCfgBlock* cfgFindBlock(\n      const std::initializer_list<DxsoCfgBlockType>& types);\n\n    void emitDclInterface(\n            bool         input,\n            uint32_t     regNumber,\n            DxsoSemantic semantic,\n            DxsoRegMask  mask,\n            bool         centroid);\n\n    void emitDclSampler(\n            uint32_t        idx,\n            DxsoTextureType type);\n\n    bool defineInput(uint32_t idx) {\n      bool alreadyDefined = m_inputMask & 1u << idx;\n      m_inputMask |= 1u << idx;\n      return alreadyDefined;\n    }\n\n    bool defineOutput(uint32_t idx) {\n      bool alreadyDefined = m_outputMask & 1u << idx;\n      m_outputMask |= 1u << idx;\n      return alreadyDefined;\n    }\n\n    uint32_t emitArrayIndex(\n            uint32_t          idx,\n      const DxsoBaseRegister* relative);\n\n    DxsoRegisterPointer emitInputPtr(\n            bool              texture,\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative);\n\n    DxsoRegisterPointer emitRegisterPtr(\n      const char*             name,\n            DxsoScalarType    ctype,\n            uint32_t          ccount,\n            uint32_t          defaultVal,\n            spv::StorageClass storageClass = spv::StorageClassPrivate,\n            spv::BuiltIn      builtIn      = spv::BuiltInMax);\n\n    DxsoRegisterValue emitLoadConstant(\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative);\n\n    DxsoRegisterPointer emitOutputPtr(\n            bool              texcrdOut,\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative);\n\n    DxsoRegisterPointer emitGetOperandPtr(\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative);\n\n    DxsoRegisterPointer emitGetOperandPtr(\n      const DxsoRegister& reg) {\n      return this->emitGetOperandPtr(\n        reg,\n        reg.hasRelative ? &reg.relative : nullptr);\n    }\n\n    uint32_t emitBoolComparison(DxsoVectorType type, DxsoComparison cmp, uint32_t a, uint32_t b);\n\n    DxsoRegisterValue emitValueLoad(\n            DxsoRegisterPointer ptr);\n\n    void emitDstStore(\n            DxsoRegisterPointer     ptr,\n            DxsoRegisterValue       value,\n            DxsoRegMask             writeMask,\n            bool                    saturate,\n            DxsoRegisterValue       predicate,\n            int8_t                  shift,\n            DxsoRegisterId          regId) {\n      if (regId.type == DxsoRegisterType::RasterizerOut && regId.num == RasterOutFog)\n        saturate = true;\n\n      if (value.type.ctype == DxsoScalarType::Float32) {\n        const uint32_t typeId = getVectorTypeId(value.type);\n\n        // There doesn't seem to be a nice float bitshift method for float vectors\n        // in Spirv that I can see... Resorting to multiplication.\n        if (shift != 0) {\n          float shiftAmount = shift < 0\n            ? 1.0f / (1 << -shift)\n            : float(1 << shift);\n\n          uint32_t shiftConst = m_module.constf32(shiftAmount);\n\n          if (value.type.ccount == 1)\n            value.id = m_module.opFMul(typeId, value.id, shiftConst);\n          else\n            value.id = m_module.opVectorTimesScalar(typeId, value.id, shiftConst);\n        }\n\n        // Saturating only makes sense on floats\n        if (saturate) {\n          value.id = m_module.opNClamp(\n            typeId, value.id,\n            m_module.constfReplicant(0.0f, value.type.ccount),\n            m_module.constfReplicant(1.0f, value.type.ccount));\n        }\n      }\n\n      this->emitValueStore(ptr, value, writeMask, predicate);\n    }\n\n    DxsoRegisterValue applyPredicate(DxsoRegisterValue pred, DxsoRegisterValue dst, DxsoRegisterValue src);\n\n    void emitValueStore(\n            DxsoRegisterPointer     ptr,\n            DxsoRegisterValue       value,\n            DxsoRegMask             writeMask,\n            DxsoRegisterValue       predicate);\n\n    DxsoRegisterValue emitClampBoundReplicant(\n            DxsoRegisterValue       srcValue,\n            float                   lb,\n            float                   ub);\n\n    DxsoRegisterValue emitSaturate(\n            DxsoRegisterValue       srcValue);\n\n    DxsoRegisterValue emitMulOperand(\n            DxsoRegisterValue       operand,\n            DxsoRegisterValue       other);\n\n    DxsoRegisterValue emitMul(\n            DxsoRegisterValue       a,\n            DxsoRegisterValue       b);\n\n    DxsoRegisterValue emitMad(\n            DxsoRegisterValue       a,\n            DxsoRegisterValue       b,\n            DxsoRegisterValue       c);\n\n    DxsoRegisterValue emitDot(\n            DxsoRegisterValue       a,\n            DxsoRegisterValue       b);\n\n    DxsoRegisterValue emitMix(\n            DxsoRegisterValue       x,\n            DxsoRegisterValue       y,\n            DxsoRegisterValue       a);\n\n    DxsoRegisterValue emitCross(\n            DxsoRegisterValue       a,\n            DxsoRegisterValue       b);\n\n    DxsoRegisterValue emitRegisterInsert(\n            DxsoRegisterValue       dstValue,\n            DxsoRegisterValue       srcValue,\n            DxsoRegMask             srcMask);\n\n    DxsoRegisterValue emitRegisterLoadRaw(\n      const DxsoBaseRegister& reg,\n      const DxsoBaseRegister* relative);\n\n    DxsoRegisterValue emitRegisterExtend(\n            DxsoRegisterValue       value,\n            uint32_t size);\n\n    DxsoRegisterValue emitSrcOperandPreSwizzleModifiers(\n            DxsoRegisterValue       value,\n            DxsoRegModifier         modifier);\n\n    DxsoRegisterValue emitSrcOperandPostSwizzleModifiers(\n            DxsoRegisterValue       value,\n            DxsoRegModifier         modifier);\n\n    DxsoRegisterValue emitRegisterSwizzle(\n            DxsoRegisterValue       value,\n            DxsoRegSwizzle          swizzle,\n            DxsoRegMask             writeMask);\n\n    DxsoRegisterValue emitRegisterLoad(\n      const DxsoBaseRegister& reg,\n            DxsoRegMask       writeMask,\n      const DxsoBaseRegister* relative);\n\n    DxsoRegisterValue emitRegisterLoad(\n      const DxsoRegister& reg,\n            DxsoRegMask   writeMask) {\n      return this->emitRegisterLoad(\n        reg, writeMask,\n        reg.hasRelative ? &reg.relative : nullptr);\n    }\n\n    DxsoRegisterValue emitPredicateLoad(const DxsoInstructionContext& ctx) {\n      if (!ctx.instruction.predicated)\n        return DxsoRegisterValue();\n\n      return emitRegisterLoad(ctx.pred, IdentityWriteMask);\n    }\n\n    DxsoRegisterValue emitRegisterLoadTexcoord(\n      const DxsoRegister& reg,\n            DxsoRegMask   writeMask) {\n      DxsoRegister lookup = reg;\n      if (reg.id.type == DxsoRegisterType::Texture)\n        lookup.id.type = DxsoRegisterType::PixelTexcoord;\n\n      return this->emitRegisterLoad(lookup, writeMask);\n    }\n\n    std::array<uint32_t, 2> emitBem(\n      const DxsoInstructionContext& ctx,\n      const DxsoRegisterValue& src0,\n      const DxsoRegisterValue& src1\n    );\n\n    ///////////////////////////////\n    // Handle shader ops\n    void emitDcl(const DxsoInstructionContext& ctx);\n\n    void emitDef(const DxsoInstructionContext& ctx);\n    void emitDefF(const DxsoInstructionContext& ctx);\n    void emitDefI(const DxsoInstructionContext& ctx);\n    void emitDefB(const DxsoInstructionContext& ctx);\n\n    bool isScalarRegister(DxsoRegisterId id);\n\n    void emitMov(const DxsoInstructionContext& ctx);\n    void emitPredicateOp(const DxsoInstructionContext& ctx);\n    void emitVectorAlu(const DxsoInstructionContext& ctx);\n    void emitMatrixAlu(const DxsoInstructionContext& ctx);\n\n    void emitControlFlowGenericLoop(\n            bool     count,\n            uint32_t initialVar,\n            uint32_t strideVar,\n            uint32_t iterationCountVar);\n\n    void emitControlFlowGenericLoopEnd();\n\n    void emitControlFlowRep(const DxsoInstructionContext& ctx);\n    void emitControlFlowEndRep(const DxsoInstructionContext& ctx);\n\n    void emitControlFlowLoop(const DxsoInstructionContext& ctx);\n    void emitControlFlowEndLoop(const DxsoInstructionContext& ctx);\n\n    void emitControlFlowBreak(const DxsoInstructionContext& ctx);\n    void emitControlFlowBreakC(const DxsoInstructionContext& ctx);\n\n    void emitControlFlowIf(const DxsoInstructionContext& ctx);\n    void emitControlFlowElse(const DxsoInstructionContext& ctx);\n    void emitControlFlowEndIf(const DxsoInstructionContext& ctx);\n\n    void emitTexCoord(const DxsoInstructionContext& ctx);\n    void emitTextureSample(const DxsoInstructionContext& ctx);\n    void emitTextureKill(const DxsoInstructionContext& ctx);\n    void emitTextureDepth(const DxsoInstructionContext& ctx);\n\n    uint32_t emitSample(\n            uint32_t                resultType,\n            DxsoSamplerInfo&        samplerInfo,\n            DxsoRegisterValue       coordinates,\n            uint32_t                reference,\n            uint32_t                fetch4,\n      const SpirvImageOperands&     operands);\n\n    ///////////////////////////////\n    // Shader finalization methods\n    void emitInputSetup();\n\n    void emitVsClipping();\n    void setupRenderStateInfo(uint32_t samplerCount);\n    void emitFog();\n    void emitPsProcessing();\n    void emitOutputDepthClamp();\n\n    void emitLinkerOutputSetup();\n\n    void emitVsFinalize();\n    void emitPsFinalize();\n\n    ///////////////////////////\n    // Type definition methods\n    uint32_t getScalarTypeId(\n            DxsoScalarType type);\n    \n    uint32_t getVectorTypeId(\n      const DxsoVectorType& type);\n    \n    uint32_t getArrayTypeId(\n      const DxsoArrayType& type);\n    \n    uint32_t getPointerTypeId(\n      const DxsoRegisterInfo& type);\n\n\n    bool isSwvp() {\n      return m_layout->bitmaskCount != 1;\n    }\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxso/dxso_ctab.cpp",
    "content": "#include \"dxso_ctab.h\"\n\nnamespace dxvk {\n\n  DxsoCtab::DxsoCtab(DxsoReader& reader, uint32_t commentTokenCount) {\n    m_size          = reader.readu32();\n\n    if (m_size != sizeof(DxsoCtab))\n      throw DxvkError(\"DxsoCtab: ctab size invalid\");\n\n    m_creator       = reader.readu32();\n    m_version       = reader.readu32();\n    m_constants     = reader.readu32();\n    m_constantInfo  = reader.readu32();\n    m_flags         = reader.readu32();\n    m_target        = reader.readu32();\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_ctab.h",
    "content": "#pragma once\n\n#include \"dxso_common.h\"\n\n#include \"dxso_reader.h\"\n\nnamespace dxvk {\n\n  /**\n    * \\brief DXSO CTAB\n    *\n    * Stores meta information about the shader\n    */\n  class DxsoCtab : public RcObject {\n\n  public:\n\n    DxsoCtab(DxsoReader& reader, uint32_t commentTokenCount);\n\n  private:\n\n    uint32_t m_size;\n    uint32_t m_creator;\n    uint32_t m_version;\n    uint32_t m_constants;\n    uint32_t m_constantInfo;\n    uint32_t m_flags;\n    uint32_t m_target;\n\n  };\n\n}"
  },
  {
    "path": "src/dxso/dxso_decoder.cpp",
    "content": "#include \"dxso_decoder.h\"\n\n#include \"dxso_tables.h\"\n\nnamespace dxvk {\n\n  bool DxsoSemantic::operator== (const DxsoSemantic& b) const {\n    return usage == b.usage && usageIndex == b.usageIndex;\n  }\n\n  bool DxsoSemantic::operator!= (const DxsoSemantic& b) const {\n    return usage != b.usage || usageIndex != b.usageIndex;\n  }\n  \n  uint32_t DxsoDecodeContext::decodeInstructionLength(uint32_t token) {\n    auto opcode = m_ctx.instruction.opcode;\n\n    uint32_t length  = 0;\n    const auto& info = this->getProgramInfo();\n\n    // Comment ops have their own system for getting length.\n    if (opcode == DxsoOpcode::Comment)\n      return (token & 0x7fff0000) >> 16;\n\n    if (opcode == DxsoOpcode::End)\n      return 0;\n\n    // SM2.0 and above has the length of the op in instruction count baked into it.\n    // SM1.4 and below have fixed lengths and run off expectation.\n    // Phase does not respect the following rules. :shrug:\n    if (opcode != DxsoOpcode::Phase) {\n      if (info.majorVersion() >= 2)\n        length = (token & 0x0f000000) >> 24;\n      else\n        length = DxsoGetDefaultOpcodeLength(opcode);\n    }\n\n    // We've already logged this...\n    if (length == InvalidOpcodeLength)\n      return 0;\n\n    // SM 1.4 has an extra param on Tex and TexCoord\n    // As stated before, it also doesn't have the length of the op baked into the opcode\n    if (info.majorVersion() == 1\n     && info.minorVersion() == 4) {\n      switch (opcode) {\n        case DxsoOpcode::TexCoord:\n        case DxsoOpcode::Tex:\n          length += 1;\n          break;\n        default:\n          break;\n      }\n    }\n\n    return length;\n  }\n\n  bool DxsoDecodeContext::relativeAddressingUsesToken(\n          DxsoInstructionArgumentType type) {\n    auto& info = this->getProgramInfo();\n\n    return (info.majorVersion() >= 2 && type == DxsoInstructionArgumentType::Source)\n        || (info.majorVersion() >= 3 && type == DxsoInstructionArgumentType::Destination);\n  }\n\n  void DxsoDecodeContext::decodeDeclaration(DxsoCodeIter& iter) {\n    uint32_t dclToken = iter.read();\n\n    m_ctx.dcl.textureType          = static_cast<DxsoTextureType>((dclToken & 0x78000000) >> 27);\n    m_ctx.dcl.semantic.usage       = static_cast<DxsoUsage>(dclToken & 0x0000000f);\n    m_ctx.dcl.semantic.usageIndex  = (dclToken & 0x000f0000) >> 16;\n  }\n\n  void DxsoDecodeContext::decodeDefinition(DxsoOpcode opcode, DxsoCodeIter& iter) {\n    const uint32_t instructionLength = std::min(m_ctx.instruction.tokenLength - 1, 4u);\n\n    for (uint32_t i = 0; i < instructionLength; i++)\n      m_ctx.def.uint32[i] = iter.read();\n  }\n\n  void DxsoDecodeContext::decodeBaseRegister(\n            DxsoBaseRegister& reg,\n            uint32_t          token) {\n    reg.id.type = static_cast<DxsoRegisterType>(\n        ((token & 0x00001800) >> 8)\n      | ((token & 0x70000000) >> 28));\n\n    reg.id.num = token & 0x000007ff;\n  }\n\n  void DxsoDecodeContext::decodeGenericRegister(\n            DxsoRegister& reg,\n            uint32_t      token) {\n    this->decodeBaseRegister(reg, token);\n\n    reg.hasRelative = (token & (1 << 13)) == 8192;\n    reg.relative.id = DxsoRegisterId {\n      DxsoRegisterType::Addr, 0 };\n    reg.relative.swizzle = IdentitySwizzle;\n\n    reg.centroid         = token & (4 << 20);\n    reg.partialPrecision = token & (2 << 20);\n  }\n\n   void DxsoDecodeContext::decodeRelativeRegister(\n            DxsoBaseRegister& reg,\n            uint32_t          token) {\n    this->decodeBaseRegister(reg, token);\n\n    reg.swizzle = DxsoRegSwizzle(\n      uint8_t((token & 0x00ff0000) >> 16));\n  }\n\n  bool DxsoDecodeContext::decodeDestinationRegister(DxsoCodeIter& iter) {\n    uint32_t token = iter.read();\n\n    this->decodeGenericRegister(m_ctx.dst, token);\n\n    m_ctx.dst.mask = DxsoRegMask(\n      uint8_t((token & 0x000f0000) >> 16));\n\n    m_ctx.dst.saturate = (token & (1 << 20)) != 0;\n\n    m_ctx.dst.shift    = (token & 0x0f000000) >> 24;\n    m_ctx.dst.shift    = (m_ctx.dst.shift & 0x7) - (m_ctx.dst.shift & 0x8);\n\n    const bool extraToken =\n      relativeAddressingUsesToken(DxsoInstructionArgumentType::Destination);\n\n    if (m_ctx.dst.hasRelative && extraToken) {\n      this->decodeRelativeRegister(m_ctx.dst.relative, iter.read());\n      return true;\n    }\n\n    return false;\n  }\n\n  bool DxsoDecodeContext::decodeSourceRegister(uint32_t i, DxsoCodeIter& iter) {\n    if (i >= m_ctx.src.size())\n      throw DxvkError(\"DxsoDecodeContext::decodeSourceRegister: source register out of range.\");\n\n    uint32_t token = iter.read();\n\n    this->decodeGenericRegister(m_ctx.src[i], token);\n\n    m_ctx.src[i].swizzle = DxsoRegSwizzle(\n      uint8_t((token & 0x00ff0000) >> 16));\n\n    m_ctx.src[i].modifier = static_cast<DxsoRegModifier>(\n      (token & 0x0f000000) >> 24);\n\n    const bool extraToken =\n      relativeAddressingUsesToken(DxsoInstructionArgumentType::Source);\n\n    if (m_ctx.src[i].hasRelative && extraToken) {\n      this->decodeRelativeRegister(m_ctx.src[i].relative, iter.read());\n      return true;\n    }\n\n    return false;\n  }\n\n\n  void DxsoDecodeContext::decodePredicateRegister(DxsoCodeIter& iter) {\n    uint32_t token = iter.read();\n\n    this->decodeGenericRegister(m_ctx.pred, token);\n\n    m_ctx.pred.swizzle = DxsoRegSwizzle(\n      uint8_t((token & 0x00ff0000) >> 16));\n\n    m_ctx.pred.modifier = static_cast<DxsoRegModifier>(\n      (token & 0x0f000000) >> 24);\n  }\n\n\n  bool DxsoDecodeContext::decodeInstruction(DxsoCodeIter& iter) {\n    uint32_t token = iter.read();\n\n    m_ctx.instructionIdx++;\n\n    m_ctx.instruction.opcode = static_cast<DxsoOpcode>(\n      token & 0x0000ffff);\n\n    m_ctx.instruction.predicated = token & (1 << 28);\n\n    m_ctx.instruction.coissue    = token & 0x40000000;\n\n    m_ctx.instruction.specificData.uint32 =\n      (token & 0x00ff0000) >> 16;\n\n    m_ctx.instruction.tokenLength =\n      this->decodeInstructionLength(token);\n\n    uint32_t tokenLength =\n      m_ctx.instruction.tokenLength;\n\n    switch (m_ctx.instruction.opcode) {\n      case DxsoOpcode::If:\n      case DxsoOpcode::Ifc:\n      case DxsoOpcode::Rep:\n      case DxsoOpcode::Loop:\n      case DxsoOpcode::BreakC:\n      case DxsoOpcode::BreakP: {\n        uint32_t sourceIdx = 0;\n        for (uint32_t i = 0; i < tokenLength; i++) {\n          if (this->decodeSourceRegister(sourceIdx, iter))\n            i++;\n\n          sourceIdx++;\n        }\n        return true;\n      }\n\n      case DxsoOpcode::Dcl:\n        this->decodeDeclaration(iter);\n        this->decodeDestinationRegister(iter);\n        return true;\n\n      case DxsoOpcode::Def:\n      case DxsoOpcode::DefI:\n      case DxsoOpcode::DefB:\n        this->decodeDestinationRegister(iter);\n        this->decodeDefinition(\n          m_ctx.instruction.opcode, iter);\n        return true;\n\n      case DxsoOpcode::Comment:\n        iter = iter.skip(tokenLength);\n        return true;\n\n      default: {\n        uint32_t sourceIdx = 0;\n        for (uint32_t i = 0; i < tokenLength; i++) {\n          if (i == 0) {\n            if (this->decodeDestinationRegister(iter))\n              i++;\n          }\n          else if (i == 1 && m_ctx.instruction.predicated) {\n            // Relative addressing makes no sense\n            // for predicate registers.\n            this->decodePredicateRegister(iter);\n          }\n          else {\n            if (this->decodeSourceRegister(sourceIdx, iter))\n              i++;\n\n            sourceIdx++;\n          }\n        }\n        return true;\n      }\n\n      case DxsoOpcode::End:\n        return false;\n    }\n  }\n\n  std::ostream& operator << (std::ostream& os, DxsoUsage usage) {\n    switch (usage) {\n      case DxsoUsage::Position:     os << \"Position\"; break;\n      case DxsoUsage::BlendWeight:  os << \"BlendWeight\"; break;\n      case DxsoUsage::BlendIndices: os << \"BlendIndices\"; break;\n      case DxsoUsage::Normal:       os << \"Normal\"; break;\n      case DxsoUsage::PointSize:    os << \"PointSize\"; break;\n      case DxsoUsage::Texcoord:     os << \"Texcoord\"; break;\n      case DxsoUsage::Tangent:      os << \"Tangent\"; break;\n      case DxsoUsage::Binormal:     os << \"Binormal\"; break;\n      case DxsoUsage::TessFactor:   os << \"TessFactor\"; break;\n      case DxsoUsage::PositionT:    os << \"PositionT\"; break;\n      case DxsoUsage::Color:        os << \"Color\"; break;\n      case DxsoUsage::Fog:          os << \"Fog\"; break;\n      case DxsoUsage::Depth:        os << \"Depth\"; break;\n      case DxsoUsage::Sample:       os << \"Sample\"; break;\n      default:\n        os << \"Invalid Format (\" << static_cast<uint32_t>(usage) << \")\"; break;\n    }\n\n    return os;\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_decoder.h",
    "content": "#pragma once\n\n#include \"dxso_common.h\"\n#include \"dxso_enums.h\"\n#include \"dxso_code.h\"\n\nnamespace dxvk {\n\n  constexpr size_t DxsoMaxTempRegs      = 32;\n  constexpr size_t DxsoMaxTextureRegs   = 10;\n  constexpr size_t DxsoMaxInterfaceRegs = 16;\n  constexpr size_t DxsoMaxOperandCount  = 8;\n\n  constexpr uint32_t DxsoRegModifierShift = 24;\n\n  class DxsoDecodeContext;\n\n  /**\n   * \\brief Source operand modifiers\n   * \n   * These are applied after loading\n   * an operand register.\n   */\n  enum class DxsoRegModifier : uint32_t {\n    None    = 0,  // r\n    Neg     = 1,  // -r\n    Bias    = 2,  // r - 0.5\n    BiasNeg = 3,  // -(r - 0.5)\n    Sign    = 4,  // fma(r, 2.0f, -1.0f)\n    SignNeg = 5,  // -fma(r, 2.0f, -1.0f)\n    Comp    = 6,  // 1 - r\n    X2      = 7,  // r * 2\n    X2Neg   = 8,  // -r * 2\n    Dz      = 9,  // r / r.z\n    Dw      = 10, // r / r.w\n    Abs     = 11, // abs(r)\n    AbsNeg  = 12, // -abs(r)\n    Not     = 13, // !r\n  };\n\n  enum class DxsoInstructionArgumentType : uint16_t {\n    Source,\n    Destination\n  };\n\n  enum class DxsoComparison : uint32_t {\n                             // < = >\n    Never        = 0,        // 0 0 0\n    GreaterThan  = 1,        // 0 0 1\n    Equal        = 2,        // 0 1 0\n    GreaterEqual = 3,        // 0 1 1\n    LessThan     = 4,        // 1 0 0\n    NotEqual     = 5,        // 1 0 1\n    LessEqual    = 6,        // 1 1 0\n    Always       = 7         // 1 1 1\n  };\n\n  enum class DxsoTexLdMode : uint32_t {\n    Regular      = 0,\n    Project      = 1,\n    Bias         = 2\n  };\n\n  union DxsoOpcodeSpecificData {\n    DxsoComparison comparison;\n    DxsoTexLdMode  texld;\n\n    uint32_t       uint32;\n  };\n\n  struct DxsoShaderInstruction {\n    DxsoOpcode             opcode;\n    bool                   predicated;\n    bool                   coissue;\n    DxsoOpcodeSpecificData specificData;\n\n    uint32_t               tokenLength;\n  };\n\n  struct DxsoRegisterId {\n    DxsoRegisterType type;\n    uint32_t         num;\n\n    bool operator == (const DxsoRegisterId& other) const { return type == other.type && num == other.num; }\n    bool operator != (const DxsoRegisterId& other) const { return type != other.type || num != other.num; }\n  };\n\n  class DxsoRegMask {\n\n  public:\n\n    DxsoRegMask(uint8_t mask)\n      : m_mask(mask) { }\n\n    DxsoRegMask(bool x, bool y, bool z, bool w)\n    : m_mask((x ? 0x1 : 0) | (y ? 0x2 : 0)\n           | (z ? 0x4 : 0) | (w ? 0x8 : 0)) { }\n\n    bool operator [] (uint32_t id) const {\n      return ((m_mask & (1u << id)) != 0);\n    }\n\n    uint32_t popCount() const {\n      const uint8_t n[16] = { 0, 1, 1, 2, 1, 2, 2, 3,\n                              1, 2, 2, 3, 2, 3, 3, 4 };\n      return n[m_mask & 0xF];\n    }\n    \n    uint32_t firstSet() const {\n      const uint8_t n[16] = { 4, 0, 1, 0, 2, 0, 1, 0,\n                              3, 0, 1, 0, 2, 0, 1, 0 };\n      return n[m_mask & 0xF];\n    }\n    \n    uint32_t minComponents() const {\n      const uint8_t n[16] = { 0, 1, 2, 2, 3, 3, 3, 3,\n                              4, 4, 4, 4, 4, 4, 4, 4 };\n      return n[m_mask & 0xF];\n    }\n\n    bool operator == (const DxsoRegMask& other) const { return m_mask == other.m_mask; }\n    bool operator != (const DxsoRegMask& other) const { return m_mask != other.m_mask; }\n\n  private:\n\n    uint8_t m_mask;\n\n  };\n\n  const DxsoRegMask IdentityWriteMask = DxsoRegMask(true, true, true, true);\n\n  class DxsoRegSwizzle {\n\n  public:\n\n    DxsoRegSwizzle(uint8_t mask)\n      : m_mask(mask) { }\n\n    DxsoRegSwizzle(uint32_t x, uint32_t y, uint32_t z, uint32_t w)\n      : m_mask((x << 0) | (y << 2) | (z << 4) | (w << 6)) {}\n\n    uint32_t operator [] (uint32_t id) const {\n      return (m_mask >> (id + id)) & 0x3;\n    }\n\n    bool operator == (const DxsoRegSwizzle& other) const { return m_mask == other.m_mask; }\n    bool operator != (const DxsoRegSwizzle& other) const { return m_mask != other.m_mask; }\n\n  private:\n\n    uint8_t m_mask;\n\n  };\n\n  const DxsoRegSwizzle IdentitySwizzle{ 0, 1, 2, 3 };\n\n  struct DxsoBaseRegister {\n    DxsoRegisterId  id               = { DxsoRegisterType::Temp, 0 };\n    bool            centroid         = false;\n    bool            partialPrecision = false;\n    bool            saturate         = false;\n    DxsoRegModifier modifier         = DxsoRegModifier::None;\n    DxsoRegMask     mask             = IdentityWriteMask;\n    DxsoRegSwizzle  swizzle          = IdentitySwizzle;\n    int8_t          shift            = 0;\n  };\n\n  struct DxsoRegister : public DxsoBaseRegister {\n    bool hasRelative = false;\n    DxsoBaseRegister relative;\n  };\n\n  struct DxsoSemantic {\n    DxsoUsage       usage;\n    uint32_t        usageIndex;\n\n    bool operator== (const DxsoSemantic& b) const;\n    bool operator!= (const DxsoSemantic& b) const;\n  };\n\n  struct DxsoDeclaration {\n    DxsoSemantic    semantic;\n\n    DxsoTextureType textureType;\n  };\n\n  union DxsoDefinition {\n    float    float32[4];\n    int32_t  int32[4];\n\n    // Not a type we actually use in compiler, but used for decoding.\n    uint32_t uint32[4];\n  };\n\n  struct DxsoInstructionContext {\n    uint32_t                    instructionIdx;\n\n    DxsoShaderInstruction       instruction;\n\n    DxsoRegister                pred;\n\n    DxsoRegister                dst;\n    std::array<\n      DxsoRegister,\n      DxsoMaxOperandCount>      src;\n\n    DxsoDefinition              def;\n\n    DxsoDeclaration             dcl;\n  };\n\n  class DxsoDecodeContext {\n\n  public:\n\n    DxsoDecodeContext(const DxsoProgramInfo& programInfo)\n      : m_programInfo( programInfo ) {\n      m_ctx.instructionIdx = 0;\n    }\n\n    /**\n     * \\brief Retrieves current instruction context\n     *\n     * This is only valid after a call to \\ref decode.\n     * \\returns Reference to last decoded instruction & its context\n     */\n    const DxsoInstructionContext& getInstructionContext() const {\n      return m_ctx;\n    }\n\n    const DxsoProgramInfo& getProgramInfo() const {\n      return m_programInfo;\n    }\n\n    /**\n     * \\brief Decodes an instruction\n     *\n     * This also advances the given code slice by the\n     * number of dwords consumed by the instruction.\n     * \\param [in] code Code slice\n     */\n    bool decodeInstruction(DxsoCodeIter& iter);\n\n  private:\n\n    uint32_t decodeInstructionLength(uint32_t token);\n\n    void decodeBaseRegister(\n            DxsoBaseRegister& reg,\n            uint32_t          token);\n    void decodeGenericRegister(\n            DxsoRegister& reg,\n            uint32_t      token);\n    void decodeRelativeRegister(\n            DxsoBaseRegister& reg,\n            uint32_t          token);\n\n    // Returns whether an extra token was read.\n    bool decodeDestinationRegister(DxsoCodeIter& iter);\n    bool decodeSourceRegister(uint32_t i, DxsoCodeIter& iter);\n    void decodePredicateRegister(DxsoCodeIter& iter);\n\n    void decodeDeclaration(DxsoCodeIter& iter);\n    void decodeDefinition(DxsoOpcode opcode, DxsoCodeIter& iter);\n\n    bool relativeAddressingUsesToken(DxsoInstructionArgumentType type);\n\n    const DxsoProgramInfo&      m_programInfo;\n\n    DxsoInstructionContext      m_ctx;\n\n  };\n\n  std::ostream& operator << (std::ostream& os, DxsoUsage usage);\n\n}"
  },
  {
    "path": "src/dxso/dxso_enums.cpp",
    "content": "#include \"dxso_enums.h\"\n\nnamespace dxvk {\n\n  std::ostream& operator << (std::ostream& os, DxsoOpcode opcode) {\n    switch (opcode) {\n      case DxsoOpcode::Nop: os << \"Nop\"; break;\n      case DxsoOpcode::Mov: os << \"Mov\"; break;\n      case DxsoOpcode::Add: os << \"Add\"; break;\n      case DxsoOpcode::Sub: os << \"Sub\"; break;\n      case DxsoOpcode::Mad: os << \"Mad\"; break;\n      case DxsoOpcode::Mul: os << \"Mul\"; break;\n      case DxsoOpcode::Rcp: os << \"Rcp\"; break;\n      case DxsoOpcode::Rsq: os << \"Rsq\"; break;\n      case DxsoOpcode::Dp3: os << \"Dp3\"; break;\n      case DxsoOpcode::Dp4: os << \"Dp4\"; break;\n      case DxsoOpcode::Min: os << \"Min\"; break;\n      case DxsoOpcode::Max: os << \"Max\"; break;\n      case DxsoOpcode::Slt: os << \"Slt\"; break;\n      case DxsoOpcode::Sge: os << \"Sge\"; break;\n      case DxsoOpcode::Exp: os << \"Exp\"; break;\n      case DxsoOpcode::Log: os << \"Log\"; break;\n      case DxsoOpcode::Lit: os << \"Lit\"; break;\n      case DxsoOpcode::Dst: os << \"Dst\"; break;\n      case DxsoOpcode::Lrp: os << \"Lrp\"; break;\n      case DxsoOpcode::Frc: os << \"Frc\"; break;\n      case DxsoOpcode::M4x4: os << \"M4x4\"; break;\n      case DxsoOpcode::M4x3: os << \"M4x3\"; break;\n      case DxsoOpcode::M3x4: os << \"M3x4\"; break;\n      case DxsoOpcode::M3x3: os << \"M3x3\"; break;\n      case DxsoOpcode::M3x2: os << \"M3x2\"; break;\n      case DxsoOpcode::Call: os << \"Call\"; break;\n      case DxsoOpcode::CallNz: os << \"CallNz\"; break;\n      case DxsoOpcode::Loop: os << \"Loop\"; break;\n      case DxsoOpcode::Ret: os << \"Ret\"; break;\n      case DxsoOpcode::EndLoop: os << \"EndLoop\"; break;\n      case DxsoOpcode::Label: os << \"Label\"; break;\n      case DxsoOpcode::Dcl: os << \"Dcl\"; break;\n      case DxsoOpcode::Pow: os << \"Pow\"; break;\n      case DxsoOpcode::Crs: os << \"Crs\"; break;\n      case DxsoOpcode::Sgn: os << \"Sgn\"; break;\n      case DxsoOpcode::Abs: os << \"Abs\"; break;\n      case DxsoOpcode::Nrm: os << \"Nrm\"; break;\n      case DxsoOpcode::SinCos: os << \"SinCos\"; break;\n      case DxsoOpcode::Rep: os << \"Rep\"; break;\n      case DxsoOpcode::EndRep: os << \"EndRep\"; break;\n      case DxsoOpcode::If: os << \"If\"; break;\n      case DxsoOpcode::Ifc: os << \"Ifc\"; break;\n      case DxsoOpcode::Else: os << \"Else\"; break;\n      case DxsoOpcode::EndIf: os << \"EndIf\"; break;\n      case DxsoOpcode::Break: os << \"Break\"; break;\n      case DxsoOpcode::BreakC: os << \"BreakC\"; break;\n      case DxsoOpcode::Mova: os << \"Mova\"; break;\n      case DxsoOpcode::DefB: os << \"DefB\"; break;\n      case DxsoOpcode::DefI: os << \"DefI\"; break;\n\n      case DxsoOpcode::TexCoord: os << \"TexCoord\"; break;\n      case DxsoOpcode::TexKill: os << \"TexKill\"; break;\n      case DxsoOpcode::Tex: os << \"Tex\"; break;\n      case DxsoOpcode::TexBem: os << \"TexBem\"; break;\n      case DxsoOpcode::TexBemL: os << \"TexBemL\"; break;\n      case DxsoOpcode::TexReg2Ar: os << \"TexReg2Ar\"; break;\n      case DxsoOpcode::TexReg2Gb: os << \"TexReg2Gb\"; break;\n      case DxsoOpcode::TexM3x2Pad: os << \"TexM3x2Pad\"; break;\n      case DxsoOpcode::TexM3x2Tex: os << \"TexM3x2Tex\"; break;\n      case DxsoOpcode::TexM3x3Pad: os << \"TexM3x3Pad\"; break;\n      case DxsoOpcode::TexM3x3Tex: os << \"TexM3x3Tex\"; break;\n      case DxsoOpcode::Reserved0: os << \"Reserved0\"; break;\n      case DxsoOpcode::TexM3x3Spec: os << \"TexM3x3Spec\"; break;\n      case DxsoOpcode::TexM3x3VSpec: os << \"TexM3x3VSpec\"; break;\n      case DxsoOpcode::ExpP: os << \"ExpP\"; break;\n      case DxsoOpcode::LogP: os << \"LogP\"; break;\n      case DxsoOpcode::Cnd: os << \"Cnd\"; break;\n      case DxsoOpcode::Def: os << \"Def\"; break;\n      case DxsoOpcode::TexReg2Rgb: os << \"TexReg2Rgb\"; break;\n      case DxsoOpcode::TexDp3Tex: os << \"TexDp3Tex\"; break;\n      case DxsoOpcode::TexM3x2Depth: os << \"TexM3x2Depth\"; break;\n      case DxsoOpcode::TexDp3: os << \"TexDp3\"; break;\n      case DxsoOpcode::TexM3x3: os << \"TexM3x3\"; break;\n      case DxsoOpcode::TexDepth: os << \"TexDepth\"; break;\n      case DxsoOpcode::Cmp: os << \"Cmp\"; break;\n      case DxsoOpcode::Bem: os << \"Bem\"; break;\n      case DxsoOpcode::Dp2Add: os << \"Dp2Add\"; break;\n      case DxsoOpcode::DsX: os << \"DsX\"; break;\n      case DxsoOpcode::DsY: os << \"DsY\"; break;\n      case DxsoOpcode::TexLdd: os << \"TexLdd\"; break;\n      case DxsoOpcode::SetP: os << \"SetP\"; break;\n      case DxsoOpcode::TexLdl: os << \"TexLdl\"; break;\n      case DxsoOpcode::BreakP: os << \"BreakP\"; break;\n\n      case DxsoOpcode::Phase: os << \"Phase\"; break;\n      case DxsoOpcode::Comment: os << \"Comment\"; break;\n      case DxsoOpcode::End: os << \"End\"; break;\n      default:\n        os << \"Invalid Opcode (\" << static_cast<uint32_t>(opcode) << \")\"; break;\n    }\n\n    return os;\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_enums.h",
    "content": "#pragma once\n\n#include \"dxso_include.h\"\n\n#include <cstdint>\n\nnamespace dxvk {\n\n  /**\n   * \\brief Instruction code listing\n   */\n  enum class DxsoOpcode : uint32_t {\n    Nop          = 0,\n    Mov          ,\n    Add          ,\n    Sub          ,\n    Mad          ,\n    Mul          ,\n    Rcp          ,\n    Rsq          ,\n    Dp3          ,\n    Dp4          ,\n    Min          ,\n    Max          ,\n    Slt          ,\n    Sge          ,\n    Exp          ,\n    Log          ,\n    Lit          ,\n    Dst          ,\n    Lrp          ,\n    Frc          ,\n    M4x4         ,\n    M4x3         ,\n    M3x4         ,\n    M3x3         ,\n    M3x2         ,\n    Call         ,\n    CallNz       ,\n    Loop         ,\n    Ret          ,\n    EndLoop      ,\n    Label        ,\n    Dcl          ,\n    Pow          ,\n    Crs          ,\n    Sgn          ,\n    Abs          ,\n    Nrm          ,\n    SinCos       ,\n    Rep          ,\n    EndRep       ,\n    If           ,\n    Ifc          ,\n    Else         ,\n    EndIf        ,\n    Break        ,\n    BreakC       ,\n    Mova         ,\n    DefB         ,\n    DefI         ,\n\n    TexCoord     = 64,\n    TexKill      ,\n    Tex          ,\n    TexBem       ,\n    TexBemL      ,\n    TexReg2Ar    ,\n    TexReg2Gb    ,\n    TexM3x2Pad   ,\n    TexM3x2Tex   ,\n    TexM3x3Pad   ,\n    TexM3x3Tex   ,\n    Reserved0    ,\n    TexM3x3Spec  ,\n    TexM3x3VSpec ,\n    ExpP         ,\n    LogP         ,\n    Cnd          ,\n    Def          ,\n    TexReg2Rgb   ,\n    TexDp3Tex    ,\n    TexM3x2Depth ,\n    TexDp3       ,\n    TexM3x3      ,\n    TexDepth     ,\n    Cmp          ,\n    Bem          ,\n    Dp2Add       ,\n    DsX          ,\n    DsY          ,\n    TexLdd       ,\n    SetP         ,\n    TexLdl       ,\n    BreakP       ,\n\n    Phase        = 0xfffd,\n    Comment      = 0xfffe,\n    End          = 0xffff\n  };\n\n  std::ostream& operator << (std::ostream& os, DxsoOpcode opcode);\n\n  enum class DxsoRegisterType : uint32_t {\n    Temp           =  0, // Temporary Register File\n    Input          =  1, // Input Register File\n    Const          =  2, // Constant Register File\n    Addr           =  3, // Address Register (VS)\n    Texture        =  3, // Texture Register File (PS)\n    RasterizerOut  =  4, // Rasterizer Register File\n    AttributeOut   =  5, // Attribute Output Register File\n    TexcoordOut    =  6, // Texture Coordinate Output Register File\n    Output         =  6, // Output register file for VS3.0+\n    ConstInt       =  7, // Constant Integer Vector Register File\n    ColorOut       =  8, // Color Output Register File\n    DepthOut       =  9, // Depth Output Register File\n    Sampler        = 10, // Sampler State Register File\n    Const2         = 11, // Constant Register File  2048 - 4095\n    Const3         = 12, // Constant Register File  4096 - 6143\n    Const4         = 13, // Constant Register File  6144 - 8191\n    ConstBool      = 14, // Constant Boolean register file\n    Loop           = 15, // Loop counter register file\n    TempFloat16    = 16, // 16-bit float temp register file\n    MiscType       = 17, // Miscellaneous (single) registers.\n    Label          = 18, // Label\n    Predicate      = 19, // Predicate register\n    PixelTexcoord  = 20\n  };\n\n  enum class DxsoUsage : uint32_t {\n    Position        = 0,\n    BlendWeight,   // 1\n    BlendIndices,  // 2\n    Normal,        // 3\n    PointSize,     // 4\n    Texcoord,      // 5\n    Tangent,       // 6\n    Binormal,      // 7\n    TessFactor,    // 8\n    PositionT,     // 9\n    Color,         // 10\n    Fog,           // 11\n    Depth,         // 12\n    Sample,        // 13\n  };\n\n  enum class DxsoTextureType : uint32_t {\n    Texture2D   = 2,\n    TextureCube = 3,\n    Texture3D   = 4\n  };\n\n  enum DxsoReasterizerOutIndices : uint32_t {\n    RasterOutPosition  = 0,\n    RasterOutFog       = 1,\n    RasterOutPointSize = 2\n  };\n\n  enum DxsoMiscTypeIndices : uint32_t {\n    MiscTypePosition,\n    MiscTypeFace,\n  };\n\n}"
  },
  {
    "path": "src/dxso/dxso_header.cpp",
    "content": "#include \"dxso_header.h\"\n\nnamespace dxvk {\n\n  DxsoHeader::DxsoHeader(DxsoReader& reader) {\n    uint32_t headerToken = reader.readu32();\n\n    uint32_t headerTypeMask = headerToken & 0xffff0000;\n\n    DxsoProgramType programType;\n    if (headerTypeMask == 0xffff0000)\n      programType = DxsoProgramTypes::PixelShader;\n    else if (headerTypeMask == 0xfffe0000)\n      programType = DxsoProgramTypes::VertexShader;\n    else\n      throw DxvkError(\"DxsoHeader: invalid header - invalid version\");\n\n    const uint32_t majorVersion = (headerToken >> 8) & 0xff;\n    const uint32_t minorVersion = headerToken & 0xff;\n\n    m_info = DxsoProgramInfo{ programType, minorVersion, majorVersion };\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_header.h",
    "content": "#pragma once\n\n#include \"dxso_common.h\"\n\n#include \"dxso_reader.h\"\n\nnamespace dxvk {\n\n/**\n  * \\brief DXSO header\n  * \n  * Stores meta information about the shader such\n  * as the version and the type.\n  */\n  class DxsoHeader {\n\n  public:\n\n    DxsoHeader(DxsoReader& reader);\n\n    const DxsoProgramInfo& info() const {\n      return m_info;\n    }\n\n  private:\n\n    DxsoProgramInfo m_info;\n\n  };\n\n}"
  },
  {
    "path": "src/dxso/dxso_include.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_shader.h\"\n\n#include \"../util/com/com_guid.h\"\n#include \"../util/com/com_object.h\"\n#include \"../util/com/com_pointer.h\"\n\n#include \"../util/log/log.h\"\n#include \"../util/log/log_debug.h\"\n\n#include \"../util/rc/util_rc.h\"\n#include \"../util/rc/util_rc_ptr.h\"\n\n#include \"../util/util_bit.h\"\n#include \"../util/util_enum.h\"\n#include \"../util/util_error.h\"\n#include \"../util/util_string.h\""
  },
  {
    "path": "src/dxso/dxso_isgn.h",
    "content": "#pragma once\n\n#include \"dxso_decoder.h\"\n\nnamespace dxvk {\n\n  struct DxsoIsgnEntry {\n    uint32_t     regNumber = 0;\n    uint32_t     slot      = 0;\n    DxsoSemantic semantic  = DxsoSemantic{ DxsoUsage::Position, 0 };\n    DxsoRegMask  mask      = IdentityWriteMask;\n    bool         centroid  = false;\n  };\n\n  struct DxsoIsgn {\n    std::array<\n      DxsoIsgnEntry,\n      2 * DxsoMaxInterfaceRegs> elems;\n    uint32_t elemCount = 0;\n  };\n\n  struct DxsoDefinedConstant {\n    uint32_t uboIdx;\n\n    // Only float constants may be indexed.\n    // So that's the only ones we care about putting in the UBO.\n    float    float32[4];\n  };\n\n  using DxsoDefinedConstants = std::vector<DxsoDefinedConstant>;\n\n  struct DxsoShaderMetaInfo {\n    bool needsConstantCopies = false;\n    uint32_t maxConstIndexF = 0;\n    uint32_t maxConstIndexI = 0;\n    uint32_t maxConstIndexB = 0;\n\n    uint32_t boolConstantMask = 0;\n  };\n\n}"
  },
  {
    "path": "src/dxso/dxso_modinfo.h",
    "content": "#pragma once\n\n#include \"dxso_options.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Shader module info\n   *\n   * Stores information which may affect shader compilation.\n   * This data can be supplied by the client API implementation.\n   */\n  struct DxsoModuleInfo {\n    DxsoOptions options;\n  };\n\n}"
  },
  {
    "path": "src/dxso/dxso_module.cpp",
    "content": "#include \"dxso_module.h\"\n\n#include \"dxso_code.h\"\n#include \"dxso_compiler.h\"\n\n#include <memory>\n\nnamespace dxvk {\n\n  DxsoModule::DxsoModule(DxsoReader& reader)\n    : m_header( reader )\n    , m_code  ( reader ) { }\n\n  DxsoAnalysisInfo DxsoModule::analyze() {\n    DxsoAnalysisInfo info;\n\n    DxsoAnalyzer analyzer(info);\n\n    this->runAnalyzer(analyzer, m_code.iter());\n\n    return info;\n  }\n\n  Rc<DxvkShader> DxsoModule::compile(\n    const DxsoModuleInfo&     moduleInfo,\n    const std::string&        fileName,\n    const DxsoAnalysisInfo&   analysis,\n    const D3D9ConstantLayout& layout) {\n    auto compiler = std::make_unique<DxsoCompiler>(\n      fileName, moduleInfo,\n      m_header.info(), analysis,\n      layout);\n\n    this->runCompiler(*compiler, m_code.iter());\n    m_isgn = compiler->isgn();\n\n    m_meta                 = compiler->meta();\n    m_constants            = compiler->constants();\n    m_maxDefinedFloatConst = compiler->maxDefinedFloatConstant();\n    m_maxDefinedIntConst   = compiler->maxDefinedIntConstant();\n    m_maxDefinedBoolConst  = compiler->maxDefinedBoolConstant();\n    m_usedSamplers         = compiler->usedSamplers();\n    m_textureTypes         = compiler->textureTypes();\n\n    compiler->finalize();\n\n    // SM 1 doesn't have explicit output registers and uses R0 instead.\n    // The shader compiler emits the C0 write in finalize, so we have to get the rt mask\n    // after that.\n    m_usedRTs = compiler->usedRTs();\n\n    return compiler->compile();\n  }\n\n  void DxsoModule::runAnalyzer(\n          DxsoAnalyzer&       analyzer,\n          DxsoCodeIter        iter) const {\n    DxsoCodeIter start = iter;\n\n    DxsoDecodeContext decoder(m_header.info());\n\n    while (decoder.decodeInstruction(iter))\n      analyzer.processInstruction(\n        decoder.getInstructionContext());\n\n    size_t tokenCount = size_t(iter.ptrAt(0) - start.ptrAt(0));\n\n    // We need to account for the header token in the bytecode size...\n\n    // At this point, start is offset by the header due to us this being\n    // a *code* iterator, and not the general reader class.\n    // [start token] ^(start caret)^ [frog rendering code] [end token] ^(end caret)^\n    // where the tokenCount above is inbetween the start and end carets.\n\n    // We need to account for this otherwise it will show up as us not\n    // accounting for the *end* token in GetFunction due to the total size being\n    // offset by -1.\n    // [start token] [frog rendering code] (end of tokenCount) [end token]\n    tokenCount += 1;\n\n    analyzer.finalize(tokenCount);\n  }\n\n  void DxsoModule::runCompiler(\n          DxsoCompiler&       compiler,\n          DxsoCodeIter        iter) const {\n    DxsoDecodeContext decoder(m_header.info());\n\n    while (decoder.decodeInstruction(iter))\n      compiler.processInstruction(\n        decoder.getInstructionContext());\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_module.h",
    "content": "#pragma once\n\n#include \"dxso_reader.h\"\n#include \"dxso_code.h\"\n#include \"dxso_header.h\"\n#include \"dxso_ctab.h\"\n\n#include \"dxso_isgn.h\"\n#include \"dxso_analysis.h\"\n\n#include \"../d3d9/d3d9_constant_layout.h\"\n\n#include <vector>\n\nnamespace dxvk {\n\n  class DxsoCompiler;\n  class DxsoCode;\n  struct DxsoModuleInfo;\n\n  /**\n   * \\brief DXSO shader module, a d3d9 shader object.\n   */\n  class DxsoModule {\n\n  public:\n\n    DxsoModule(DxsoReader& reader);\n\n    const DxsoProgramInfo& info() {\n      return m_header.info();\n    }\n\n    DxsoAnalysisInfo analyze();\n\n    /**\n     * \\brief Compiles DXSO shader to SPIR-V module\n     * \n     * \\param [in] moduleInfo DXSO module info\n     * \\param [in] fileName File name, will be added to\n     *        the compiled SPIR-V for debugging purposes.\n     * \\returns The compiled shader object\n     */\n    Rc<DxvkShader> compile(\n      const DxsoModuleInfo&     moduleInfo,\n      const std::string&        fileName,\n      const DxsoAnalysisInfo&   analysis,\n      const D3D9ConstantLayout& layout);\n\n    const DxsoIsgn& isgn() {\n      return m_isgn;\n    }\n\n    const DxsoShaderMetaInfo& meta() { return m_meta; }\n\n    const DxsoDefinedConstants& constants() { return m_constants; }\n\n    uint32_t usedSamplers() { return m_usedSamplers; }\n\n    uint32_t usedRTs() { return m_usedRTs; }\n\n    int32_t maxDefinedFloatConstant() { return m_maxDefinedFloatConst; }\n\n    int32_t maxDefinedIntConstant() { return m_maxDefinedIntConst; }\n\n    int32_t maxDefinedBoolConstant() { return m_maxDefinedBoolConst; }\n\n    uint32_t textureTypes() { return m_textureTypes; }\n\n  private:\n\n    void runCompiler(\n            DxsoCompiler&       compiler,\n            DxsoCodeIter        iter) const;\n\n    void runAnalyzer(\n            DxsoAnalyzer&       analyzer,\n            DxsoCodeIter        iter) const;\n\n    DxsoHeader      m_header;\n    DxsoCode        m_code;\n\n    DxsoIsgn        m_isgn;\n    uint32_t        m_usedSamplers = 0u;\n    uint32_t        m_usedRTs      = 0u;\n    uint32_t        m_textureTypes = 0u;\n\n    DxsoShaderMetaInfo   m_meta;\n    int32_t              m_maxDefinedFloatConst = -1;\n    int32_t              m_maxDefinedIntConst = -1;\n    int32_t              m_maxDefinedBoolConst = -1;\n    DxsoDefinedConstants m_constants;\n\n  };\n\n}"
  },
  {
    "path": "src/dxso/dxso_options.cpp",
    "content": "#include \"dxso_options.h\"\n\n#include \"../d3d9/d3d9_device.h\"\n\nnamespace dxvk {\n\n  DxsoOptions::DxsoOptions() {}\n\n  DxsoOptions::DxsoOptions(D3D9DeviceEx* pDevice, const D3D9Options& options) {\n    const Rc<DxvkDevice> device = pDevice->GetDXVKDevice();\n    const Rc<DxvkAdapter> adapter = device->adapter();\n\n    const DxvkDeviceInfo& devInfo = adapter->deviceProperties();\n\n    // Apply shader-related options\n    d3d9FloatEmulation   = options.d3d9FloatEmulation;\n\n    forceSamplerTypeSpecConstants = options.forceSamplerTypeSpecConstants;\n    forceSampleRateShading = options.forceSampleRateShading;\n\n    vertexFloatConstantBufferAsSSBO = pDevice->GetVertexConstantLayout().floatSize() > devInfo.core.properties.limits.maxUniformBufferRange;\n\n    sincosEmulation     = device->getShaderCompileOptions().flags.test(DxvkShaderCompileFlag::LowerSinCos);\n  }\n\n}\n"
  },
  {
    "path": "src/dxso/dxso_options.h",
    "content": "#pragma once\n\n#include \"../dxvk/dxvk_device.h\"\n#include \"../d3d9/d3d9_options.h\"\n\nnamespace dxvk {\n\n  class D3D9DeviceEx;\n  struct D3D9Options;\n\n  struct DxsoOptions {\n    DxsoOptions();\n    DxsoOptions(D3D9DeviceEx* pDevice, const D3D9Options& options);\n\n    /// Whether to emulate d3d9 float behaviour using clampps\n    /// True:  Perform emulation to emulate behaviour (ie. anything * 0 = 0)\n    /// False: Don't do anything.\n    D3D9FloatEmulation d3d9FloatEmulation;\n\n    /// Always use a spec constant to determine sampler type (instead of just in PS 1.x)\n    /// Works around a game bug in Halo CE where it gives cube textures to 2d/volume samplers\n    bool forceSamplerTypeSpecConstants;\n\n    /// Interpolate pixel shader inputs at the sample location rather than pixel center\n    bool forceSampleRateShading;\n\n    /// Should the SWVP float constant buffer be a SSBO (because of the size on NV)\n    bool vertexFloatConstantBufferAsSSBO;\n\n    /// Whether or not we need to use custom sincos\n    bool sincosEmulation = false;\n  };\n\n}\n"
  },
  {
    "path": "src/dxso/dxso_reader.cpp",
    "content": "#include \"dxso_reader.h\"\n\n#include <cstring>\n\nnamespace dxvk {\n\n  DxsoTag DxsoReader::readTag() {\n    DxsoTag tag;\n    this->read(&tag, 4);\n    return tag;\n  }\n\n  void DxsoReader::read(void* dst, size_t n) {\n    std::memcpy(dst, m_data + m_pos, n);\n    m_pos += n;\n  }\n\n  void DxsoReader::skip(size_t n) {\n    m_pos += n;\n  }\n\n  void DxsoReader::store(std::ostream && stream, size_t size) const {\n    stream.write(m_data, size);\n  }\n\n}\n"
  },
  {
    "path": "src/dxso/dxso_reader.h",
    "content": "#pragma once\n\n#include \"dxso_include.h\"\n\n#include <cstdint>\n\nnamespace dxvk {\n\n  /**\n   * \\brief Four-character tag\n   */\n  class DxsoTag {\n\n  public:\n\n    DxsoTag() {\n      for (size_t i = 0; i < 4; i++)\n        m_chars[i] = '\\0';\n    }\n\n    DxsoTag(const char* tag) {\n      for (size_t i = 0; i < 4; i++)\n        m_chars[i] = tag[i];\n    }\n\n    bool operator == (const DxsoTag& other) const {\n      bool result = true;\n      for (size_t i = 0; i < 4; i++)\n        result &= m_chars[i] == other.m_chars[i];\n      return result;\n    }\n\n    bool operator != (const DxsoTag& other) const {\n      return !this->operator == (other);\n    }\n\n    const char* operator & () const { return m_chars; }\n          char* operator & ()       { return m_chars; }\n\n  private:\n\n    char m_chars[4];\n\n  };\n\n\n  /**\n    * \\brief DXSO (d3d9) bytecode reader\n    *\n    * Holds references to the shader byte code and\n    * provides methods to read\n    */\n  class DxsoReader {\n\n  public:\n\n    DxsoReader(const char* data)\n      : DxsoReader(data, 0) { }\n\n    size_t pos() {\n      return m_pos;\n    }\n\n    auto readu32() { return this->readNum<uint32_t> (); }\n    auto readf32() { return this->readNum<float>    (); }\n\n    DxsoTag readTag();\n\n    void read(void* dst, size_t n);\n\n    void skip(size_t n);\n\n    void store(std::ostream&& stream, size_t size) const;\n\n    const char* currentPtr() {\n      return m_data + m_pos;\n    }\n\n  private:\n\n    DxsoReader(const char* data, size_t pos)\n      : m_data(data), m_pos(pos) { }\n\n    const char* m_data = nullptr;\n    size_t      m_pos = 0;\n\n    template<typename T>\n    T readNum() {\n      T result;\n      this->read(&result, sizeof(result));\n      return result;\n    }\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxso/dxso_tables.cpp",
    "content": "#include \"dxso_tables.h\"\n\nnamespace dxvk {\n\n  uint32_t DxsoGetDefaultOpcodeLength(DxsoOpcode opcode) {\n\n    switch (opcode) {\n      case DxsoOpcode::Nop: return 0;\n      case DxsoOpcode::Mov: return 2;\n      case DxsoOpcode::Add: return 3;\n      case DxsoOpcode::Sub: return 3;\n      case DxsoOpcode::Mad: return 4;\n      case DxsoOpcode::Mul: return 3;\n      case DxsoOpcode::Rcp: return 2;\n      case DxsoOpcode::Rsq: return 2;\n      case DxsoOpcode::Dp3: return 3;\n      case DxsoOpcode::Dp4: return 3;\n      case DxsoOpcode::Min: return 3;\n      case DxsoOpcode::Max: return 3;\n      case DxsoOpcode::Slt: return 3;\n      case DxsoOpcode::Sge: return 3;\n      case DxsoOpcode::Exp: return 2;\n      case DxsoOpcode::Log: return 2;\n      case DxsoOpcode::Lit: return 2;\n      case DxsoOpcode::Dst: return 3;\n      case DxsoOpcode::Lrp: return 4;\n      case DxsoOpcode::Frc: return 2;\n      case DxsoOpcode::M4x4: return 3;\n      case DxsoOpcode::M4x3: return 3;\n      case DxsoOpcode::M3x4: return 3;\n      case DxsoOpcode::M3x3: return 3;\n      case DxsoOpcode::M3x2: return 3;\n      case DxsoOpcode::Call: return 1;\n      case DxsoOpcode::CallNz: return 2;\n      case DxsoOpcode::Loop: return 2;\n      case DxsoOpcode::Ret: return 0;\n      case DxsoOpcode::EndLoop: return 0;\n      case DxsoOpcode::Label: return 1;\n      case DxsoOpcode::Dcl: return 2;\n      case DxsoOpcode::Pow: return 3;\n      case DxsoOpcode::Crs: return 3;\n      case DxsoOpcode::Sgn: return 4;\n      case DxsoOpcode::Abs: return 2;\n      case DxsoOpcode::Nrm: return 2;\n      case DxsoOpcode::SinCos: return 4;\n      case DxsoOpcode::Rep: return 1;\n      case DxsoOpcode::EndRep: return 0;\n      case DxsoOpcode::If: return 1;\n      case DxsoOpcode::Ifc: return 2;\n      case DxsoOpcode::Else: return 0;\n      case DxsoOpcode::EndIf: return 0;\n      case DxsoOpcode::Break: return 0;\n      case DxsoOpcode::BreakC: return 2;\n      case DxsoOpcode::Mova: return 2;\n      case DxsoOpcode::DefB: return 2;\n      case DxsoOpcode::DefI: return 5;\n      case DxsoOpcode::TexCoord: return 1;\n      case DxsoOpcode::TexKill: return 1;\n      case DxsoOpcode::Tex: return 1;\n      case DxsoOpcode::TexBem: return 2;\n      case DxsoOpcode::TexBemL: return 2;\n      case DxsoOpcode::TexReg2Ar: return 2;\n      case DxsoOpcode::TexReg2Gb: return 2;\n      case DxsoOpcode::TexM3x2Pad: return 2;\n      case DxsoOpcode::TexM3x2Tex: return 2;\n      case DxsoOpcode::TexM3x3Pad: return 2;\n      case DxsoOpcode::TexM3x3Tex: return 2;\n      case DxsoOpcode::TexM3x3Spec: return 3;\n      case DxsoOpcode::TexM3x3VSpec: return 2;\n      case DxsoOpcode::ExpP: return 2;\n      case DxsoOpcode::LogP: return 2;\n      case DxsoOpcode::Cnd: return 4;\n      case DxsoOpcode::Def: return 5;\n      case DxsoOpcode::TexReg2Rgb: return 2;\n      case DxsoOpcode::TexDp3Tex: return 2;\n      case DxsoOpcode::TexM3x2Depth: return 2;\n      case DxsoOpcode::TexDp3: return 2;\n      case DxsoOpcode::TexM3x3: return 2;\n      case DxsoOpcode::TexDepth: return 1;\n      case DxsoOpcode::Cmp: return 4;\n      case DxsoOpcode::Bem: return 3;\n      case DxsoOpcode::Dp2Add: return 4;\n      case DxsoOpcode::DsX: return 2;\n      case DxsoOpcode::DsY: return 2;\n      case DxsoOpcode::TexLdd: return 5;\n      case DxsoOpcode::SetP: return 3;\n      case DxsoOpcode::TexLdl: return 3;\n      case DxsoOpcode::BreakP: return 2;\n      default: Logger::warn(\"DxsoGetDefaultOpcodeLength: unknown opcode to get default length for.\"); return std::numeric_limits<uint32_t>::max();\n    }\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_tables.h",
    "content": "#pragma once\n\n#include \"dxso_enums.h\"\n\nnamespace dxvk {\n\n  constexpr uint32_t InvalidOpcodeLength = std::numeric_limits<uint32_t>::max();\n\n  uint32_t DxsoGetDefaultOpcodeLength(DxsoOpcode opcode);\n\n}"
  },
  {
    "path": "src/dxso/dxso_util.cpp",
    "content": "#include \"dxso_util.h\"\n\n#include \"dxso_include.h\"\n\nnamespace dxvk {\n\n  dxvk::mutex                  g_linkerSlotMutex;\n  uint32_t                     g_linkerSlotCount = 12;\n  std::array<DxsoSemantic, 32> g_linkerSlots = {\n    {\n      {DxsoUsage::Normal,   0},\n      {DxsoUsage::Texcoord,   0},\n      {DxsoUsage::Texcoord,   1},\n      {DxsoUsage::Texcoord,   2},\n      {DxsoUsage::Texcoord,   3},\n      {DxsoUsage::Texcoord,   4},\n      {DxsoUsage::Texcoord,   5},\n      {DxsoUsage::Texcoord,   6},\n      {DxsoUsage::Texcoord,   7},\n\n      {DxsoUsage::Color,      0},\n      {DxsoUsage::Color,      1},\n\n      {DxsoUsage::Fog,        0},\n    }};\n  // We set fixed locations for the outputs that fixed function vertex shaders\n  // can produce so the uber shader doesn't need to be patched at runtime.\n\n  uint32_t RegisterLinkerSlot(DxsoSemantic semantic) {\n    // Lock, because games could be trying\n    // to make multiple shaders at a time.\n    std::lock_guard<dxvk::mutex> lock(g_linkerSlotMutex);\n\n    // Need to choose a slot that maps nicely and similarly\n    // between vertex and pixel shaders\n\n    // Find or map a slot.\n    uint32_t slot = g_linkerSlotCount;\n    for (uint32_t j = 0; j < g_linkerSlotCount; j++) {\n      if (g_linkerSlots[j] == semantic) {\n        slot = j;\n        break;\n      }\n    }\n\n    if (slot == g_linkerSlotCount)\n      g_linkerSlots[g_linkerSlotCount++] = semantic;\n\n    return slot;\n  }\n\n}"
  },
  {
    "path": "src/dxso/dxso_util.h",
    "content": "#pragma once\n\n#include <cstdint>\n\n#include \"dxso_common.h\"\n#include \"dxso_decoder.h\"\n\n#include \"../d3d9/d3d9_caps.h\"\n\nnamespace dxvk {\n\n  enum class DxsoBindingType : uint32_t {\n    ConstantBuffer,\n    Image,\n  };\n\n  enum class DxsoConstantBufferType : uint32_t {\n    Float,\n    Int,\n    Bool\n  };\n\n  enum DxsoConstantBuffers : uint32_t {\n    VSConstantBuffer = 0,\n    VSFloatConstantBuffer = 0,\n    VSIntConstantBuffer = 1,\n    VSBoolConstantBuffer = 2,\n    VSClipPlanes     = 3,\n    VSFixedFunction  = 4,\n    VSVertexBlendData = 5,\n    VSCount,\n\n    PSConstantBuffer = 0,\n    PSFixedFunction  = 1,\n    PSShared         = 2,\n    PSCount\n  };\n\n  constexpr uint32_t computeResourceSlotId(\n        DxsoProgramType shaderStage,\n        DxsoBindingType bindingType,\n        uint32_t        bindingIndex) {\n    const uint32_t stageOffset = (DxsoConstantBuffers::VSCount + caps::MaxTexturesVS) * uint32_t(shaderStage);\n\n    if (bindingType == DxsoBindingType::ConstantBuffer)\n      return bindingIndex + stageOffset;\n    else // if (bindingType == DxsoBindingType::Image)\n      return bindingIndex + stageOffset + (shaderStage == DxsoProgramType::PixelShader ? DxsoConstantBuffers::PSCount : DxsoConstantBuffers::VSCount);\n  }\n\n  // TODO: Intergrate into compute resource slot ID/refactor all of this?\n  constexpr uint32_t getSWVPBufferSlot() {\n    return DxsoConstantBuffers::VSCount + caps::MaxTexturesVS + DxsoConstantBuffers::PSCount + caps::MaxTexturesPS + 1; // From last pixel shader slot, above.\n  }\n\n  constexpr uint32_t getSpecConstantBufferSlot() {\n    return getSWVPBufferSlot() + 1;\n  }\n\n  uint32_t RegisterLinkerSlot(DxsoSemantic semantic);\n\n}"
  },
  {
    "path": "src/dxso/meson.build",
    "content": "dxso_src = files([\n  'dxso_common.cpp',\n  'dxso_options.cpp',\n  'dxso_module.cpp',\n  'dxso_reader.cpp',\n  'dxso_header.cpp',\n  'dxso_ctab.cpp',\n  'dxso_util.cpp',\n  'dxso_code.cpp',\n  'dxso_tables.cpp',\n  'dxso_decoder.cpp',\n  'dxso_analysis.cpp',\n  'dxso_compiler.cpp',\n  'dxso_enums.cpp'\n])\n\ndxso_lib = static_library('dxso', dxso_src,\n  include_directories : [ dxvk_include_path ],\n)\n\ndxso_dep = declare_dependency(\n  link_with           : [ dxso_lib ],\n  include_directories : [ dxvk_include_path ],\n)\n"
  },
  {
    "path": "src/dxvk/dxvk_access.cpp",
    "content": "#include \"dxvk_access.h\"\n\nnamespace dxvk {\n\n  DxvkObjectTracker::DxvkObjectTracker() {\n    m_head = std::make_unique<List>();\n    m_next = m_head.get();\n  }\n\n\n  DxvkObjectTracker::~DxvkObjectTracker() {\n    // List should be empty unless something bad has already happened\n    clear();\n  }\n\n\n  void DxvkObjectTracker::clear() {\n    List* list = nullptr;\n\n    for (size_t i = 0; i < m_size; i++) {\n      if (!(i & ListMask))\n        list = list ? list->next.get() : m_head.get();\n\n      auto* ref = std::launder(reinterpret_cast<DxvkTrackingRef*>(list->storage[i & ListMask].data));\n      ref->~DxvkTrackingRef();\n    }\n\n    m_next = m_head.get();\n    m_size = 0u;\n  }\n\n\n  void DxvkObjectTracker::advanceList() {\n    if (!m_next->next)\n      m_next->next = std::make_unique<List>();\n\n    m_next = m_next->next.get();\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_access.h",
    "content": "#pragma once\n\n#include <array>\n#include <cstdint>\n#include <memory>\n#include <utility>\n\n#include \"../util/util_flags.h\"\n\n#include \"../util/rc/util_rc_ptr.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Resource access flags\n   */\n  enum class DxvkAccess : uint32_t {\n    None    = 0,\n    Read    = 1,\n    Write   = 2,\n    Move    = 3,\n  };\n\n  using DxvkAccessFlags = Flags<DxvkAccess>;\n\n\n  /**\n   * \\brief Tracking reference\n   *\n   * Virtual base class for tracking references. The destructor\n   * of each subclass is responsible for releasing the tracked\n   * object in an implementation-defined manner.\n   */\n  class DxvkTrackingRef {\n\n  public:\n\n    /**\n     * \\brief Releases tracked object and destroys reference\n     *\n     * This is essentially a destructor with a parameter, and\n     * subclasses \\e must invoke their own destructor here.\n     * \\param [in] submission Last completed GPU submission\n     */\n    virtual ~DxvkTrackingRef() { }\n\n  };\n\n\n  /**\n   * \\brief Tracking reference storage\n   *\n   * Provides storage for the bae object and an additional pointer.\n   * All tracking references must be compatible with this layout. \n   */\n  struct DxvkTrackingRefStorage {\n    alignas(DxvkTrackingRef)\n    char data[sizeof(DxvkTrackingRef) + sizeof(void*)];\n  };\n\n\n  /**\n   * \\brief Typed tracking reference for normal ref-counted object\n   */\n  template<typename T>\n  class DxvkObjectRef : public DxvkTrackingRef {\n\n  public:\n\n    explicit DxvkObjectRef(Rc<T>&& object)\n    : m_object(std::move(object)) { }\n\n  private:\n\n    Rc<T> m_object;\n\n  };\n\n\n  /**\n   * \\brief Object tracker\n   *\n   * Stores tracking references which keep objects alive until the GPU\n   * is done using them. Uses a list of arrays in order to avoid having\n   * to move or copy the stored references at any time.\n   */\n  class DxvkObjectTracker {\n    constexpr static size_t ListCapacity = 1024;\n    constexpr static size_t ListMask = ListCapacity - 1u;\n  public:\n\n    DxvkObjectTracker();\n\n    ~DxvkObjectTracker();\n\n    template<typename T, typename... Args>\n    force_inline void track(Args&&... args) {\n      static_assert(sizeof(T) <= sizeof(DxvkTrackingRefStorage));\n      new (m_next->storage[(m_size++) & ListMask].data) T(std::forward<Args>(args)...);\n\n      if (unlikely(!(m_size & ListMask)))\n        advanceList();\n    }\n\n    void clear();\n\n  private:\n\n    struct List {\n      std::array<DxvkTrackingRefStorage, ListCapacity> storage = { };\n      std::unique_ptr<List> next;\n    };\n\n    std::unique_ptr<List> m_head;\n    List*                 m_next = nullptr;\n    size_t                m_size = 0u;\n\n    void advanceList();\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_adapter.cpp",
    "content": "#include <cstring>\n#include <unordered_set>\n\n#include \"dxvk_adapter.h\"\n#include \"dxvk_device.h\"\n#include \"dxvk_instance.h\"\n\nnamespace dxvk {\n\n  DxvkDeviceQueue getDeviceQueue(const Rc<vk::DeviceFn>& vkd, const DxvkDeviceCapabilities& caps, DxvkDeviceQueueIndex queue) {\n    DxvkDeviceQueue result = { };\n    result.queueFamily = queue.family;\n    result.queueIndex = queue.index;\n\n    if (queue.family != VK_QUEUE_FAMILY_IGNORED) {\n      result.properties = caps.getQueueProperties(queue.family);\n      vkd->vkGetDeviceQueue(vkd->device(), queue.family, queue.index, &result.queueHandle);\n    }\n\n    return result;\n  }\n\n\n  DxvkAdapter::DxvkAdapter(\n          DxvkInstance&       instance,\n          VkPhysicalDevice    handle)\n  : m_instance      (&instance),\n    m_handle        (handle),\n    m_capabilities  (instance, handle, nullptr) {\n    const auto& properties = m_capabilities.getProperties();\n\n    if (properties.vk11.deviceLUIDValid) {\n      D3DKMT_OPENADAPTERFROMLUID open = { };\n      memcpy(&open.AdapterLuid, properties.vk11.deviceLUID, sizeof(open.AdapterLuid));\n\n      if (D3DKMTOpenAdapterFromLuid(&open))\n        Logger::warn(\"Failed to open D3DKMT adapter\");\n      else\n        m_kmtLocal = open.hAdapter;\n    }\n  }\n  \n  \n  DxvkAdapter::~DxvkAdapter() {\n    if (m_kmtLocal) {\n      D3DKMT_CLOSEADAPTER close = { };\n      close.hAdapter = m_kmtLocal;\n      D3DKMTCloseAdapter(&close);\n    }\n  }\n\n\n  Rc<vk::InstanceFn> DxvkAdapter::vki() const {\n    return m_instance->vki();\n  }\n\n  \n  bool DxvkAdapter::isCompatible(std::string& error) {\n    std::array<char, 1024u> message = { };\n\n    if (m_capabilities.isSuitable(message.size(), message.data()))\n      return true;\n\n    error = std::string(message.data());\n    return false;\n  }\n\n\n  DxvkAdapterMemoryInfo DxvkAdapter::getMemoryHeapInfo() const {\n    bool hasMemoryBudget = m_capabilities.getFeatures().extMemoryBudget;\n\n    auto vk = m_instance->vki();\n\n    VkPhysicalDeviceMemoryBudgetPropertiesEXT memBudget = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };\n    VkPhysicalDeviceMemoryProperties2 memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2 };\n    memProps.pNext = hasMemoryBudget ? &memBudget : nullptr;\n\n    vk->vkGetPhysicalDeviceMemoryProperties2(m_handle, &memProps);\n    \n    DxvkAdapterMemoryInfo info = { };\n    info.heapCount = memProps.memoryProperties.memoryHeapCount;\n\n    for (uint32_t i = 0; i < info.heapCount; i++) {\n      info.heaps[i].heapFlags = memProps.memoryProperties.memoryHeaps[i].flags;\n      info.heaps[i].heapSize = memProps.memoryProperties.memoryHeaps[i].size;\n\n      if (hasMemoryBudget) {\n        // Handle DXVK's memory allocations separately so that\n        // freeing  resources actually is visible to applications.\n        VkDeviceSize allocated = m_memoryStats[i].allocated.load();\n        VkDeviceSize used = m_memoryStats[i].used.load();\n\n        info.heaps[i].memoryBudget    = memBudget.heapBudget[i];\n        info.heaps[i].memoryAllocated = std::max(memBudget.heapUsage[i], allocated) - allocated + used;\n      } else {\n        info.heaps[i].memoryBudget    = memProps.memoryProperties.memoryHeaps[i].size;\n        info.heaps[i].memoryAllocated = m_memoryStats[i].used.load();\n      }\n    }\n\n    return info;\n  }\n\n\n  DxvkFormatFeatures DxvkAdapter::getFormatFeatures(VkFormat format) const {\n    auto vk = m_instance->vki();\n\n    VkFormatProperties3 properties3 = { VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_3 };\n    VkFormatProperties2 properties2 = { VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, &properties3 };\n    vk->vkGetPhysicalDeviceFormatProperties2(m_handle, format, &properties2);\n\n    DxvkFormatFeatures result;\n    result.optimal = properties3.optimalTilingFeatures;\n    result.linear  = properties3.linearTilingFeatures;\n    result.buffer  = properties3.bufferFeatures;\n    return result;\n  }\n\n\n  std::optional<DxvkFormatLimits> DxvkAdapter::getFormatLimits(\n    const DxvkFormatQuery&          query) const {\n    auto vk = m_instance->vki();\n\n    VkPhysicalDeviceExternalImageFormatInfo externalInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO };\n    externalInfo.handleType = query.handleType;\n\n    VkPhysicalDeviceImageFormatInfo2 info = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2 };\n    info.format = query.format;\n    info.type   = query.type;\n    info.tiling = query.tiling;\n    info.usage  = query.usage;\n    info.flags  = query.flags;\n\n    if (externalInfo.handleType)\n      externalInfo.pNext = std::exchange(info.pNext, &externalInfo);\n\n    VkExternalImageFormatProperties externalProperties = { VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES };\n    VkImageFormatProperties2 properties = { VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2 };\n\n    if (externalInfo.handleType)\n      externalProperties.pNext = std::exchange(properties.pNext, &externalProperties);\n\n    VkResult vr = vk->vkGetPhysicalDeviceImageFormatProperties2(m_handle, &info, &properties);\n\n    if (vr != VK_SUCCESS)\n      return std::nullopt;\n\n    DxvkFormatLimits result = { };\n    result.maxExtent        = properties.imageFormatProperties.maxExtent;\n    result.maxMipLevels     = properties.imageFormatProperties.maxMipLevels;\n    result.maxArrayLayers   = properties.imageFormatProperties.maxArrayLayers;\n    result.sampleCounts     = properties.imageFormatProperties.sampleCounts;\n    result.maxResourceSize  = properties.imageFormatProperties.maxResourceSize;\n    result.externalFeatures = externalProperties.externalMemoryProperties.externalMemoryFeatures;\n    return result;\n  }\n\n\n  void DxvkAdapter::enableExtensions(const DxvkExtensionList& extensions) {\n    for (const auto& ext : extensions)\n      m_extraExtensions.push_back(ext);\n  }\n\n\n  Rc<DxvkDevice> DxvkAdapter::createDevice() {\n    auto vk = m_instance->vki();\n\n    Logger::info(\"Creating device:\");\n    m_capabilities.logDeviceInfo();\n\n    // Get device features to enable\n    size_t featureBlobSize = 0u;\n    m_capabilities.queryDeviceFeatures(&featureBlobSize, nullptr);\n\n    std::vector<char> featureBlob(featureBlobSize);\n    m_capabilities.queryDeviceFeatures(&featureBlobSize, featureBlob.data());\n\n    auto features = reinterpret_cast<const VkPhysicalDeviceFeatures2*>(featureBlob.data());\n\n    // Get extension list and add extra extensions\n    uint32_t extensionCount = 0u;\n    m_capabilities.queryDeviceExtensions(&extensionCount, nullptr);\n\n    std::vector<VkExtensionProperties> extensions(extensionCount);\n    m_capabilities.queryDeviceExtensions(&extensionCount, extensions.data());\n\n    for (const auto& extra : m_extraExtensions) {\n      bool found = false;\n\n      for (const auto& enabled : extensions) {\n        if ((found = !std::strncmp(extra.extensionName, enabled.extensionName, sizeof(enabled.extensionName))))\n          break;\n      }\n\n      if (!found)\n        extensions.push_back(extra);\n    }\n\n    // Create extension list that we can pass to Vulkan\n    std::vector<const char*> extensionNames;\n    extensionNames.reserve(extensions.size());\n\n    for (const auto& ext : extensions)\n      extensionNames.push_back(ext.extensionName);\n\n    // Query queue infos\n    DxvkDeviceQueueMapping queueMapping = m_capabilities.getQueueMapping();\n\n    uint32_t queueCount = { };\n    m_capabilities.queryDeviceQueues(&queueCount, nullptr);\n\n    std::vector<VkDeviceQueueCreateInfo> queues(queueCount, { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO });\n    m_capabilities.queryDeviceQueues(&queueCount, queues.data());\n\n    uint32_t priorityCount = 0u;\n\n    for (const auto& q : queues)\n      priorityCount += q.queueCount;\n\n    std::vector<float> queuePriorities(priorityCount);\n\n    uint32_t priorityIndex = 0u;\n\n    for (auto& q : queues) {\n      q.pQueuePriorities = &queuePriorities[priorityIndex];\n      priorityIndex += q.queueCount;\n    }\n\n    m_capabilities.queryDeviceQueues(&queueCount, queues.data());\n\n    // Create the actual Vulkan device\n    VkDeviceCreateInfo deviceInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };\n    deviceInfo.pNext = features->pNext;\n    deviceInfo.queueCreateInfoCount = queues.size();\n    deviceInfo.pQueueCreateInfos = queues.data();\n    deviceInfo.enabledExtensionCount = extensionNames.size();\n    deviceInfo.ppEnabledExtensionNames = extensionNames.data();\n    deviceInfo.pEnabledFeatures = &features->features;\n\n    VkDevice device = VK_NULL_HANDLE;\n    VkResult vr = vk->vkCreateDevice(m_handle, &deviceInfo, nullptr, &device);\n\n    if (vr)\n      throw DxvkError(str::format(\"Failed to create Vulkan device: \", vr));\n\n    Rc<vk::DeviceFn> vkd = new vk::DeviceFn(vk, true, device);\n\n    DxvkDeviceQueueSet deviceQueues = { };\n    deviceQueues.graphics = getDeviceQueue(vkd, m_capabilities, queueMapping.graphics);\n    deviceQueues.transfer = getDeviceQueue(vkd, m_capabilities, queueMapping.transfer);\n    deviceQueues.sparse   = getDeviceQueue(vkd, m_capabilities, queueMapping.sparse);\n\n    return new DxvkDevice(m_instance, this, vkd, m_capabilities.getFeatures(), deviceQueues, DxvkQueueCallback());\n  }\n\n\n  Rc<DxvkDevice> DxvkAdapter::importDevice(\n    const DxvkDeviceImportInfo& args) {\n    const float queuePriority = 1.0f;\n\n    VkDeviceQueueCreateInfo queueInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };\n    queueInfo.queueFamilyIndex = args.queueFamily;\n    queueInfo.queueCount = 1u;\n    queueInfo.pQueuePriorities = &queuePriority;\n\n    VkDeviceCreateInfo deviceInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };\n    deviceInfo.pNext = args.features;\n    deviceInfo.enabledExtensionCount = args.extensionCount;\n    deviceInfo.ppEnabledExtensionNames = args.extensionNames;\n    deviceInfo.queueCreateInfoCount = 1u;\n    deviceInfo.pQueueCreateInfos = &queueInfo;\n\n    DxvkDeviceCapabilities importCaps(*m_instance, m_handle, &deviceInfo);\n\n    Logger::info(\"Importing device:\");\n    importCaps.logDeviceInfo();\n\n    DxvkDeviceQueueMapping queueMapping = importCaps.getQueueMapping();\n\n    Rc<vk::DeviceFn> vkd = new vk::DeviceFn(m_instance->vki(), false, args.device);\n\n    DxvkDeviceQueueSet deviceQueues = { };\n    deviceQueues.graphics = getDeviceQueue(vkd, importCaps, queueMapping.graphics);\n    deviceQueues.transfer = getDeviceQueue(vkd, importCaps, queueMapping.transfer);\n    deviceQueues.sparse   = getDeviceQueue(vkd, importCaps, queueMapping.sparse);\n\n    return new DxvkDevice(m_instance, this, vkd, importCaps.getFeatures(), deviceQueues, args.queueCallback);\n  }\n\n\n  void DxvkAdapter::notifyMemoryStats(\n          uint32_t            heap,\n          int64_t             allocated,\n          int64_t             used) {\n    if (heap < m_memoryStats.size()) {\n      m_memoryStats[heap].allocated += allocated;\n      m_memoryStats[heap].used += used;\n    }\n  }\n\n\n  bool DxvkAdapter::matchesDriver(\n          VkDriverIdKHR       driver,\n          Version             minVer,\n          Version             maxVer) const {\n    const auto& properties = m_capabilities.getProperties();\n    bool driverMatches = driver == properties.vk12.driverID;\n\n    if (minVer) driverMatches &= properties.driverVersion >= minVer;\n    if (maxVer) driverMatches &= properties.driverVersion <  maxVer;\n\n    return driverMatches;\n  }\n\n\n  bool DxvkAdapter::matchesDriver(\n          VkDriverIdKHR       driver) const {\n    const auto& properties = m_capabilities.getProperties();\n    return driver == properties.vk12.driverID;\n  }\n\n\n  bool DxvkAdapter::isUnifiedMemoryArchitecture() const {\n    auto memory = this->memoryProperties();\n    bool result = true;\n\n    for (uint32_t i = 0; i < memory.memoryHeapCount; i++)\n      result = result && (memory.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);\n\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_adapter.h",
    "content": "#pragma once\n\n#include <functional>\n#include <optional>\n\n#include \"dxvk_device_info.h\"\n#include \"dxvk_extension_provider.h\"\n#include \"dxvk_include.h\"\n#include \"dxvk_format.h\"\n\n#include \"../util/util_gdi.h\"\n\nnamespace dxvk {\n  \n  class DxvkDevice;\n  class DxvkInstance;\n\n  using DxvkQueueCallback = std::function<void (bool)>;\n\n  /**\n   * \\brief GPU vendors\n   * Based on PCIe IDs.\n   */\n  enum class DxvkGpuVendor : uint16_t {\n    Amd    = 0x1002,\n    Nvidia = 0x10de,\n    Intel  = 0x8086,\n  };\n\n  /**\n   * \\brief Adapter memory hfeap info\n   * \n   * Stores info about a heap, and the amount\n   * of memory allocated from it by the app.\n   */\n  struct DxvkAdapterMemoryHeapInfo {\n    VkMemoryHeapFlags heapFlags;\n    VkDeviceSize heapSize;\n    VkDeviceSize memoryBudget;\n    VkDeviceSize memoryAllocated;\n  };\n\n  /**\n   * \\brief Adapter memory info\n   * \n   * Stores properties and allocation\n   * info of each available heap.\n   */\n  struct DxvkAdapterMemoryInfo {\n    uint32_t                  heapCount;\n    DxvkAdapterMemoryHeapInfo heaps[VK_MAX_MEMORY_HEAPS];\n  };\n\n  /**\n   * \\brief Retrieves queue indices\n   */\n  struct DxvkAdapterQueueIndices {\n    uint32_t graphics;\n    uint32_t transfer;\n    uint32_t sparse;\n  };\n\n\n  /**\n   * \\brief Adapter memory statistics\n   *\n   * Periodically updated by the devices using this adapter.\n   */\n  struct DxvkAdapterMemoryStats {\n    std::atomic<uint64_t> allocated = { 0u };\n    std::atomic<uint64_t> used = { 0u };\n  };\n\n\n  /**\n   * \\brief Device import info\n   */\n  struct DxvkDeviceImportInfo {\n    VkDevice          device          = VK_NULL_HANDLE;\n    VkQueue           queue           = VK_NULL_HANDLE;\n    uint32_t          queueFamily     = VK_QUEUE_FAMILY_IGNORED;\n    uint32_t          extensionCount  = 0u;\n    const char**      extensionNames  = nullptr;\n    const VkPhysicalDeviceFeatures2* features = nullptr;\n    DxvkQueueCallback queueCallback   = { };\n  };\n\n  /**\n   * \\brief DXVK adapter\n   * \n   * Corresponds to a physical device in Vulkan. Provides\n   * all kinds of information about the device itself and\n   * the supported feature set.\n   */\n  class DxvkAdapter : public RcObject {\n    \n  public:\n    \n    DxvkAdapter(\n            DxvkInstance&       instance,\n            VkPhysicalDevice    handle);\n    ~DxvkAdapter();\n    \n    /**\n     * \\brief Vulkan instance functions\n     * \\returns Vulkan instance functions\n     */\n    Rc<vk::InstanceFn> vki() const;\n    \n    /**\n     * \\brief Physical device handle\n     * \\returns The adapter handle\n     */\n    VkPhysicalDevice handle() const {\n      return m_handle;\n    }\n    \n    /**\n     * \\brief D3DKMT adapter local handle\n     * \\returns The adapter D3DKMT local handle\n     * \\returns \\c 0 if there's no matching D3DKMT adapter\n     */\n    D3DKMT_HANDLE kmtLocal() const {\n      return m_kmtLocal;\n    }\n    \n    /**\n     * \\brief Physical device properties\n     * \n     * Returns a read-only reference to the core\n     * properties of the Vulkan physical device.\n     * \\returns Physical device core properties\n     */\n    const DxvkDeviceInfo& deviceProperties() const {\n      return m_capabilities.getProperties();\n    }\n    \n    /**\n     * \\brief Supportred device features\n     * \n     * Queries the supported device features.\n     * \\returns Device features\n     */\n    const DxvkDeviceFeatures& features() const {\n      return m_capabilities.getFeatures();\n    }\n    \n    /**\n     * \\brief Memory properties\n     *\n     * Queries the memory types and memory heaps of\n     * the device. This is useful for memory allocators.\n     * \\returns Device memory properties\n     */\n    const VkPhysicalDeviceMemoryProperties& memoryProperties() const {\n      return m_capabilities.getMemoryInfo().core.memoryProperties;\n    }\n\n    /**\n     * \\brief Checks whether the adapter is usable for DXVK\n     *\n     * \\param [out] error Detailed error message on error\n     * \\returns \\c true if the adapter supports required features\n     */\n    bool isCompatible(std::string& error);\n\n    /**\n     * \\brief Retrieves memory heap info\n     * \n     * Returns properties of all available memory heaps,\n     * both device-local and non-local heaps, and the\n     * amount of memory allocated from those heaps by\n     * logical devices.\n     * \\returns Memory heap info\n     */\n    DxvkAdapterMemoryInfo getMemoryHeapInfo() const;\n    \n    /**\n     * \\brief Queries format feature support\n     *\n     * \\param [in] format Format to query\n     * \\returns Format feature bits\n     */\n    DxvkFormatFeatures getFormatFeatures(\n            VkFormat                  format) const;\n\n    /**\n     * \\brief Queries format limits\n     *\n     * \\param [in] query Format query info\n     * \\returns Format limits if the given image is supported\n     */\n    std::optional<DxvkFormatLimits> getFormatLimits(\n      const DxvkFormatQuery&          query) const;\n\n    /**\n     * \\brief Retrieves queue family indices\n     * \\returns Indices for all queue families\n     */\n    DxvkAdapterQueueIndices findQueueFamilies() const;\n    \n    /**\n     * \\brief Tests whether all required features are supported\n     * \n     * \\param [in] features Required device features\n     * \\returns \\c true if all features are supported\n     */\n    bool checkFeatureSupport(\n      const DxvkDeviceFeatures& required) const;\n    \n    /**\n     * \\brief Enables extensions for this adapter\n     *\n     * When creating a device, all extensions that\n     * are added using this method will be enabled\n     * in addition to the ones required by DXVK.\n     * This is used for OpenVR support.\n     */\n    void enableExtensions(\n      const DxvkExtensionList&  extensions);\n    \n    /**\n     * \\brief Creates a DXVK device\n     * \n     * Creates a logical device for this adapter.\n     * \\returns Device handle\n     */\n    Rc<DxvkDevice> createDevice();\n    \n    /**\n     * \\brief Imports a foreign device\n     * \n     * \\param [in] args Device import info\n     * \\returns Device handle\n     */\n    Rc<DxvkDevice> importDevice(\n      const DxvkDeviceImportInfo& args);\n    \n    /**\n     * \\brief Registers heap memory allocation\n     * \n     * Updates memory alloc info accordingly.\n     * \\param [in] heap Memory heap index\n     * \\param [in] allocated Allocated size delta\n     * \\param [in] used Used size delta\n     */\n    void notifyMemoryStats(\n            uint32_t            heap,\n            int64_t             allocated,\n            int64_t             used);\n    \n    /**\n     * \\brief Tests if the driver matches certain criteria\n     *\n     * \\param [in] driver Driver ID\n     * \\param [in] minVer Match versions starting with this one\n     * \\param [in] maxVer Match versions lower than this one\n     * \\returns \\c True if the driver matches these criteria\n     */\n    bool matchesDriver(\n            VkDriverIdKHR       driver,\n            Version             minVer,\n            Version             maxVer) const;\n\n    /**\n     * \\brief Tests if the driver matches certain criteria\n     *\n     * \\param [in] driver Driver ID\n     * \\returns \\c True if the driver matches these criteria\n     */\n    bool matchesDriver(\n            VkDriverIdKHR       driver) const;\n    \n    /**\n     * \\brief Checks whether this is a UMA system\n     *\n     * Basically tests whether all heaps are device-local.\n     * Can be used for various optimizations in client APIs.\n     * \\returns \\c true if the system has unified memory.\n     */\n    bool isUnifiedMemoryArchitecture() const;\n\n    /**\n     * \\brief Registers a relationship with another GPU\n     *\n     * Used for display enumeration purposes.\n     * \\param [in] dgpu Dedicated GPU adatper\n     */\n    void linkToDGPU(Rc<DxvkAdapter> dgpu) {\n        dgpu->m_linkedIGPUAdapter = this;\n        m_linkedToDGPU = true;\n    }\n\n    /**\n     * \\brief Retrieves linked integrated GPU\n     * \\returns Integrated GPU adapter\n     */\n    Rc<DxvkAdapter> linkedIGPUAdapter() const {\n        return m_linkedIGPUAdapter;\n    }\n\n    /**\n     * \\brief Checks whether the GPU is linked\n     * \\returns \\c true if the GPU is linked\n     */\n    bool isLinkedToDGPU() const {\n        return m_linkedToDGPU;\n    }\n\n  private:\n    \n    DxvkInstance*           m_instance  = nullptr;\n    VkPhysicalDevice        m_handle    = VK_NULL_HANDLE;\n    D3DKMT_HANDLE           m_kmtLocal = 0;\n\n    DxvkDeviceCapabilities  m_capabilities;\n\n    std::vector<VkExtensionProperties> m_extraExtensions;\n\n    Rc<DxvkAdapter>     m_linkedIGPUAdapter;\n    bool                m_linkedToDGPU = false;\n\n    std::array<DxvkAdapterMemoryStats, VK_MAX_MEMORY_HEAPS> m_memoryStats = { };\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_allocator.cpp",
    "content": "#include <algorithm>\n#include <utility>\n\n#include \"dxvk_allocator.h\"\n\n#include \"../util/util_bit.h\"\n#include \"../util/util_likely.h\"\n\nnamespace dxvk {\n\n  DxvkPageAllocator::DxvkPageAllocator() {\n\n  }\n\n\n  DxvkPageAllocator::~DxvkPageAllocator() {\n\n  }\n\n\n  int64_t DxvkPageAllocator::alloc(uint64_t size, uint64_t alignment) {\n    uint32_t pageCount = (size + PageSize - 1u) / PageSize;\n    uint32_t pageAlign = (alignment + PageSize - 1u) / PageSize;\n\n    return std::max<int64_t>(-1, int64_t(allocPages(pageCount, pageAlign)) * int64_t(PageSize));\n  }\n\n\n  int32_t DxvkPageAllocator::allocPages(uint32_t count, uint32_t alignment) {\n    int32_t index = searchFreeList(count);\n\n    while (index--) {\n      PageRange entry = m_freeList[index];\n\n      // The chunk index is the same regardless of alignment.\n      // Skip chunk if it does not accept new allocations.\n      uint32_t chunkIndex = entry.index >> ChunkPageBits;\n\n      if (unlikely(m_chunks[chunkIndex].disabled))\n        continue;\n\n      if (likely(!(entry.index & (alignment - 1u)))) {\n        // If the current free range is sufficiently aligned, we can use\n        // it as-is and simply modify the remaining free list entry.\n        uint32_t pageIndex = entry.index;\n\n        entry.index += count;\n        entry.count -= count;\n\n        insertFreeRange(entry, index);\n\n        m_chunks[chunkIndex].pagesUsed += count;\n        return pageIndex;\n      } else {\n        // Apply alignment and skip if the free range is too small.\n        uint32_t pageIndex = align(entry.index, alignment);\n\n        if (pageIndex + count > entry.index + entry.count)\n          continue;\n\n        // Insert free range before the first allocated page,\n        // guaranteed to be non-empty at this point.\n        PageRange prevRange = { };\n        prevRange.index = entry.index;\n        prevRange.count = pageIndex - entry.index;\n\n        insertFreeRange(prevRange, index);\n\n        // Insert free range after the last allocated page.\n        PageRange nextRange = { };\n        nextRange.index = pageIndex + count;\n        nextRange.count = entry.index + entry.count - nextRange.index;\n\n        if (nextRange.count)\n          insertFreeRange(nextRange, -1);\n\n        m_chunks[chunkIndex].pagesUsed += count;\n\n        return pageIndex;\n      }\n    }\n\n    return -1;    \n  }\n\n\n  bool DxvkPageAllocator::free(uint64_t address, uint64_t size) {\n    uint32_t pageIndex = address / PageSize;\n    uint32_t pageCount = (size + PageSize - 1u) / PageSize;\n\n    return freePages(pageIndex, pageCount);\n  }\n\n\n  bool DxvkPageAllocator::freePages(uint32_t index, uint32_t count) {\n    // Use the lookup table to quickly determine which\n    // free ranges we can actually merge with\n    int32_t prevRange = -1;\n    int32_t nextRange = -1;\n\n    if (index & ChunkPageMask)\n      prevRange = m_freeListLutByPage[index - 1];\n\n    if ((index + count) & ChunkPageMask)\n      nextRange = m_freeListLutByPage[index + count];\n\n    if (prevRange < 0) {\n      if (nextRange < 0) {\n        // No adjacent range, need to insert a new one\n        PageRange range = { };\n        range.index = index;\n        range.count = count;\n\n        insertFreeRange(range, -1);\n      } else {\n        // One adjacent range after the current one\n        PageRange range = m_freeList[nextRange];\n        range.index = index;\n        range.count += count;\n\n        insertFreeRange(range, nextRange);\n      }\n    } else if (nextRange < 0) {\n        // One adjacent range before the current one\n      PageRange range = m_freeList[prevRange];\n      range.count += count;\n\n      insertFreeRange(range, prevRange);\n    } else {\n      // Two adjacent ranges, need to merge with both\n      // and replace one while removing the other.\n      PageRange prev = m_freeList[prevRange];\n      PageRange next = m_freeList[nextRange];\n\n      PageRange mergedRange = { };\n      mergedRange.index = prev.index;\n      mergedRange.count = next.index + next.count - prev.index;\n\n      PageRange emptyRange = { };\n\n      // Remove the range at the higher index, then replace the one at the\n      // lower index with the merged range. The order is important here since\n      // having overlapping entries in the free list would cause issues for\n      // the look-up table, and using the correct indices is important since\n      // the index for the second operation could otherwise be invalidated.\n      insertFreeRange(emptyRange, std::max(prevRange, nextRange));\n      insertFreeRange(mergedRange, std::min(prevRange, nextRange));\n    }\n\n    uint32_t chunkIndex = index >> ChunkPageBits;\n    return !(m_chunks[chunkIndex].pagesUsed -= count);\n  }\n\n\n  uint32_t DxvkPageAllocator::addChunk(uint64_t size) {\n    int32_t chunkIndex = m_freeChunk;\n\n    if (chunkIndex < 0) {\n      chunkIndex = m_chunks.size();\n\n      m_freeListLutByPage.resize((chunkIndex + 1u) << ChunkPageBits, -1);\n      m_chunks.emplace_back();\n    }\n\n    auto& chunk = m_chunks[chunkIndex];\n    m_freeChunk = chunk.nextChunk;\n\n    chunk.pageCount = size / PageSize;\n    chunk.pagesUsed = 0u;\n    chunk.nextChunk = -1;\n    chunk.disabled = false;\n\n    PageRange pageRange = { };\n    pageRange.index = uint32_t(chunkIndex) << ChunkPageBits;\n    pageRange.count = chunk.pageCount;\n\n    insertFreeRange(pageRange, -1);\n\n    return uint32_t(chunkIndex);\n  }\n\n\n  void DxvkPageAllocator::removeChunk(uint32_t chunkIndex) {\n    auto& chunk = m_chunks[chunkIndex];\n    chunk.pageCount = 0u;\n    chunk.pagesUsed = 0u;\n    chunk.nextChunk = std::exchange(m_freeChunk, int32_t(chunkIndex));\n    chunk.disabled = true;\n\n    uint32_t pageIndex = chunkIndex << ChunkPageBits;\n\n    PageRange pageRange = { };\n    pageRange.index = pageIndex;\n    pageRange.count = 0;\n\n    insertFreeRange(pageRange, m_freeListLutByPage[pageIndex]);\n  }\n\n\n  void DxvkPageAllocator::killChunk(uint32_t chunkIndex) {\n    m_chunks[chunkIndex].disabled = true;\n  }\n\n\n  void DxvkPageAllocator::reviveChunk(uint32_t chunkIndex) {\n    m_chunks[chunkIndex].disabled = false;\n  }\n\n\n  uint32_t DxvkPageAllocator::reviveChunks() {\n    uint32_t count = 0u;\n\n    for (uint32_t i = 0; i < m_chunks.size(); i++) {\n      if (m_chunks[i].pageCount && m_chunks[i].disabled) {\n        m_chunks[i].disabled = false;\n        count += 1u;\n      }\n    }\n\n    return count;\n  }\n\n\n  void DxvkPageAllocator::getPageAllocationMask(uint32_t chunkIndex, uint32_t* pageMask) const {\n    // Initialize bit mask with all ones\n    const auto& chunk = m_chunks[chunkIndex];\n\n    uint32_t fullCount = chunk.pageCount / 32u;\n    uint32_t lastCount = chunk.pageCount % 32u;\n\n    for (uint32_t i = 0; i < fullCount; i++)\n      pageMask[i] = ~0u;\n\n    if (lastCount)\n      pageMask[fullCount] = (1u << lastCount) - 1u;\n\n    // Iterate over free list and set all pages included\n    // in the current chunk to 0.\n    for (PageRange range : m_freeList) {\n      if ((range.index >> ChunkPageBits) != chunkIndex)\n        continue;\n\n      range.index &= ChunkPageMask;\n\n      uint32_t index = range.index / 32u;\n      uint32_t shift = range.index % 32u;\n\n      if (shift + range.count < 32u) {\n        // Entire free range fits in one single mask\n        pageMask[index] ^= ((1u << range.count) - 1u) << shift;\n      } else {\n        if (shift) {\n          pageMask[index++] ^= ~0u << shift;\n          range.count -= 32u - shift;\n        }\n\n        while (range.count >= 32u) {\n          pageMask[index++] = 0u;\n          range.count -= 32u;\n        }\n\n        if (range.count)\n          pageMask[index++] &= ~0u << range.count;\n      }\n    }\n  }\n\n\n  int32_t DxvkPageAllocator::searchFreeList(uint32_t count) {\n    // Find the insertion index of a free list entry with the given page count.\n    // All entries with an index lower than but not equal to the return value\n    // will have a page count greater than or equal to count.\n    if (unlikely(m_freeList.empty()))\n      return 0u;\n\n    // Do a binary search, but optimize for the common\n    // case where we request a small page count\n    uint32_t lo = 0u;\n    uint32_t hi = m_freeList.size();\n\n    if (count <= m_freeList.back().count)\n      return int32_t(hi);\n\n    while (lo < hi) {\n      uint32_t mid = (lo + hi) / 2u;\n\n      if (count <= m_freeList[mid].count)\n        lo = mid + 1;\n      else\n        hi = mid;\n    }\n\n    return int32_t(lo);\n  }\n\n\n  void DxvkPageAllocator::addLutEntry(const PageRange& range, int32_t index) {\n    m_freeListLutByPage[range.index] = index;\n    m_freeListLutByPage[range.index + range.count - 1u] = index;\n  }\n\n\n  void DxvkPageAllocator::removeLutEntry(const PageRange& range) {\n    m_freeListLutByPage[range.index] = -1;\n    m_freeListLutByPage[range.index + range.count - 1u] = -1;\n  }\n\n\n  void DxvkPageAllocator::insertFreeRange(PageRange newRange, int32_t currentIndex) {\n    size_t count = m_freeList.size();\n    size_t index = size_t(currentIndex);\n\n    if (unlikely(currentIndex < 0)) {\n      m_freeList.emplace_back();\n      index = count++;\n    }\n\n    // Remove old range from the LUT since it gets replaced\n    PageRange oldRange = m_freeList[index];\n\n    if (likely(oldRange.count))\n      removeLutEntry(oldRange);\n\n    // Move range within the free list until the proper ordering\n    // is restored again and update LUT entries for all ranges we\n    // move in the process.\n    if (newRange.count < oldRange.count) {\n      while (index + 1u < count) {\n        PageRange next = m_freeList[index + 1u];\n\n        if (newRange.count >= next.count)\n          break;\n\n        addLutEntry(next, index);\n        m_freeList[index++] = next;\n      }\n    } else if (newRange.count > oldRange.count) {\n      while (index) {\n        PageRange prev = m_freeList[index - 1u];\n\n        if (newRange.count <= prev.count)\n          break;\n\n        addLutEntry(prev, index);\n        m_freeList[index--] = prev;\n      }\n    }\n\n    if (newRange.count) {\n      m_freeList[index] = newRange;\n      addLutEntry(newRange, index);\n    } else {\n      m_freeList.pop_back();\n    }\n  }\n\n\n\n  DxvkPoolAllocator::DxvkPoolAllocator(DxvkPageAllocator& pageAllocator)\n  : m_pageAllocator(&pageAllocator) {\n\n  }\n\n\n  DxvkPoolAllocator::~DxvkPoolAllocator() {\n\n  }\n\n\n  int64_t DxvkPoolAllocator::alloc(uint64_t size) {\n    uint32_t listIndex = computeListIndex(size);\n    uint32_t poolCapacity = computePoolCapacity(listIndex);\n\n    // Obtain a page for the size category\n    int32_t pageIndex = m_pageLists[listIndex].head;\n\n    if (likely(pageIndex >= 0)) {\n      uint32_t chunkIndex = uint32_t(pageIndex) >> DxvkPageAllocator::ChunkPageBits;\n\n      // If the selected page is from a dead chunk, do not allocate\n      // into it anymore so that the chunk can actually be freed.\n      if (unlikely(!m_pageAllocator->chunkIsAvailable(chunkIndex))) {\n        int32_t nextIndex = pageIndex;\n\n        do {\n          // This works because we add pages to the end\n          removePageFromList(nextIndex, listIndex);\n          addPageToList(nextIndex, listIndex);\n\n          nextIndex = m_pageLists[listIndex].head;\n          chunkIndex = uint32_t(nextIndex) >> DxvkPageAllocator::ChunkPageBits;\n        } while (nextIndex != pageIndex && !m_pageAllocator->chunkIsAvailable(chunkIndex));\n\n        // Allocate a new page if the entire list is dead\n        pageIndex = nextIndex != pageIndex ? nextIndex : -1;\n      }\n    }\n\n    if (unlikely(pageIndex < 0)) {\n      if ((pageIndex = allocPage(listIndex)) < 0)\n        return -1;\n\n      // Initialize suballocator for the page\n      PageInfo& page = m_pageInfos[pageIndex];\n\n      if (likely(poolCapacity <= MaskBits)) {\n        // Initialize free mask with the first item marked as used\n        page.pool = (MaskType(2) << (poolCapacity - 1u)) - 2u;\n      } else {\n        // This is also going to have its first item used already\n        page.pool = allocPagePool(poolCapacity);\n      }\n\n      return computeByteAddress(pageIndex, 0, listIndex);\n    }\n\n    if (likely(poolCapacity <= MaskBits)) {\n      // Fast path that uses the pool index as an allocator.\n      // Frequent allocations should ideally hit this path.\n      PageInfo& page = m_pageInfos[pageIndex];\n\n      uint32_t itemIndex = bit::tzcnt(page.pool);\n      page.pool &= page.pool - 1u;\n\n      if (unlikely(!page.pool))\n        removePageFromList(pageIndex, listIndex);\n\n      return computeByteAddress(pageIndex, itemIndex, listIndex);\n    } else {\n      PageInfo& page = m_pageInfos[pageIndex];\n      PagePool& pool = m_pagePools[page.pool];\n\n      // Check top-level masks to find which low-level mask to use\n      uint32_t maskIndex = bit::tzcnt(uint32_t(pool.freeMask));\n      MaskType maskBit = MaskType(1) << maskIndex;\n\n      pool.usedMask |= maskBit;\n\n      // Allocate item from the selected low-level mask\n      MaskType& mask = pool.subPools[maskIndex];\n      uint32_t itemIndex = bit::tzcnt(mask) + maskIndex * MaskBits;\n\n      if (!(mask &= mask - 1u)) {\n        pool.freeMask &= ~maskBit;\n\n        if (unlikely(!pool.freeMask))\n          removePageFromList(pageIndex, listIndex);\n      }\n\n      return computeByteAddress(pageIndex, itemIndex, listIndex);\n    }\n  }\n\n\n  bool DxvkPoolAllocator::free(uint64_t address, uint64_t size) {\n    uint32_t listIndex = computeListIndex(size);\n\n    uint32_t pageIndex = computePageIndexFromByteAddress(address);\n    uint32_t itemIndex = computeItemIndexFromByteAddress(address, listIndex);\n\n    uint32_t poolCapacity = computePoolCapacity(listIndex);\n\n    // Return the allocation to the given pool and add the page back\n    // to the free list if it was previously full. If the page is now\n    // unused, return it to the allocator.\n    if (likely(poolCapacity <= MaskBits)) {\n      PageInfo& page = m_pageInfos[pageIndex];\n\n      if (unlikely(!page.pool))\n        addPageToList(pageIndex, listIndex);\n\n      page.pool |= MaskType(1) << itemIndex;\n\n      if (unlikely(bit::tzcnt(page.pool + 1u) >= poolCapacity))\n        return freePage(pageIndex, listIndex);\n\n      return false;\n    } else {\n      PageInfo& page = m_pageInfos[pageIndex];\n      PagePool& pool = m_pagePools[page.pool];\n\n      if (unlikely(!pool.freeMask))\n        addPageToList(pageIndex, listIndex);\n\n      uint32_t maskIndex = itemIndex / MaskBits;\n      MaskType maskBit = MaskType(1) << maskIndex;\n\n      MaskType& mask = pool.subPools[maskIndex];\n      mask |= MaskType(1) << (itemIndex % MaskBits);\n\n      pool.freeMask |= maskBit;\n\n      if (!(mask + 1u)) {\n        pool.usedMask &= ~maskBit;\n\n        if (unlikely(!pool.usedMask)) {\n          freePagePool(page.pool);\n          return freePage(pageIndex, listIndex);\n        }\n      }\n\n      return false;\n    }\n  }\n\n\n  int32_t DxvkPoolAllocator::allocPage(uint32_t listIndex) {\n    int32_t pageIndex = m_pageAllocator->allocPages(1u, 1u);\n\n    if (unlikely(pageIndex < 0))\n      return -1;\n\n    if (unlikely(uint32_t(pageIndex) >= m_pageInfos.size())) {\n      uint32_t chunkCount = (pageIndex >> DxvkPageAllocator::ChunkPageBits) + 1u;\n      m_pageInfos.resize(chunkCount << DxvkPageAllocator::ChunkPageBits);\n    }\n\n    addPageToList(pageIndex, listIndex);\n    return pageIndex;\n  }\n\n\n  bool DxvkPoolAllocator::freePage(uint32_t pageIndex, uint32_t listIndex) {\n    removePageFromList(pageIndex, listIndex);\n\n    return m_pageAllocator->freePages(pageIndex, 1u);\n  }\n\n\n  void DxvkPoolAllocator::addPageToList(uint32_t pageIndex, uint32_t listIndex) {\n    // Add page to the end of the list. Allocations within a single page\n    // often have similar lifetimes, so not reusing the page immediately\n    // increases the chances of it getting freed.\n    PageInfo& page = m_pageInfos[pageIndex];\n    page.prev = m_pageLists[listIndex].tail;\n\n    if (page.prev >= 0)\n      m_pageInfos[page.prev].next = pageIndex;\n    else\n      m_pageLists[listIndex].head = pageIndex;\n\n    m_pageLists[listIndex].tail = pageIndex;\n  }\n\n\n  void DxvkPoolAllocator::removePageFromList(uint32_t pageIndex, uint32_t listIndex) {\n    // The list of non-full pages is organized in a double-linked list so\n    // that entries can get removed in constant time whenever a page gets\n    // filled or removed.\n    PageInfo& page = m_pageInfos[pageIndex];\n\n    if (page.prev >= 0)\n      m_pageInfos[page.prev].next = page.next;\n    else\n      m_pageLists[listIndex].head = page.next;\n\n    if (page.next >= 0)\n      m_pageInfos[page.next].prev = page.prev;\n    else\n      m_pageLists[listIndex].tail = page.prev;\n\n    page.prev = -1;\n    page.next = -1;\n  }\n\n\n  uint32_t DxvkPoolAllocator::allocPagePool(uint32_t capacity) {\n    PagePool* pool;\n    uint32_t poolIndex;\n\n    if (unlikely(m_freePool < 0)) {\n      // Allocate new pool as necessary\n      pool = &m_pagePools.emplace_back();\n      pool->subPools.fill(MaskType(-1));\n\n      poolIndex = uint32_t(m_pagePools.size() - 1u);\n    } else {\n      // Otherwise, just use the free list\n      pool = &m_pagePools[m_freePool];\n      poolIndex = std::exchange(m_freePool, pool->nextPool);\n    }\n\n    // Initialize free mask to the correct capacity. Everything\n    // else is assumed to be in its default initialized state.\n    uint32_t maskCount = capacity / MaskBits;\n    pool->freeMask = (1u << maskCount) - 1u;\n    pool->usedMask = 1u;\n    pool->subPools[0] = MaskType(-2);\n    return poolIndex;\n  }\n\n\n  void DxvkPoolAllocator::freePagePool(uint32_t poolIndex) {\n    PagePool* pool = &m_pagePools[poolIndex];\n    pool->nextPool = m_freePool;\n\n    m_freePool = poolIndex;\n  }\n\n\n  uint32_t DxvkPoolAllocator::computeListIndex(uint64_t size) {\n    size = std::max(size, MinSize);\n\n    // Use leading zero count to determine the size category and\n    // basically round up to the next power of two. Pools are\n    // ordered by allocation size in descending order.\n    return bit::lzcnt(uint32_t(size) - 1u) - (33u - DxvkPageAllocator::PageBits);\n  }\n\n\n  uint32_t DxvkPoolAllocator::computePoolCapacity(uint32_t index) {\n    // Number of objects we can allocate in the pool\n    return 2u << index;\n  }\n\n\n  uint64_t DxvkPoolAllocator::computeByteAddress(uint32_t page, uint32_t index, uint32_t list) {\n    uint32_t shift = DxvkPageAllocator::PageBits - 1u - list;\n    return DxvkPageAllocator::PageSize * uint64_t(page) + (index << shift);\n  }\n\n\n  uint32_t DxvkPoolAllocator::computePageIndexFromByteAddress(uint64_t address) {\n    return address / DxvkPageAllocator::PageSize;\n  }\n\n\n  uint32_t DxvkPoolAllocator::computeItemIndexFromByteAddress(uint64_t address, uint32_t list) {\n    uint32_t shift = DxvkPageAllocator::PageBits - 1u - list;\n    return (address & (DxvkPageAllocator::PageSize - 1u)) >> shift;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_allocator.h",
    "content": "#pragma once\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <type_traits>\n#include <vector>\n\n#include \"../util/util_env.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Page allocator\n   *\n   * Implements a best-fit allocation strategy for coarse allocations\n   * using an ordered free list. While allocating and freeing memory\n   * are both linear in the worst case, minimum-size allocations can\n   * generally be performed in constant time, with larger allocations\n   * getting gradually slower.\n   */\n  class DxvkPageAllocator {\n\n  public:\n\n    /// Page size. While the allocator interface is fully designed around\n    /// pages, defining a page size is useful for classes built on top of it.\n    constexpr static uint32_t PageBits = 16;\n    constexpr static uint64_t PageSize = 1u << PageBits;\n\n    /// Maximum number of pages per chunk. Chunks represent contiguous memory\n    /// allocations whose free regions can be merged.\n    constexpr static uint32_t ChunkPageBits = 12u;\n    constexpr static uint32_t ChunkPageMask = (1u << ChunkPageBits) - 1u;\n\n    /// Chunk address bits. Can be used to quickly compute the chunk index\n    /// and allocation offset within the chunk from a raw byte address.\n    constexpr static uint32_t ChunkAddressBits = ChunkPageBits + PageBits;\n    constexpr static uint64_t ChunkAddressMask = (1u << ChunkAddressBits) - 1u;\n\n    constexpr static uint64_t MaxChunkSize = 1u << ChunkAddressBits;\n\n\n    DxvkPageAllocator();\n\n    ~DxvkPageAllocator();\n\n    /**\n     * \\brief Queries total number of chunks\n     *\n     * This number may include chuks that have already been removed.\n     * \\returns Total chunk count\n     */\n    uint32_t chunkCount() const {\n      return uint32_t(m_chunks.size());\n    }\n\n    /**\n     * \\brief Queries number of available pages in a chunk\n     *\n     * \\param [in] chunkIndex Chunk index\n     * \\returns Capacity of the given chunk\n     */\n    uint32_t pageCount(uint32_t chunkIndex) const {\n      return m_chunks.at(chunkIndex).pageCount;\n    }\n\n    /**\n     * \\brief Queries number of allocated pages in a chunk\n     *\n     * \\param [in] chunkIndex Chunk index\n     * \\returns Used page count in the given chunk\n     */\n    uint32_t pagesUsed(uint32_t chunkIndex) const {\n      return m_chunks.at(chunkIndex).pagesUsed;\n    }\n\n    /**\n     * \\brief Checks whether a chunk is alive\n     *\n     * \\param [in] chunkIndex Chunk index\n     * \\returns \\c true if chunk can be used\n     */\n    bool chunkIsAvailable(uint32_t chunkIndex) const {\n      return !m_chunks.at(chunkIndex).disabled;\n    }\n\n    /**\n     * \\brief Allocates given number of bytes from the pool\n     *\n     * \\param [in] size Allocation size, in bytes\n     * \\param [in] alignment Required aligment, in bytes\n     * \\returns Allocated byte address\n     */\n    int64_t alloc(uint64_t size, uint64_t alignment);\n\n    /**\n     * \\brief Allocates pages\n     *\n     * \\param [in] count Number of pages to allocate.\n     *    Must be multiple of \\c alignment.\n     * \\param [in] alignment Required alignment, in pages\n     * \\returns Page index, or -1 if not enough memory\n     *    is available in the chunk.\n     */\n    int32_t allocPages(uint32_t count, uint32_t alignment);\n\n    /**\n     * \\brief Frees allocated memory region\n     *\n     * \\param [in] address Allocated address, in bytes\n     * \\param [in] size Allocation size, in bytes\n     * \\returns \\c true if a chunk was freed\n     */\n    bool free(uint64_t address, uint64_t size);\n\n    /**\n     * \\brief Frees pages\n     *\n     * \\param [in] index Index of first page to free\n     * \\param [in] count Number of pages to free\n     * \\returns \\c true if a chunk was freed\n     */\n    bool freePages(uint32_t index, uint32_t count);\n\n    /**\n     * \\brief Adds a chunk to the allocator\n     *\n     * Adds the given region to the free list, so\n     * that subsequent allocations can succeed.\n     * \\param [in] size Total chunk size, in bytes\n     * \\returns Chunk index\n     */\n    uint32_t addChunk(uint64_t size);\n\n    /**\n     * \\brief Removes chunk from the allocator\n     *\n     * Must only be used if the entire chunk is unused.\n     * \\param [in] chunkIndex Chunk index\n     */\n    void removeChunk(uint32_t chunkIndex);\n\n    /**\n     * \\brief Disables a chunk\n     *\n     * Makes an entire chunk unavailable for subsequent allocations.\n     * This can be useful when moving allocations out of that chunk\n     * in an attempt to free some memory.\n     * \\param [in] chunkIndex Chunk index\n     */\n    void killChunk(uint32_t chunkIndex);\n\n    /**\n     * \\brief Re-enables a chunk\n     *\n     * Makes all disabled chunks available for allocations again.\n     * Should be used before allocating new chunk memory.\n     * \\param [in] chunkIndex Chunk index\n     */\n    void reviveChunk(uint32_t chunkIndex);\n\n    /**\n     * \\brief Re-enables all disabled chunks\n     * \\returns Number of chunks re-enabled\n     */\n    uint32_t reviveChunks();\n\n    /**\n     * \\brief Queries page allocation mask\n     *\n     * Should be used for informational purposes only. Retrieves\n     * a bit mask where each set bit represents an allocated page,\n     * with the page index corresponding to the page index. The\n     * output array must be sized appropriately.\n     * \\param [out] chunkIndex Chunk index\n     * \\param [out] pageMask Page mask\n     */\n    void getPageAllocationMask(uint32_t chunkIndex, uint32_t* pageMask) const;\n\n  private:\n\n    struct ChunkInfo {\n      uint32_t  pageCount = 0u;\n      uint32_t  pagesUsed = 0u;\n      int32_t   nextChunk = -1;\n      bool      disabled  = false;\n    };\n\n    struct PageRange {\n      uint32_t  index = 0u;\n      uint32_t  count = 0u;\n    };\n\n    std::vector<PageRange>  m_freeList;\n    std::vector<int32_t>    m_freeListLutByPage;\n\n    std::vector<ChunkInfo>  m_chunks;\n    int32_t                 m_freeChunk = -1;\n\n    int32_t searchFreeList(uint32_t count);\n\n    void addLutEntry(const PageRange& range, int32_t index);\n\n    void removeLutEntry(const PageRange& range);\n\n    void insertFreeRange(PageRange newRange, int32_t currentIndex);\n\n  };\n\n\n  /**\n   * \\brief Pool allocator\n   *\n   * Implements an fast allocator for objects less than the size of one\n   * page. Uses a regular page allocator to allocate backing storage for\n   * each object pool.\n   */\n  class DxvkPoolAllocator {\n    // Use the machine's native word size for bit masks to enable fast paths\n    using MaskType = std::conditional_t<env::is32BitHostPlatform(), uint32_t, uint64_t>;\n\n    constexpr static uint32_t MaskBits = sizeof(MaskType) * 8u;\n\n    constexpr static uint32_t MaxCapacityBits = 8u;\n    constexpr static uint32_t MaxCapacity = 1u << MaxCapacityBits;\n\n    constexpr static uint32_t MasksPerPage = MaxCapacity / MaskBits;\n  public:\n\n    /// Allocation granularity. Smaller allocations are rounded up to\n    /// be at least of this size.\n    constexpr static uint64_t MinSize = DxvkPageAllocator::PageSize >> MaxCapacityBits;\n\n    /// Minimum supported allocation size. Always set to half a page\n    /// so that any pools we manage can at least hold two allocations.\n    constexpr static uint64_t MaxSize = DxvkPageAllocator::PageSize >> 1u;\n\n    DxvkPoolAllocator(DxvkPageAllocator& pageAllocator);\n\n    ~DxvkPoolAllocator();\n\n    /**\n     * \\brief Allocates given number of bytes from the pool\n     *\n     * \\param [in] size Allocation size, in bytes\n     * \\returns Allocated address, in bytes\n     */\n    int64_t alloc(uint64_t size);\n\n    /**\n     * \\brief Frees allocated memory region\n     *\n     * \\param [in] address Memory address, in bytes\n     * \\param [in] size Allocation size, in bytes\n     * \\returns \\c true if a chunk was freed\n     */\n    bool free(uint64_t address, uint64_t size);\n\n  private:\n\n    struct PageList {\n      int32_t   head = -1;\n      int32_t   tail = -1;\n    };\n\n    struct PageInfo {\n      MaskType  pool = 0u;\n      int32_t   prev = -1;\n      int32_t   next = -1;\n    };\n\n    struct PagePool {\n      int32_t   nextPool  = -1;\n      uint16_t  freeMask  = 0u;\n      uint16_t  usedMask  = 0u;\n\n      std::array<MaskType, MasksPerPage> subPools = { };\n    };\n\n    DxvkPageAllocator*      m_pageAllocator = nullptr;\n\n    std::vector<PageInfo>   m_pageInfos;\n    std::vector<PagePool>   m_pagePools;\n    int32_t                 m_freePool = -1;\n\n    std::array<PageList, MaxCapacityBits> m_pageLists = { };\n\n    int32_t allocPage(uint32_t listIndex);\n\n    bool freePage(uint32_t pageIndex, uint32_t listIndex);\n\n    void addPageToList(uint32_t pageIndex, uint32_t listIndex);\n\n    void removePageFromList(uint32_t pageIndex, uint32_t listIndex);\n\n    uint32_t allocPagePool(uint32_t capacity);\n\n    void freePagePool(uint32_t poolIndex);\n\n    static uint32_t computeListIndex(uint64_t size);\n\n    static uint32_t computePoolCapacity(uint32_t index);\n\n    static uint64_t computeByteAddress(uint32_t page, uint32_t index, uint32_t list);\n\n    static uint32_t computePageIndexFromByteAddress(uint64_t address);\n\n    static uint32_t computeItemIndexFromByteAddress(uint64_t address, uint32_t list);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_annotation.h",
    "content": "#pragma once\n\n#include <d3d11_1.h>\n#include <d3d9.h>\n\nMIDL_INTERFACE(\"7f2c2f72-1cc8-4979-8d9c-7e3faeddecde\")\nIDXVKUserDefinedAnnotation : public ID3DUserDefinedAnnotation {\n\npublic:\n\n  INT STDMETHODCALLTYPE BeginEvent(\n          LPCWSTR                 Name) final {\n    return this->BeginEvent(0, Name);\n  }\n\n  void STDMETHODCALLTYPE SetMarker(\n          LPCWSTR                 Name) final {\n    this->SetMarker(0, Name);\n  }\n\n  virtual INT STDMETHODCALLTYPE BeginEvent(\n          D3DCOLOR                Color,\n          LPCWSTR                 Name) = 0;\n\n  virtual void STDMETHODCALLTYPE SetMarker(\n          D3DCOLOR                Color,\n          LPCWSTR                 Name) = 0;\n\n};\n\n#ifndef _MSC_VER\n__CRT_UUID_DECL(IDXVKUserDefinedAnnotation, 0x7f2c2f72,0x1cc8,0x4979,0x8d,0x9c,0x7e,0x3f,0xae,0xdd,0xec,0xde);\n#endif\n"
  },
  {
    "path": "src/dxvk/dxvk_barrier.cpp",
    "content": "#include \"dxvk_barrier.h\"\n#include \"dxvk_device.h\"\n\nnamespace dxvk {\n  \n  DxvkBarrierTracker::DxvkBarrierTracker() {\n    // Having an accessible 0 node makes certain things easier to\n    // implement and allows us to use 0 as an invalid node index.\n    m_nodes.emplace_back();\n\n    // Pre-allocate root nodes for the implicit hash table\n    for (uint32_t i = 0; i < 2u * HashTableSize; i++)\n      allocateNode();\n  }\n\n\n  DxvkBarrierTracker::~DxvkBarrierTracker() {\n\n  }\n\n\n  bool DxvkBarrierTracker::findRange(\n    const DxvkAddressRange&           range,\n          DxvkAccess                  accessType) const {\n    uint32_t rootIndex = computeRootIndex(range, accessType);\n    uint32_t nodeIndex = findNode(range, rootIndex);\n\n    if (likely(!nodeIndex || range.accessOp == DxvkAccessOp::None))\n      return nodeIndex;\n\n    // If we are checking for a specific order-invariant store\n    // op, the op must have been the only op used to access the\n    // resource, and the tracked range must cover the requested\n    // range in its entirety so we can rule out that other parts\n    // of the resource have been accessed in a different way.\n    const auto& node = m_nodes[nodeIndex];\n\n    if (node.addressRange.accessOp != range.accessOp)\n      return true;\n\n    return !node.addressRange.contains(range);\n  }\n\n\n  void DxvkBarrierTracker::insertRange(\n    const DxvkAddressRange&           range,\n          DxvkAccess                  accessType) {\n    // If we can just insert the node with no conflicts,\n    // we don't have to do anything.\n    uint32_t rootIndex = computeRootIndex(range, accessType);\n    uint32_t nodeIndex = insertNode(range, rootIndex);\n\n    if (likely(!nodeIndex))\n      return;\n\n    // If there's an existing node and it contains the entire\n    // range we want to add already, also don't do anything.\n    // If there are conflicting access ops, reset it.\n    auto& node = m_nodes[nodeIndex];\n\n    if (node.addressRange.accessOp != range.accessOp)\n      node.addressRange.accessOp = DxvkAccessOp::None;\n\n    if (node.addressRange.contains(range))\n      return;\n\n    // Otherwise, check if there are any other overlapping ranges.\n    // If that is not the case, simply update the range we found.\n    bool hasOverlap = false;\n\n    if (range.rangeStart < node.addressRange.rangeStart) {\n      DxvkAddressRange testRange;\n      testRange.resource = range.resource;\n      testRange.rangeStart = range.rangeStart;\n      testRange.rangeEnd = node.addressRange.rangeStart - 1u;\n\n      hasOverlap = findNode(testRange, rootIndex);\n    }\n\n    if (range.rangeEnd > node.addressRange.rangeEnd && !hasOverlap) {\n      DxvkAddressRange testRange;\n      testRange.resource = range.resource;\n      testRange.rangeStart = node.addressRange.rangeEnd + 1u;\n      testRange.rangeEnd = range.rangeEnd;\n\n      hasOverlap = findNode(testRange, rootIndex);\n    }\n\n    if (!hasOverlap) {\n      node.addressRange.rangeStart = std::min(node.addressRange.rangeStart, range.rangeStart);\n      node.addressRange.rangeEnd = std::max(node.addressRange.rangeEnd, range.rangeEnd);\n      return;\n    }\n\n    // If there are multiple ranges overlapping the one being\n    // inserted, remove them all and insert the merged range.\n    DxvkAddressRange mergedRange = range;\n\n    while (nodeIndex) {\n      auto& node = m_nodes[nodeIndex];\n      mergedRange.rangeStart = std::min(mergedRange.rangeStart, node.addressRange.rangeStart);\n      mergedRange.rangeEnd = std::max(mergedRange.rangeEnd, node.addressRange.rangeEnd);\n\n      if (mergedRange.accessOp != node.addressRange.accessOp)\n        mergedRange.accessOp = DxvkAccessOp::None;\n\n      removeNode(nodeIndex, rootIndex);\n\n      nodeIndex = findNode(range, rootIndex);\n    }\n\n    insertNode(mergedRange, rootIndex);\n  }\n\n\n  void DxvkBarrierTracker::clear() {\n    m_rootMaskValid = 0u;\n\n    while (m_rootMaskSubtree) {\n      // Free subtrees if any, but keep the root node intact\n      uint32_t rootIndex = bit::tzcnt(m_rootMaskSubtree) + 1u;\n\n      auto& root = m_nodes[rootIndex];\n\n      if (root.header) {\n        freeNode(root.child(0));\n        freeNode(root.child(1));\n\n        root.header = 0u;\n      }\n\n      m_rootMaskSubtree &= m_rootMaskSubtree - 1u;\n    }\n  }\n\n\n  uint32_t DxvkBarrierTracker::allocateNode() {\n    if (!m_free.empty()) {\n      uint32_t nodeIndex = m_free.back();\n      m_free.pop_back();\n\n      // Free any subtree that the node might still have\n      auto& node = m_nodes[nodeIndex];\n      freeNode(node.child(0));\n      freeNode(node.child(1));\n\n      node.header = 0u;\n      return nodeIndex;\n    } else {\n      // Allocate entirely new node in the array\n      uint32_t nodeIndex = m_nodes.size();\n      m_nodes.emplace_back();\n      return nodeIndex;\n    }\n  }\n\n\n  void DxvkBarrierTracker::freeNode(uint32_t node) {\n    if (node)\n      m_free.push_back(node);\n  }\n\n\n  uint32_t DxvkBarrierTracker::findNode(\n    const DxvkAddressRange&           range,\n          uint32_t                    rootIndex) const {\n    // Check if the given root is valid at all\n    uint64_t rootBit = uint64_t(1u) << (rootIndex - 1u);\n\n    if (!(m_rootMaskValid & rootBit))\n      return false;\n\n    // Traverse search tree normally\n    uint32_t nodeIndex = rootIndex;\n\n    while (nodeIndex) {\n      auto& node = m_nodes[nodeIndex];\n\n      if (node.addressRange.overlaps(range))\n        return nodeIndex;\n\n      nodeIndex = node.child(uint32_t(node.addressRange.lt(range)));\n    }\n\n    return 0u;\n  }\n\n\n  uint32_t DxvkBarrierTracker::insertNode(\n    const DxvkAddressRange&           range,\n          uint32_t                    rootIndex) {\n    // Check if the given root is valid at all\n    uint64_t rootBit = uint64_t(1u) << (rootIndex - 1u);\n\n    if (!(m_rootMaskValid & rootBit)) {\n      m_rootMaskValid |= rootBit;\n\n      // Update root node as necessary. Also reset\n      // its red-ness if we set it during deletion.\n      auto& node = m_nodes[rootIndex];\n      node.header = 0;\n      node.addressRange = range;\n      return 0;\n    } else {\n      // Traverse tree and abort if we find any range\n      // overlapping the one we're trying to insert.\n      uint32_t parentIndex = rootIndex;\n      uint32_t childIndex = 0u;\n\n      while (true) {\n        auto& parent = m_nodes[parentIndex];\n\n        if (parent.addressRange.overlaps(range))\n          return parentIndex;\n\n        childIndex = parent.addressRange.lt(range);\n\n        if (!parent.child(childIndex))\n          break;\n\n        parentIndex = parent.child(childIndex);\n      }\n\n      // Create and insert new node into the tree\n      uint32_t nodeIndex = allocateNode();\n\n      auto& parent = m_nodes[parentIndex];\n      parent.setChild(childIndex, nodeIndex);\n\n      auto& node = m_nodes[nodeIndex];\n      node.setRed(true);\n      node.setParent(parentIndex);\n      node.addressRange = range;\n\n      // Only do the fixup to maintain red-black properties if\n      // we haven't marked the root node as red in a deletion.\n      if (parentIndex != rootIndex && !m_nodes[rootIndex].isRed())\n        rebalancePostInsert(nodeIndex, rootIndex);\n\n      m_rootMaskSubtree |= rootBit;\n      return 0u;\n    }\n  }\n\n\n  void DxvkBarrierTracker::removeNode(\n          uint32_t                    nodeIndex,\n          uint32_t                    rootIndex) {\n    auto& node = m_nodes[nodeIndex];\n\n    uint32_t l = node.child(0);\n    uint32_t r = node.child(1);\n\n    if (l && r) {\n      // Both children are valid. Take the payload from the smallest\n      // node in the right subtree and delete that node instead.\n      uint32_t childIndex = r;\n\n      while (m_nodes[childIndex].child(0))\n        childIndex = m_nodes[childIndex].child(0);\n\n      node.addressRange = m_nodes[childIndex].addressRange;\n      removeNode(childIndex, rootIndex);\n    } else {\n      // Deletion is expected to be exceptionally rare, to the point of\n      // being irrelevant in practice since it can only ever happen if an\n      // app reads multiple disjoint blocks of a resource and then reads\n      // another range covering multiple of those blocks again. Instead\n      // of implementing a complex post-delete fixup, mark the root as\n      // red and allow the tree to go unbalanced until the next reset.\n      if (!node.isRed() && (nodeIndex != rootIndex))\n        m_nodes[rootIndex].setRed(true);\n\n      // We're deleting the a node with one or no children. To avoid\n      // special-casing the root node, copy the child node to it and\n      // update links as necessary.\n      uint32_t childIndex = std::max(l, r);\n      uint32_t parentIndex = node.parent();\n\n      if (childIndex) {\n        auto& child = m_nodes[childIndex];\n\n        uint32_t cl = child.child(0);\n        uint32_t cr = child.child(1);\n\n        node.setChild(0, cl);\n        node.setChild(1, cr);\n\n        if (nodeIndex != rootIndex)\n          node.setRed(child.isRed());\n\n        node.addressRange = child.addressRange;\n\n        if (cl) m_nodes[cl].setParent(nodeIndex);\n        if (cr) m_nodes[cr].setParent(nodeIndex);\n\n        child.header = 0u;\n        freeNode(childIndex);\n      } else if (nodeIndex != rootIndex) {\n        // Removing leaf node, update parent link and move on.\n        auto& parent = m_nodes[parentIndex];\n\n        uint32_t which = uint32_t(parent.child(1) == nodeIndex);\n        parent.setChild(which, 0u);\n\n        node.header = 0;\n        freeNode(nodeIndex);\n      } else {\n        // Removing root with no children, mark tree as invalid\n        uint64_t rootBit = uint64_t(1u) << (rootIndex - 1u);\n\n        m_rootMaskSubtree &= ~rootBit;\n        m_rootMaskValid &= ~rootBit;\n      }\n    }\n  }\n\n\n  void DxvkBarrierTracker::rebalancePostInsert(\n          uint32_t                    nodeIndex,\n          uint32_t                    rootIndex) {\n    while (nodeIndex != rootIndex) {\n      auto& node = m_nodes[nodeIndex];\n      auto& p = m_nodes[node.parent()];\n\n      if (!p.isRed())\n        break;\n\n      auto& g = m_nodes[p.parent()];\n\n      if (g.child(1) == node.parent()) {\n        auto& u = m_nodes[g.child(0)];\n\n        if (g.child(0) && u.isRed()) {\n          g.setRed(true);\n          u.setRed(false);\n          p.setRed(false);\n\n          nodeIndex = p.parent();\n        } else {\n          if (p.child(0) == nodeIndex)\n            rotateRight(node.parent(), rootIndex);\n\n          p.setRed(false);\n          g.setRed(true);\n\n          rotateLeft(p.parent(), rootIndex);\n        }\n      } else {\n        auto& u = m_nodes[g.child(1)];\n\n        if (g.child(1) && u.isRed()) {\n          g.setRed(true);\n          u.setRed(false);\n          p.setRed(false);\n\n          nodeIndex = p.parent();\n        } else {\n          if (p.child(1) == nodeIndex)\n            rotateLeft(node.parent(), rootIndex);\n\n          p.setRed(false);\n          g.setRed(true);\n\n          rotateRight(p.parent(), rootIndex);\n        }\n      }\n    }\n\n    m_nodes[rootIndex].setRed(false);\n  }\n\n\n  void DxvkBarrierTracker::rotateLeft(\n          uint32_t                    nodeIndex,\n          uint32_t                    rootIndex) {\n    // This implements rotations in such a way that the node to\n    // rotate around does not move. This is important to avoid\n    // having a special case for the root node, and avoids having\n    // to access the parent or special-case the root node.\n    auto& node = m_nodes[nodeIndex];\n\n    auto l = node.child(0);\n    auto r = node.child(1);\n\n    auto rl = m_nodes[r].child(0);\n    auto rr = m_nodes[r].child(1);\n\n    m_nodes[l].setParent(r);\n\n    bool isRed = m_nodes[r].isRed();\n    m_nodes[r].setRed(node.isRed());\n    m_nodes[r].setChild(0, l);\n    m_nodes[r].setChild(1, rl);\n\n    m_nodes[rr].setParent(nodeIndex);\n\n    node.setRed(isRed && nodeIndex != rootIndex);\n    node.setChild(0, r);\n    node.setChild(1, rr);\n\n    std::swap(node.addressRange, m_nodes[r].addressRange);\n  }\n\n\n  void DxvkBarrierTracker::rotateRight(\n          uint32_t                    nodeIndex,\n          uint32_t                    rootIndex) {\n    auto& node = m_nodes[nodeIndex];\n\n    auto l = node.child(0);\n    auto r = node.child(1);\n\n    auto ll = m_nodes[l].child(0);\n    auto lr = m_nodes[l].child(1);\n\n    m_nodes[r].setParent(l);\n\n    bool isRed = m_nodes[l].isRed();\n    m_nodes[l].setRed(node.isRed());\n    m_nodes[l].setChild(0, lr);\n    m_nodes[l].setChild(1, r);\n\n    m_nodes[ll].setParent(nodeIndex);\n\n    node.setRed(isRed && nodeIndex != rootIndex);\n    node.setChild(0, ll);\n    node.setChild(1, l);\n\n    std::swap(node.addressRange, m_nodes[l].addressRange);\n  }\n\n\n\n  DxvkBarrierBatch::DxvkBarrierBatch(const DxvkDevice& device, DxvkCmdBuffer cmdBuffer)\n  : m_cmdBuffer(cmdBuffer), m_keepImageBarriers(device.perfHints().preferRenderPassOps) { }\n\n\n  DxvkBarrierBatch::~DxvkBarrierBatch() {\n\n  }\n\n\n  void DxvkBarrierBatch::addMemoryBarrier(\n    const VkMemoryBarrier2&           barrier) {\n    if (unlikely(barrier.dstAccessMask & vk::AccessHostMask)) {\n      m_hostSrcStages |= barrier.srcStageMask & vk::StageDeviceMask;\n      m_hostDstAccess |= barrier.dstAccessMask & vk::AccessHostMask;\n    }\n\n    m_memoryBarrier.srcStageMask |= barrier.srcStageMask & vk::StageDeviceMask;\n    m_memoryBarrier.srcAccessMask |= barrier.srcAccessMask & vk::AccessWriteMask;\n    m_memoryBarrier.dstStageMask |= barrier.dstStageMask & vk::StageDeviceMask;\n    m_memoryBarrier.dstAccessMask |= barrier.dstAccessMask & vk::AccessDeviceMask;\n  }\n\n\n  void DxvkBarrierBatch::addImageBarrier(\n    const VkImageMemoryBarrier2&      barrier) {\n    if (unlikely(barrier.dstAccessMask & vk::AccessHostMask)) {\n      m_hostSrcStages |= barrier.srcStageMask & vk::StageDeviceMask;\n      m_hostDstAccess |= barrier.dstAccessMask & vk::AccessHostMask;\n    }\n\n    if (barrier.oldLayout != barrier.newLayout\n     || barrier.srcQueueFamilyIndex != barrier.dstQueueFamilyIndex\n     || m_keepImageBarriers) {\n      auto& entry = m_imageBarriers.emplace_back(barrier);\n\n      entry.srcStageMask &= vk::StageDeviceMask;\n      entry.srcAccessMask &= vk::AccessWriteMask;\n      entry.dstStageMask &= vk::StageDeviceMask;\n      entry.dstAccessMask &= vk::AccessDeviceMask;\n    } else {\n      m_memoryBarrier.srcStageMask |= barrier.srcStageMask & vk::StageDeviceMask;\n      m_memoryBarrier.srcAccessMask |= barrier.srcAccessMask & vk::AccessWriteMask;\n      m_memoryBarrier.dstStageMask |= barrier.dstStageMask & vk::StageDeviceMask;\n      m_memoryBarrier.dstAccessMask |= barrier.dstAccessMask & vk::AccessDeviceMask;\n    }\n  }\n\n\n  void DxvkBarrierBatch::flush(\n    const Rc<DxvkCommandList>&        list) {\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n\n    if (m_memoryBarrier.srcStageMask | m_memoryBarrier.dstStageMask) {\n      depInfo.memoryBarrierCount = 1;\n      depInfo.pMemoryBarriers = &m_memoryBarrier;\n    }\n\n    if (!m_imageBarriers.empty()) {\n      depInfo.imageMemoryBarrierCount = m_imageBarriers.size();\n      depInfo.pImageMemoryBarriers = m_imageBarriers.data();\n    }\n\n    if (!(depInfo.memoryBarrierCount | depInfo.imageMemoryBarrierCount))\n      return;\n\n    list->cmdPipelineBarrier(m_cmdBuffer, &depInfo);\n\n    m_memoryBarrier.srcStageMask = 0u;\n    m_memoryBarrier.srcAccessMask = 0u;\n    m_memoryBarrier.dstStageMask = 0u;\n    m_memoryBarrier.dstAccessMask = 0u;\n\n    m_imageBarriers.clear();\n  }\n\n\n  void DxvkBarrierBatch::finalize(\n    const Rc<DxvkCommandList>&        list) {\n    if (m_hostDstAccess) {\n      m_memoryBarrier.srcStageMask |= m_hostSrcStages;\n      m_memoryBarrier.dstStageMask |= VK_PIPELINE_STAGE_2_HOST_BIT;\n      m_memoryBarrier.dstAccessMask |= m_hostDstAccess;\n\n      m_hostSrcStages = 0u;\n      m_hostDstAccess = 0u;\n    }\n\n    flush(list);\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_barrier.h",
    "content": "#pragma once\n\n#include <utility>\n#include <vector>\n\n#include \"dxvk_buffer.h\"\n#include \"dxvk_cmdlist.h\"\n#include \"dxvk_image.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Buffer access info\n   */\n  struct DxvkResourceAccess {\n    DxvkResourceAccess() = default;\n\n    DxvkResourceAccess(\n            DxvkBuffer&               resource,\n            VkDeviceSize              offset,\n            VkDeviceSize              size,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess)\n    : stages      (dstStages),\n      access      (dstAccess),\n      buffer      (&resource),\n      bufferOffset(offset),\n      bufferSize  (size) { }\n\n    DxvkResourceAccess(\n            DxvkBufferView&           view,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess)\n    : stages      (dstStages),\n      access      (dstAccess),\n      buffer      (view.buffer()),\n      bufferOffset(view.info().offset),\n      bufferSize  (view.info().size) { }\n\n    DxvkResourceAccess(\n            DxvkBufferView&           view,\n            VkDeviceSize              offset,\n            VkDeviceSize              size,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess)\n    : stages      (dstStages),\n      access      (dstAccess),\n      buffer      (view.buffer()) {\n      auto* formatInfo = view.formatInfo();\n\n      VkDeviceSize elementSize = formatInfo\n        ? formatInfo->elementSize\n        : 1u;\n\n      bufferOffset = elementSize * offset + view.info().offset;\n      bufferSize = elementSize * size;\n    }\n\n    DxvkResourceAccess(\n            DxvkImage&                resource,\n      const VkImageSubresourceRange&  subresources,\n            VkImageLayout             dstLayout,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess,\n            bool                      discardImage)\n    : stages      (dstStages),\n      access      (dstAccess),\n      image       (&resource),\n      imageSubresources(subresources),\n      imageLayout (dstLayout),\n      discard     (discardImage) { }\n\n    DxvkResourceAccess(\n            DxvkImageView&            view,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess,\n            bool                      discardView)\n    : stages      (dstStages),\n      access      (dstAccess),\n      image       (view.image()),\n      imageSubresources(view.imageSubresources()),\n      imageLayout (view.getLayout()),\n      discard     (discardView) { }\n\n    VkPipelineStageFlags2 stages = 0u;\n    VkAccessFlags2 access = 0u;\n\n    DxvkBuffer* buffer = nullptr;\n    VkDeviceSize bufferOffset = 0u;\n    VkDeviceSize bufferSize = 0u;\n\n    DxvkImage* image = nullptr;\n    VkImageSubresourceRange imageSubresources = { };\n    VkOffset3D imageOffset = { };\n    VkExtent3D imageExtent = { };\n    VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    VkBool32 discard = VK_FALSE;\n  };\n\n\n  /**\n   * \\brief Address range\n   */\n  struct DxvkAddressRange {\n    /// Unique resource handle\n    bit::uint48_t resource = bit::uint48_t(0u);\n    /// Access modes used for the given address range\n    DxvkAccessOp accessOp = DxvkAccessOp::None;\n    /// Range start. For buffers, this shall be a byte offset,\n    /// images can encode the first subresource index here.\n    uint64_t rangeStart = 0u;\n    /// Range end. For buffers, this is the offset of the last byte\n    /// included in the range, i.e. offset + size - 1. For images,\n    /// this is the last subresource included in the range.\n    uint64_t rangeEnd = 0u;\n\n    bool contains(const DxvkAddressRange& other) const {\n      return uint64_t(resource) == uint64_t(other.resource)\n          && rangeStart <= other.rangeStart\n          && rangeEnd >= other.rangeEnd;\n    }\n\n    bool overlaps(const DxvkAddressRange& other) const {\n      return uint64_t(resource) == uint64_t(other.resource)\n          && rangeEnd >= other.rangeStart\n          && rangeStart <= other.rangeEnd;\n    }\n\n    bool lt(const DxvkAddressRange& other) const {\n      return (uint64_t(resource) < uint64_t(other.resource))\n          || (uint64_t(resource) == uint64_t(other.resource) && rangeStart < other.rangeStart);\n    }\n  };\n\n\n  /**\n   * \\brief Barrier tree node\n   *\n   * Node of a red-black tree, consisting of a packed node\n   * header as well as aresource address range. GCC generates\n   * weird code with bitfields here, so pack manually.\n   */\n  struct DxvkBarrierTreeNode {\n    constexpr static uint64_t NodeIndexMask = (1u << 21) - 1u;\n\n    // Packed header with node indices and the node color.\n    // [0:0]: Set if the node is red, clear otherwise.\n    // [21:1]: Index of the left child node, may be 0.\n    // [42:22]: Index of the right child node, may be 0.\n    // [43:63]: Index of the parent node, may be 0 for the root.\n    uint64_t header = 0u;\n\n    // Address range of the node\n    DxvkAddressRange addressRange = { };\n\n    void setRed(bool red) {\n      header &= ~uint64_t(1u);\n      header |= uint64_t(red);\n    }\n\n    bool isRed() const {\n      return header & 1u;\n    }\n\n    void setParent(uint32_t node) {\n      header &= ~(NodeIndexMask << 43);\n      header |= uint64_t(node) << 43;\n    }\n\n    void setChild(uint32_t index, uint32_t node) {\n      uint32_t shift = (index ? 22 : 1);\n      header &= ~(NodeIndexMask << shift);\n      header |= uint64_t(node) << shift;\n    }\n\n    uint32_t parent() const {\n      return uint32_t((header >> 43) & NodeIndexMask);\n    }\n\n    uint32_t child(uint32_t index) const {\n      uint32_t shift = (index ? 22 : 1);\n      return uint32_t((header >> shift) & NodeIndexMask);\n    }\n\n    bool isRoot() const {\n      return parent() == 0u;\n    }\n  };\n\n\n  /**\n   * \\brief Barrier tracker\n   *\n   * Provides a two-part hash table for read and written resource\n   * ranges, which is backed by binary trees to handle individual\n   * address ranges as well as collisions.\n   */\n  class DxvkBarrierTracker {\n    constexpr static uint32_t HashTableSize = 32u;\n  public:\n\n    DxvkBarrierTracker();\n\n    ~DxvkBarrierTracker();\n\n    /**\n     * \\brief Checks whether there is a pending access of a given type\n     *\n     * \\param [in] range Resource range\n     * \\param [in] accessType Access type\n     * \\returns \\c true if the range has a pending access\n     */\n    bool findRange(\n      const DxvkAddressRange&           range,\n            DxvkAccess                  accessType) const;\n\n    /**\n     * \\brief Inserts address range for a given access type\n     *\n     * \\param [in] range Resource range\n     * \\param [in] accessType Access type\n     */\n    void insertRange(\n      const DxvkAddressRange&           range,\n            DxvkAccess                  accessType);\n\n    /**\n     * \\brief Clears the entire structure\n     *\n     * Invalidates all hash table entries and trees.\n     */\n    void clear();\n\n    /**\n     * \\brief Checks whether any resources are dirty\n     * \\returns \\c true if the tracker is empty.\n     */\n    bool empty() const {\n      return !m_rootMaskValid;\n    }\n\n  private:\n\n    uint64_t m_rootMaskValid = 0u;\n    uint64_t m_rootMaskSubtree = 0u;\n\n    std::vector<DxvkBarrierTreeNode>  m_nodes;\n    std::vector<uint32_t>             m_free;\n\n    uint32_t allocateNode();\n\n    void freeNode(uint32_t node);\n\n    uint32_t findNode(\n      const DxvkAddressRange&           range,\n            uint32_t                    rootIndex) const;\n\n    uint32_t insertNode(\n      const DxvkAddressRange&           range,\n            uint32_t                    rootIndex);\n\n    void removeNode(\n            uint32_t                    nodeIndex,\n            uint32_t                    rootIndex);\n\n    void rebalancePostInsert(\n            uint32_t                    nodeIndex,\n            uint32_t                    rootIndex);\n\n    void rotateLeft(\n            uint32_t                    nodeIndex,\n            uint32_t                    rootIndex);\n\n    void rotateRight(\n            uint32_t                    nodeIndex,\n            uint32_t                    rootIndex);\n\n    static uint32_t computeRootIndex(\n      const DxvkAddressRange&           range,\n            DxvkAccess                  access) {\n      // TODO revisit once we use internal allocation\n      // objects or resource cookies here.\n      size_t hash = uint64_t(range.resource) * 93887;\n             hash ^= (hash >> 16);\n\n      // Reserve the upper half of the implicit hash table for written\n      // ranges, and add 1 because 0 refers to the actual null node.\n      return 1u + (hash % HashTableSize) + (access == DxvkAccess::Write ? HashTableSize : 0u);\n    }\n\n  };\n\n\n  /**\n   * \\brief Barrier batch\n   *\n   * Simple helper class to accumulate barriers that can then\n   * be recorded into a command buffer in a single step.\n   */\n  class DxvkBarrierBatch {\n\n  public:\n\n    DxvkBarrierBatch(const DxvkDevice& device, DxvkCmdBuffer cmdBuffer);\n    ~DxvkBarrierBatch();\n\n    /**\n     * \\brief Adds a memory barrier\n     *\n     * Host read access will only be flushed\n     * at the end of a command list.\n     * \\param [in] barrier Memory barrier\n     */\n    void addMemoryBarrier(\n      const VkMemoryBarrier2&           barrier);\n\n    /**\n     * \\brief Adds an image barrier\n     *\n     * This will automatically turn into a normal memory barrier\n     * if no queue family ownership transfer or layout transition\n     * happens.\n     * \\param [in] barrier Memory barrier\n     */\n    void addImageBarrier(\n      const VkImageMemoryBarrier2&      barrier);\n\n    /**\n     * \\brief Flushes batched memory barriers\n     * \\param [in] list Command list\n     */\n    void flush(\n      const Rc<DxvkCommandList>&        list);\n\n    /**\n     * \\brief Flushes batched memory and host barriers\n     * \\param [in] list Command list\n     */\n    void finalize(\n      const Rc<DxvkCommandList>&        list);\n\n    /**\n     * \\brief Check whether there are pending layout transitions\n     * \\returns \\c true if there are any image layout transitions\n     */\n    bool hasLayoutTransitions() const {\n      return !m_imageBarriers.empty();\n    }\n\n    /**\n     * \\brief Checks whether there are barriers using the given source stages\n     * \\returns \\c true if any barriers use the given source stages\n     */\n    bool hasPendingStages(VkPipelineStageFlags2 stages) const {\n      if (m_memoryBarrier.srcStageMask & stages)\n        return true;\n\n      for (const auto& b : m_imageBarriers) {\n        if (b.srcStageMask & stages)\n          return true;\n      }\n\n      return false;\n    }\n\n    /**\n     * \\brief Checks whether there are barriers using any of the given access flags\n     * \\returns \\c true if any barriers use the given source access flags\n     */\n    bool hasPendingAccess(VkAccessFlags2 access) const {\n      access |= VK_ACCESS_2_MEMORY_WRITE_BIT;\n\n      if (m_memoryBarrier.srcAccessMask & access)\n        return true;\n\n      for (const auto& b : m_imageBarriers) {\n        if (b.srcAccessMask & access)\n          return true;\n      }\n\n      return false;\n    }\n\n    /**\n     * \\brief Checks whether there are barriers targeting any of the given access flags\n     *\n     * \\param [in] access Access flags to check. Will implicitly also include\n     *             checks for memory read/write as necessary.\n     * \\returns \\c true if any barriers use the given source access flags\n     */\n    bool hasTargetAccess(VkAccessFlags2 access) const {\n      if (access & vk::AccessReadMask) access |= VK_ACCESS_2_MEMORY_READ_BIT;\n      if (access & vk::AccessWriteMask) access |= VK_ACCESS_2_MEMORY_WRITE_BIT;\n\n      if (m_memoryBarrier.dstAccessMask & access)\n        return true;\n\n      for (const auto& b : m_imageBarriers) {\n        if (b.dstAccessMask & access)\n          return true;\n      }\n\n      return false;\n    }\n\n  private:\n\n    DxvkCmdBuffer         m_cmdBuffer;\n\n    VkMemoryBarrier2      m_memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n\n    VkPipelineStageFlags2 m_hostSrcStages = 0u;\n    VkAccessFlags2        m_hostDstAccess = 0u;\n\n    bool                  m_keepImageBarriers = false;\n\n    std::vector<VkImageMemoryBarrier2> m_imageBarriers = { };\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_bind_mask.h",
    "content": "#pragma once\n\n#include <type_traits>\n\n#include \"dxvk_buffer.h\"\n#include \"dxvk_descriptor_pool.h\"\n#include \"dxvk_image.h\"\n#include \"dxvk_limits.h\"\n#include \"dxvk_sampler.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Binding mask\n   * \n   * Used to track which resource slots have a compatible\n   * binding and which ones don't. This is used to set up\n   * binding-related specialization constants in shaders.\n   * \\tparam N Number of binding slots\n   */\n  template<uint32_t BindingCount>\n  class DxvkBindingSet {\n    using MaskType = std::conditional_t<(BindingCount > 32), uintptr_t, uint32_t>;\n\n    constexpr static MaskType SetBit = MaskType(1u);\n    constexpr static MaskType SetMask = ~MaskType(0u);\n\n    constexpr static uint32_t BitCount = 8 * sizeof(MaskType);\n    constexpr static uint32_t IntCount = (BindingCount + BitCount - 1) / BitCount;\n  public:\n    \n    /**\n     * \\brief Tests whether a binding is active\n     * \n     * \\param [in] slot The binding ID\n     * \\returns \\c true if the binding is active\n     */\n    bool test(uint32_t slot) const {\n      const uint32_t intId = computeIntId(slot);\n      const uint32_t bitId = computeBitId(slot);\n      const MaskType bitMask = SetBit << bitId;\n      return (m_slots[intId] & bitMask) != 0;\n    }\n    \n    /**\n     * \\brief Changes a single binding\n     * \n     * \\param [in] slot The binding ID\n     * \\param [in] value New binding state\n     * \\returns \\c true if the state has changed\n     */\n    bool set(uint32_t slot, bool value) {\n      const uint32_t intId = computeIntId(slot);\n      const uint32_t bitId = computeBitId(slot);\n      const MaskType bitMask = SetBit << bitId;\n      \n      const MaskType prev = m_slots[intId];\n      const MaskType next = value\n        ? prev |  bitMask\n        : prev & ~bitMask;\n      m_slots[intId] = next;\n      return prev != next;\n    }\n\n    /**\n     * \\brief Marks a binding as active\n     * \n     * \\param [in] slot The binding ID\n     * \\returns \\c true if the state has changed\n     */\n    bool set(uint32_t slot) {\n      return set(slot, true);\n    }\n\n    /**\n     * \\brief Marks a binding as inactive\n     * \n     * \\param [in] slot The binding ID\n     * \\returns \\c true if the state has changed\n     */\n    bool clr(uint32_t slot) {\n      return set(slot, false);\n    }\n    \n    /**\n     * \\brief Clears binding state\n     * \n     * Useful to zero out any bindings\n     * that are not used by a pipeline.\n     */\n    void clear() {\n      for (uint32_t i = 0; i < IntCount; i++)\n        m_slots[i] = 0;\n    }\n\n    /**\n     * \\brief Enables multiple bindings\n     *\n     * Leaves bindings outside of this range unaffected.\n     * \\param [in] first First binding to enable\n     * \\param [in] count Number of bindings to enable\n     */\n    void setRange(uint32_t first, uint32_t count) {\n      if (!count)\n        return;\n\n      uint32_t firstInt = computeIntId(first);\n      uint32_t firstBit = computeBitId(first);\n\n      uint32_t lastInt = computeIntId(first + count - 1);\n      uint32_t lastBit = computeBitId(first + count - 1) + 1;\n\n      if (firstInt == lastInt) {\n        m_slots[firstInt] |= (count < BitCount)\n          ? ((SetBit << count) - 1) << firstBit\n          : (SetMask);\n      } else {\n        m_slots[firstInt] |= SetMask << firstBit;\n        m_slots[lastInt] |= SetMask >> (BitCount - lastBit);\n\n        for (uint32_t i = firstInt + 1; i < lastInt; i++)\n          m_slots[i] = SetMask;\n      }\n    }\n\n    /**\n     * \\brief Finds next set binding\n     *\n     * \\param [in] first Fist bit to consider\n     * \\returns Binding ID, or -1 if none was found\n     */\n    int32_t findNext(uint32_t first) const {\n      if (unlikely(first >= BindingCount))\n        return -1;\n\n      uint32_t intId = computeIntId(first);\n      uint32_t bitId = computeBitId(first);\n\n      MaskType mask = m_slots[intId] & ~((SetBit << bitId) - 1);\n\n      while (!mask && ++intId < IntCount)\n        mask = m_slots[intId];\n      \n      if (!mask)\n        return -1;\n      \n      return BitCount * intId + bit::tzcnt(mask);\n    }\n\n    bool operator == (const DxvkBindingSet& other) const {\n      bool eq = true;\n      for (uint32_t i = 0; i < IntCount; i++)\n        eq &= m_slots[i] == other.m_slots[i];\n      return eq;\n    }\n\n    bool operator != (const DxvkBindingSet& other) const {\n      return !this->operator == (other);\n    }\n    \n  private:\n    \n    MaskType m_slots[IntCount];\n\n    static uint32_t computeIntId(uint32_t slot) {\n      if constexpr (IntCount > 1)\n        return slot / BitCount;\n      else\n        return 0;\n    }\n\n    static uint32_t computeBitId(uint32_t slot) {\n      if constexpr (IntCount > 1)\n        return slot % BitCount;\n      else\n        return slot;\n    }\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_buffer.cpp",
    "content": "#include \"dxvk_barrier.h\"\n#include \"dxvk_buffer.h\"\n#include \"dxvk_device.h\"\n\n#include <algorithm>\n\nnamespace dxvk {\n  \n  DxvkBuffer::DxvkBuffer(\n          DxvkDevice*           device,\n    const DxvkBufferCreateInfo& createInfo,\n          DxvkMemoryAllocator&  allocator,\n          VkMemoryPropertyFlags memFlags)\n  : DxvkPagedResource(allocator),\n    m_vkd           (device->vkd()),\n    m_properties    (memFlags),\n    m_shaderStages  (util::shaderStages(createInfo.stages)),\n    m_sharingMode   (device->getSharingMode()),\n    m_info          (createInfo) {\n    m_allocator->registerResource(this);\n\n    // Assign debug name to buffer\n    if (device->debugFlags().test(DxvkDebugFlag::Capture)) {\n      m_debugName = createDebugName(createInfo.debugName);\n      m_info.debugName = m_debugName.c_str();\n    } else {\n      m_info.debugName = nullptr;\n    }\n\n    // Unconditionally enable BDA usage\n    m_info.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;\n\n    // Create and assign actual buffer resource\n    assignStorage(allocateStorage());\n  }\n\n\n  DxvkBuffer::DxvkBuffer(\n          DxvkDevice*           device,\n    const DxvkBufferCreateInfo& createInfo,\n    const DxvkBufferImportInfo& importInfo,\n          DxvkMemoryAllocator&  allocator,\n          VkMemoryPropertyFlags memFlags)\n  : DxvkPagedResource(allocator),\n    m_vkd           (device->vkd()),\n    m_properties    (memFlags),\n    m_shaderStages  (util::shaderStages(createInfo.stages)),\n    m_sharingMode   (device->getSharingMode()),\n    m_info          (createInfo),\n    m_stableAddress (true) {\n    m_allocator->registerResource(this);\n\n    DxvkAllocationInfo allocationInfo = { };\n    allocationInfo.resourceCookie = cookie();\n\n    VkBufferCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };\n    info.flags = m_info.flags;\n    info.usage = m_info.usage;\n    info.size = m_info.size;\n    m_sharingMode.fill(info);\n\n    assignStorage(allocator.importBufferResource(info, allocationInfo, importInfo));\n  }\n\n\n  DxvkBuffer::~DxvkBuffer() {\n    m_allocator->unregisterResource(this);\n  }\n\n\n  bool DxvkBuffer::canRelocate() const {\n    return !m_bufferInfo.mapPtr && !m_stableAddress\n        && !(m_info.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT);\n  }\n\n\n  Rc<DxvkBufferView> DxvkBuffer::createView(\n    const DxvkBufferViewKey& info) {\n    std::unique_lock lock(m_viewMutex);\n\n    auto entry = m_views.emplace(std::piecewise_construct,\n      std::make_tuple(info), std::make_tuple(this, info));\n\n    return &entry.first->second;\n  }\n\n\n  DxvkSparsePageTable* DxvkBuffer::getSparsePageTable() {\n    return m_storage->getSparsePageTable();\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkBuffer::relocateStorage(\n          DxvkAllocationModes         mode) {\n    // The resource may become non-relocatable even after we allocate new\n    // backing storage, but if it already is then don't waste memory.\n    if (!canRelocate())\n      return nullptr;\n\n    DxvkAllocationInfo allocationInfo = { };\n    allocationInfo.resourceCookie = cookie();\n    allocationInfo.properties = m_properties;\n    allocationInfo.mode = mode;\n\n    VkBufferCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };\n    info.flags = m_info.flags;\n    info.usage = m_info.usage;\n    info.size = m_info.size;\n    m_sharingMode.fill(info);\n\n    return m_allocator->createBufferResource(info, allocationInfo, nullptr);\n  }\n\n\n  void DxvkBuffer::setDebugName(const char* name) {\n    if (likely(!m_info.debugName))\n      return;\n\n    m_debugName = createDebugName(name);\n    m_info.debugName = m_debugName.c_str();\n\n    updateDebugName();\n  }\n\n\n  void DxvkBuffer::updateDebugName() {\n    if (m_storage->flags().test(DxvkAllocationFlag::OwnsBuffer)) {\n      VkDebugUtilsObjectNameInfoEXT nameInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT };\n      nameInfo.objectType = VK_OBJECT_TYPE_BUFFER;\n      nameInfo.objectHandle = vk::getObjectHandle(m_bufferInfo.buffer);\n      nameInfo.pObjectName = m_info.debugName;\n\n      m_vkd->vkSetDebugUtilsObjectNameEXT(m_vkd->device(), &nameInfo);\n    }\n  }\n\n\n  std::string DxvkBuffer::createDebugName(const char* name) const {\n    return str::format(vk::isValidDebugName(name) ? name : \"Buffer\", \" (\", cookie(), \")\");\n  }\n\n\n\n\n  void DxvkBufferView::updateViews() {\n    if (likely(m_key.format))\n      m_formatted = m_buffer->m_storage->createBufferView(m_key);\n\n    if (likely(m_buffer->info().usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)) {\n      DxvkBufferViewKey rawKey = m_key;\n      rawKey.format = VK_FORMAT_UNDEFINED;\n      rawKey.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n\n      m_raw = m_buffer->m_storage->createBufferView(rawKey);\n    }\n\n    m_version = m_buffer->m_version;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_buffer.h",
    "content": "#pragma once\n\n#include <unordered_map>\n#include <vector>\n\n#include \"dxvk_descriptor_pool.h\"\n#include \"dxvk_format.h\"\n#include \"dxvk_hash.h\"\n#include \"dxvk_memory.h\"\n#include \"dxvk_sparse.h\"\n\nnamespace dxvk {\n\n  class DxvkBuffer;\n\n  /**\n   * \\brief Buffer create info\n   *\n   * The properties of a buffer that are\n   * passed to \\ref DxvkDevice::createBuffer\n   */\n  struct DxvkBufferCreateInfo {\n    /// Size of the buffer, in bytes\n    VkDeviceSize size = 0u;\n\n    /// Buffer usage flags\n    VkBufferUsageFlags usage = 0u;\n\n    /// Pipeline stages that can access\n    /// the contents of the buffer.\n    VkPipelineStageFlags stages = 0u;\n\n    /// Allowed access patterns\n    VkAccessFlags access = 0u;\n\n    /// Buffer create flags\n    VkBufferCreateFlags flags = 0;\n\n    /// Debug name.\n    const char* debugName = nullptr;\n  };\n\n\n  /**\n   * \\brief Virtual buffer view\n   */\n  class DxvkBufferView {\n\n  public:\n\n    DxvkBufferView(\n            DxvkBuffer*                 buffer,\n      const DxvkBufferViewKey&          key)\n    : m_buffer(buffer), m_key(key) { }\n\n    void incRef();\n    void decRef();\n\n    /**\n     * \\brief Retrieves buffer view handle\n     *\n     * Creates a new view if the buffer has been invalidated.\n     * \\param [in] raw Whether to return a raw or formatted descriptor.\n     * \\returns Vulkan buffer view handle\n     */\n    const DxvkDescriptor* getDescriptor(bool raw);\n\n    /**\n     * \\brief Retrieves buffer slice handle\n     * \\returns Buffer slice handle\n     */\n    DxvkResourceBufferInfo getSliceInfo() const;\n\n    /**\n     * \\brief Element count\n     *\n     * Number of typed elements contained in the buffer view.\n     * Depends on the buffer view format.\n     * \\returns Element count\n     */\n    VkDeviceSize elementCount() const {\n      auto format = lookupFormatInfo(m_key.format);\n      return m_key.size / format->elementSize;\n    }\n\n    /**\n     * \\brief Buffer view properties\n     * \\returns Buffer view properties\n     */\n    DxvkBufferViewKey info() const {\n      return m_key;\n    }\n\n    /**\n     * \\brief Underlying buffer object\n     * \\returns Underlying buffer object\n     */\n    DxvkBuffer* buffer() const {\n      return m_buffer;\n    }\n\n    /**\n     * \\brief View format info\n     * \\returns View format info\n     */\n    const DxvkFormatInfo* formatInfo() const {\n      return lookupFormatInfo(m_key.format);\n    }\n\n  private:\n\n    DxvkBuffer*       m_buffer  = nullptr;\n    DxvkBufferViewKey m_key     = { };\n\n    uint32_t          m_version = 0u;\n\n    const DxvkDescriptor* m_raw       = nullptr;\n    const DxvkDescriptor* m_formatted = nullptr;\n\n    void updateViews();\n\n  };\n\n\n  /**\n   * \\brief Virtual buffer resource\n   * \n   * A simple buffer resource that stores linear,\n   * unformatted data. Can be accessed by the host\n   * if allocated on an appropriate memory type.\n   */\n  class DxvkBuffer : public DxvkPagedResource {\n    friend DxvkBufferView;\n\n    constexpr static VkDeviceSize MaxAllocationSize = DxvkPageAllocator::PageSize;\n    constexpr static VkDeviceSize MinAllocationSize = DxvkPoolAllocator::MinSize;\n\n    constexpr static VkDeviceSize MinMappedAllocationSize = DxvkPageAllocator::PageSize / 32u;\n    constexpr static VkDeviceSize MinMappedSlicesPerAllocation = 3u;\n  public:\n    \n    DxvkBuffer(\n            DxvkDevice*           device,\n      const DxvkBufferCreateInfo& createInfo,\n            DxvkMemoryAllocator&  memAlloc,\n            VkMemoryPropertyFlags memFlags);\n\n    DxvkBuffer(\n            DxvkDevice*           device,\n      const DxvkBufferCreateInfo& createInfo,\n      const DxvkBufferImportInfo& importInfo,\n            DxvkMemoryAllocator&  memAlloc,\n            VkMemoryPropertyFlags memFlags);\n\n    ~DxvkBuffer();\n    \n    /**\n     * \\brief Buffer properties\n     * \\returns Buffer properties\n     */\n    const DxvkBufferCreateInfo& info() const {\n      return m_info;\n    }\n    \n    /**\n     * \\brief Memory type flags\n     * \n     * Use this to determine whether a\n     * buffer is mapped to host memory.\n     * \\returns Vulkan memory flags\n     */\n    VkMemoryPropertyFlags memFlags() const {\n      return m_properties;\n    }\n    \n    /**\n     * \\brief Map pointer\n     * \n     * If the buffer has been created on a host-visible\n     * memory type, the buffer memory is mapped and can\n     * be accessed by the host.\n     * \\param [in] offset Byte offset into mapped region\n     * \\returns Pointer to mapped memory region\n     */\n    void* mapPtr(VkDeviceSize offset) const {\n      return m_bufferInfo.mapPtr\n        ? reinterpret_cast<char*>(m_bufferInfo.mapPtr) + offset\n        : nullptr;\n    }\n\n    /**\n     * \\brief Queries shader stages that can access this buffer\n     *\n     * Derived from the pipeline stage mask passed in during creation.\n     * \\returns Shader stages that may access this buffer\n     */\n    VkShaderStageFlags getShaderStages() const {\n      return m_shaderStages;\n    }\n    \n    /**\n     * \\brief Retrieves slice handle\n     * \\returns Buffer slice handle\n     */\n    DxvkResourceBufferInfo getSliceInfo() const {\n      return getSliceInfo(0u, m_info.size);\n    }\n\n    /**\n     * \\brief Retrieves sub slice handle\n     * \n     * \\param [in] offset Offset into buffer\n     * \\param [in] length Sub slice length\n     * \\returns Buffer slice handle\n     */\n    DxvkResourceBufferInfo getSliceInfo(VkDeviceSize offset, VkDeviceSize length) const {\n      DxvkResourceBufferInfo result;\n      result.buffer = m_bufferInfo.buffer;\n      result.offset = m_bufferInfo.offset + offset;\n      result.size = length;\n      result.mapPtr = mapPtr(offset);\n      result.gpuAddress = m_bufferInfo.gpuAddress + offset;\n      return result;\n    }\n\n    /**\n     * \\brief Retrieves descriptor info\n     *\n     * \\param [in] offset Buffer slice offset\n     * \\param [in] length Buffer slice length\n     * \\returns Buffer slice descriptor\n     */\n    DxvkLegacyDescriptor getDescriptor(VkDeviceSize offset, VkDeviceSize length) const {\n      DxvkLegacyDescriptor result = { };\n      result.buffer.buffer = m_bufferInfo.buffer;\n      result.buffer.offset = m_bufferInfo.offset + offset;\n      result.buffer.range = length;\n      return result;\n    }\n\n    /**\n     * \\brief Transform feedback vertex stride\n     * \n     * Used when drawing after transform feedback,\n     * \\returns The current xfb vertex stride\n     */\n    uint32_t getXfbVertexStride() const {\n      return m_xfbStride;\n    }\n    \n    /**\n     * \\brief Set transform feedback vertex stride\n     * \n     * When the buffer is used as a transform feedback\n     * buffer, this will be set to the vertex stride\n     * defined by the geometry shader.\n     * \\param [in] stride Vertex stride\n     */\n    void setXfbVertexStride(uint32_t stride) {\n      m_xfbStride = stride;\n    }\n\n    /**\n     * \\brief Allocates new buffer slice\n     * \\returns The new backing resource\n     */\n    Rc<DxvkResourceAllocation> allocateStorage() {\n      return allocateStorage(nullptr);\n    }\n\n    /**\n     * \\brief Allocates new buffer slice with cache\n     *\n     * Uses the given cache to service small allocations without\n     * having to block the actual allocator if possible.\n     * \\param [in] cache Optional allocation cache\n     * \\returns The new buffer slice\n     */\n    Rc<DxvkResourceAllocation> allocateStorage(DxvkLocalAllocationCache* cache) {\n      DxvkAllocationInfo allocationInfo = { };\n      allocationInfo.resourceCookie = cookie();\n      allocationInfo.properties = m_properties;\n\n      VkBufferCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };\n      info.flags = m_info.flags;\n      info.usage = m_info.usage;\n      info.size = m_info.size;\n      m_sharingMode.fill(info);\n\n      return m_allocator->createBufferResource(info, allocationInfo, cache);\n    }\n\n    /**\n     * \\brief Replaces backing resource\n     * \n     * Replaces the underlying buffer and implicitly marks\n     * any buffer views using this resource as dirty. Do\n     * not call this directly as this is called implicitly\n     * by the context's \\c invalidateBuffer method.\n     * \\param [in] slice The new backing resource\n     * \\returns Previous buffer allocation\n     */\n    Rc<DxvkResourceAllocation> assignStorage(Rc<DxvkResourceAllocation>&& slice) {\n      Rc<DxvkResourceAllocation> result = std::move(m_storage);\n\n      m_storage = std::move(slice);\n      m_bufferInfo = m_storage->getBufferInfo();\n\n      if (unlikely(m_info.debugName))\n        updateDebugName();\n\n      // If this is a device-local buffer, update residency\n      if (!(m_properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {\n        auto common = m_properties & m_storage->getMemoryProperties();\n\n        updateResidencyStatus((common & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)\n          ? DxvkResourceResidency::Resident\n          : DxvkResourceResidency::Evicted);\n      }\n\n      // Implicitly invalidate views\n      m_version += 1u;\n      return result;\n    }\n\n    /**\n     * \\brief Retrieves current backing storage\n     * \\returns Current buffer allocation\n     */\n    Rc<DxvkResourceAllocation> storage() const {\n      return m_storage;\n    }\n\n    /**\n     * \\brief Retrieves resource ID for barrier tracking\n     * \\returns Unique resource ID\n     */\n    bit::uint48_t getResourceId() const {\n      constexpr static size_t Align = alignof(DxvkResourceAllocation);\n      return bit::uint48_t(reinterpret_cast<uintptr_t>(m_storage.ptr()) / (Align & -Align));\n    }\n\n    /**\n     * \\brief Checks whether the buffer can be relocated\n     *\n     * Buffers that require a stable GPU or CPU address cannot be\n     * moved, unless it's done explicitly done by the client API.\n     * \\returns \\c true if the backend can safely relocate the buffer\n     */\n    bool canRelocate() const;\n\n    /**\n     * \\brief Enables stable GPU address\n     *\n     * Subsequent calls to \\c canRelocate will be \\c false, preventing\n     * the buffer from being relocated or invalidated by the backend.\n     */\n    void enableStableAddress() {\n      m_stableAddress = true;\n    }\n\n    /**\n     * \\brief Creates or retrieves a buffer view\n     *\n     * \\param [in] info Buffer view create info\n     * \\returns Newly created buffer view\n     */\n    Rc<DxvkBufferView> createView(\n      const DxvkBufferViewKey& info);\n\n    /**\n     * \\brief Retrieves sparse binding table\n     * \\returns Sparse binding table\n     */\n    DxvkSparsePageTable* getSparsePageTable();\n\n    /**\n     * \\brief Allocates new backing storage with constraints\n     *\n     * \\param [in] mode Allocation mode flags\n     * \\returns Operation status and allocation\n     */\n    Rc<DxvkResourceAllocation> relocateStorage(\n            DxvkAllocationModes         mode);\n\n    /**\n     * \\brief Sets debug name for the backing resource\n     * \\param [in] name New debug name\n     */\n    void setDebugName(const char* name);\n\n    /**\n     * \\brief Retrieves debug name\n     * \\returns Debug name\n     */\n    const char* getDebugName() const {\n      return m_debugName.c_str();\n    }\n\n  private:\n\n    Rc<vk::DeviceFn>            m_vkd;\n    VkMemoryPropertyFlags       m_properties    = 0u;\n    VkShaderStageFlags          m_shaderStages  = 0u;\n    DxvkSharingModeInfo         m_sharingMode   = { };\n\n    DxvkBufferCreateInfo        m_info          = { };\n\n    uint32_t                    m_xfbStride     = 0u;\n    uint32_t                    m_version       = 0u;\n\n    bool                        m_stableAddress = false;\n\n    DxvkResourceBufferInfo      m_bufferInfo    = { };\n\n    Rc<DxvkResourceAllocation>  m_storage;\n\n    dxvk::mutex                 m_viewMutex;\n    std::unordered_map<DxvkBufferViewKey,\n      DxvkBufferView, DxvkHash, DxvkEq> m_views;\n\n    std::string                 m_debugName;\n\n    void updateDebugName();\n\n    std::string createDebugName(const char* name) const;\n\n  };\n\n\n  /**\n   * \\brief Buffer relocation info\n   */\n  struct DxvkRelocateBufferInfo {\n    /// Buffer object. Stores metadata.\n    Rc<DxvkBuffer> buffer;\n    /// Backing storage to copy to\n    Rc<DxvkResourceAllocation> storage;\n  };\n  \n  \n  /**\n   * \\brief Buffer slice\n   * \n   * Stores the buffer and a sub-range of the buffer.\n   * Slices are considered equal if the buffer and\n   * the buffer range are the same.\n   */\n  class DxvkBufferSlice {\n    \n  public:\n    \n    DxvkBufferSlice() { }\n\n    DxvkBufferSlice(\n            Rc<DxvkBuffer>  buffer,\n            VkDeviceSize    rangeOffset,\n            VkDeviceSize    rangeLength)\n    : m_buffer(std::move(buffer)),\n      m_offset(rangeOffset),\n      m_length(rangeLength) { }\n\n    explicit DxvkBufferSlice(Rc<DxvkBuffer> buffer)\n    : m_buffer(std::move(buffer)),\n      m_offset(0),\n      m_length(m_buffer->info().size) { }\n\n    explicit DxvkBufferSlice(const Rc<DxvkBufferView>& view)\n    : DxvkBufferSlice(view->buffer(), view->info().offset, view->info().size) { }\n\n    DxvkBufferSlice(const DxvkBufferSlice& ) = default;\n    DxvkBufferSlice(      DxvkBufferSlice&&) = default;\n\n    DxvkBufferSlice& operator = (const DxvkBufferSlice& other) {\n      if (m_buffer != other.m_buffer)\n        m_buffer = other.m_buffer;\n      m_offset = other.m_offset;\n      m_length = other.m_length;\n      return *this;\n    }\n\n    DxvkBufferSlice& operator = (DxvkBufferSlice&&) = default;\n\n    /**\n     * \\brief Buffer slice offset and length\n     * \\returns Buffer slice offset and length\n     */\n    size_t offset() const { return m_offset; }\n    size_t length() const { return m_length; }\n\n    /**\n     * \\brief Underlying buffer\n     * \\returns The virtual buffer\n     */\n    const Rc<DxvkBuffer>& buffer() const {\n      return m_buffer;\n    }\n    \n    /**\n     * \\brief Buffer info\n     * \n     * Retrieves the properties of the underlying\n     * virtual buffer. Should not be used directly\n     * by client APIs.\n     * \\returns Buffer properties\n     */\n    const DxvkBufferCreateInfo& bufferInfo() const {\n      return m_buffer->info();\n    }\n    \n    /**\n     * \\brief Buffer sub slice\n     * \n     * Takes a sub slice from this slice.\n     * \\param [in] offset Sub slice offset\n     * \\param [in] length Sub slice length\n     * \\returns The sub slice object\n     */\n    DxvkBufferSlice subSlice(VkDeviceSize offset, VkDeviceSize length) const {\n      return DxvkBufferSlice(m_buffer, m_offset + offset, length);\n    }\n    \n    /**\n     * \\brief Checks whether the slice is valid\n     * \n     * A buffer slice that does not point to any virtual\n     * buffer object is considered undefined and cannot\n     * be used for any operations.\n     * \\returns \\c true if the slice is defined\n     */\n    bool defined() const {\n      return m_buffer != nullptr;\n    }\n    \n    /**\n     * \\brief Retrieves buffer slice handle\n     * \n     * Returns the buffer handle and offset\n     * \\returns Buffer slice handle\n     */\n    DxvkResourceBufferInfo getSliceInfo() const {\n      return m_buffer\n        ? m_buffer->getSliceInfo(m_offset, m_length)\n        : DxvkResourceBufferInfo();\n    }\n\n    /**\n     * \\brief Retrieves sub slice handle\n     * \n     * \\param [in] offset Offset into buffer\n     * \\param [in] length Sub slice length\n     * \\returns Buffer slice handle\n     */\n    DxvkResourceBufferInfo getSliceInfo(VkDeviceSize offset, VkDeviceSize length) const {\n      return m_buffer\n        ? m_buffer->getSliceInfo(m_offset + offset, length)\n        : DxvkResourceBufferInfo();\n    }\n\n    /**\n     * \\brief Retrieves descriptor info\n     * \\returns Buffer slice descriptor\n     */\n    DxvkLegacyDescriptor getDescriptor() const {\n      return m_buffer->getDescriptor(m_offset, m_length);\n    }\n\n    /**\n     * \\brief Pointer to mapped memory region\n     * \n     * \\param [in] offset Offset into the slice\n     * \\returns Pointer into mapped buffer memory\n     */\n    void* mapPtr(VkDeviceSize offset) const  {\n      return m_buffer != nullptr\n        ? m_buffer->mapPtr(m_offset + offset)\n        : nullptr;\n    }\n\n    /**\n     * \\brief Checks whether two slices are equal\n     * \n     * Two slices are considered equal if they point to\n     * the same memory region within the same buffer.\n     * \\param [in] other The slice to compare to\n     * \\returns \\c true if the two slices are the same\n     */\n    bool matches(const DxvkBufferSlice& other) const {\n      return this->m_buffer == other.m_buffer\n          && this->m_offset == other.m_offset\n          && this->m_length == other.m_length;\n    }\n\n    /**\n     * \\brief Checks whether two slices are from the same buffer\n     *\n     * This returns \\c true if the two slices are taken\n     * from the same buffer, but may have different ranges.\n     * \\param [in] other The slice to compare to\n     * \\returns \\c true if the buffer objects are the same\n     */\n    bool matchesBuffer(const DxvkBufferSlice& other) const {\n      return this->m_buffer == other.m_buffer;\n    }\n\n    /**\n     * \\brief Checks whether two slices have the same range\n     * \n     * This returns \\c true if the two slices have the same\n     * offset and size, even if the buffers are different.\n     * May be useful if the buffers are know to be the same.\n     * \\param [in] other The slice to compare to\n     * \\returns \\c true if the buffer objects are the same\n     */\n    bool matchesRange(const DxvkBufferSlice& other) const {\n      return this->m_offset == other.m_offset\n          && this->m_length == other.m_length;\n    }\n\n    /**\n     * \\brief Sets buffer range\n     *\n     * \\param [in] offset New offset\n     * \\param [in] length New length\n     */\n    void setRange(VkDeviceSize offset, VkDeviceSize length) {\n      m_offset = offset;\n      m_length = length;\n    }\n\n  private:\n    \n    Rc<DxvkBuffer> m_buffer = nullptr;\n    VkDeviceSize   m_offset = 0;\n    VkDeviceSize   m_length = 0;\n    \n  };\n\n\n\n  inline const DxvkDescriptor* DxvkBufferView::getDescriptor(bool raw) {\n    if (unlikely(m_version < m_buffer->m_version))\n      updateViews();\n\n    return raw ? m_raw : m_formatted;\n  }\n\n\n  inline DxvkResourceBufferInfo DxvkBufferView::getSliceInfo() const {\n    return m_buffer->getSliceInfo(m_key.offset, m_key.size);\n  }\n\n\n  force_inline void DxvkBufferView::incRef() {\n    m_buffer->incRef();\n  }\n\n\n  force_inline void DxvkBufferView::decRef() {\n    m_buffer->decRef();\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_cmdlist.cpp",
    "content": "\n#include \"dxvk_cmdlist.h\"\n#include \"dxvk_device.h\"\n\nnamespace dxvk {\n\n  DxvkCommandSubmission::DxvkCommandSubmission() {\n\n  }\n\n\n  DxvkCommandSubmission::~DxvkCommandSubmission() {\n\n  }\n\n\n  void DxvkCommandSubmission::waitSemaphore(\n          VkSemaphore           semaphore,\n          uint64_t              value,\n          VkPipelineStageFlags2 stageMask) {\n    VkSemaphoreSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO };\n    submitInfo.semaphore = semaphore;\n    submitInfo.value     = value;\n    submitInfo.stageMask = stageMask;\n\n    m_semaphoreWaits.push_back(submitInfo);\n  }\n\n\n  void DxvkCommandSubmission::signalSemaphore(\n          VkSemaphore           semaphore,\n          uint64_t              value,\n          VkPipelineStageFlags2 stageMask) {\n    VkSemaphoreSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO };\n    submitInfo.semaphore = semaphore;\n    submitInfo.value     = value;\n    submitInfo.stageMask = stageMask;\n\n    m_semaphoreSignals.push_back(submitInfo);\n  }\n\n\n  void DxvkCommandSubmission::executeCommandBuffer(\n          VkCommandBuffer       commandBuffer) {\n    VkCommandBufferSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO };\n    submitInfo.commandBuffer = commandBuffer;\n\n    m_commandBuffers.push_back(submitInfo);\n  }\n\n\n  VkResult DxvkCommandSubmission::submit(\n          DxvkDevice*           device,\n          VkQueue               queue,\n          uint64_t              frameId) {\n    auto vk = device->vkd();\n\n    VkLatencySubmissionPresentIdNV latencyInfo = { VK_STRUCTURE_TYPE_LATENCY_SUBMISSION_PRESENT_ID_NV };\n    latencyInfo.presentID = frameId;\n\n    VkSubmitInfo2 submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO_2 };\n\n    if (!m_semaphoreWaits.empty()) {\n      submitInfo.waitSemaphoreInfoCount = m_semaphoreWaits.size();\n      submitInfo.pWaitSemaphoreInfos = m_semaphoreWaits.data();\n    }\n\n    if (!m_commandBuffers.empty()) {\n      submitInfo.commandBufferInfoCount = m_commandBuffers.size();\n      submitInfo.pCommandBufferInfos = m_commandBuffers.data();\n    }\n\n    if (!m_semaphoreSignals.empty()) {\n      submitInfo.signalSemaphoreInfoCount = m_semaphoreSignals.size();\n      submitInfo.pSignalSemaphoreInfos = m_semaphoreSignals.data();\n    }\n\n    if (frameId && device->features().nvLowLatency2)\n      latencyInfo.pNext = std::exchange(submitInfo.pNext, &latencyInfo);\n\n    VkResult vr = VK_SUCCESS;\n\n    if (!this->isEmpty())\n      vr = vk->vkQueueSubmit2(queue, 1, &submitInfo, VK_NULL_HANDLE);\n\n    this->reset();\n    return vr;\n  }\n\n\n  void DxvkCommandSubmission::reset() {\n    m_semaphoreWaits.clear();\n    m_semaphoreSignals.clear();\n    m_commandBuffers.clear();\n  }\n\n\n  bool DxvkCommandSubmission::isEmpty() const {\n    return m_semaphoreWaits.empty()\n        && m_semaphoreSignals.empty()\n        && m_commandBuffers.empty();\n  }\n\n\n  DxvkCommandPool::DxvkCommandPool(\n          DxvkDevice*           device,\n          uint32_t              queueFamily)\n  : m_device(device) {\n    auto vk = m_device->vkd();\n\n    VkCommandPoolCreateInfo poolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };\n    poolInfo.queueFamilyIndex = queueFamily;\n\n    if (vk->vkCreateCommandPool(vk->device(), &poolInfo, nullptr, &m_commandPool))\n      throw DxvkError(\"DxvkCommandPool: Failed to create command pool\");\n  }\n\n\n  DxvkCommandPool::~DxvkCommandPool() {\n    auto vk = m_device->vkd();\n\n    vk->vkDestroyCommandPool(vk->device(), m_commandPool, nullptr);\n  }\n\n\n  VkCommandBuffer DxvkCommandPool::getCommandBuffer(DxvkCmdBuffer type) {\n    auto vk = m_device->vkd();\n\n    if (m_nextPrimary == m_primaryBuffers.size()) {\n      // Allocate a new command buffer and add it to the list\n      VkCommandBufferAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };\n      allocInfo.commandPool = m_commandPool;\n      allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n      allocInfo.commandBufferCount = 1;\n\n      VkCommandBuffer commandBuffer = VK_NULL_HANDLE;\n\n      if (vk->vkAllocateCommandBuffers(vk->device(), &allocInfo, &commandBuffer))\n        throw DxvkError(\"DxvkCommandPool: Failed to allocate command buffer\");\n\n      m_primaryBuffers.push_back(commandBuffer);\n    }\n\n    // Take existing command buffer. All command buffers\n    // will be in reset state, so we can begin it safely.\n    VkCommandBuffer commandBuffer = m_primaryBuffers[m_nextPrimary++];\n\n    VkCommandBufferBeginInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };\n    info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\n    if (vk->vkBeginCommandBuffer(commandBuffer, &info))\n      throw DxvkError(\"DxvkCommandPool: Failed to begin command buffer\");\n\n    if (m_device->debugFlags().test(DxvkDebugFlag::Capture)) {\n      auto vki = m_device->vki();\n\n      VkDebugUtilsLabelEXT label = { };\n\n      switch (type) {\n        case DxvkCmdBuffer::ExecBuffer: label = vk::makeLabel(0xdcc0a2, \"Graphics commands\"); break;\n        case DxvkCmdBuffer::InitBuffer: label = vk::makeLabel(0xc0dca2, \"Init commands\"); break;\n        case DxvkCmdBuffer::InitBarriers: label = vk::makeLabel(0xd0e6b8, \"Init barriers\"); break;\n        case DxvkCmdBuffer::SdmaBuffer: label = vk::makeLabel(0xc0a2dc, \"Upload commands\"); break;\n        case DxvkCmdBuffer::SdmaBarriers: label = vk::makeLabel(0xd0b8e6, \"Upload barriers\"); break;\n        default: ;\n      }\n\n      if (label.pLabelName)\n        vki->vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &label);\n    }\n\n    return commandBuffer;\n  }\n\n\n  VkCommandBuffer DxvkCommandPool::getSecondaryCommandBuffer(\n    const VkCommandBufferInheritanceInfo& inheritanceInfo) {\n    auto vk = m_device->vkd();\n\n    if (m_nextSecondary == m_secondaryBuffers.size()) {\n      // Allocate a new command buffer and add it to the list\n      VkCommandBufferAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };\n      allocInfo.commandPool = m_commandPool;\n      allocInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;\n      allocInfo.commandBufferCount = 1;\n\n      VkCommandBuffer commandBuffer = VK_NULL_HANDLE;\n\n      if (vk->vkAllocateCommandBuffers(vk->device(), &allocInfo, &commandBuffer))\n        throw DxvkError(\"DxvkCommandPool: Failed to allocate secondary command buffer\");\n\n      m_secondaryBuffers.push_back(commandBuffer);\n    }\n\n    // Assume that the secondary command buffer contains only rendering commands\n    VkCommandBuffer commandBuffer = m_secondaryBuffers[m_nextSecondary++];\n\n    VkCommandBufferBeginInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };\n    info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT\n               | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;\n    info.pInheritanceInfo = &inheritanceInfo;\n\n    if (vk->vkBeginCommandBuffer(commandBuffer, &info))\n      throw DxvkError(\"DxvkCommandPool: Failed to begin secondary command buffer\");\n\n    return commandBuffer;\n  }\n\n\n  void DxvkCommandPool::reset() {\n    auto vk = m_device->vkd();\n\n    if (m_nextPrimary || m_nextSecondary) {\n      if (vk->vkResetCommandPool(vk->device(), m_commandPool, 0))\n        throw DxvkError(\"DxvkCommandPool: Failed to reset command pool\");\n\n      m_nextPrimary = 0;\n      m_nextSecondary = 0;\n    }\n  }\n\n\n  DxvkCommandList::DxvkCommandList(DxvkDevice* device)\n  : m_device        (device),\n    m_vkd           (device->vkd()),\n    m_vki           (device->vki()) {\n    const auto& graphicsQueue = m_device->queues().graphics;\n    const auto& transferQueue = m_device->queues().transfer;\n\n    m_graphicsPool = new DxvkCommandPool(device, graphicsQueue.queueFamily);\n\n    if (transferQueue.queueFamily != graphicsQueue.queueFamily)\n      m_transferPool = new DxvkCommandPool(device, transferQueue.queueFamily);\n    else\n      m_transferPool = m_graphicsPool;\n  }\n  \n  \n  DxvkCommandList::~DxvkCommandList() {\n    this->reset();\n  }\n  \n  \n  VkResult DxvkCommandList::submit(\n    const DxvkTimelineSemaphores&       semaphores,\n          DxvkTimelineSemaphoreValues&  timelines,\n          uint64_t                      trackedId) {\n    // Wait for pending descriptor copies to finish\n    m_descriptorSync.synchronize();\n\n    VkResult status = VK_SUCCESS;\n\n    static const std::array<DxvkCmdBuffer, 2> SdmaCmdBuffers =\n      { DxvkCmdBuffer::SdmaBarriers, DxvkCmdBuffer::SdmaBuffer };\n    static const std::array<DxvkCmdBuffer, 2> InitCmdBuffers =\n      { DxvkCmdBuffer::InitBarriers, DxvkCmdBuffer::InitBuffer };\n\n    const auto& graphics = m_device->queues().graphics;\n    const auto& transfer = m_device->queues().transfer;\n    const auto& sparse = m_device->queues().sparse;\n\n    m_commandSubmission.reset();\n\n    for (size_t i = 0; i < m_cmdSubmissions.size(); i++) {\n      bool isFirst = i == 0;\n      bool isLast  = i == m_cmdSubmissions.size() - 1;\n\n      const auto& cmd = m_cmdSubmissions[i];\n\n      auto sparseBind = cmd.sparseBind\n        ? &m_cmdSparseBinds[cmd.sparseCmd]\n        : nullptr;\n\n      if (isFirst) {\n        // Wait for per-command list semaphores on first submission\n        for (size_t i = 0; i < m_waitSemaphores.size(); i++) {\n          m_commandSubmission.waitSemaphore(m_waitSemaphores[i].fence->handle(),\n            m_waitSemaphores[i].value, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);\n        }\n      }\n\n      if (sparseBind) {\n        // Sparse binding needs to serialize command execution, so wait\n        // for any prior submissions, then block any subsequent ones\n        sparseBind->waitSemaphore(semaphores.graphics, timelines.graphics);\n        sparseBind->waitSemaphore(semaphores.transfer, timelines.transfer);\n\n        sparseBind->signalSemaphore(semaphores.graphics, ++timelines.graphics);\n\n        if ((status = sparseBind->submit(m_device, sparse.queueHandle)))\n          return status;\n\n        m_commandSubmission.waitSemaphore(semaphores.graphics,\n          timelines.graphics, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);\n      }\n\n      // Execute transfer command buffer, if any\n      for (auto cmdBuffer : SdmaCmdBuffers) {\n        if (cmd.cmdBuffers[uint32_t(cmdBuffer)])\n          m_commandSubmission.executeCommandBuffer(cmd.cmdBuffers[uint32_t(cmdBuffer)]);\n      }\n\n      // If we had either a transfer command or a semaphore wait, submit to the\n      // transfer queue so that all subsequent commands get stalled as necessary.\n      if (m_device->hasDedicatedTransferQueue() && !m_commandSubmission.isEmpty()) {\n        m_commandSubmission.signalSemaphore(semaphores.transfer,\n          ++timelines.transfer, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);\n\n        if ((status = m_commandSubmission.submit(m_device, transfer.queueHandle, trackedId)))\n          return status;\n\n        m_commandSubmission.waitSemaphore(semaphores.transfer,\n          timelines.transfer, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);\n      }\n\n      // We promise to never do weird stuff to WSI images on\n      // the transfer queue, so blocking graphics is sufficient\n      if (isFirst && m_wsiSemaphores.acquire) {\n        m_commandSubmission.waitSemaphore(m_wsiSemaphores.acquire,\n          0, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);\n      }\n\n      // Submit initialization commands, if any\n      for (auto cmdBuffer : InitCmdBuffers) {\n        if (cmd.cmdBuffers[uint32_t(cmdBuffer)])\n          m_commandSubmission.executeCommandBuffer(cmd.cmdBuffers[uint32_t(cmdBuffer)]);\n      }\n\n      // Only submit the main command buffer if it has actually been used\n      if (cmd.execCommands)\n        m_commandSubmission.executeCommandBuffer(cmd.cmdBuffers[uint32_t(DxvkCmdBuffer::ExecBuffer)]);\n\n      if (isLast) {\n        // Signal per-command list semaphores on the final submission\n        for (size_t i = 0; i < m_signalSemaphores.size(); i++) {\n          m_commandSubmission.signalSemaphore(m_signalSemaphores[i].fence->handle(),\n            m_signalSemaphores[i].value, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);\n        }\n\n        // Signal WSI semaphore on the final submission\n        if (m_wsiSemaphores.present) {\n          m_commandSubmission.signalSemaphore(m_wsiSemaphores.present,\n            0, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);\n        }\n      }\n\n      m_commandSubmission.signalSemaphore(semaphores.graphics,\n        ++timelines.graphics, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);\n\n      // Finally, submit all graphics commands of the current submission\n      if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle, trackedId)))\n        return status;\n\n      // If there are WSI semaphores involved, do another submit only\n      // containing a timeline semaphore signal so that we can be sure\n      // that they are safe to use afterwards.\n      if ((m_wsiSemaphores.present || m_wsiSemaphores.acquire) && isLast) {\n        m_commandSubmission.signalSemaphore(semaphores.graphics,\n          ++timelines.graphics, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);\n\n        if ((status = m_commandSubmission.submit(m_device, graphics.queueHandle, trackedId)))\n          return status;\n      }\n\n      // Finally, submit semaphore wait on the transfer queue. If this\n      // is not the final iteration, fold the wait into the next one.\n      if (cmd.syncSdma) {\n        m_commandSubmission.waitSemaphore(semaphores.graphics,\n          timelines.graphics, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT);\n\n        if (isLast && (status = m_commandSubmission.submit(m_device, transfer.queueHandle, trackedId)))\n          return status;\n      }\n    }\n\n    return VK_SUCCESS;\n  }\n  \n  \n  void DxvkCommandList::init() {\n    // Make sure the main command buffer is initialized since we can\n    // reasonably expect that to always get used. Saves some checks\n    // during command recording.\n    m_cmd = DxvkCommandSubmissionInfo();\n    m_cmd.cmdBuffers[uint32_t(DxvkCmdBuffer::ExecBuffer)] = allocateCommandBuffer(DxvkCmdBuffer::ExecBuffer);\n  }\n  \n  \n  void DxvkCommandList::finalize() {\n    // Record commands to upload descriptors if necessary, and\n    // reset the descriptor range to not keep it alive for too\n    // long. Descriptor ranges are tracked when bound.\n    if (m_device->canUseDescriptorHeap() || m_device->canUseDescriptorBuffer()) {\n      countDescriptorStats(m_descriptorRange, m_descriptorOffset);\n\n      m_descriptorRange = nullptr;\n      m_descriptorHeap = nullptr;\n    } else {\n      m_descriptorPool->updateStats(m_statCounters);\n    }\n\n    // Commit current set of command buffers\n    m_cmdSubmissions.push_back(m_cmd);\n\n    // For consistency, end all command buffers here,\n    // regardless of whether they have been used.\n    for (uint32_t i = 0; i < m_cmd.cmdBuffers.size(); i++) {\n      if (m_cmd.cmdBuffers[i])\n        endCommandBuffer(m_cmd.cmdBuffers[i]);\n    }\n\n    // Reset all command buffer handles\n    m_cmd = DxvkCommandSubmissionInfo();\n\n    // Increment queue submission count\n    uint64_t submissionCount = m_cmdSubmissions.size();\n    m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, submissionCount);\n  }\n\n\n  void DxvkCommandList::next() {\n    bool push = m_cmd.sparseBind || m_cmd.execCommands;\n\n    for (uint32_t i = 0; i < m_cmd.cmdBuffers.size(); i++) {\n      DxvkCmdBuffer cmdBuffer = DxvkCmdBuffer(i);\n\n      if (cmdBuffer == DxvkCmdBuffer::ExecBuffer && !m_cmd.execCommands)\n        continue;\n\n      if (m_cmd.cmdBuffers[i]) {\n        endCommandBuffer(m_cmd.cmdBuffers[i]);\n\n        m_cmd.cmdBuffers[i] = cmdBuffer == DxvkCmdBuffer::ExecBuffer\n          ? allocateCommandBuffer(cmdBuffer)\n          : VK_NULL_HANDLE;\n\n        push = true;\n      }\n    }\n\n    if (!push)\n      return;\n\n    m_cmdSubmissions.push_back(m_cmd);\n\n    m_cmd.execCommands = VK_FALSE;\n    m_cmd.syncSdma = VK_FALSE;\n    m_cmd.sparseBind = VK_FALSE;\n  }\n\n  \n  void DxvkCommandList::reset() {\n    // We will re-apply heap bindings first thing in a\n    // new command list, so reset this flag here\n    m_descriptorHeapInvalidated = false;\n\n    // Free resources and other objects\n    // that are no longer in use\n    m_objectTracker.clear();\n\n    // Less important stuff\n    m_signalTracker.reset();\n    m_statCounters.reset();\n\n    // Recycle descriptor pools\n    if (m_descriptorPool) {\n      m_descriptorPool->notifyCompletion(m_trackingId);\n      m_descriptorPool = nullptr;\n    }\n\n    // Release pipelines\n    for (auto pipeline : m_pipelines)\n      pipeline->releasePipeline();\n\n    m_pipelines.clear();\n\n    m_waitSemaphores.clear();\n    m_signalSemaphores.clear();\n\n    m_cmdSubmissions.clear();\n    m_cmdSparseBinds.clear();\n\n    m_wsiSemaphores = PresenterSync();\n\n    // Reset actual command buffers and pools\n    m_graphicsPool->reset();\n    m_transferPool->reset();\n  }\n\n\n  void DxvkCommandList::bindResources(\n          DxvkCmdBuffer                 cmdBuffer,\n    const DxvkPipelineLayout*           layout,\n          uint32_t                      descriptorCount,\n    const DxvkDescriptorWrite*          descriptorInfos,\n          size_t                        pushDataSize,\n    const void*                         pushData) {\n    if (m_device->canUseDescriptorHeap()) {\n      bindResourcesDescriptorHeap(cmdBuffer, layout,\n        descriptorCount, descriptorInfos, pushDataSize, pushData);\n    } else if (m_device->canUseDescriptorBuffer()) {\n      bindResourcesDescriptorBuffer(cmdBuffer, layout,\n        descriptorCount, descriptorInfos, pushDataSize, pushData);\n    } else {\n      bindResourcesLegacy(cmdBuffer, layout,\n        descriptorCount, descriptorInfos, pushDataSize, pushData);\n    }\n  }\n\n\n  void DxvkCommandList::bindResourcesLegacy(\n          DxvkCmdBuffer                 cmdBuffer,\n    const DxvkPipelineLayout*           layout,\n          uint32_t                      descriptorCount,\n    const DxvkDescriptorWrite*          descriptorInfos,\n          size_t                        pushDataSize,\n    const void*                         pushData) {\n    // Update descriptor set as necessary\n    auto setLayout = layout->getDescriptorSetLayout(0u);\n\n    if (descriptorCount && setLayout && !setLayout->isEmpty()) {\n      VkDescriptorSet set = m_descriptorPool->alloc(m_trackingId, setLayout);\n\n      small_vector<DxvkLegacyDescriptor, 16u> descriptors;\n\n      for (uint32_t i = 0u; i < descriptorCount; i++) {\n        const auto& info = descriptorInfos[i];\n        auto& descriptor = descriptors.emplace_back();\n\n        switch (info.descriptorType) {\n          case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:\n          case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: {\n            if (info.descriptor) {\n              descriptor.buffer = info.descriptor->legacy.buffer;\n            } else {\n              descriptor.buffer.buffer = info.buffer.buffer;\n              descriptor.buffer.offset = info.buffer.offset;\n              descriptor.buffer.range = info.buffer.size;\n\n              if (!descriptor.buffer.buffer)\n                descriptor.buffer.range = VK_WHOLE_SIZE;\n            }\n          } break;\n\n          case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:\n          case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: {\n            if (info.descriptor)\n              descriptor.bufferView = info.descriptor->legacy.bufferView;\n          } break;\n\n          case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:\n          case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {\n            if (info.descriptor)\n              descriptor.image = info.descriptor->legacy.image;\n          } break;\n\n          default:\n            Logger::err(str::format(\"Unhandled descriptor type \", info.descriptorType));\n        }\n      }\n\n      this->updateDescriptorSetWithTemplate(set,\n        setLayout->getSetUpdateTemplate(),\n        descriptors.data());\n\n      // Bind set as well as the global sampler heap, if requested\n      small_vector<VkDescriptorSet, 2u> sets;\n\n      if (layout->usesSamplerHeap())\n        sets.push_back(m_device->getSamplerDescriptorSet().set);\n\n      sets.push_back(set);\n\n      this->cmdBindDescriptorSets(cmdBuffer,\n        layout->getBindPoint(),\n        layout->getPipelineLayout(),\n        0u, sets.size(), sets.data());\n    }\n\n    // Update push constants\n    DxvkPushDataBlock pushDataBlock = layout->getPushData();\n\n    if (pushDataSize && !pushDataBlock.isEmpty()) {\n      std::array<char, MaxTotalPushDataSize> dataCopy;\n      std::memcpy(dataCopy.data(), pushData,\n        std::min(dataCopy.size(), pushDataSize));\n\n      this->cmdPushConstants(cmdBuffer,\n        layout->getPipelineLayout(),\n        pushDataBlock.getStageMask(),\n        pushDataBlock.getOffset(),\n        pushDataBlock.getSize(),\n        dataCopy.data());\n    }\n  }\n\n\n  void DxvkCommandList::bindResourcesDescriptorHeap(\n          DxvkCmdBuffer                 cmdBuffer,\n    const DxvkPipelineLayout*           layout,\n          uint32_t                      descriptorCount,\n    const DxvkDescriptorWrite*          descriptorInfos,\n          size_t                        pushDataSize,\n    const void*                         pushData) {\n    auto setLayout = layout->getDescriptorSetLayout(0u);\n\n    // Whether heaps are valid is command list state, not context state,\n    // to facilitate interactions with external rendering\n    this->ensureDescriptorHeapBinding();\n\n    // For built-in pipelines, the push data layout will have shader-defined\n    // consants first, then a byte offset to the descriptor set, in contrast\n    // to regular pipelines.\n    DxvkPushDataBlock pushDataBlock = layout->getPushData();\n\n    if (pushDataSize && !pushDataBlock.isEmpty()) {\n      VkPushDataInfoEXT pushInfo = { VK_STRUCTURE_TYPE_PUSH_DATA_INFO_EXT };\n      pushInfo.offset = 0u;\n      pushInfo.data.address = pushData;\n      pushInfo.data.size = pushDataSize;\n\n      this->cmdPushData(cmdBuffer, &pushInfo);\n    }\n\n    if (descriptorCount && setLayout && !setLayout->isEmpty()) {\n      auto vk = m_device->vkd();\n\n      // Assume that a descriptor heap is already active and that\n      // we're not recording into a secondary command buffer.\n      if (!canAllocateDescriptors(layout))\n        createDescriptorRange();\n\n      // Need to pre-allocate arrays with a fixed size so pointers remain valid\n      small_vector<DxvkDescriptor, 8u> buffers(descriptorCount);\n      small_vector<VkHostAddressRangeEXT, 8u> hostRanges(descriptorCount);\n      small_vector<VkDeviceAddressRangeEXT, 8u> bufferRanges(descriptorCount);\n      small_vector<VkResourceDescriptorInfoEXT, 8u> writes(descriptorCount);\n\n      // Populate descriptor arrays with necessary information\n      small_vector<const DxvkDescriptor*, 8u> descriptors;\n      descriptors.reserve(descriptorCount);\n\n      uint32_t writeCount = 0u;\n\n      for (uint32_t i = 0u; i < descriptorCount; i++) {\n        const auto& info = descriptorInfos[i];\n\n        switch (info.descriptorType) {\n          case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:\n          case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: {\n            auto& descriptor = buffers[writeCount];\n            descriptors.push_back(&descriptor);\n\n            auto& hostRange = hostRanges[writeCount];\n            hostRange = descriptor.getHostAddressRange();\n\n            auto& bufferRange = bufferRanges[writeCount];\n            bufferRange.address = info.buffer.gpuAddress;\n            bufferRange.size = info.buffer.size;\n\n            auto& write = writes[writeCount];\n            write.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT;\n            write.type = info.descriptorType;\n            write.data.pAddressRange = &bufferRange;\n\n            writeCount += 1u;\n          } break;\n\n          case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:\n          case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:\n          case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:\n          case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {\n            auto descriptor = info.descriptor;\n\n            if (!descriptor)\n              descriptor = m_device->getDescriptorProperties().getNullDescriptor(info.descriptorType);\n\n            descriptors.push_back(descriptor);\n          } break;\n\n          default:\n            Logger::err(str::format(\"Unhandled descriptor type \", info.descriptorType));\n        }\n      }\n\n      // Write out buffer descriptors\n      if (writeCount) {\n        vk->vkWriteResourceDescriptorsEXT(vk->device(),\n          writeCount, writes.data(), hostRanges.data());\n      }\n\n      // Allocate descriptor storage and update the set\n      auto setLayout = layout->getDescriptorSetLayout(0u);\n      auto storage = allocateDescriptors(setLayout);\n\n      setLayout->update(storage.mapPtr, descriptors.data());\n\n      // Bind the set by updating the appropriate push constant\n      uint32_t setOffset = storage.offset >> layout->getDescriptorOffsetShift();\n\n      VkPushDataInfoEXT pushInfo = { VK_STRUCTURE_TYPE_PUSH_DATA_INFO_EXT };\n      pushInfo.offset = pushDataBlock.getSize();\n      pushInfo.data.address = &setOffset;\n      pushInfo.data.size = sizeof(setOffset);\n\n      this->cmdPushData(cmdBuffer, &pushInfo);\n    }\n  }\n\n\n  void DxvkCommandList::bindResourcesDescriptorBuffer(\n          DxvkCmdBuffer                 cmdBuffer,\n    const DxvkPipelineLayout*           layout,\n          uint32_t                      descriptorCount,\n    const DxvkDescriptorWrite*          descriptorInfos,\n          size_t                        pushDataSize,\n    const void*                         pushData) {\n\n    auto setLayout = layout->getDescriptorSetLayout(0u);\n\n    if (descriptorCount && setLayout && !setLayout->isEmpty()) {\n      auto vk = m_device->vkd();\n\n      // Assume that a descriptor heap is already active and that\n      // we're not recording into a secondary command buffer.\n      if (!canAllocateDescriptors(layout))\n        createDescriptorRange();\n\n      // Populate descriptor arrays with necessary information\n      small_vector<const DxvkDescriptor*, 8u> descriptors;\n      descriptors.reserve(descriptorCount);\n\n      small_vector<DxvkDescriptor, 8u> buffers;\n      buffers.reserve(descriptorCount);\n\n      for (uint32_t i = 0u; i < descriptorCount; i++) {\n        const auto& info = descriptorInfos[i];\n\n        switch (info.descriptorType) {\n          case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:\n          case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: {\n            auto& descriptor = buffers.emplace_back();\n\n            VkDescriptorAddressInfoEXT bufferInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT };\n            bufferInfo.address = info.buffer.gpuAddress;\n            bufferInfo.range = info.buffer.size;\n\n            VkDescriptorGetInfoEXT descriptorInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };\n            descriptorInfo.type = info.descriptorType;\n\n            if (info.buffer.size) {\n              (info.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER\n                ? descriptorInfo.data.pStorageBuffer\n                : descriptorInfo.data.pUniformBuffer) = &bufferInfo;\n            }\n\n            vk->vkGetDescriptorEXT(vk->device(), &descriptorInfo,\n              m_device->getDescriptorProperties().getDescriptorTypeInfo(info.descriptorType).size,\n              descriptor.descriptor.data());\n\n            descriptors.push_back(&descriptor);\n          } break;\n\n          case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:\n          case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:\n          case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:\n          case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {\n            auto descriptor = info.descriptor;\n\n            if (!descriptor)\n              descriptor = m_device->getDescriptorProperties().getNullDescriptor(info.descriptorType);\n\n            descriptors.push_back(descriptor);\n          } break;\n\n          default:\n            Logger::err(str::format(\"Unhandled descriptor type \", info.descriptorType));\n        }\n      }\n\n      // Allocate descriptor storage and update the set\n      auto setLayout = layout->getDescriptorSetLayout(0u);\n      auto storage = allocateDescriptors(setLayout);\n\n      setLayout->update(storage.mapPtr, descriptors.data());\n\n      // Bind actual descriptors\n      std::array<uint32_t,     2u> bufferIndices = { };\n      std::array<VkDeviceSize, 2u> bufferOffsets = { };\n\n      uint32_t setCount = 0u;\n\n      if (layout->usesSamplerHeap()) {\n        bufferIndices[setCount] = 0u;\n        bufferOffsets[setCount] = 0u;\n        setCount++;\n      }\n\n      bufferIndices[setCount] = 1u;\n      bufferOffsets[setCount] = storage.offset;\n      setCount++;\n\n      cmdSetDescriptorBufferOffsetsEXT(cmdBuffer,\n        layout->getBindPoint(),\n        layout->getPipelineLayout(),\n        0u, setCount,\n        bufferIndices.data(),\n        bufferOffsets.data());\n    }\n\n    // Update push constants\n    DxvkPushDataBlock pushDataBlock = layout->getPushData();\n\n    if (pushDataSize && !pushDataBlock.isEmpty()) {\n      std::array<char, MaxTotalPushDataSize> dataCopy;\n      std::memcpy(dataCopy.data(), pushData,\n        std::min(dataCopy.size(), pushDataSize));\n\n      this->cmdPushConstants(cmdBuffer,\n        layout->getPipelineLayout(),\n        pushDataBlock.getStageMask(),\n        pushDataBlock.getOffset(),\n        pushDataBlock.getSize(),\n        dataCopy.data());\n    }\n  }\n\n\n  bool DxvkCommandList::createDescriptorRange() {\n    countDescriptorStats(m_descriptorRange, m_descriptorOffset);\n\n    auto oldBaseAddress = m_descriptorRange\n      ? m_descriptorRange->getHeapInfo().gpuAddress\n      : 0u;\n\n    m_descriptorRange = m_descriptorHeap->allocRange();\n    auto newBaseAddress = m_descriptorRange->getHeapInfo().gpuAddress;\n\n    if (newBaseAddress != oldBaseAddress) {\n      if (m_execBuffer) {\n        m_descriptorRange = nullptr;\n        return false;\n      }\n\n      if (m_device->canUseDescriptorHeap())\n        rebindResourceHeap();\n      else if (m_device->canUseDescriptorBuffer())\n        rebindDescriptorBuffers();\n    }\n\n    m_descriptorOffset = m_descriptorRange->getAllocationOffset();\n\n    track(m_descriptorRange);\n    return true;\n  }\n\n\n  void DxvkCommandList::beginSecondaryCommandBuffer(\n          VkCommandBufferInheritanceInfo inheritanceInfo) {\n    VkCommandBufferInheritanceDescriptorHeapInfoEXT heapInheritance = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_DESCRIPTOR_HEAP_INFO_EXT };\n\n    VkBindHeapInfoEXT samplerHeap = { };\n    VkBindHeapInfoEXT resourceHeap = { };\n\n    if (m_device->canUseDescriptorHeap()) {\n      samplerHeap = getHeapBindInfo(m_device->getSamplerDescriptorHeap());\n      resourceHeap = getHeapBindInfo(m_descriptorRange->getHeapInfo());\n\n      heapInheritance.pNext = std::exchange(inheritanceInfo.pNext, &heapInheritance);\n      heapInheritance.pSamplerHeapBindInfo = &samplerHeap;\n      heapInheritance.pResourceHeapBindInfo = &resourceHeap;\n    }\n\n    VkCommandBuffer secondary = m_graphicsPool->getSecondaryCommandBuffer(inheritanceInfo);\n\n    if (m_device->canUseDescriptorBuffer())\n      bindDescriptorBuffers(secondary);\n\n    m_execBuffer = std::exchange(m_cmd.cmdBuffers[uint32_t(DxvkCmdBuffer::ExecBuffer)], secondary);\n  }\n\n\n  VkCommandBuffer DxvkCommandList::endSecondaryCommandBuffer() {\n    VkCommandBuffer cmd = getCmdBuffer();\n\n    if (m_vkd->vkEndCommandBuffer(cmd))\n      throw DxvkError(\"DxvkCommandList: Failed to end secondary command buffer\");\n\n    m_cmd.cmdBuffers[uint32_t(DxvkCmdBuffer::ExecBuffer)] = m_execBuffer;\n    m_execBuffer = VK_NULL_HANDLE;\n    return cmd;\n  }\n\n\n  void DxvkCommandList::cmdExecuteCommands(\n          uint32_t                count,\n          VkCommandBuffer*        commandBuffers) {\n    m_cmd.execCommands = true;\n\n    VkCommandBuffer primary = getCmdBuffer();\n    m_vkd->vkCmdExecuteCommands(primary, count, commandBuffers);\n\n    if (m_device->canUseDescriptorBuffer())\n      bindDescriptorBuffers(primary);\n  }\n\n\n  void DxvkCommandList::setDescriptorHeap(\n          Rc<DxvkResourceDescriptorHeap> heap) {\n    // External rendering reapplies state, but we\n    // really want to avoid that for heap binding\n    if (m_descriptorHeap == heap)\n      return;\n\n    m_descriptorHeap = std::move(heap);\n    m_descriptorRange = m_descriptorHeap->getRange();\n    m_descriptorOffset = m_descriptorRange->getAllocationOffset();\n\n    if (m_device->canUseDescriptorHeap())\n      rebindResourceHeap();\n    else if (m_device->canUseDescriptorBuffer())\n      rebindDescriptorBuffers();\n\n    track(m_descriptorRange);\n  }\n\n\n  void DxvkCommandList::rebindSamplerHeap() {\n    // Secondary command buffer must not be active when this gets called\n    for (uint32_t i = uint32_t(DxvkCmdBuffer::ExecBuffer); i <= uint32_t(DxvkCmdBuffer::InitBarriers); i++)\n      bindSamplerHeap(m_cmd.cmdBuffers[i]);\n  }\n\n\n  void DxvkCommandList::rebindResourceHeap() {\n    // Secondary command buffer must not be active when this gets called\n    for (uint32_t i = uint32_t(DxvkCmdBuffer::ExecBuffer); i <= uint32_t(DxvkCmdBuffer::InitBarriers); i++)\n      bindResourceHeap(m_cmd.cmdBuffers[i]);\n  }\n\n\n  void DxvkCommandList::rebindDescriptorBuffers() {\n    // Secondary command buffer must not be active when this gets called\n    for (uint32_t i = uint32_t(DxvkCmdBuffer::ExecBuffer); i <= uint32_t(DxvkCmdBuffer::InitBuffer); i++)\n      bindDescriptorBuffers(m_cmd.cmdBuffers[i]);\n  }\n\n\n  void DxvkCommandList::bindSamplerHeap(VkCommandBuffer cmdBuffer) {\n    auto vk = m_device->vkd();\n\n    if (!cmdBuffer)\n      return;\n\n    VkBindHeapInfoEXT bindInfo = getHeapBindInfo(m_device->getSamplerDescriptorHeap());\n    vk->vkCmdBindSamplerHeapEXT(cmdBuffer, &bindInfo);\n  }\n\n\n  void DxvkCommandList::bindResourceHeap(VkCommandBuffer cmdBuffer) {\n    auto vk = m_device->vkd();\n\n    if (!cmdBuffer || !m_descriptorRange)\n      return;\n\n    VkBindHeapInfoEXT bindInfo = getHeapBindInfo(m_descriptorRange->getHeapInfo());\n    vk->vkCmdBindResourceHeapEXT(cmdBuffer, &bindInfo);\n  }\n\n\n  void DxvkCommandList::bindDescriptorBuffers(VkCommandBuffer cmdBuffer) {\n    auto vk = m_device->vkd();\n\n    if (!cmdBuffer || !m_descriptorRange)\n      return;\n\n    auto samplerInfo = m_device->getSamplerDescriptorHeap();\n    auto resourceInfo = m_descriptorRange->getHeapInfo();\n\n    std::array<VkDescriptorBufferBindingInfoEXT, 2u> heaps = { };\n\n    auto& samplerHeap = heaps[0u];\n    samplerHeap.sType = { VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT };\n    samplerHeap.address = samplerInfo.gpuAddress;\n    samplerHeap.usage = VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    auto& resourceHeap = heaps[1u];\n    resourceHeap.sType = { VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT };\n    resourceHeap.address = resourceInfo.gpuAddress;\n    resourceHeap.usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    vk->vkCmdBindDescriptorBuffersEXT(cmdBuffer, heaps.size(), heaps.data());\n  }\n\n\n  void DxvkCommandList::endCommandBuffer(VkCommandBuffer cmdBuffer) {\n    auto vk = m_device->vkd();\n\n    if (m_device->debugFlags().test(DxvkDebugFlag::Capture))\n      m_vki->vkCmdEndDebugUtilsLabelEXT(cmdBuffer);\n\n    if (vk->vkEndCommandBuffer(cmdBuffer))\n      throw DxvkError(\"DxvkCommandList: Failed to end command buffer\");\n  }\n\n\n  VkCommandBuffer DxvkCommandList::allocateCommandBuffer(DxvkCmdBuffer type) {\n    VkCommandBuffer cmdBuffer = (type >= DxvkCmdBuffer::SdmaBuffer)\n      ? m_transferPool->getCommandBuffer(type)\n      : m_graphicsPool->getCommandBuffer(type);\n\n    if (type <= DxvkCmdBuffer::InitBarriers && m_device->canUseDescriptorHeap()) {\n      bindSamplerHeap(cmdBuffer);\n      bindResourceHeap(cmdBuffer);\n    }\n\n    if (type <= DxvkCmdBuffer::InitBuffer && m_device->canUseDescriptorBuffer())\n      bindDescriptorBuffers(cmdBuffer);\n\n    return cmdBuffer;\n  }\n\n\n  void DxvkCommandList::countDescriptorStats(\n    const Rc<DxvkResourceDescriptorRange>& range,\n          VkDeviceSize                  baseOffset) {\n    if (range) {\n      VkDeviceSize dataSize = range->getAllocationOffset() - baseOffset;\n      addStatCtr(DxvkStatCounter::DescriptorHeapUsed, dataSize);\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_cmdlist.h",
    "content": "#pragma once\n\n#include <limits>\n\n#include \"dxvk_bind_mask.h\"\n#include \"dxvk_buffer.h\"\n#include \"dxvk_descriptor.h\"\n#include \"dxvk_descriptor_heap.h\"\n#include \"dxvk_descriptor_pool.h\"\n#include \"dxvk_descriptor_worker.h\"\n#include \"dxvk_fence.h\"\n#include \"dxvk_gpu_event.h\"\n#include \"dxvk_gpu_query.h\"\n#include \"dxvk_graphics.h\"\n#include \"dxvk_limits.h\"\n#include \"dxvk_pipelayout.h\"\n#include \"dxvk_presenter.h\"\n#include \"dxvk_signal.h\"\n#include \"dxvk_sparse.h\"\n#include \"dxvk_stats.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Immediate descriptor write\n   *\n   * Takes descriptor info either from an existing\n   * view descriptor or from a buffer range.\n   */\n  struct DxvkDescriptorWrite {\n    /** Actual descriptor type */\n    VkDescriptorType descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM;\n    /** Pointer to view descriptor. Used for all image descriptors\n     *  as well as texel buffer descriptors. If \\c nullptr, a null\n     *  descriptor of the corresponding type will be created. */\n    const DxvkDescriptor* descriptor = nullptr;\n    /** Buffer info, used for storage and uniform buffers. May be\n     *  used to build a null descriptor. */\n    DxvkResourceBufferInfo buffer = { };\n  };\n\n  \n  /**\n   * \\brief Timeline semaphore pair\n   *\n   * One semaphore for each queue.\n   */\n  struct DxvkTimelineSemaphores {\n    VkSemaphore graphics = VK_NULL_HANDLE;\n    VkSemaphore transfer = VK_NULL_HANDLE;\n  };\n\n\n  /**\n   * \\brief Timeline semaphore values\n   */\n  struct DxvkTimelineSemaphoreValues {\n    uint64_t graphics = 0u;\n    uint64_t transfer = 0u;\n  };\n\n\n  /**\n   * \\brief Command buffer flags\n   * \n   * A set of flags used to specify which of\n   * the command buffers need to be submitted.\n   */\n  enum class DxvkCmdBuffer : uint32_t {\n    ExecBuffer,\n    InitBuffer,\n    InitBarriers,\n    SdmaBuffer,\n    SdmaBarriers,\n\n    Count\n  };\n  \n  /**\n   * \\brief Queue command submission\n   *\n   * Convenience class that holds data for a single\n   * command submission, which then easily be executed.\n   */\n  class DxvkCommandSubmission {\n\n  public:\n\n    DxvkCommandSubmission();\n    ~DxvkCommandSubmission();\n\n    /**\n     * \\brief Adds a semaphore to wait on\n     *\n     * \\param [in] semaphore The semaphore\n     * \\param [in] value Semaphore value\n     * \\param [in] stageMask Stages to block\n     */\n    void waitSemaphore(\n            VkSemaphore           semaphore,\n            uint64_t              value,\n            VkPipelineStageFlags2 stageMask);\n\n    /**\n     * \\brief Adds a semaphore to signal\n     *\n     * \\param [in] semaphore The semaphore\n     * \\param [in] value Semaphore value\n     * \\param [in] stageMask Stages to signal on\n     */\n    void signalSemaphore(\n            VkSemaphore           semaphore,\n            uint64_t              value,\n            VkPipelineStageFlags2 stageMask);\n\n    /**\n     * \\brief Adds a command buffer to execute\n     * \\param [in] commandBuffer The command buffer\n     */\n    void executeCommandBuffer(\n            VkCommandBuffer       commandBuffer);\n\n    /**\n     * \\brief Executes submission and resets object\n     *\n     * \\param [in] device DXVK device\n     * \\param [in] queue Queue to submit to\n     * \\param [in] frameId Latency frame ID\n     * \\returns Submission return value\n     */\n    VkResult submit(\n            DxvkDevice*           device,\n            VkQueue               queue,\n            uint64_t              frameId);\n\n    /**\n     * \\brief Resets object\n     */\n    void reset();\n\n    /**\n     * \\brief Checks whether the submission is empty\n     *\n     * \\returns \\c true if there are no command\n     *    buffers or semaphores.\n     */\n    bool isEmpty() const;\n\n  private:\n\n    small_vector<VkSemaphoreSubmitInfo, 4>      m_semaphoreWaits;\n    small_vector<VkSemaphoreSubmitInfo, 4>      m_semaphoreSignals;\n    small_vector<VkCommandBufferSubmitInfo, 4>  m_commandBuffers;\n\n  };\n\n\n  /**\n   * \\brief Command submission info\n   *\n   * Stores a set of command buffers, as well as a\n   * mask of command buffers that were actually used.\n   */\n  struct DxvkCommandSubmissionInfo {\n    bool                execCommands = false;\n    bool                syncSdma    = false;\n    bool                sparseBind  = false;\n    bool                reserved    = false;\n    uint32_t            sparseCmd   = 0;\n\n    std::array<VkCommandBuffer, uint32_t(DxvkCmdBuffer::Count)> cmdBuffers = { };\n  };\n\n\n  /**\n   * \\brief Command pool\n   *\n   * Simple command pool abstraction that allows\n   * us to easily obtain command buffers.\n   */\n  class DxvkCommandPool : public RcObject {\n\n  public:\n\n    /**\n     * \\brief Creates command pool\n     *\n     * \\param [in] device DXVK device\n     * \\param [in] queueFamily Target queue family\n     */\n    DxvkCommandPool(\n            DxvkDevice*           device,\n            uint32_t              queueFamily);\n\n    ~DxvkCommandPool();\n\n    /**\n     * \\brief Retrieves or allocates a command buffer\n     *\n     * \\param [in] type Command buffer type\n     * \\returns New command buffer in begun state\n     */\n    VkCommandBuffer getCommandBuffer(DxvkCmdBuffer type);\n\n    /**\n     * \\brief Retrieves or allocates secondary command buffer\n     *\n     * \\param [in] inheritanceInfo Inheritance info\n     * \\returns New command buffer in begun state\n     */\n    VkCommandBuffer getSecondaryCommandBuffer(\n      const VkCommandBufferInheritanceInfo& inheritanceInfo);\n\n    /**\n     * \\brief Resets command pool and all command buffers\n     */\n    void reset();\n\n  private:\n\n    DxvkDevice*                   m_device;\n\n    VkCommandPool                 m_commandPool = VK_NULL_HANDLE;\n\n    std::vector<VkCommandBuffer>  m_primaryBuffers;\n    std::vector<VkCommandBuffer>  m_secondaryBuffers;\n\n    size_t                        m_nextPrimary   = 0u;\n    size_t                        m_nextSecondary = 0u;\n\n  };\n\n\n  /**\n   * \\brief Command list\n   * \n   * Stores a command buffer that a context can use to record Vulkan\n   * commands. The command list shall also reference the resources\n   * used by the recorded commands for automatic lifetime tracking.\n   * When the command list has completed execution, resources that\n   * are no longer used may get destroyed.\n   */\n  class DxvkCommandList : public RcObject {\n    \n  public:\n    \n    DxvkCommandList(DxvkDevice* device);\n    ~DxvkCommandList();\n    \n    /**\n     * \\brief Submits command list\n     *\n     * \\param [in] semaphores Timeline semaphore pair\n     * \\param [in] timelines Timeline semaphore values\n     * \\param [in] frameId Latency frame ID\n     * \\returns Submission status\n     */\n    VkResult submit(\n      const DxvkTimelineSemaphores&       semaphores,\n            DxvkTimelineSemaphoreValues&  timelines,\n            uint64_t                      frameId);\n    \n    /**\n     * \\brief Stat counters\n     * \n     * Retrieves some info about per-command list\n     * statistics, such as the number of draw calls\n     * or the number of pipelines compiled.\n     * \\returns Reference to stat counters\n     */\n    DxvkStatCounters& statCounters() {\n      return m_statCounters;\n    }\n    \n    /**\n     * \\brief Increments a stat counter value\n     * \n     * \\param [in] ctr The counter to increment\n     * \\param [in] val The value to add\n     */\n    void addStatCtr(DxvkStatCounter ctr, uint64_t val) {\n      m_statCounters.addCtr(ctr, val);\n    }\n    \n    /**\n     * \\brief Initializes command buffers\n     *\n     * Prepares command list for command recording.\n     */\n    void init();\n    \n    /**\n     * \\brief Ends recording\n     *\n     * Ends command buffer recording, making\n     * the command list ready for submission.\n     * \\param [in] stats Stat counters\n     */\n    void finalize();\n\n    /**\n     * \\brief Interrupts recording\n     *\n     * Begins a new set of command buffers while adding the\n     * current set to the submission list. This can be useful\n     * to split the command list into multiple submissions.\n     */\n    void next();\n    \n    /**\n     * \\brief Tracks an object\n     *\n     * Keeps the object alive until the command list finishes\n     * execution on the GPU.\n     * \\param [in] object Object to track\n     */\n    template<typename T>\n    void track(Rc<T> object) {\n      static_assert(!std::is_same_v<T, DxvkSampler>);\n      m_objectTracker.track<DxvkObjectRef<T>>(std::move(object));\n    }\n\n    /**\n     * \\brief Tracks a sampler object\n     *\n     * Special code path that uses the tracking ID to ensure samplers\n     * only get tracked once per submission. This is useful since\n     * sampler objects are processed much the same way as resources.\n     * \\param [in] sampler Sampler object\n     */\n    void track(const Rc<DxvkSampler>& sampler) {\n      if (sampler->trackId(m_trackingId))\n        m_objectTracker.track<DxvkObjectRef<DxvkSampler>>(sampler.ptr());\n    }\n\n    void track(Rc<DxvkSampler>&& sampler) {\n      if (sampler->trackId(m_trackingId))\n        m_objectTracker.track<DxvkObjectRef<DxvkSampler>>(std::move(sampler));\n    }\n\n    /**\n     * \\brief Tracks a resource with access mode\n     *\n     * Keeps the object alive and tracks resource access for\n     * the purpoe of CPU access synchronization. The different\n     * overloads try to reduce atomic operations.\n     * \\param [in] object Object to track\n     * \\param [in] access Resource access mode\n     */\n    template<typename T>\n    void track(Rc<T>&& object, DxvkAccess access) {\n      if (object->trackId(m_trackingId, access))\n        m_objectTracker.track<DxvkResourceRef>(std::move(object), access);\n    }\n\n    template<typename T>\n    void track(const Rc<T>& object, DxvkAccess access) {\n      if (object->trackId(m_trackingId, access))\n        m_objectTracker.track<DxvkResourceRef>(object.ptr(), access);\n    }\n\n    template<typename T>\n    void track(T* object, DxvkAccess access) {\n      if (object->trackId(m_trackingId, access))\n        m_objectTracker.track<DxvkResourceRef>(object, access);\n    }\n\n    /**\n     * \\brief Tracks a graphics pipeline\n     * \\param [in] pipeline Pipeline\n     */\n    void trackGraphicsPipeline(DxvkGraphicsPipeline* pipeline) {\n      pipeline->acquirePipeline();\n      m_pipelines.push_back(pipeline);\n    }\n\n    /**\n     * \\brief Queues signal\n     * \n     * The signal will be notified once the command\n     * buffer has finished executing on the GPU.\n     * \\param [in] signal The signal\n     * \\param [in] value Signal value\n     */\n    void queueSignal(const Rc<sync::Signal>& signal, uint64_t value) {\n      m_signalTracker.add(signal, value);\n    }\n\n    /**\n     * \\brief Notifies resources and signals\n     */\n    void notifyObjects() {\n      m_objectTracker.clear();\n      m_signalTracker.notify();\n    }\n\n    /**\n     * \\brief Waits for fence\n     *\n     * \\param [in] fence Fence to wait on\n     * \\param [in] value Value to wait for\n     */\n    void waitFence(Rc<DxvkFence> fence, uint64_t value) {\n      m_waitSemaphores.emplace_back(std::move(fence), value);\n    }\n    \n    /**\n     * \\brief Signals fence\n     *\n     * \\param [in] fence Fence to signal\n     * \\param [in] value Value to signal to\n     */\n    void signalFence(Rc<DxvkFence> fence, uint64_t value) {\n      m_signalSemaphores.emplace_back(std::move(fence), value);\n    }\n\n    /**\n     * \\brief Sets WSI semaphores to synchronize with\n     *\n     * The given semaphores must be binary semaphores.\n     * \\param [in] wsiSemaphores Pair of WSI semaphores\n     */\n    void setWsiSemaphores(const PresenterSync& wsiSemaphores) {\n      m_wsiSemaphores = wsiSemaphores;\n    }\n\n    /**\n     * \\brief Sets flag to stall transfer queue\n     *\n     * If set, the current submission will submit a semaphore\n     * wait to the transfer queue in order to stall subsequent\n     * submissions. Necessary in case of resource relocations.\n     */\n    void setSubmissionBarrier() {\n      m_cmd.syncSdma = VK_TRUE;\n    }\n\n    /**\n     * \\brief Resets the command list\n     * \n     * Resets the internal command buffer of the command list and\n     * marks all tracked resources as unused. When submitting the\n     * command list to the device, this method will be called once\n     * the command list completes execution.\n     */\n    void reset();\n\n    /**\n     * \\brief Tries to allocates and bind an empty descriptor range\n     *\n     * This will fail if the base address of the allocated range changes\n     * while a secondary command buffer is currently active. In that case,\n     * the secodary command buffer \\e must be ended first.\n     * \\returns \\c true if a new range was successfully allocated and bound.\n     */\n    bool createDescriptorRange();\n\n    /**\n     * \\brief Checks whether current descriptor range can service an allocation\n     *\n     * \\param [in] pipelineLayout The pipeline layout\n     * \\returns \\c true if the current descriptor range has enough space\n     *    to allocate all descriptor sets in the given pipeline layout.\n     */\n    bool canAllocateDescriptors(const DxvkPipelineLayout* layout) const {\n      return m_descriptorRange && m_descriptorRange->testAllocation(layout->getDescriptorMemorySize());\n    }\n\n    /**\n     * \\brief Allocates descriptor memory for a given layout\n     *\n     * The caller \\e must ensure that enough space is available in the\n     * current descriptor range by calling \\c canAllocateDescriptors,\n     * and allocate a new descriptor range if necessary.\n     * \\param [in] layout Descriptor set layout\n     * \\returns Allocated descriptor heap range\n     */\n    DxvkResourceBufferInfo allocateDescriptors(const DxvkDescriptorSetLayout* layout) const {\n      return m_descriptorRange->alloc(layout->getMemorySize());\n    }\n\n    /**\n     * \\brief Sets resources and push constants\n     *\n     * Allocates and writes a descriptor set and sets push constant\n     * data all in one go. This method is primarily intended to be\n     * used with meta pipelines and external rendering.\n     *\n     * If \\c descriptorCount is 0, no descriptors will be updated,\n     * and the currently bound set must be layout-compatible with\n     * the pipeline. Similarly, when setting \\c pushDataSize to 0,\n     * no push constant data will be updated. This behaviour is\n     * useful when doing back-to-back draws with the same pipeline.\n     * \\param [in] cmdBuffer Target command buffer\n     * \\param [in] layout Pipeline layout. Must only have one\n     *    single non-empty descriptor set at index 0.\n     * \\param [in] descriptorCount Number of descriptor infos\n     * \\param [in] descriptorInfos Descriptors\n     * \\param [in] pushDataSize Size of push constant data\n     * \\param [in] pushData Pointer to push constant data\n     */\n    void bindResources(\n            DxvkCmdBuffer                 cmdBuffer,\n      const DxvkPipelineLayout*           layout,\n            uint32_t                      descriptorCount,\n      const DxvkDescriptorWrite*          descriptorInfos,\n            size_t                        pushDataSize,\n      const void*                         pushData);\n\n    /**\n     * \\brief Begins a secondary command buffer\n     *\n     * All subsequent commands targeted at the execution command\n     * buffer will be recorded into a secondary command buffer\n     * instead until \\c endSecondaryCommandBuffer is called.\n     * \\param [in] inheritanceInfo Command buffer inheritance info\n     */\n    void beginSecondaryCommandBuffer(\n            VkCommandBufferInheritanceInfo inheritanceInfo);\n\n    /**\n     * \\brief Ends secondary command buffer\n     *\n     * Ends current secondary command buffer so that subsequent\n     * execution commands will be recorded into the primary\n     * command buffer again. The secondary command buffer can\n     * be executed manually with \\c execCommands.\n     * \\returns Command buffer handle\n     */\n    VkCommandBuffer endSecondaryCommandBuffer();\n\n    /**\n     * \\brief Records secondary command buffers into primary\n     *\n     * \\param [in] count Number of command buffers to execute\n     * \\param [in] commandBuffers Command buffer handles\n     */\n    void cmdExecuteCommands(\n            uint32_t                count,\n            VkCommandBuffer*        commandBuffers);\n\n\n    void updateDescriptorSets(\n            uint32_t                      descriptorWriteCount,\n      const VkWriteDescriptorSet*         pDescriptorWrites) {\n      m_vkd->vkUpdateDescriptorSets(m_vkd->device(),\n        descriptorWriteCount, pDescriptorWrites,\n        0, nullptr);\n    }\n    \n    \n    void updateDescriptorSetWithTemplate(\n            VkDescriptorSet               descriptorSet,\n            VkDescriptorUpdateTemplate    descriptorTemplate,\n      const void*                         data) {\n      m_vkd->vkUpdateDescriptorSetWithTemplate(m_vkd->device(),\n        descriptorSet, descriptorTemplate, data);\n    }\n\n\n    void cmdBeginQuery(\n            VkQueryPool             queryPool,\n            uint32_t                query,\n            VkQueryControlFlags     flags) {\n      m_cmd.execCommands = true;\n\n      m_vkd->vkCmdBeginQuery(getCmdBuffer(), queryPool, query, flags);\n    }\n    \n    \n    void cmdBeginQueryIndexed(\n            VkQueryPool             queryPool,\n            uint32_t                query,\n            VkQueryControlFlags     flags,\n            uint32_t                index) {\n      m_cmd.execCommands = true;\n\n      m_vkd->vkCmdBeginQueryIndexedEXT(getCmdBuffer(),\n        queryPool, query, flags, index);\n    }\n\n\n    void cmdBeginRendering(\n            DxvkCmdBuffer             cmdBuffer,\n      const VkRenderingInfo*          pRenderingInfo) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdBeginRendering(getCmdBuffer(cmdBuffer), pRenderingInfo);\n    }\n\n\n    void cmdBeginTransformFeedback(\n            uint32_t                  firstBuffer,\n            uint32_t                  bufferCount,\n      const VkBuffer*                 counterBuffers,\n      const VkDeviceSize*             counterOffsets) {\n      m_vkd->vkCmdBeginTransformFeedbackEXT(getCmdBuffer(),\n        firstBuffer, bufferCount, counterBuffers, counterOffsets);\n    }\n    \n    \n    void cmdBindDescriptorSets(\n            DxvkCmdBuffer             cmdBuffer,\n            VkPipelineBindPoint       pipeline,\n            VkPipelineLayout          pipelineLayout,\n            uint32_t                  firstSet,\n            uint32_t                  descriptorSetCount,\n      const VkDescriptorSet*          descriptorSets) {\n      m_vkd->vkCmdBindDescriptorSets(getCmdBuffer(cmdBuffer),\n        pipeline, pipelineLayout, firstSet, descriptorSetCount,\n        descriptorSets, 0, nullptr);\n    }\n\n\n    void cmdSetDescriptorBufferOffsetsEXT(\n            DxvkCmdBuffer             cmdBuffer,\n            VkPipelineBindPoint       pipeline,\n            VkPipelineLayout          layout,\n            uint32_t                  firstSet,\n            uint32_t                  setCount,\n      const uint32_t*                 pBufferIndices,\n      const VkDeviceSize*             pOffsets) {\n      m_vkd->vkCmdSetDescriptorBufferOffsetsEXT(getCmdBuffer(cmdBuffer),\n        pipeline, layout, firstSet, setCount, pBufferIndices, pOffsets);\n    }\n\n\n\n    void cmdBindIndexBuffer(\n            VkBuffer                buffer,\n            VkDeviceSize            offset,\n            VkIndexType             indexType) {\n      m_vkd->vkCmdBindIndexBuffer(getCmdBuffer(),\n        buffer, offset, indexType);\n    }\n    \n    \n    void cmdBindIndexBuffer2(\n            VkBuffer                buffer,\n            VkDeviceSize            offset,\n            VkDeviceSize            size,\n            VkIndexType             indexType) {\n      m_vkd->vkCmdBindIndexBuffer2KHR(getCmdBuffer(),\n        buffer, offset, size, indexType);\n    }\n\n\n    void cmdBindPipeline(\n            DxvkCmdBuffer           cmdBuffer,\n            VkPipelineBindPoint     pipelineBindPoint,\n            VkPipeline              pipeline) {\n      m_vkd->vkCmdBindPipeline(getCmdBuffer(cmdBuffer),\n        pipelineBindPoint, pipeline);\n    }\n\n\n    void cmdBindTransformFeedbackBuffers(\n            uint32_t                firstBinding,\n            uint32_t                bindingCount,\n      const VkBuffer*               pBuffers,\n      const VkDeviceSize*           pOffsets,\n      const VkDeviceSize*           pSizes) {\n      m_vkd->vkCmdBindTransformFeedbackBuffersEXT(getCmdBuffer(),\n        firstBinding, bindingCount, pBuffers, pOffsets, pSizes);\n    }\n    \n    \n    void cmdBindVertexBuffers(\n            uint32_t                firstBinding,\n            uint32_t                bindingCount,\n      const VkBuffer*               pBuffers,\n      const VkDeviceSize*           pOffsets,\n      const VkDeviceSize*           pSizes,\n      const VkDeviceSize*           pStrides) {\n      m_vkd->vkCmdBindVertexBuffers2(getCmdBuffer(),\n        firstBinding, bindingCount, pBuffers, pOffsets,\n        pSizes, pStrides);\n    }\n    \n    void cmdLaunchCuKernel(VkCuLaunchInfoNVX launchInfo) {\n      m_cmd.execCommands = true;\n\n      m_vkd->vkCmdCuLaunchKernelNVX(getCmdBuffer(), &launchInfo);\n    }\n    \n\n    void cmdBlitImage(\n        const VkBlitImageInfo2*     pBlitInfo) {\n      m_cmd.execCommands = true;\n\n      m_vkd->vkCmdBlitImage2(getCmdBuffer(), pBlitInfo);\n    }\n    \n    \n    void cmdClearAttachments(\n            DxvkCmdBuffer           cmdBuffer,\n            uint32_t                attachmentCount,\n      const VkClearAttachment*      pAttachments,\n            uint32_t                rectCount,\n      const VkClearRect*            pRects) {\n      m_vkd->vkCmdClearAttachments(getCmdBuffer(cmdBuffer),\n        attachmentCount, pAttachments, rectCount, pRects);\n    }\n    \n    \n    void cmdClearColorImage(\n            DxvkCmdBuffer           cmdBuffer,\n            VkImage                 image,\n            VkImageLayout           imageLayout,\n      const VkClearColorValue*      pColor,\n            uint32_t                rangeCount,\n      const VkImageSubresourceRange* pRanges) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdClearColorImage(getCmdBuffer(cmdBuffer),\n        image, imageLayout, pColor,\n        rangeCount, pRanges);\n    }\n    \n    \n    void cmdClearDepthStencilImage(\n            DxvkCmdBuffer           cmdBuffer,\n            VkImage                 image,\n            VkImageLayout           imageLayout,\n      const VkClearDepthStencilValue* pDepthStencil,\n            uint32_t                rangeCount,\n      const VkImageSubresourceRange* pRanges) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdClearDepthStencilImage(getCmdBuffer(cmdBuffer),\n        image, imageLayout, pDepthStencil,\n        rangeCount, pRanges);\n    }\n    \n    \n    void cmdCopyBuffer(\n            DxvkCmdBuffer           cmdBuffer,\n      const VkCopyBufferInfo2*      copyInfo) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdCopyBuffer2(getCmdBuffer(cmdBuffer), copyInfo);\n    }\n    \n    \n    void cmdCopyBufferToImage(\n            DxvkCmdBuffer           cmdBuffer,\n      const VkCopyBufferToImageInfo2* copyInfo) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdCopyBufferToImage2(getCmdBuffer(cmdBuffer), copyInfo);\n    }\n    \n    \n    void cmdCopyImage(\n            DxvkCmdBuffer           cmdBuffer,\n      const VkCopyImageInfo2*       copyInfo) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdCopyImage2(getCmdBuffer(cmdBuffer), copyInfo);\n    }\n    \n    \n    void cmdCopyImageToBuffer(\n            DxvkCmdBuffer           cmdBuffer,\n      const VkCopyImageToBufferInfo2* copyInfo) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdCopyImageToBuffer2(getCmdBuffer(cmdBuffer), copyInfo);\n    }\n\n\n    void cmdCopyQueryPoolResults(\n            DxvkCmdBuffer           cmdBuffer,\n            VkQueryPool             queryPool,\n            uint32_t                firstQuery,\n            uint32_t                queryCount,\n            VkBuffer                dstBuffer,\n            VkDeviceSize            dstOffset,\n            VkDeviceSize            stride,\n            VkQueryResultFlags      flags) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdCopyQueryPoolResults(getCmdBuffer(cmdBuffer),\n        queryPool, firstQuery, queryCount,\n        dstBuffer, dstOffset, stride, flags);\n    }\n    \n    \n    void cmdDispatch(\n            DxvkCmdBuffer           cmdBuffer,\n            uint32_t                x,\n            uint32_t                y,\n            uint32_t                z) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdDispatch(getCmdBuffer(cmdBuffer), x, y, z);\n    }\n    \n    \n    void cmdDispatchIndirect(\n            DxvkCmdBuffer           cmdBuffer,\n            VkBuffer                buffer,\n            VkDeviceSize            offset) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdDispatchIndirect(getCmdBuffer(cmdBuffer), buffer, offset);\n    }\n    \n    \n    void cmdDraw(\n            uint32_t                vertexCount,\n            uint32_t                instanceCount,\n            uint32_t                firstVertex,\n            uint32_t                firstInstance) {\n      m_vkd->vkCmdDraw(getCmdBuffer(),\n        vertexCount, instanceCount,\n        firstVertex, firstInstance);\n    }\n\n\n    void cmdDrawMulti(\n            uint32_t                drawCount,\n      const VkMultiDrawInfoEXT*     drawInfos,\n            uint32_t                instanceCount,\n            uint32_t                firstInstance) {\n      m_vkd->vkCmdDrawMultiEXT(getCmdBuffer(),\n        drawCount, drawInfos, instanceCount, firstInstance, sizeof(*drawInfos));\n    }\n    \n    \n    void cmdDrawIndirect(\n            VkBuffer                buffer,\n            VkDeviceSize            offset,\n            uint32_t                drawCount,\n            uint32_t                stride) {\n      m_vkd->vkCmdDrawIndirect(getCmdBuffer(),\n        buffer, offset, drawCount, stride);\n    }\n    \n    \n    void cmdDrawIndirectCount(\n            VkBuffer                buffer,\n            VkDeviceSize            offset,\n            VkBuffer                countBuffer,\n            VkDeviceSize            countOffset,\n            uint32_t                maxDrawCount,\n            uint32_t                stride) {\n      m_vkd->vkCmdDrawIndirectCount(getCmdBuffer(), buffer,\n        offset, countBuffer, countOffset, maxDrawCount, stride);\n    }\n    \n    \n    void cmdDrawIndexed(\n            uint32_t                indexCount,\n            uint32_t                instanceCount,\n            uint32_t                firstIndex,\n            int32_t                 vertexOffset,\n            uint32_t                firstInstance) {\n      m_vkd->vkCmdDrawIndexed(getCmdBuffer(),\n        indexCount, instanceCount,\n        firstIndex, vertexOffset,\n        firstInstance);\n    }\n    \n    \n    void cmdDrawMultiIndexed(\n            uint32_t                drawCount,\n      const VkMultiDrawIndexedInfoEXT* drawInfos,\n            uint32_t                instanceCount,\n            uint32_t                firstInstance) {\n      m_vkd->vkCmdDrawMultiIndexedEXT(getCmdBuffer(), drawCount,\n        drawInfos, instanceCount, firstInstance, sizeof(*drawInfos), nullptr);\n    }\n\n\n    void cmdDrawIndexedIndirect(\n            VkBuffer                buffer,\n            VkDeviceSize            offset,\n            uint32_t                drawCount,\n            uint32_t                stride) {\n      m_vkd->vkCmdDrawIndexedIndirect(getCmdBuffer(),\n        buffer, offset, drawCount, stride);\n    }\n\n\n    void cmdDrawIndexedIndirectCount(\n            VkBuffer                buffer,\n            VkDeviceSize            offset,\n            VkBuffer                countBuffer,\n            VkDeviceSize            countOffset,\n            uint32_t                maxDrawCount,\n            uint32_t                stride) {\n      m_vkd->vkCmdDrawIndexedIndirectCount(getCmdBuffer(),\n        buffer, offset, countBuffer, countOffset, maxDrawCount, stride);\n    }\n    \n    \n    void cmdDrawIndirectVertexCount(\n            uint32_t                instanceCount,\n            uint32_t                firstInstance,\n            VkBuffer                counterBuffer,\n            VkDeviceSize            counterBufferOffset,\n            uint32_t                counterOffset,\n            uint32_t                vertexStride) {\n      m_vkd->vkCmdDrawIndirectByteCountEXT(getCmdBuffer(),\n        instanceCount, firstInstance, counterBuffer,\n        counterBufferOffset, counterOffset, vertexStride);\n    }\n    \n    \n    void cmdEndQuery(\n            VkQueryPool             queryPool,\n            uint32_t                query) {\n      m_vkd->vkCmdEndQuery(getCmdBuffer(), queryPool, query);\n    }\n\n\n    void cmdEndQueryIndexed(\n            VkQueryPool             queryPool,\n            uint32_t                query,\n            uint32_t                index) {\n      m_vkd->vkCmdEndQueryIndexedEXT(getCmdBuffer(),\n        queryPool, query, index);\n    }\n    \n    \n    void cmdEndRendering(\n            DxvkCmdBuffer             cmdBuffer) {\n      m_vkd->vkCmdEndRendering(getCmdBuffer(cmdBuffer));\n    }\n\n\n    void cmdEndTransformFeedback(\n            uint32_t                  firstBuffer,\n            uint32_t                  bufferCount,\n      const VkBuffer*                 counterBuffers,\n      const VkDeviceSize*             counterOffsets) {\n      m_vkd->vkCmdEndTransformFeedbackEXT(getCmdBuffer(),\n        firstBuffer, bufferCount, counterBuffers, counterOffsets);\n    }\n\n\n    void cmdFillBuffer(\n            DxvkCmdBuffer           cmdBuffer,\n            VkBuffer                dstBuffer,\n            VkDeviceSize            dstOffset,\n            VkDeviceSize            size,\n            uint32_t                data) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdFillBuffer(getCmdBuffer(cmdBuffer),\n        dstBuffer, dstOffset, size, data);\n    }\n    \n    \n    void cmdPipelineBarrier(\n            DxvkCmdBuffer           cmdBuffer,\n      const VkDependencyInfo*       dependencyInfo) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n      m_statCounters.addCtr(DxvkStatCounter::CmdBarrierCount, 1);\n\n      m_vkd->vkCmdPipelineBarrier2(getCmdBuffer(cmdBuffer), dependencyInfo);\n    }\n\n\n    void cmdPushConstants(\n            DxvkCmdBuffer           cmdBuffer,\n            VkPipelineLayout        layout,\n            VkShaderStageFlags      stageFlags,\n            uint32_t                offset,\n            uint32_t                size,\n      const void*                   pValues) {\n      m_vkd->vkCmdPushConstants(getCmdBuffer(cmdBuffer),\n        layout, stageFlags, offset, size, pValues);\n    }\n\n\n    void cmdPushData(\n            DxvkCmdBuffer           cmdBuffer,\n      const VkPushDataInfoEXT*      info) {\n      m_vkd->vkCmdPushDataEXT(getCmdBuffer(cmdBuffer), info);\n    }\n\n\n    void cmdResetQueryPool(\n            DxvkCmdBuffer           cmdBuffer,\n            VkQueryPool             queryPool,\n            uint32_t                firstQuery,\n            uint32_t                queryCount) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdResetQueryPool(getCmdBuffer(cmdBuffer),\n        queryPool, firstQuery, queryCount);\n    }\n\n\n    void cmdResolveImage(\n      const VkResolveImageInfo2*    resolveInfo) {\n      m_cmd.execCommands = true;\n\n      m_vkd->vkCmdResolveImage2(getCmdBuffer(), resolveInfo);\n    }\n    \n    \n    void cmdUpdateBuffer(\n            DxvkCmdBuffer           cmdBuffer,\n            VkBuffer                dstBuffer,\n            VkDeviceSize            dstOffset,\n            VkDeviceSize            dataSize,\n      const void*                   pData) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdUpdateBuffer(getCmdBuffer(cmdBuffer),\n        dstBuffer, dstOffset, dataSize, pData);\n    }\n\n\n    void cmdSetAlphaToCoverageState(\n            VkBool32                alphaToCoverageEnable) {\n      m_vkd->vkCmdSetAlphaToCoverageEnableEXT(getCmdBuffer(), alphaToCoverageEnable);\n    }\n\n    \n    void cmdSetBlendConstants(const float blendConstants[4]) {\n      m_vkd->vkCmdSetBlendConstants(getCmdBuffer(), blendConstants);\n    }\n    \n\n    void cmdSetDepthClipState(\n            VkBool32                depthClipEnable) {\n      m_vkd->vkCmdSetDepthClipEnableEXT(getCmdBuffer(), depthClipEnable);\n    }\n\n\n    void cmdSetDepthBias(\n            float                   depthBiasConstantFactor,\n            float                   depthBiasClamp,\n            float                   depthBiasSlopeFactor) {\n      auto cmdBuffer = getCmdBuffer();\n\n      m_vkd->vkCmdSetDepthBiasEnable(cmdBuffer,\n        depthBiasConstantFactor != 0.0f ||\n        depthBiasSlopeFactor != 0.0f);\n\n      m_vkd->vkCmdSetDepthBias(cmdBuffer,\n        depthBiasConstantFactor,\n        depthBiasClamp,\n        depthBiasSlopeFactor);\n    }\n\n\n    void cmdSetDepthBias2(\n      const VkDepthBiasInfoEXT*     depthBiasInfo) {\n      auto cmdBuffer = getCmdBuffer();\n\n      m_vkd->vkCmdSetDepthBiasEnable(cmdBuffer,\n        depthBiasInfo->depthBiasConstantFactor != 0.0f ||\n        depthBiasInfo->depthBiasSlopeFactor != 0.0f);\n\n      m_vkd->vkCmdSetDepthBias2EXT(cmdBuffer, depthBiasInfo);\n    }\n\n\n    void cmdSetDepthBounds(\n            float                   minDepthBounds,\n            float                   maxDepthBounds) {\n      auto cmdBuffer = getCmdBuffer();\n\n      m_vkd->vkCmdSetDepthBoundsTestEnable(cmdBuffer,\n        minDepthBounds > 0.0f || maxDepthBounds < 1.0f);\n\n      m_vkd->vkCmdSetDepthBounds(cmdBuffer,\n        minDepthBounds, maxDepthBounds);\n    }\n\n\n    void cmdSetDepthTest(\n            VkBool32                depthTestEnable) {\n      m_vkd->vkCmdSetDepthTestEnable(getCmdBuffer(), depthTestEnable);\n    }\n\n\n    void cmdSetDepthWrite(\n            VkBool32                depthWriteEnable) {\n      m_vkd->vkCmdSetDepthWriteEnable(getCmdBuffer(), depthWriteEnable);\n    }\n\n\n    void cmdSetDepthCompareOp(\n            VkCompareOp             depthCompareOp) {\n      m_vkd->vkCmdSetDepthCompareOp(getCmdBuffer(), depthCompareOp);\n    }\n\n\n    void cmdSetEvent(\n            VkEvent                 event,\n      const VkDependencyInfo*       dependencyInfo) {\n      m_cmd.execCommands = true;\n\n      m_vkd->vkCmdSetEvent2(getCmdBuffer(), event, dependencyInfo);\n    }\n\n\n    void cmdSetMultisampleState(\n            VkSampleCountFlagBits   sampleCount,\n            VkSampleMask            sampleMask) {\n      VkCommandBuffer cmdBuffer = getCmdBuffer();\n\n      m_vkd->vkCmdSetRasterizationSamplesEXT(cmdBuffer, sampleCount);\n      m_vkd->vkCmdSetSampleMaskEXT(cmdBuffer, sampleCount, &sampleMask);\n    }\n\n\n    void cmdSetRasterizerState(\n            VkCullModeFlags         cullMode,\n            VkFrontFace             frontFace) {\n      VkCommandBuffer cmdBuffer = getCmdBuffer();\n\n      m_vkd->vkCmdSetCullMode(cmdBuffer, cullMode);\n      m_vkd->vkCmdSetFrontFace(cmdBuffer, frontFace);\n    }\n\n    \n    void cmdSetSampleLocations(\n            VkBool32                enable,\n      const VkSampleLocationsInfoEXT* sampleLocations) {\n      VkCommandBuffer cmdBuffer = getCmdBuffer();\n\n      m_vkd->vkCmdSetSampleLocationsEnableEXT(cmdBuffer, enable);\n\n      if (enable)\n        m_vkd->vkCmdSetSampleLocationsEXT(cmdBuffer, sampleLocations);\n    }\n\n    void cmdSetScissor(\n            uint32_t                scissorCount,\n      const VkRect2D*               scissors) {\n      m_vkd->vkCmdSetScissorWithCount(getCmdBuffer(), scissorCount, scissors);\n    }\n\n\n    void cmdSetStencilTest(\n            VkBool32                enableStencilTest) {\n      m_vkd->vkCmdSetStencilTestEnable(getCmdBuffer(), enableStencilTest);\n    }\n\n\n    void cmdSetStencilOp(\n            VkStencilFaceFlags      faceMask,\n      const VkStencilOpState&       op) {\n      m_vkd->vkCmdSetStencilOp(getCmdBuffer(), faceMask,\n        op.failOp, op.passOp, op.depthFailOp, op.compareOp);\n    }\n\n\n    void cmdSetStencilCompareMask(\n            VkStencilFaceFlags      faceMask,\n            uint32_t                compareMask) {\n      m_vkd->vkCmdSetStencilCompareMask(getCmdBuffer(), faceMask, compareMask);\n    }\n\n\n    void cmdSetStencilReference(\n            VkStencilFaceFlags      faceMask,\n            uint32_t                reference) {\n      m_vkd->vkCmdSetStencilReference(getCmdBuffer(),\n        faceMask, reference);\n    }\n\n\n    void cmdSetStencilWriteMask(\n            VkStencilFaceFlags      faceMask,\n            uint32_t                writeMask) {\n      m_vkd->vkCmdSetStencilWriteMask(getCmdBuffer(), faceMask, writeMask);\n    }\n\n\n    void cmdSetViewport(\n            uint32_t                viewportCount,\n      const VkViewport*             viewports) {\n      m_vkd->vkCmdSetViewportWithCount(getCmdBuffer(), viewportCount, viewports);\n    }\n\n\n    void cmdWriteTimestamp(\n            DxvkCmdBuffer           cmdBuffer,\n            VkPipelineStageFlagBits2 pipelineStage,\n            VkQueryPool             queryPool,\n            uint32_t                query) {\n      m_cmd.execCommands |= cmdBuffer == DxvkCmdBuffer::ExecBuffer;\n\n      m_vkd->vkCmdWriteTimestamp2(getCmdBuffer(cmdBuffer),\n        pipelineStage, queryPool, query);\n    }\n    \n\n    void cmdBeginDebugUtilsLabel(\n            DxvkCmdBuffer           cmdBuffer,\n      const VkDebugUtilsLabelEXT&   labelInfo) {\n      m_cmd.execCommands = true;\n\n      m_vki->vkCmdBeginDebugUtilsLabelEXT(getCmdBuffer(cmdBuffer), &labelInfo);\n    }\n\n\n    void cmdEndDebugUtilsLabel(\n            DxvkCmdBuffer           cmdBuffer) {\n      m_cmd.execCommands = true;\n\n      m_vki->vkCmdEndDebugUtilsLabelEXT(getCmdBuffer(cmdBuffer));\n    }\n\n\n    void cmdInsertDebugUtilsLabel(\n            DxvkCmdBuffer           cmdBuffer,\n      const VkDebugUtilsLabelEXT&   labelInfo) {\n      m_cmd.execCommands = true;\n\n      m_vki->vkCmdInsertDebugUtilsLabelEXT(getCmdBuffer(cmdBuffer), &labelInfo);\n    }\n\n\n    void resetQuery(\n            VkQueryPool             queryPool,\n            uint32_t                queryId) {\n      m_vkd->vkResetQueryPool(\n        m_vkd->device(), queryPool, queryId, 1);\n    }\n\n\n    void bindBufferMemory(\n      const DxvkSparseBufferBindKey& key,\n      const DxvkResourceMemoryInfo& memory) {\n      getSparseBindSubmission().bindBufferMemory(key, memory);\n    }\n\n\n    void bindImageMemory(\n      const DxvkSparseImageBindKey& key,\n      const DxvkResourceMemoryInfo& memory) {\n      getSparseBindSubmission().bindImageMemory(key, memory);\n    }\n\n\n    void bindImageOpaqueMemory(\n      const DxvkSparseImageOpaqueBindKey& key,\n      const DxvkResourceMemoryInfo& memory) {\n      getSparseBindSubmission().bindImageOpaqueMemory(key, memory);\n    }\n\n\n    void setDescriptorPool(\n            Rc<DxvkDescriptorPool>        pool) {\n      m_descriptorPool = pool;\n    }\n\n\n    void setDescriptorHeap(\n            Rc<DxvkResourceDescriptorHeap> heap);\n\n\n    void setTrackingId(uint64_t id) {\n      m_trackingId = id;\n    }\n\n    void setDescriptorSyncHandle(sync::SyncPoint syncHandle) {\n      m_descriptorSync = std::move(syncHandle);\n    }\n\n    void ensureDescriptorHeapBinding() {\n      if (unlikely(m_descriptorHeapInvalidated)) {\n        this->rebindSamplerHeap();\n        this->rebindResourceHeap();\n\n        m_descriptorHeapInvalidated = false;\n      }\n    }\n\n    void invalidateDescriptorHeapBinding() {\n      // Re-bind heaps on next draw/dispatch\n      m_descriptorHeapInvalidated = true;\n    }\n\n  private:\n    \n    DxvkDevice*               m_device;\n    Rc<vk::DeviceFn>          m_vkd;\n    Rc<vk::InstanceFn>        m_vki;\n    \n    Rc<DxvkCommandPool>       m_graphicsPool;\n    Rc<DxvkCommandPool>       m_transferPool;\n\n    DxvkCommandSubmissionInfo m_cmd;\n    VkCommandBuffer           m_execBuffer = VK_NULL_HANDLE;\n\n    PresenterSync             m_wsiSemaphores = { };\n    uint64_t                  m_trackingId = 0u;\n\n    DxvkObjectTracker         m_objectTracker;\n    DxvkSignalTracker         m_signalTracker;\n    DxvkStatCounters          m_statCounters;\n\n    DxvkCommandSubmission     m_commandSubmission;\n\n    small_vector<DxvkFenceValuePair, 4> m_waitSemaphores;\n    small_vector<DxvkFenceValuePair, 4> m_signalSemaphores;\n\n    small_vector<DxvkCommandSubmissionInfo, 4> m_cmdSubmissions;\n    small_vector<DxvkSparseBindSubmission, 4>  m_cmdSparseBinds;\n    \n    std::vector<Rc<DxvkDescriptorPool>> m_descriptorPools;\n\n    Rc<DxvkDescriptorPool>    m_descriptorPool;\n    sync::SyncPoint           m_descriptorSync;\n\n    Rc<DxvkResourceDescriptorHeap>  m_descriptorHeap;\n    Rc<DxvkResourceDescriptorRange> m_descriptorRange;\n    VkDeviceSize                    m_descriptorOffset = 0u;\n\n    std::vector<DxvkGraphicsPipeline*> m_pipelines;\n\n    bool m_descriptorHeapInvalidated = false;\n\n    force_inline VkCommandBuffer getCmdBuffer() const {\n      // Allocation logic will always provide an execution buffer\n      return m_cmd.cmdBuffers[uint32_t(DxvkCmdBuffer::ExecBuffer)];\n    }\n\n    force_inline VkCommandBuffer getCmdBuffer(DxvkCmdBuffer cmdBuffer) {\n      VkCommandBuffer buffer = m_cmd.cmdBuffers[uint32_t(cmdBuffer)];\n\n      if (likely(cmdBuffer == DxvkCmdBuffer::ExecBuffer || buffer))\n        return buffer;\n\n      // Allocate a new command buffer if necessary\n      buffer = allocateCommandBuffer(cmdBuffer);\n      m_cmd.cmdBuffers[uint32_t(cmdBuffer)] = buffer;\n      return buffer;\n    }\n\n    DxvkSparseBindSubmission& getSparseBindSubmission() {\n      if (likely(m_cmd.sparseBind))\n        return m_cmdSparseBinds[m_cmd.sparseCmd];\n\n      m_cmd.sparseBind = VK_TRUE;\n      m_cmd.sparseCmd = uint32_t(m_cmdSparseBinds.size());\n\n      return m_cmdSparseBinds.emplace_back();\n    }\n\n    void bindResourcesLegacy(\n            DxvkCmdBuffer                 cmdBuffer,\n      const DxvkPipelineLayout*           layout,\n            uint32_t                      descriptorCount,\n      const DxvkDescriptorWrite*          descriptorInfos,\n            size_t                        pushDataSize,\n      const void*                         pushData);\n\n    void bindResourcesDescriptorHeap(\n            DxvkCmdBuffer                 cmdBuffer,\n      const DxvkPipelineLayout*           layout,\n            uint32_t                      descriptorCount,\n      const DxvkDescriptorWrite*          descriptorInfos,\n            size_t                        pushDataSize,\n      const void*                         pushData);\n\n    void bindResourcesDescriptorBuffer(\n            DxvkCmdBuffer                 cmdBuffer,\n      const DxvkPipelineLayout*           layout,\n            uint32_t                      descriptorCount,\n      const DxvkDescriptorWrite*          descriptorInfos,\n            size_t                        pushDataSize,\n      const void*                         pushData);\n\n    void rebindSamplerHeap();\n\n    void rebindResourceHeap();\n\n    void rebindDescriptorBuffers();\n\n    void bindSamplerHeap(VkCommandBuffer cmdBuffer);\n\n    void bindResourceHeap(VkCommandBuffer cmdBuffer);\n\n    void bindDescriptorBuffers(VkCommandBuffer cmdBuffer);\n\n    void endCommandBuffer(VkCommandBuffer cmdBuffer);\n\n    VkCommandBuffer allocateCommandBuffer(DxvkCmdBuffer type);\n\n    void countDescriptorStats(\n      const Rc<DxvkResourceDescriptorRange>& range,\n            VkDeviceSize                  baseOffset);\n\n    static VkBindHeapInfoEXT getHeapBindInfo(const DxvkDescriptorHeapBindingInfo& heapInfo) {\n      VkBindHeapInfoEXT bindInfo = { VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT };\n      bindInfo.heapRange.address = heapInfo.gpuAddress;\n      bindInfo.heapRange.size = heapInfo.bufferSize;\n      bindInfo.reservedRangeSize = heapInfo.reservedSize;\n      return bindInfo;\n    }\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_compute.cpp",
    "content": "#include <cstring>\n#include <iomanip>\n#include <sstream>\n\n#include \"../util/util_time.h\"\n\n#include \"dxvk_compute.h\"\n#include \"dxvk_device.h\"\n#include \"dxvk_graphics.h\"\n#include \"dxvk_pipemanager.h\"\n\nnamespace dxvk {\n  \n  DxvkComputePipeline::DxvkComputePipeline(\n          DxvkDevice*                 device,\n          DxvkPipelineManager*        pipeMgr,\n          DxvkComputePipelineShaders  shaders,\n          DxvkShaderPipelineLibrary*  library)\n  : m_device        (device),\n    m_stats         (&pipeMgr->m_stats),\n    m_library       (library),\n    m_shaders       (std::move(shaders)),\n    m_layout        (device, pipeMgr, m_shaders.cs->getLayout()),\n    m_debugName     (createDebugName()) {\n\n  }\n  \n  \n  DxvkComputePipeline::~DxvkComputePipeline() {\n    m_library->releasePipelineHandle();\n\n    m_pipelines.forEach([this] (const DxvkComputePipelineInstance& instance) {\n      this->destroyPipeline(instance.handle);\n    });\n  }\n  \n  \n  VkPipeline DxvkComputePipeline::getPipelineHandle(\n    const DxvkComputePipelineStateInfo& state) {\n    if (!m_libraryHandle) {\n      // Retrieve actual pipeline handle on first use. This\n      // may wait for an ongoing compile job to finish, or\n      // compile the pipeline immediately on the calling thread.\n      m_libraryHandle = m_library->acquirePipelineHandle().handle;\n    }\n\n    if (*m_libraryHandle) {\n      // Compute pipelines without spec constants are always\n      // pre-compiled, so we'll almost always hit this path\n      return *m_libraryHandle;\n    } else {\n      // Slow path for compute shaders that do use spec constants\n      DxvkComputePipelineInstance* instance = this->findInstance(state);\n\n      if (unlikely(!instance)) {\n        std::lock_guard<dxvk::mutex> lock(m_mutex);\n        instance = this->findInstance(state);\n\n        if (!instance)\n          instance = this->createInstance(state);\n      }\n\n      return instance->handle;\n    }\n  }\n\n\n  void DxvkComputePipeline::compilePipeline(\n    const DxvkComputePipelineStateInfo& state) {\n    if (!m_library) {\n      std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n      if (!this->findInstance(state))\n        this->createInstance(state);\n    }\n  }\n  \n  \n  DxvkComputePipelineInstance* DxvkComputePipeline::createInstance(\n    const DxvkComputePipelineStateInfo& state) {\n    VkPipeline newPipelineHandle = this->createPipeline(state);\n\n    m_stats->numComputePipelines += 1;\n    return m_pipelines.add(state, newPipelineHandle);\n  }\n\n  \n  DxvkComputePipelineInstance* DxvkComputePipeline::findInstance(\n    const DxvkComputePipelineStateInfo& state) {\n    return m_pipelines.find(state);\n  }\n  \n  \n  VkPipeline DxvkComputePipeline::createPipeline(\n    const DxvkComputePipelineStateInfo& state) const {\n    auto vk = m_device->vkd();\n    auto layout = m_layout.getLayout(DxvkPipelineLayoutType::Merged);\n\n    DxvkPipelineSpecConstantState scState(m_shaders.cs->metadata().specConstantMask, state.sc);\n    \n    DxvkShaderStageInfo stageInfo(m_device, layout);\n    stageInfo.addStage(VK_SHADER_STAGE_COMPUTE_BIT, \n      m_shaders.cs->getCode(m_layout.getBindingMap(DxvkPipelineLayoutType::Merged), nullptr),\n      &scState.scInfo);\n\n    VkPipelineCreateFlags2CreateInfo flags = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO };\n\n    if (m_device->canUseDescriptorHeap())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (m_device->canUseDescriptorBuffer())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkComputePipelineCreateInfo info = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };\n    info.stage                = *stageInfo.getStageInfos();\n    info.layout               = layout->getPipelineLayout();\n    info.basePipelineIndex    = -1;\n\n    if (flags.flags)\n      flags.pNext = std::exchange(info.pNext, &flags);\n\n    VkPipeline pipeline = VK_NULL_HANDLE;\n    VkResult vr = vk->vkCreateComputePipelines(vk->device(),\n          VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);\n\n    if (vr != VK_SUCCESS) {\n      Logger::err(str::format(\"DxvkComputePipeline: Failed to compile pipeline: \", vr));\n      this->logPipelineState(LogLevel::Error, state);\n      return VK_NULL_HANDLE;\n    }\n\n    return pipeline;\n  }\n\n\n  void DxvkComputePipeline::destroyPipeline(VkPipeline pipeline) {\n    auto vk = m_device->vkd();\n\n    vk->vkDestroyPipeline(vk->device(), pipeline, nullptr);\n  }\n  \n  \n  void DxvkComputePipeline::logPipelineState(\n          LogLevel                      level,\n    const DxvkComputePipelineStateInfo& state) const {\n    std::stringstream sstr;\n    sstr << \"  cs  : \" << m_shaders.cs->debugName() << std::endl;\n\n    bool hasSpecConstants = false;\n\n    for (uint32_t i = 0; i < MaxNumSpecConstants; i++) {\n      if (state.sc.specConstants[i]) {\n        if (!hasSpecConstants) {\n          sstr << \"Specialization constants:\" << std::endl;\n          hasSpecConstants = true;\n        }\n\n        sstr << \"  \" << i << \": 0x\" << std::hex << std::setw(8) << std::setfill('0') << state.sc.specConstants[i] << std::dec << std::endl;\n      }\n    }\n\n    Logger::log(level, sstr.str());\n  }\n\n\n  std::string DxvkComputePipeline::createDebugName() const {\n    std::string shaderName = m_shaders.cs->debugName();\n    size_t len = std::min(shaderName.size(), size_t(10));\n\n    return str::format(\"[\", shaderName.substr(0, len), \"]\");\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_compute.h",
    "content": "#pragma once\n\n#include <optional>\n#include <vector>\n\n#include \"dxvk_bind_mask.h\"\n#include \"dxvk_graphics_state.h\"\n#include \"dxvk_pipelayout.h\"\n#include \"dxvk_shader.h\"\n#include \"dxvk_stats.h\"\n\nnamespace dxvk {\n  \n  class DxvkDevice;\n  class DxvkPipelineManager;\n  struct DxvkPipelineStats;\n\n\n  /**\n   * \\brief Shaders used in compute pipelines\n   */\n  struct DxvkComputePipelineShaders {\n    Rc<DxvkShader> cs;\n\n    bool eq(const DxvkComputePipelineShaders& other) const {\n      return cs == other.cs;\n    }\n\n    size_t hash() const {\n      return DxvkShader::getCookie(cs);\n    }\n  };\n\n\n  /**\n   * \\brief Compute pipeline instance\n   */\n  struct DxvkComputePipelineInstance {\n    DxvkComputePipelineInstance() { }\n    DxvkComputePipelineInstance(VkPipeline handle_)\n    : handle(handle_) { }\n\n    VkPipeline handle = VK_NULL_HANDLE;\n  };\n  \n  \n  /**\n   * \\brief Compute pipeline\n   * \n   * Stores a compute pipeline object and the corresponding\n   * pipeline layout. Unlike graphics pipelines, compute\n   * pipelines do not need to be recompiled against any sort\n   * of pipeline state.\n   */\n  class DxvkComputePipeline {\n    \n  public:\n    \n    DxvkComputePipeline(\n            DxvkDevice*                 device,\n            DxvkPipelineManager*        pipeMgr,\n            DxvkComputePipelineShaders  shaders,\n            DxvkShaderPipelineLibrary*  library);\n\n    ~DxvkComputePipeline();\n    \n    /**\n     * \\brief Shaders used by the pipeline\n     * \\returns Shaders used by the pipeline\n     */\n    const DxvkComputePipelineShaders& shaders() const {\n      return m_shaders;\n    }\n    \n    /**\n     * \\brief Queries pipeline layout\n     * \\returns Pipeline layout\n     */\n    const DxvkPipelineBindings* getLayout() const {\n      return &m_layout;\n    }\n\n    /**\n     * \\brief Queries spec constant mask\n     *\n     * This only includes user spec constants.\n     * \\returns Bit mask of used spec constants\n     */\n    uint32_t getSpecConstantMask() const {\n      constexpr uint32_t globalMask = (1u << MaxNumSpecConstants) - 1;\n      return m_shaders.cs->metadata().specConstantMask & globalMask;\n    }\n    \n    /**\n     * \\brief Retrieves pipeline handle\n     * \n     * \\param [in] state Pipeline state\n     * \\returns Pipeline handle\n     */\n    VkPipeline getPipelineHandle(\n      const DxvkComputePipelineStateInfo& state);\n    \n    /**\n     * \\brief Compiles a pipeline\n     * \n     * Asynchronously compiles the given pipeline\n     * and stores the result for future use.\n     * \\param [in] state Pipeline state\n     */\n    void compilePipeline(\n      const DxvkComputePipelineStateInfo& state);\n\n    /**\n     * \\brief Debug name\n     *\n     * Consists of the compute shader's debug name.\n     * \\returns Debug name\n     */\n    const char* debugName() const {\n      return m_debugName.c_str();\n    }\n\n  private:\n    \n    DxvkDevice*                 m_device = nullptr;\n    DxvkPipelineStats*          m_stats = nullptr;\n\n    DxvkShaderPipelineLibrary*  m_library = nullptr;\n    std::optional<VkPipeline>   m_libraryHandle;\n\n    DxvkComputePipelineShaders  m_shaders;\n    DxvkPipelineBindings        m_layout;\n    \n    std::string                 m_debugName;\n\n    alignas(CACHE_LINE_SIZE)\n    dxvk::mutex                             m_mutex;\n    DxvkPipelineVariantTable<\n      DxvkComputePipelineStateInfo,\n      DxvkComputePipelineInstance>          m_pipelines;\n    \n    DxvkComputePipelineInstance* createInstance(\n      const DxvkComputePipelineStateInfo& state);\n    \n    DxvkComputePipelineInstance* findInstance(\n      const DxvkComputePipelineStateInfo& state);\n    \n    VkPipeline createPipeline(\n      const DxvkComputePipelineStateInfo& state) const;\n    \n    void destroyPipeline(\n            VkPipeline                    pipeline);\n\n    void logPipelineState(\n            LogLevel                      level,\n      const DxvkComputePipelineStateInfo& state) const;\n\n    std::string createDebugName() const;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_constant_state.cpp",
    "content": "#include \"dxvk_constant_state.h\"\n\nnamespace dxvk {\n\n  bool DxvkStencilOp::normalize(VkCompareOp depthOp) {\n    if (writeMask()) {\n      // If the depth test always passes, this is irrelevant\n      if (depthOp == VK_COMPARE_OP_ALWAYS)\n        setDepthFailOp(VK_STENCIL_OP_KEEP);\n\n      // Also mask out unused ops if the stencil test\n      // always pases or always fails\n      if (compareOp() == VK_COMPARE_OP_ALWAYS)\n        setFailOp(VK_STENCIL_OP_KEEP);\n      else if (compareOp() == VK_COMPARE_OP_NEVER)\n        setPassOp(VK_STENCIL_OP_KEEP);\n\n      // If all stencil ops are no-ops, clear write mask\n      if (passOp() == VK_STENCIL_OP_KEEP\n       && failOp() == VK_STENCIL_OP_KEEP\n       && depthFailOp() == VK_STENCIL_OP_KEEP)\n        setWriteMask(0u);\n    } else {\n      // Normalize stencil ops if write mask is 0\n      setPassOp(VK_STENCIL_OP_KEEP);\n      setFailOp(VK_STENCIL_OP_KEEP);\n      setDepthFailOp(VK_STENCIL_OP_KEEP);\n    }\n\n    // Check if the stencil test for this face is a no-op\n    return writeMask() || compareOp() != VK_COMPARE_OP_ALWAYS;\n  }\n\n\n  void DxvkDepthStencilState::normalize() {\n    if (depthTest()) {\n      // If depth func is equal or if the depth test always fails, depth\n      // writes will not have any observable effect so we can skip them.\n      if (depthCompareOp() == VK_COMPARE_OP_EQUAL\n       || depthCompareOp() == VK_COMPARE_OP_NEVER)\n        setDepthWrite(false);\n\n      // If the depth test always passes and no writes are performed, the\n      // depth test as a whole is a no-op and can safely be disabled.\n      if (depthCompareOp() == VK_COMPARE_OP_ALWAYS && !depthWrite())\n        setDepthTest(false);\n    } else {\n      setDepthWrite(false);\n      setDepthCompareOp(VK_COMPARE_OP_ALWAYS);\n    }\n\n    if (stencilTest()) {\n      // Normalize stencil op and disable stencil testing if both are no-ops.\n      bool frontIsNoOp = !m_stencilOpFront.normalize(depthCompareOp());\n      bool backIsNoOp = !m_stencilOpBack.normalize(depthCompareOp());\n\n      if (frontIsNoOp && backIsNoOp)\n        setStencilTest(false);\n    }\n\n    // Normalize stencil ops if stencil test is disabled\n    if (!stencilTest()) {\n      setStencilOpFront(DxvkStencilOp());\n      setStencilOpBack(DxvkStencilOp());\n    }\n  }\n\n\n  void DxvkBlendMode::normalize() {\n    constexpr VkColorComponentFlags colorMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT;\n    constexpr VkColorComponentFlags alphaMask = VK_COLOR_COMPONENT_A_BIT;\n\n    VkColorComponentFlags newWriteMask = writeMask();\n\n    if (!newWriteMask)\n      setBlendEnable(false);\n\n    if (blendEnable()) {\n      // If alpha or color are effectively not modified given the blend\n      // function, set the corresponding part of the write mask to 0.\n      if (colorBlendOp() == VK_BLEND_OP_ADD\n       && colorSrcFactor() == VK_BLEND_FACTOR_ZERO\n       && colorDstFactor() == VK_BLEND_FACTOR_ONE)\n        newWriteMask &= ~colorMask;\n\n      if (alphaBlendOp() == VK_BLEND_OP_ADD\n       && alphaSrcFactor() == VK_BLEND_FACTOR_ZERO\n       && alphaDstFactor() == VK_BLEND_FACTOR_ONE)\n        newWriteMask &= ~alphaMask;\n\n      // Check whether blending is equivalent to passing through\n      // the source data as if blending was disabled.\n      bool needsBlending = false;\n\n      if (newWriteMask & colorMask) {\n        needsBlending |= colorSrcFactor() != VK_BLEND_FACTOR_ONE\n                      || colorDstFactor() != VK_BLEND_FACTOR_ZERO\n                      || colorBlendOp()   != VK_BLEND_OP_ADD;\n      }\n\n      if (newWriteMask & alphaMask) {\n        needsBlending |= alphaSrcFactor() != VK_BLEND_FACTOR_ONE\n                      || alphaDstFactor() != VK_BLEND_FACTOR_ZERO\n                      || alphaBlendOp()   != VK_BLEND_OP_ADD;\n      }\n\n      if (!needsBlending)\n        setBlendEnable(false);\n    }\n\n    if (!blendEnable() || !(newWriteMask & colorMask))\n      setColorOp(VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD);\n\n    if (!blendEnable() || !(newWriteMask & alphaMask))\n      setAlphaOp(VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD);\n\n    setWriteMask(newWriteMask);\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_constant_state.h",
    "content": "#pragma once\n\n#include \"dxvk_buffer.h\"\n#include \"dxvk_framebuffer.h\"\n#include \"dxvk_limits.h\"\n#include \"dxvk_shader.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Blend constants\n   * \n   * Stores a blend factor\n   * as an RGBA color value.\n   */\n  struct DxvkBlendConstants {\n    float r, g, b, a;\n\n    bool operator == (const DxvkBlendConstants& other) const {\n      return this->r == other.r && this->g == other.g\n          && this->b == other.b && this->a == other.a;\n    }\n\n    bool operator != (const DxvkBlendConstants& other) const {\n      return this->r != other.r || this->g != other.g\n          || this->b != other.b || this->a != other.a;\n    }\n  };\n\n\n  /**\n   * \\brief Depth bias representation\n   * \n   * Stores depth bias representation info.\n   */\n  struct DxvkDepthBiasRepresentation {\n    VkDepthBiasRepresentationEXT depthBiasRepresentation;\n    VkBool32                     depthBiasExact;\n\n    bool operator == (const DxvkDepthBiasRepresentation& other) const {\n      return depthBiasRepresentation == other.depthBiasRepresentation\n          && depthBiasExact          == other.depthBiasExact;\n    }\n\n    bool operator != (const DxvkDepthBiasRepresentation& other) const {\n      return depthBiasRepresentation != other.depthBiasRepresentation\n          || depthBiasExact          != other.depthBiasExact;\n    }\n  };\n\n\n  /**\n   * \\brief Depth bias\n   * \n   * Stores depth bias values.\n   */\n  struct DxvkDepthBias {\n    float                        depthBiasConstant;\n    float                        depthBiasSlope;\n    float                        depthBiasClamp;\n\n    bool operator == (const DxvkDepthBias& other) const {\n      return depthBiasConstant       == other.depthBiasConstant\n          && depthBiasSlope          == other.depthBiasSlope\n          && depthBiasClamp          == other.depthBiasClamp;\n    }\n\n    bool operator != (const DxvkDepthBias& other) const {\n      return depthBiasConstant       != other.depthBiasConstant\n          || depthBiasSlope          != other.depthBiasSlope\n          || depthBiasClamp          != other.depthBiasClamp;\n    }\n  };\n\n\n  /**\n   * \\brief Depth bounds\n   * \n   * Stores depth bounds values.\n   */\n  struct DxvkDepthBounds {\n    float minDepthBounds;\n    float maxDepthBounds;\n\n    bool operator == (const DxvkDepthBounds& other) const {\n      return minDepthBounds == other.minDepthBounds\n          && maxDepthBounds == other.maxDepthBounds;\n    }\n\n    bool operator != (const DxvkDepthBounds& other) const {\n      return minDepthBounds != other.minDepthBounds\n          || maxDepthBounds != other.maxDepthBounds;\n    }\n  };\n  \n  \n  /**\n   * \\brief Input assembly state\n   * \n   * Stores the primitive topology and\n   * whether or not primitive restart\n   * is enabled.\n   */\n  struct DxvkInputAssemblyState {\n\n  public:\n\n    DxvkInputAssemblyState() = default;\n\n    DxvkInputAssemblyState(VkPrimitiveTopology topology, bool restart)\n    : m_primitiveTopology (uint16_t(topology)),\n      m_primitiveRestart  (uint16_t(restart)),\n      m_patchVertexCount  (0u),\n      m_reserved          (0u) { }\n\n    VkPrimitiveTopology primitiveTopology() const {\n      return VkPrimitiveTopology(m_primitiveTopology) <= VK_PRIMITIVE_TOPOLOGY_PATCH_LIST\n        ? VkPrimitiveTopology(m_primitiveTopology)\n        : VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;\n    }\n\n    bool primitiveRestart() const {\n      return m_primitiveRestart;\n    }\n\n    uint32_t patchVertexCount() const {\n      return m_patchVertexCount;\n    }\n\n    void setPrimitiveTopology(VkPrimitiveTopology topology) {\n      m_primitiveTopology = uint16_t(topology);\n    }\n\n    void setPrimitiveRestart(bool enable) {\n      m_primitiveRestart = enable;\n    }\n\n    void setPatchVertexCount(uint32_t count) {\n      m_patchVertexCount = count;\n    }\n\n  private:\n\n    uint16_t m_primitiveTopology  : 4;\n    uint16_t m_primitiveRestart   : 1;\n    uint16_t m_patchVertexCount   : 6;\n    uint16_t m_reserved           : 5;\n\n  };\n\n\n  /**\n   * \\brief Rasterizer state\n   * \n   * Stores the operating mode of the\n   * rasterizer, including the depth bias.\n   */\n  struct DxvkRasterizerState {\n\n  public:\n\n    VkPolygonMode polygonMode() const {\n      return VkPolygonMode(m_polygonMode);\n    }\n\n    VkCullModeFlags cullMode() const {\n      return VkCullModeFlags(m_cullMode);\n    }\n\n    VkFrontFace frontFace() const {\n      return VkFrontFace(m_frontFace);\n    }\n\n    bool depthClip() const {\n      return m_depthClipEnable;\n    }\n\n    VkConservativeRasterizationModeEXT conservativeMode() const {\n      return VkConservativeRasterizationModeEXT(m_conservativeMode);\n    }\n\n    VkSampleCountFlags sampleCount() const {\n      return VkSampleCountFlags(m_sampleCount);\n    }\n\n    bool flatShading() const {\n      return m_flatShading;\n    }\n\n    VkLineRasterizationModeEXT lineMode() const {\n      return VkLineRasterizationModeEXT(m_lineMode);\n    }\n\n    void setPolygonMode(VkPolygonMode mode) {\n      m_polygonMode = uint16_t(mode);\n    }\n\n    void setCullMode(VkCullModeFlags mode) {\n      m_cullMode = uint16_t(mode);\n    }\n\n    void setFrontFace(VkFrontFace face) {\n      m_frontFace = uint16_t(face);\n    }\n\n    void setDepthClip(bool enable) {\n      m_depthClipEnable = enable;\n    }\n\n    void setConservativeMode(VkConservativeRasterizationModeEXT mode) {\n      m_conservativeMode = uint16_t(mode);\n    }\n\n    void setSampleCount(VkSampleCountFlags count) {\n      m_sampleCount = uint16_t(count);\n    }\n\n    void setFlatShading(bool enable) {\n      m_flatShading = enable;\n    }\n\n    void setLineMode(VkLineRasterizationModeEXT mode) {\n      m_lineMode = uint16_t(mode);\n    }\n\n  private:\n\n    uint16_t m_polygonMode       : 2;\n    uint16_t m_cullMode          : 2;\n    uint16_t m_frontFace         : 1;\n    uint16_t m_depthClipEnable   : 1;\n    uint16_t m_conservativeMode  : 2;\n    uint16_t m_sampleCount       : 5;\n    uint16_t m_flatShading       : 1;\n    uint16_t m_lineMode          : 2;\n\n  };\n  \n  \n  /**\n   * \\brief Multisample state\n   * \n   * Defines how to handle certain\n   * aspects of multisampling.\n   */\n  class DxvkMultisampleState {\n\n  public:\n\n    uint16_t sampleMask() const {\n      return m_sampleMask;\n    }\n\n    bool alphaToCoverage() const {\n      return m_enableAlphaToCoverage;\n    }\n\n    void setSampleMask(uint16_t mask) {\n      m_sampleMask = mask;\n    }\n\n    void setAlphaToCoverage(bool alphaToCoverage) {\n      m_enableAlphaToCoverage = alphaToCoverage;\n    }\n\n  private:\n\n    uint16_t m_sampleMask;\n    uint16_t m_enableAlphaToCoverage : 1;\n    uint16_t m_reserved              : 15;\n\n  };\n  \n\n  /**\n   * \\brief Stencil operation\n   */\n  class DxvkStencilOp {\n\n  public:\n\n    VkStencilOp failOp() const {\n      return VkStencilOp(m_failOp);\n    }\n\n    VkStencilOp passOp() const {\n      return VkStencilOp(m_passOp);\n    }\n\n    VkStencilOp depthFailOp() const {\n      return VkStencilOp(m_depthFailOp);\n    }\n\n    VkCompareOp compareOp() const {\n      return VkCompareOp(m_compareOp);\n    }\n\n    uint8_t compareMask() const {\n      return m_compareMask;\n    }\n\n    uint8_t writeMask() const {\n      return m_writeMask;\n    }\n\n    void setFailOp(VkStencilOp op) {\n      m_failOp = uint16_t(op);\n    }\n\n    void setPassOp(VkStencilOp op) {\n      m_passOp = uint16_t(op);\n    }\n\n    void setDepthFailOp(VkStencilOp op) {\n      m_depthFailOp = uint16_t(op);\n    }\n\n    void setCompareOp(VkCompareOp op) {\n      m_compareOp = uint16_t(op);\n    }\n\n    void setCompareMask(uint8_t mask) {\n      m_compareMask = mask;\n    }\n\n    void setWriteMask(uint8_t mask) {\n      m_writeMask = mask;\n    }\n\n    bool eq(const DxvkStencilOp& other) const {\n      return !std::memcmp(this, &other, sizeof(*this));\n    }\n\n    bool normalize(VkCompareOp depthOp);\n\n  private:\n\n    uint16_t m_failOp            : 3;\n    uint16_t m_passOp            : 3;\n    uint16_t m_depthFailOp       : 3;\n    uint16_t m_compareOp         : 3;\n    uint16_t m_reserved          : 4;\n    uint8_t  m_compareMask;\n    uint8_t  m_writeMask;\n\n  };\n\n\n  /**\n   * \\brief Depth-stencil state\n   * \n   * Defines the depth test and stencil\n   * operations for the graphics pipeline.\n   */\n  class DxvkDepthStencilState {\n\n  public:\n\n    bool depthTest() const {\n      return m_enableDepthTest;\n    }\n\n    bool depthWrite() const {\n      return m_enableDepthWrite;\n    }\n\n    bool stencilTest() const {\n      return m_enableStencilTest;\n    }\n\n    VkCompareOp depthCompareOp() const {\n      return VkCompareOp(m_depthCompareOp);\n    }\n\n    DxvkStencilOp stencilOpFront() const {\n      return m_stencilOpFront;\n    }\n\n    DxvkStencilOp stencilOpBack() const {\n      return m_stencilOpBack;\n    }\n\n    void setDepthTest(bool depthTest) {\n      m_enableDepthTest = depthTest;\n    }\n\n    void setDepthWrite(bool depthWrite) {\n      m_enableDepthWrite = depthWrite;\n    }\n\n    void setStencilTest(bool stencilTest) {\n      m_enableStencilTest = stencilTest;\n    }\n\n    void setDepthCompareOp(VkCompareOp compareOp) {\n      m_depthCompareOp = uint16_t(compareOp);\n    }\n\n    void setStencilOpFront(DxvkStencilOp op) {\n      m_stencilOpFront = op;\n    }\n\n    void setStencilOpBack(DxvkStencilOp op) {\n      m_stencilOpBack = op;\n    }\n\n    void normalize();\n\n  private:\n\n    uint16_t m_enableDepthTest   : 1;\n    uint16_t m_enableDepthWrite  : 1;\n    uint16_t m_enableStencilTest : 1;\n    uint16_t m_depthCompareOp    : 3;\n    uint16_t m_reserved          : 10;\n    DxvkStencilOp m_stencilOpFront;\n    DxvkStencilOp m_stencilOpBack;\n\n  };\n  \n  \n  /**\n   * \\brief Logic op state\n   * Defines a logic op.\n   */\n  class DxvkLogicOpState {\n\n  public:\n\n    bool logicOpEnable() const {\n      return m_logicOpEnable;\n    }\n\n    VkLogicOp logicOp() const {\n      return VkLogicOp(m_logicOp);\n    }\n\n    void setLogicOp(bool enable, VkLogicOp op) {\n      m_logicOpEnable = enable;\n      m_logicOp = uint8_t(op);\n    }\n\n  private:\n\n    uint8_t m_logicOpEnable : 1;\n    uint8_t m_logicOp       : 4;\n    uint8_t m_reserved      : 3;\n\n  };\n  \n  \n  /**\n   * \\brief Blend mode for a single attachment\n   * \n   * Stores the blend state for a single color attachment.\n   * Blend modes can be set separately for each attachment.\n   */\n  class DxvkBlendMode {\n\n  public:\n\n    bool blendEnable() const {\n      return m_enableBlending;\n    }\n\n    VkBlendFactor colorSrcFactor() const {\n      return VkBlendFactor(m_colorSrcFactor);\n    }\n\n    VkBlendFactor colorDstFactor() const {\n      return VkBlendFactor(m_colorDstFactor);\n    }\n\n    VkBlendOp colorBlendOp() const {\n      return VkBlendOp(m_colorBlendOp);\n    }\n\n    VkBlendFactor alphaSrcFactor() const {\n      return VkBlendFactor(m_alphaSrcFactor);\n    }\n\n    VkBlendFactor alphaDstFactor() const {\n      return VkBlendFactor(m_alphaDstFactor);\n    }\n\n    VkBlendOp alphaBlendOp() const {\n      return VkBlendOp(m_alphaBlendOp);\n    }\n\n    VkColorComponentFlags writeMask() const {\n      return VkColorComponentFlags(m_writeMask);\n    }\n\n    void setBlendEnable(bool enable) {\n      m_enableBlending = enable;\n    }\n\n    void setColorOp(VkBlendFactor srcFactor, VkBlendFactor dstFactor, VkBlendOp op) {\n      m_colorSrcFactor = uint32_t(srcFactor);\n      m_colorDstFactor = uint32_t(dstFactor);\n      m_colorBlendOp = uint32_t(op);\n    }\n\n    void setAlphaOp(VkBlendFactor srcFactor, VkBlendFactor dstFactor, VkBlendOp op) {\n      m_alphaSrcFactor = uint32_t(srcFactor);\n      m_alphaDstFactor = uint32_t(dstFactor);\n      m_alphaBlendOp = uint32_t(op);\n    }\n\n    void setWriteMask(VkColorComponentFlags writeMask) {\n      m_writeMask = writeMask;\n    }\n\n    void normalize();\n\n  private:\n\n    uint32_t m_enableBlending : 1;\n    uint32_t m_colorSrcFactor : 5;\n    uint32_t m_colorDstFactor : 5;\n    uint32_t m_colorBlendOp   : 3;\n    uint32_t m_alphaSrcFactor : 5;\n    uint32_t m_alphaDstFactor : 5;\n    uint32_t m_alphaBlendOp   : 3;\n    uint32_t m_writeMask      : 4;\n    uint32_t m_reserved       : 1;\n\n  };\n  \n  \n  /**\n   * \\brief Vertex attribute description\n   */\n  struct DxvkVertexAttribute {\n    uint32_t location;\n    uint32_t binding;\n    VkFormat format;\n    uint32_t offset;\n  };\n\n\n  /**\n   * \\brief Packed vertex attribute\n   *\n   * Compact representation of a vertex attribute.\n   */\n  struct DxvkPackedVertexAttribute {\n    DxvkPackedVertexAttribute() = default;\n    DxvkPackedVertexAttribute(const DxvkVertexAttribute& a)\n    : location  (a.location),\n      binding   (a.binding),\n      format    (uint32_t(a.format)),\n      offset    (a.offset),\n      reserved  (0u) { }\n\n    uint32_t location   : 5;\n    uint32_t binding    : 5;\n    uint32_t format     : 7;\n    uint32_t offset     : 11;\n    uint32_t reserved   : 4;\n\n    DxvkVertexAttribute unpack() const {\n      DxvkVertexAttribute result = { };\n      result.location = location;\n      result.binding = binding;\n      result.format = VkFormat(format);\n      result.offset = offset;\n      return result;\n    }\n  };\n\n\n  /**\n   * \\brief Vertex binding description\n   */\n  struct DxvkVertexBinding {\n    uint32_t binding;\n    uint32_t extent;\n    VkVertexInputRate inputRate;\n    uint32_t divisor;\n  };\n\n\n  /**\n   * \\brief Packed vertex attribute\n   *\n   * Compact representation of a vertex binding.\n   */\n  struct DxvkPackedVertexBinding {\n    DxvkPackedVertexBinding() = default;\n    DxvkPackedVertexBinding(const DxvkVertexBinding& b)\n    : binding   (b.binding),\n      extent    (b.extent),\n      inputRate (uint32_t(b.inputRate)),\n      divisor   (b.divisor < (1u << 14) ? b.divisor : 0u) { }\n\n    uint32_t binding    : 5;\n    uint32_t extent     : 12;\n    uint32_t inputRate  : 1;\n    uint32_t divisor    : 14;\n\n    DxvkVertexBinding unpack() const {\n      DxvkVertexBinding result = { };\n      result.binding = binding;\n      result.extent = extent;\n      result.inputRate = VkVertexInputRate(inputRate);\n      result.divisor = divisor;\n      return result;\n    }\n  };\n\n  /**\n   * \\brief Packed attribute binding\n   *\n   * Relies on attriute and bining\n   * structures to have the same size.\n   */\n  class DxvkVertexInput {\n\n  public:\n\n    DxvkVertexInput() = default;\n\n    DxvkVertexInput(const DxvkVertexAttribute& attribute)\n    : m_attribute(attribute) { }\n\n    DxvkVertexInput(const DxvkVertexBinding& binding)\n    : m_binding(binding) { }\n\n    DxvkVertexAttribute attribute() const {\n      return m_attribute.unpack();\n    }\n\n    DxvkVertexBinding binding() const {\n      return m_binding.unpack();\n    }\n\n  private:\n\n    union {\n      DxvkPackedVertexAttribute m_attribute;\n      DxvkPackedVertexBinding   m_binding;\n    };\n\n  };\n\n  static_assert(sizeof(DxvkVertexInput) == sizeof(uint32_t));\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_context.cpp",
    "content": "#include <algorithm>\n#include <cmath>\n#include <cstring>\n#include <map>\n#include <vector>\n#include <utility>\n\n#include \"dxvk_device.h\"\n#include \"dxvk_context.h\"\n\nnamespace dxvk {\n  \n  DxvkContext::DxvkContext(const Rc<DxvkDevice>& device)\n  : m_device      (device),\n    m_common      (&device->m_objects),\n    m_sdmaAcquires(*device, DxvkCmdBuffer::SdmaBarriers),\n    m_sdmaBarriers(*device, DxvkCmdBuffer::SdmaBuffer),\n    m_initAcquires(*device, DxvkCmdBuffer::InitBarriers),\n    m_initBarriers(*device, DxvkCmdBuffer::InitBuffer),\n    m_execBarriers(*device, DxvkCmdBuffer::ExecBuffer),\n    m_queryManager(m_common->queryPool()),\n    m_descriptorWorker(device),\n    m_implicitResolves(device) {\n    // Create descriptor heap or legacy pool object,\n    // depending on feature support.\n    if (device->canUseDescriptorHeap()) {\n      m_descriptorHeap = new DxvkResourceDescriptorHeap(device.ptr());\n\n      m_features.set(DxvkContextFeature::DescriptorHeap);\n    } else if (device->canUseDescriptorBuffer()) {\n      m_descriptorHeap = new DxvkResourceDescriptorHeap(device.ptr());\n\n      m_features.set(DxvkContextFeature::DescriptorBuffer);\n    } else {\n      m_descriptorPool = new DxvkDescriptorPool(device.ptr());\n    }\n\n    // Init framebuffer info with default render pass in case\n    // the app does not explicitly bind any render targets\n    m_state.om.framebufferInfo = makeFramebufferInfo(m_state.om.renderTargets);\n\n    // Global barrier for graphics pipelines. This is only used to\n    // avoid write-after-read hazards after a render pass, so the\n    // access mask here can be zero.\n    m_renderPassBarrierDst.stages = m_device->getShaderPipelineStages()\n                                  | VK_PIPELINE_STAGE_TRANSFER_BIT\n                                  | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT\n                                  | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n                                  | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n\n    if (m_device->features().extTransformFeedback.transformFeedback)\n      m_renderPassBarrierDst.stages |= VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT;\n\n    // Store the lifetime tracking bit as a context feature so\n    // that we don't have to scan device features at draw time\n    if (m_device->mustTrackPipelineLifetime())\n      m_features.set(DxvkContextFeature::TrackGraphicsPipeline);\n\n    // Variable multisample rate is needed to efficiently support\n    // rendering without bound render targets, otherwise we may\n    // have to interrupt the current render pass whenever the\n    // requested rasterizer sample count changes\n    if (m_device->features().core.features.variableMultisampleRate)\n      m_features.set(DxvkContextFeature::VariableMultisampleRate);\n\n    // Check whether we can batch direct draws\n    if (m_device->features().extMultiDraw.multiDraw\n     && m_device->properties().extMultiDraw.maxMultiDrawCount >= DirectMultiDrawBatchSize)\n      m_features.set(DxvkContextFeature::DirectMultiDraw);\n\n    // Add a fast path to query debug utils support\n    if (m_device->debugFlags().test(DxvkDebugFlag::Capture))\n      m_features.set(DxvkContextFeature::DebugUtils);\n  }\n  \n  \n  DxvkContext::~DxvkContext() {\n    \n  }\n  \n  \n  void DxvkContext::beginRecording(const Rc<DxvkCommandList>& cmdList) {\n    m_cmd = cmdList;\n    m_cmd->init();\n\n    this->beginCurrentCommands();\n  }\n  \n  \n  Rc<DxvkCommandList> DxvkContext::endRecording(\n    const VkDebugUtilsLabelEXT*       reason) {\n    this->endCurrentCommands();\n    this->relocateQueuedResources();\n\n    m_implicitResolves.cleanup(m_trackingId);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      // Make sure to emit the submission reason always at the very end\n      if (reason && reason->pLabelName && reason->pLabelName[0])\n        m_cmd->cmdInsertDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer, *reason);\n    }\n\n    m_cmd->finalize();\n    return std::exchange(m_cmd, nullptr);\n  }\n\n\n  void DxvkContext::endFrame() {\n    m_renderPassIndex = 0u;\n  }\n\n\n  void DxvkContext::beginLatencyTracking(\n    const Rc<DxvkLatencyTracker>&     tracker,\n          uint64_t                    frameId) {\n    if (tracker && (!m_latencyTracker || m_latencyTracker == tracker)) {\n      tracker->notifyCsRenderBegin(frameId);\n\n      m_latencyTracker = tracker;\n      m_latencyFrameId = frameId;\n\n      m_endLatencyTracking = false;\n    }\n  }\n\n\n  void DxvkContext::endLatencyTracking(\n    const Rc<DxvkLatencyTracker>&     tracker) {\n    if (tracker && tracker == m_latencyTracker)\n      m_endLatencyTracking = true;\n  }\n\n\n  void DxvkContext::flushCommandList(\n    const VkDebugUtilsLabelEXT*       reason,\n          DxvkSubmitStatus*           status) {\n    // Flush pending descriptor updates and assign the sync\n    // point to the submission\n    if (m_features.any(DxvkContextFeature::DescriptorHeap,\n                       DxvkContextFeature::DescriptorBuffer))\n      m_cmd->setDescriptorSyncHandle(m_descriptorWorker.getSyncHandle());\n\n    // Need to call this before submitting so that the last GPU\n    // submission does not happen before the render end signal.\n    if (m_endLatencyTracking && m_latencyTracker)\n      m_latencyTracker->notifyCsRenderEnd(m_latencyFrameId);\n\n    m_device->submitCommandList(this->endRecording(reason),\n      m_latencyTracker, m_latencyFrameId, status);\n\n    // Ensure that subsequent submissions do not see the tracker.\n    // It is important to hide certain internal submissions in\n    // case the application is CPU-bound.\n    if (m_endLatencyTracking) {\n      m_latencyTracker = nullptr;\n      m_latencyFrameId = 0u;\n\n      m_endLatencyTracking = false;\n    }\n\n    // If we have a zero buffer, see if we can get rid of it\n    freeZeroBuffer();\n\n    this->beginRecording(\n      m_device->createCommandList());\n  }\n\n\n  Rc<DxvkCommandList> DxvkContext::beginExternalRendering() {\n    // Flush and invalidate everything\n    endCurrentCommands();\n    beginCurrentCommands();\n\n    return m_cmd;\n  }\n\n  \n  void DxvkContext::beginQuery(const Rc<DxvkQuery>& query) {\n    m_queryManager.enableQuery(m_cmd, query);\n  }\n\n\n  void DxvkContext::endQuery(const Rc<DxvkQuery>& query) {\n    m_queryManager.disableQuery(m_cmd, query);\n  }\n  \n  \n  void DxvkContext::blitImageView(\n    const Rc<DxvkImageView>&    dstView,\n    const VkOffset3D*           dstOffsets,\n    const Rc<DxvkImageView>&    srcView,\n    const VkOffset3D*           srcOffsets,\n          VkFilter              filter) {\n    this->endCurrentPass(true);\n\n    auto mapping = util::resolveSrcComponentMapping(\n      dstView->info().unpackSwizzle(),\n      srcView->info().unpackSwizzle());\n\n    // Use render pass path if we need format reinterpretation or if any\n    // of the images are multisampled, which HW blit does not support.\n    bool useFb = dstView->image()->info().sampleCount != VK_SAMPLE_COUNT_1_BIT\n              || srcView->image()->info().sampleCount != VK_SAMPLE_COUNT_1_BIT\n              || dstView->image()->info().format != dstView->info().format\n              || srcView->image()->info().format != srcView->info().format\n              || !util::isIdentityMapping(mapping);\n\n    if (!useFb) {\n      // Otherwise, verify that the vkCmdBlit path is supported for the given formats\n      auto dstFeatures = m_device->adapter()->getFormatFeatures(dstView->info().format);\n      auto srcFeatures = m_device->adapter()->getFormatFeatures(srcView->info().format);\n\n      auto dstBits = dstView->image()->info().tiling == VK_IMAGE_TILING_OPTIMAL ? dstFeatures.optimal : dstFeatures.linear;\n      auto srcBits = srcView->image()->info().tiling == VK_IMAGE_TILING_OPTIMAL ? srcFeatures.optimal : srcFeatures.linear;\n\n      useFb |= !(dstBits & VK_FORMAT_FEATURE_2_BLIT_DST_BIT)\n            || !(srcBits & VK_FORMAT_FEATURE_2_BLIT_SRC_BIT);\n    }\n\n    if (useFb) {\n      this->blitImageFb(dstView, dstOffsets,\n        srcView, srcOffsets, filter);\n    } else {\n      this->blitImageHw(dstView, dstOffsets,\n        srcView, srcOffsets, filter);\n    }\n  }\n\n\n  void DxvkContext::clearBuffer(\n    const Rc<DxvkBuffer>&       buffer,\n          VkDeviceSize          offset,\n          VkDeviceSize          length,\n          uint32_t              value) {\n    DxvkResourceAccess access(*buffer, offset, length,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n\n    DxvkCmdBuffer cmdBuffer = prepareOutOfOrderTransfer(\n      DxvkCmdBuffer::InitBuffer, 1u, &access);\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer)\n      endCurrentPass(true);\n\n    syncResources(cmdBuffer, 1u, &access);\n\n    auto bufferSlice = buffer->getSliceInfo(offset, align(length, sizeof(uint32_t)));\n\n    if (length > sizeof(value)) {\n      m_cmd->cmdFillBuffer(cmdBuffer,\n        bufferSlice.buffer,\n        bufferSlice.offset,\n        bufferSlice.size,\n        value);\n    } else {\n      m_cmd->cmdUpdateBuffer(cmdBuffer,\n        bufferSlice.buffer,\n        bufferSlice.offset,\n        bufferSlice.size,\n        &value);\n    }\n  }\n  \n  \n  void DxvkContext::clearBufferView(\n    const Rc<DxvkBufferView>&   bufferView,\n          VkDeviceSize          offset,\n          VkDeviceSize          length,\n          VkClearColorValue     value) {\n    DxvkResourceAccess access(*bufferView, offset, length,\n      VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT);\n\n    DxvkCmdBuffer cmdBuffer = prepareOutOfOrderTransfer(\n      DxvkCmdBuffer::InitBuffer, 1u, &access);\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer) {\n      endCurrentPass(true);\n      invalidateState();\n    }\n\n    syncResources(cmdBuffer, 1u, &access);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      const char* dstName = bufferView->buffer()->info().debugName;\n\n      m_cmd->cmdBeginDebugUtilsLabel(cmdBuffer,\n        vk::makeLabel(0xf0dcdc, str::format(\"Clear view (\",\n          dstName ? dstName : \"unknown\", \")\").c_str()));\n    }\n\n    // Query pipeline objects to use for this clear operation\n    DxvkMetaClearPipeline pipeInfo = m_common->metaClear().getClearBufferPipeline(\n      lookupFormatInfo(bufferView->info().format)->flags);\n\n    // Create a descriptor set pointing to the view\n    DxvkDescriptorWrite descriptor = { };\n    descriptor.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;\n    descriptor.descriptor = bufferView->getDescriptor(false);\n\n    // Prepare shader arguments\n    DxvkMetaClearArgs pushArgs = { };\n    pushArgs.clearValue = value;\n    pushArgs.offset = VkOffset3D {  int32_t(offset), 0, 0 };\n    pushArgs.extent = VkExtent3D { uint32_t(length), 1, 1 };\n\n    VkExtent3D workgroups = util::computeBlockCount(\n      pushArgs.extent, pipeInfo.workgroupSize);\n\n    m_cmd->cmdBindPipeline(cmdBuffer,\n      VK_PIPELINE_BIND_POINT_COMPUTE, pipeInfo.pipeline);\n\n    m_cmd->bindResources(cmdBuffer, pipeInfo.layout,\n      1u, &descriptor, sizeof(pushArgs), &pushArgs);\n\n    m_cmd->cmdDispatch(cmdBuffer,\n      workgroups.width, workgroups.height, workgroups.depth);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(cmdBuffer);\n  }\n  \n  \n  void DxvkContext::clearRenderTarget(\n    const Rc<DxvkImageView>&    imageView,\n          VkImageAspectFlags    clearAspects,\n          VkClearValue          clearValue,\n          VkImageAspectFlags    discardAspects) {\n    // Make sure the color components are ordered correctly\n    if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) {\n      clearValue.color = util::swizzleClearColor(clearValue.color,\n        util::invertComponentMapping(imageView->info().unpackSwizzle()));\n    }\n\n    // Check whether the render target view is an attachment\n    // of the current framebuffer and is included entirely.\n    // If not, we need to create a temporary framebuffer.\n    int32_t attachmentIndex = -1;\n\n    if (m_state.om.framebufferInfo.isFullSize(imageView))\n      attachmentIndex = m_state.om.framebufferInfo.findAttachment(imageView);\n\n    if (attachmentIndex < 0) {\n      // Suspend works here because we'll end up with one of these scenarios:\n      // 1) The render pass gets ended for good, in which case we emit barriers\n      // 2) The clear gets folded into render pass ops, so the layout is correct\n      // 3) The clear gets executed separately, in which case updateRenderTargets\n      //    will indirectly emit barriers for the given render target.\n      // If there is overlap, we need to explicitly transition affected attachments.\n      this->endCurrentPass(true);\n    } else if (!m_state.om.framebufferInfo.isWritable(attachmentIndex, clearAspects)) {\n      // We cannot inline clears if the clear aspects are not writable. End the\n      // render pass on the next draw to ensure that the image gets cleared.\n      if (m_flags.test(DxvkContextFlag::GpRenderPassActive))\n        m_flags.set(DxvkContextFlag::GpRenderPassNeedsFlush);\n    }\n\n    // Unconditionally defer clears until either the next render pass, or the\n    // next draw if there is no reason to interrupt the render pass. This is\n    // useful to adjust store ops for tilers, and ensures that pending resolves\n    // are handled correctly.\n    if (discardAspects)\n      this->deferDiscard(imageView, discardAspects);\n\n    if (clearAspects)\n      this->deferClear(imageView, clearAspects, clearValue);\n\n    // Invalidate implicit resolves\n    if (imageView->isMultisampled()) {\n      auto subresources = imageView->imageSubresources();\n      subresources.aspectMask = clearAspects;\n\n      m_implicitResolves.invalidate(*imageView->image(), subresources);\n    }\n  }\n  \n  \n  void DxvkContext::clearImageView(\n    const Rc<DxvkImageView>&    imageView,\n          VkOffset3D            offset,\n          VkExtent3D            extent,\n          VkImageAspectFlags    aspect,\n          VkClearValue          value) {\n    const VkImageUsageFlags viewUsage = imageView->info().usage;\n\n    if (aspect & VK_IMAGE_ASPECT_COLOR_BIT) {\n      value.color = util::swizzleClearColor(value.color,\n        util::invertComponentMapping(imageView->info().unpackSwizzle()));\n    }\n    \n    if (viewUsage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))\n      this->clearImageViewFb(imageView, offset, extent, aspect, value);\n    else if (viewUsage & VK_IMAGE_USAGE_STORAGE_BIT)\n      this->clearImageViewCs(imageView, offset, extent, value);\n\n    if (imageView->isMultisampled()) {\n      auto subresources = imageView->imageSubresources();\n      subresources.aspectMask = aspect;\n\n      m_implicitResolves.invalidate(*imageView->image(), subresources);\n    }\n  }\n  \n  \n  void DxvkContext::copyBuffer(\n    const Rc<DxvkBuffer>&       dstBuffer,\n          VkDeviceSize          dstOffset,\n    const Rc<DxvkBuffer>&       srcBuffer,\n          VkDeviceSize          srcOffset,\n          VkDeviceSize          numBytes) {\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*dstBuffer, dstOffset, numBytes, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n    accessBatch.emplace_back(*srcBuffer, srcOffset, numBytes, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT);\n\n    DxvkCmdBuffer cmdBuffer = prepareOutOfOrderTransfer(DxvkCmdBuffer::InitBuffer, accessBatch.size(), accessBatch.data());\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer)\n      this->endCurrentPass(true);\n\n    syncResources(cmdBuffer, accessBatch.size(), accessBatch.data());\n\n    auto srcSlice = srcBuffer->getSliceInfo(srcOffset, numBytes);\n    auto dstSlice = dstBuffer->getSliceInfo(dstOffset, numBytes);\n\n    VkBufferCopy2 copyRegion = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 };\n    copyRegion.srcOffset = srcSlice.offset;\n    copyRegion.dstOffset = dstSlice.offset;\n    copyRegion.size      = dstSlice.size;\n\n    VkCopyBufferInfo2 copyInfo = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 };\n    copyInfo.srcBuffer = srcSlice.buffer;\n    copyInfo.dstBuffer = dstSlice.buffer;\n    copyInfo.regionCount = 1;\n    copyInfo.pRegions = &copyRegion;\n\n    m_cmd->cmdCopyBuffer(cmdBuffer, &copyInfo);\n  }\n  \n  \n  void DxvkContext::copyBufferRegion(\n    const Rc<DxvkBuffer>&       dstBuffer,\n          VkDeviceSize          dstOffset,\n          VkDeviceSize          srcOffset,\n          VkDeviceSize          numBytes) {\n    VkDeviceSize loOvl = std::max(dstOffset, srcOffset);\n    VkDeviceSize hiOvl = std::min(dstOffset, srcOffset) + numBytes;\n\n    if (hiOvl > loOvl) {\n      DxvkBufferCreateInfo bufInfo;\n      bufInfo.size    = numBytes;\n      bufInfo.usage   = VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                      | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n      bufInfo.stages  = VK_PIPELINE_STAGE_TRANSFER_BIT;\n      bufInfo.access  = VK_ACCESS_TRANSFER_WRITE_BIT\n                      | VK_ACCESS_TRANSFER_READ_BIT;\n      bufInfo.debugName = \"Temp buffer\";\n\n      auto tmpBuffer = m_device->createBuffer(\n        bufInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n      \n      VkDeviceSize tmpOffset = 0;\n      \n      this->copyBuffer(tmpBuffer, tmpOffset, dstBuffer, srcOffset, numBytes);\n      this->copyBuffer(dstBuffer, dstOffset, tmpBuffer, tmpOffset, numBytes);\n    } else {\n      this->copyBuffer(dstBuffer, dstOffset, dstBuffer, srcOffset, numBytes);\n    }\n  }\n\n\n  void DxvkContext::copyBufferToImage(\n    const Rc<DxvkImage>&        dstImage,\n          VkImageSubresourceLayers dstSubresource,\n          VkOffset3D            dstOffset,\n          VkExtent3D            dstExtent,\n    const Rc<DxvkBuffer>&       srcBuffer,\n          VkDeviceSize          srcOffset,\n          VkDeviceSize          rowAlignment,\n          VkDeviceSize          sliceAlignment,\n          VkFormat              srcFormat) {\n    bool useFb = !formatsAreBufferCopyCompatible(dstImage->info().format, srcFormat)\n              || dstImage->info().sampleCount != VK_SAMPLE_COUNT_1_BIT;\n\n    if (useFb) {\n      copyBufferToImageFb(dstImage, dstSubresource, dstOffset, dstExtent,\n        srcBuffer, srcOffset, rowAlignment, sliceAlignment,\n        srcFormat ? srcFormat : dstImage->info().format);\n    } else {\n      copyBufferToImageHw(dstImage, dstSubresource, dstOffset, dstExtent,\n        srcBuffer, srcOffset, rowAlignment, sliceAlignment);\n    }\n  }\n  \n  \n  void DxvkContext::copyImage(\n    const Rc<DxvkImage>&        dstImage,\n          VkImageSubresourceLayers dstSubresource,\n          VkOffset3D            dstOffset,\n    const Rc<DxvkImage>&        srcImage,\n          VkImageSubresourceLayers srcSubresource,\n          VkOffset3D            srcOffset,\n          VkExtent3D            extent) {\n    if (this->copyImageClear(dstImage, dstSubresource, dstOffset, extent, srcImage, srcSubresource))\n      return;\n\n    bool useFb = !formatsAreImageCopyCompatible(dstImage->info().format, srcImage->info().format);\n\n    if (m_device->perfHints().preferFbDepthStencilCopy) {\n      useFb |= (dstSubresource.aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n            && (dstImage->info().usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)\n            && (srcImage->info().usage & VK_IMAGE_USAGE_SAMPLED_BIT)\n            && (srcImage != dstImage);\n    }\n\n    if (!useFb) {\n      this->copyImageHw(\n        dstImage, dstSubresource, dstOffset,\n        srcImage, srcSubresource, srcOffset,\n        extent);\n    } else {\n      this->copyImageFb(\n        dstImage, dstSubresource, dstOffset,\n        srcImage, srcSubresource, srcOffset,\n        extent);\n    }\n\n    if (dstImage->info().sampleCount > VK_SAMPLE_COUNT_1_BIT)\n      m_implicitResolves.invalidate(*dstImage, vk::makeSubresourceRange(dstSubresource));\n  }\n  \n  \n  void DxvkContext::copyImageRegion(\n    const Rc<DxvkImage>&        dstImage,\n          VkImageSubresourceLayers dstSubresource,\n          VkOffset3D            dstOffset,\n          VkOffset3D            srcOffset,\n          VkExtent3D            extent) {\n    VkOffset3D loOvl = {\n      std::max(dstOffset.x, srcOffset.x),\n      std::max(dstOffset.y, srcOffset.y),\n      std::max(dstOffset.z, srcOffset.z) };\n    \n    VkOffset3D hiOvl = {\n      std::min(dstOffset.x, srcOffset.x) + int32_t(extent.width),\n      std::min(dstOffset.y, srcOffset.y) + int32_t(extent.height),\n      std::min(dstOffset.z, srcOffset.z) + int32_t(extent.depth) };\n    \n    bool overlap = hiOvl.x > loOvl.x\n                && hiOvl.y > loOvl.y\n                && hiOvl.z > loOvl.z;\n    \n    if (overlap) {\n      DxvkImageCreateInfo imgInfo;\n      imgInfo.type          = dstImage->info().type;\n      imgInfo.format        = dstImage->info().format;\n      imgInfo.flags         = 0;\n      imgInfo.sampleCount   = dstImage->info().sampleCount;\n      imgInfo.extent        = extent;\n      imgInfo.numLayers     = dstSubresource.layerCount;\n      imgInfo.mipLevels     = 1;\n      imgInfo.usage         = VK_IMAGE_USAGE_TRANSFER_DST_BIT\n                            | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;\n      imgInfo.stages        = VK_PIPELINE_STAGE_TRANSFER_BIT;\n      imgInfo.access        = VK_ACCESS_TRANSFER_WRITE_BIT\n                            | VK_ACCESS_TRANSFER_READ_BIT;\n      imgInfo.tiling        = dstImage->info().tiling;\n      imgInfo.layout        = VK_IMAGE_LAYOUT_GENERAL;\n      imgInfo.debugName     = \"Temp image\";\n\n      auto tmpImage = m_device->createImage(\n        imgInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n      \n      VkImageSubresourceLayers tmpSubresource;\n      tmpSubresource.aspectMask     = dstSubresource.aspectMask;\n      tmpSubresource.mipLevel       = 0;\n      tmpSubresource.baseArrayLayer = 0;\n      tmpSubresource.layerCount     = dstSubresource.layerCount;\n\n      VkOffset3D tmpOffset = { 0, 0, 0 };\n\n      this->copyImage(\n        tmpImage, tmpSubresource, tmpOffset,\n        dstImage, dstSubresource, srcOffset,\n        extent);\n      \n      this->copyImage(\n        dstImage, dstSubresource, dstOffset,\n        tmpImage, tmpSubresource, tmpOffset,\n        extent);\n    } else {\n      this->copyImage(\n        dstImage, dstSubresource, dstOffset,\n        dstImage, dstSubresource, srcOffset,\n        extent);\n    }\n  }\n\n\n  void DxvkContext::copyImageToBuffer(\n    const Rc<DxvkBuffer>&       dstBuffer,\n          VkDeviceSize          dstOffset,\n          VkDeviceSize          rowAlignment,\n          VkDeviceSize          sliceAlignment,\n          VkFormat              dstFormat,\n    const Rc<DxvkImage>&        srcImage,\n          VkImageSubresourceLayers srcSubresource,\n          VkOffset3D            srcOffset,\n          VkExtent3D            srcExtent) {\n    bool useFb = !formatsAreBufferCopyCompatible(srcImage->info().format, dstFormat);\n\n    if (useFb) {\n      copyImageToBufferCs(dstBuffer, dstOffset, rowAlignment, sliceAlignment,\n        dstFormat ? dstFormat : srcImage->info().format,\n        srcImage, srcSubresource, srcOffset, srcExtent);\n    } else {\n      copyImageToBufferHw(dstBuffer, dstOffset, rowAlignment, sliceAlignment,\n        srcImage, srcSubresource, srcOffset, srcExtent);\n    }\n  }\n\n\n  void DxvkContext::copyPackedBufferImage(\n    const Rc<DxvkBuffer>&       dstBuffer,\n          VkDeviceSize          dstBufferOffset,\n          VkOffset3D            dstOffset,\n          VkExtent3D            dstSize,\n    const Rc<DxvkBuffer>&       srcBuffer,\n          VkDeviceSize          srcBufferOffset,\n          VkOffset3D            srcOffset,\n          VkExtent3D            srcSize,\n          VkExtent3D            extent,\n          VkDeviceSize          elementSize) {\n    this->endCurrentPass(true);\n    this->invalidateState();\n\n    // We'll use texel buffer views with an appropriately\n    // sized integer format to perform the copy\n    VkFormat format = VK_FORMAT_UNDEFINED;\n\n    switch (elementSize) {\n      case  1: format = VK_FORMAT_R8_UINT; break;\n      case  2: format = VK_FORMAT_R16_UINT; break;\n      case  4: format = VK_FORMAT_R32_UINT; break;\n      case  8: format = VK_FORMAT_R32G32_UINT; break;\n      case 12: format = VK_FORMAT_R32G32B32_UINT; break;\n      case 16: format = VK_FORMAT_R32G32B32A32_UINT; break;\n    }\n\n    if (!format) {\n      Logger::err(str::format(\"DxvkContext: copyPackedBufferImage: Unsupported element size \", elementSize));\n      return;\n    }\n\n    DxvkBufferViewKey viewInfo;\n    viewInfo.format = format;\n    viewInfo.offset = dstBufferOffset;\n    viewInfo.size = elementSize * util::flattenImageExtent(dstSize);\n    viewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;\n    Rc<DxvkBufferView> dstView = dstBuffer->createView(viewInfo);\n\n    viewInfo.offset = srcBufferOffset;\n    viewInfo.size = elementSize * util::flattenImageExtent(srcSize);\n    viewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n    Rc<DxvkBufferView> srcView = srcBuffer->createView(viewInfo);\n\n    if (srcBuffer == dstBuffer\n     && srcView->info().offset < dstView->info().offset + dstView->info().size\n     && srcView->info().offset + srcView->info().size > dstView->info().offset) {\n      // Create temporary copy in case of overlapping regions\n      DxvkBufferCreateInfo bufferInfo;\n      bufferInfo.size   = srcView->info().size;\n      bufferInfo.usage  = VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                        | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n      bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                        | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;\n      bufferInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT\n                        | VK_ACCESS_SHADER_READ_BIT;\n      bufferInfo.debugName = \"Temp buffer\";\n\n      Rc<DxvkBuffer> tmpBuffer = m_device->createBuffer(bufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      auto tmpBufferSlice = tmpBuffer->getSliceInfo();\n      auto srcBufferSlice = srcView->getSliceInfo();\n\n      viewInfo.offset = 0;\n      auto tmpView = tmpBuffer->createView(viewInfo);\n\n      small_vector<DxvkResourceAccess, 2u> accessBatch;\n      accessBatch.emplace_back(*srcView, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT);\n      accessBatch.emplace_back(*tmpView, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n      syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n      VkBufferCopy2 copyRegion = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 };\n      copyRegion.srcOffset = srcBufferSlice.offset;\n      copyRegion.dstOffset = tmpBufferSlice.offset;\n      copyRegion.size = tmpBufferSlice.size;\n\n      VkCopyBufferInfo2 copyInfo = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 };\n      copyInfo.srcBuffer = srcBufferSlice.buffer;\n      copyInfo.dstBuffer = tmpBufferSlice.buffer;\n      copyInfo.regionCount = 1;\n      copyInfo.pRegions = &copyRegion;\n\n      m_cmd->cmdCopyBuffer(DxvkCmdBuffer::ExecBuffer, &copyInfo);\n\n      srcView = std::move(tmpView);\n    }\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*srcView, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT);\n    accessBatch.emplace_back(*dstView, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    auto pipeInfo = m_common->metaCopy().getCopyFormattedBufferPipeline();\n\n    std::array<DxvkDescriptorWrite, 2> descriptors = { };\n\n    auto& dstDescriptor = descriptors[0u];\n    dstDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;\n    dstDescriptor.descriptor = dstView->getDescriptor(false);\n\n    auto& srcDescriptor = descriptors[1u];\n    srcDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;\n    srcDescriptor.descriptor = srcView->getDescriptor(false);\n\n    DxvkFormattedBufferCopyArgs args = { };\n    args.dstOffset = dstOffset;\n    args.srcOffset = srcOffset;\n    args.extent = extent;\n    args.dstSize = { dstSize.width, dstSize.height };\n    args.srcSize = { srcSize.width, srcSize.height };\n\n    m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_COMPUTE, pipeInfo.pipeline);\n\n    m_cmd->bindResources(DxvkCmdBuffer::ExecBuffer,\n      pipeInfo.layout, descriptors.size(), descriptors.data(),\n      sizeof(args), &args);\n\n    m_cmd->cmdDispatch(DxvkCmdBuffer::ExecBuffer,\n      (extent.width  + 7) / 8,\n      (extent.height + 7) / 8,\n      extent.depth);\n  }\n\n\n  void DxvkContext::copySparsePagesToBuffer(\n    const Rc<DxvkBuffer>&       dstBuffer,\n          VkDeviceSize          dstOffset,\n    const Rc<DxvkPagedResource>& srcResource,\n          uint32_t              pageCount,\n    const uint32_t*             pages) {\n    this->copySparsePages<true>(\n      srcResource, pageCount, pages,\n      dstBuffer, dstOffset);\n  }\n\n\n  void DxvkContext::copySparsePagesFromBuffer(\n    const Rc<DxvkPagedResource>& dstResource,\n          uint32_t              pageCount,\n    const uint32_t*             pages,\n    const Rc<DxvkBuffer>&       srcBuffer,\n          VkDeviceSize          srcOffset) {\n    this->copySparsePages<false>(\n      dstResource, pageCount, pages,\n      srcBuffer, srcOffset);\n  }\n\n\n  void DxvkContext::discardImage(\n    const Rc<DxvkImage>&          image) {\n    VkImageUsageFlags imageUsage = image->info().usage;\n\n    if (!(imageUsage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)))\n      return;\n\n    // Perform store op optimization on bound render targets\n    VkImageSubresourceRange subresource = { };\n    subresource.aspectMask = image->formatInfo()->aspectMask;\n    subresource.layerCount = image->info().numLayers;\n    subresource.levelCount = image->info().mipLevels;\n\n    discardRenderTarget(*image, subresource);\n\n    // We don't really have a good way to queue up discards for\n    // subsequent render passes here without a view, so don't.\n  }\n\n\n  void DxvkContext::dispatch(\n          uint32_t x,\n          uint32_t y,\n          uint32_t z) {\n    if (this->commitComputeState<false>()) {\n      m_queryManager.beginQueries(m_cmd,\n        VK_QUERY_TYPE_PIPELINE_STATISTICS);\n      \n      m_cmd->cmdDispatch(DxvkCmdBuffer::ExecBuffer, x, y, z);\n      m_cmd->addStatCtr(DxvkStatCounter::CmdDispatchCalls, 1u);\n\n      m_queryManager.endQueries(m_cmd,\n        VK_QUERY_TYPE_PIPELINE_STATISTICS);\n    }\n  }\n  \n  \n  void DxvkContext::dispatchIndirect(\n          VkDeviceSize      offset) {\n    auto argInfo = m_state.id.argBuffer.getSliceInfo();\n\n    if (this->commitComputeState<true>()) {\n      m_queryManager.beginQueries(m_cmd,\n        VK_QUERY_TYPE_PIPELINE_STATISTICS);\n\n      m_cmd->cmdDispatchIndirect(DxvkCmdBuffer::ExecBuffer,\n        argInfo.buffer, argInfo.offset + offset);\n      m_cmd->addStatCtr(DxvkStatCounter::CmdDispatchCalls, 1u);\n\n      m_queryManager.endQueries(m_cmd,\n        VK_QUERY_TYPE_PIPELINE_STATISTICS);\n\n      accessDrawBuffer(offset, 1, 0, sizeof(VkDispatchIndirectCommand));\n\n      this->trackDrawBuffer();\n    }\n  }\n  \n  \n  void DxvkContext::draw(\n          uint32_t          count,\n    const VkDrawIndirectCommand* draws) {\n    drawGeneric<false>(count, draws);\n  }\n  \n  \n  void DxvkContext::drawIndirect(\n          VkDeviceSize      offset,\n          uint32_t          count,\n          uint32_t          stride,\n          bool              unroll) {\n    drawIndirectGeneric<false>(offset, count, stride, unroll);\n  }\n  \n  \n  void DxvkContext::drawIndirectCount(\n          VkDeviceSize      offset,\n          VkDeviceSize      countOffset,\n          uint32_t          maxCount,\n          uint32_t          stride) {\n    drawIndirectCountGeneric<false>(offset, countOffset, maxCount, stride);\n  }\n  \n  \n  void DxvkContext::drawIndexed(\n          uint32_t          count,\n    const VkDrawIndexedIndirectCommand* draws) {\n    drawGeneric<true>(count, draws);\n  }\n\n\n  void DxvkContext::drawIndexedIndirect(\n          VkDeviceSize      offset,\n          uint32_t          count,\n          uint32_t          stride,\n          bool              unroll) {\n    drawIndirectGeneric<true>(offset, count, stride, unroll);\n  }\n  \n  \n  void DxvkContext::drawIndexedIndirectCount(\n          VkDeviceSize      offset,\n          VkDeviceSize      countOffset,\n          uint32_t          maxCount,\n          uint32_t          stride) {\n    drawIndirectCountGeneric<true>(offset, countOffset, maxCount, stride);\n  }\n\n\n  void DxvkContext::drawIndirectXfb(\n          VkDeviceSize      counterOffset,\n          uint32_t          counterDivisor,\n          uint32_t          counterBias) {\n    if (this->commitGraphicsState<false, true>()) {\n      auto argInfo = m_state.id.cntBuffer.getSliceInfo();\n\n      m_cmd->cmdDrawIndirectVertexCount(1, 0,\n        argInfo.buffer, argInfo.offset + counterOffset,\n        counterBias, counterDivisor);\n      m_cmd->addStatCtr(DxvkStatCounter::CmdDrawCalls, 1u);\n\n      // The count will generally be written from streamout\n      if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)\n       || m_state.id.cntBuffer.buffer()->hasGfxStores())\n        accessDrawCountBuffer(counterOffset);\n    }\n  }\n\n\n  void DxvkContext::initBuffer(\n    const Rc<DxvkBuffer>&           buffer) {\n    auto dstSlice = buffer->getSliceInfo();\n\n    // If the buffer is suballocated, clear the entire allocated\n    // region, which is guaranteed to have a nicely aligned size\n    if (!buffer->storage()->flags().test(DxvkAllocationFlag::OwnsBuffer)) {\n      auto bufferInfo = buffer->storage()->getBufferInfo();\n\n      dstSlice.buffer = bufferInfo.buffer;\n      dstSlice.offset = bufferInfo.offset;\n      dstSlice.size = bufferInfo.size;\n    }\n\n    // Buffer size may be misaligned, in which case we have\n    // to use a plain buffer copy to fill the last few bytes.\n    constexpr VkDeviceSize MinCopyAndFillSize = 1u << 20;\n\n    VkDeviceSize copySize = dstSlice.size & 3u;\n    VkDeviceSize fillSize = dstSlice.size - copySize;\n\n    // If the buffer is small, just dispatch one single copy\n    if (copySize && dstSlice.size < MinCopyAndFillSize) {\n      copySize = dstSlice.size;\n      fillSize = 0u;\n    }\n\n    if (fillSize) {\n      m_cmd->cmdFillBuffer(DxvkCmdBuffer::SdmaBuffer,\n        dstSlice.buffer, dstSlice.offset, fillSize, 0u);\n    }\n\n    if (copySize) {\n      auto srcSlice = createZeroBuffer(copySize)->getSliceInfo();\n\n      VkBufferCopy2 copyRegion = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 };\n      copyRegion.srcOffset = srcSlice.offset;\n      copyRegion.dstOffset = dstSlice.offset + fillSize;\n      copyRegion.size      = copySize;\n\n      VkCopyBufferInfo2 copyInfo = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 };\n      copyInfo.srcBuffer = srcSlice.buffer;\n      copyInfo.dstBuffer = dstSlice.buffer;\n      copyInfo.regionCount = 1;\n      copyInfo.pRegions = &copyRegion;\n\n      m_cmd->cmdCopyBuffer(DxvkCmdBuffer::SdmaBuffer, &copyInfo);\n    }\n\n    accessBufferTransfer(*buffer,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n\n    m_cmd->track(buffer, DxvkAccess::Write);\n  }\n\n\n  void DxvkContext::initImage(\n    const Rc<DxvkImage>&            image,\n          VkImageLayout             initialLayout) {\n    VkImageSubresourceRange subresources = image->getAvailableSubresources();\n\n    if (initialLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) {\n      accessImage(DxvkCmdBuffer::InitBarriers,\n        *image, subresources, initialLayout,\n        VK_PIPELINE_STAGE_2_NONE, 0, DxvkAccessOp::None);\n\n      m_cmd->track(image, DxvkAccess::None);\n    } else {\n      auto formatInfo = image->formatInfo();\n      auto bufferInfo = image->storage()->getBufferInfo();\n\n      if (!formatInfo->flags.any(DxvkFormatFlag::BlockCompressed, DxvkFormatFlag::MultiPlane)) {\n        // Clear commands can only happen on the graphics queue\n        VkImageLayout clearLayout = image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n\n        addImageInitTransition(*image, subresources, clearLayout,\n          VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n        flushImageLayoutTransitions(DxvkCmdBuffer::InitBuffer);\n\n        if (subresources.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n          VkClearDepthStencilValue value = { };\n\n          m_cmd->cmdClearDepthStencilImage(DxvkCmdBuffer::InitBuffer,\n            image->handle(), clearLayout, &value, 1, &subresources);\n        } else {\n          VkClearColorValue value = { };\n\n          m_cmd->cmdClearColorImage(DxvkCmdBuffer::InitBuffer,\n            image->handle(), clearLayout, &value, 1, &subresources);\n        }\n\n        accessImage(DxvkCmdBuffer::InitBuffer, *image, subresources, clearLayout,\n          VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT, DxvkAccessOp::None);\n      } else if (bufferInfo.buffer) {\n        // If the image allocation has a buffer, use that to clear the backing storage\n        // to zero. There is no strict guarantee that the image contents read zero, but\n        // in practice this should be good enough and avoids having to create a very\n        // large zero buffer in some cases.\n        m_cmd->cmdFillBuffer(DxvkCmdBuffer::SdmaBuffer,\n          bufferInfo.buffer, bufferInfo.offset, bufferInfo.size, 0u);\n\n        accessMemory(DxvkCmdBuffer::SdmaBuffer,\n          VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT,\n          VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_NONE);\n\n        accessImage(DxvkCmdBuffer::InitBarriers, *image, subresources, VK_IMAGE_LAYOUT_UNDEFINED,\n          VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_NONE, DxvkAccessOp::None);\n      } else {\n        // We're copying from a buffer, use the transfer queue\n        addImageInitTransition(*image, subresources, VK_IMAGE_LAYOUT_GENERAL,\n          VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n        flushImageLayoutTransitions(DxvkCmdBuffer::SdmaBuffer);\n\n        for (auto aspects = formatInfo->aspectMask; aspects; ) {\n          auto aspect = vk::getNextAspect(aspects);\n          auto extent = image->mipLevelExtent(subresources.baseMipLevel);\n          auto elementSize = formatInfo->elementSize;\n\n          if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n            auto plane = &formatInfo->planes[vk::getPlaneIndex(aspect)];\n            extent.width  /= plane->blockSize.width;\n            extent.height /= plane->blockSize.height;\n            elementSize = plane->elementSize;\n          }\n\n          // Allocate enough staging buffer memory to fit one\n          // single subresource, then dispatch multiple copies\n          VkExtent3D blockCount = util::computeBlockCount(extent, formatInfo->blockSize);\n          VkDeviceSize dataSize = util::flattenImageExtent(blockCount) * elementSize;\n\n          auto zeroSlice = createZeroBuffer(dataSize)->getSliceInfo();\n\n          for (uint32_t level = 0; level < subresources.levelCount; level++) {\n            VkOffset3D offset = VkOffset3D { 0, 0, 0 };\n            VkExtent3D extent = image->mipLevelExtent(subresources.baseMipLevel + level);\n\n            if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n              auto plane = &formatInfo->planes[vk::getPlaneIndex(aspect)];\n              extent.width  /= plane->blockSize.width;\n              extent.height /= plane->blockSize.height;\n            }\n\n            for (uint32_t layer = 0; layer < subresources.layerCount; layer++) {\n              VkBufferImageCopy2 copyRegion = { VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 };\n              copyRegion.bufferOffset = zeroSlice.offset;\n              copyRegion.imageSubresource = vk::makeSubresourceLayers(\n                vk::pickSubresource(subresources, level, layer));\n              copyRegion.imageSubresource.aspectMask = aspect;\n              copyRegion.imageOffset = offset;\n              copyRegion.imageExtent = extent;\n\n              VkCopyBufferToImageInfo2 copyInfo = { VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 };\n              copyInfo.srcBuffer = zeroSlice.buffer;\n              copyInfo.dstImage = image->handle();\n              copyInfo.dstImageLayout = VK_IMAGE_LAYOUT_GENERAL;\n              copyInfo.regionCount = 1;\n              copyInfo.pRegions = &copyRegion;\n\n              m_cmd->cmdCopyBufferToImage(DxvkCmdBuffer::SdmaBuffer, &copyInfo);\n            }\n          }\n        }\n\n        accessImageTransfer(*image, subresources, VK_IMAGE_LAYOUT_GENERAL,\n          VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n      }\n\n      m_cmd->track(image, DxvkAccess::Write);\n    }\n\n    // The image will be in its default layout after this\n    image->trackLayout(image->getAvailableSubresources(), image->info().layout);\n  }\n  \n  \n  void DxvkContext::initSparseImage(\n    const Rc<DxvkImage>&            image) {\n    auto vk = m_device->vkd();\n\n    // Query sparse memory requirements\n    uint32_t reqCount = 0;\n    vk->vkGetImageSparseMemoryRequirements(vk->device(), image->handle(), &reqCount, nullptr);\n\n    std::vector<VkSparseImageMemoryRequirements> req(reqCount);\n    vk->vkGetImageSparseMemoryRequirements(vk->device(), image->handle(), &reqCount, req.data());\n\n    // Bind metadata aspects. Since the image was just created,\n    // we do not need to interrupt our command list for that.\n    DxvkResourceMemoryInfo memoryInfo = image->getMemoryInfo();\n\n    for (const auto& r : req) {\n      if (!(r.formatProperties.aspectMask & VK_IMAGE_ASPECT_METADATA_BIT))\n        continue;\n\n      uint32_t layerCount = (r.formatProperties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT)\n        ? 1u : image->info().numLayers;\n\n      for (uint32_t i = 0; i < layerCount; i++) {\n        DxvkSparseImageOpaqueBindKey key;\n        key.image   = image->handle();\n        key.offset  = r.imageMipTailOffset + i * r.imageMipTailStride;\n        key.size    = r.imageMipTailSize;\n        key.flags   = VK_SPARSE_MEMORY_BIND_METADATA_BIT;\n\n        DxvkResourceMemoryInfo page;\n        page.memory = memoryInfo.memory;\n        page.offset = memoryInfo.offset;\n        page.size = r.imageMipTailSize;\n\n        m_cmd->bindImageOpaqueMemory(key, page);\n\n        memoryInfo.offset += r.imageMipTailSize;\n      }\n    }\n\n    // Perform initial layout transition\n    accessImage(DxvkCmdBuffer::InitBuffer, *image, image->getAvailableSubresources(),\n      VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_2_NONE, 0, DxvkAccessOp::None);\n\n    image->trackLayout(image->getAvailableSubresources(), image->info().layout);\n\n    m_cmd->track(image, DxvkAccess::Write);\n  }\n\n\n  void DxvkContext::emitGraphicsBarrier(\n          VkPipelineStageFlags      srcStages,\n          VkAccessFlags             srcAccess,\n          VkPipelineStageFlags      dstStages,\n          VkAccessFlags             dstAccess) {\n    // Emit barrier early so we can fold this into\n    // the spill render pass barrier if possible\n    if (srcStages | dstStages) {\n      accessMemory(DxvkCmdBuffer::ExecBuffer,\n        srcStages, srcAccess, dstStages, dstAccess);\n    }\n\n    this->endCurrentPass(true);\n\n    // Flush barriers if there was no active render pass.\n    // This is necessary because there are no resources\n    // associated with the barrier to allow tracking.\n    if (srcStages | dstStages)\n      flushBarriers();\n  }\n\n\n  void DxvkContext::acquireExternalResource(\n      const Rc<DxvkPagedResource>&    resource,\n            VkImageLayout             layout) {\n    DxvkResourceAccess access;\n    access.stages = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;\n    access.access = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT;\n    access.buffer = dynamic_cast<DxvkBuffer*>(resource.ptr());\n    access.image = dynamic_cast<DxvkImage*>(resource.ptr());\n\n    if (access.buffer) {\n      access.bufferOffset = 0u;\n      access.bufferSize = access.buffer->info().size;\n    } else if (access.image) {\n      access.imageSubresources = access.image->getAvailableSubresources();\n      access.imageLayout = layout;\n\n      // Need to overwrite the tracked layout\n      access.image->trackLayout(access.imageSubresources, access.imageLayout);\n    }\n\n    // Try to acquire on the init command buffer since this will generally\n    // be the first use of the resource in a command list. Otherwise, we\n    // need to flush barriers since there may be release barriers already.\n    DxvkCmdBuffer cmdBuffer = prepareOutOfOrderTransfer(\n      DxvkCmdBuffer::InitBarriers, 1u, &access);\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer)\n      endCurrentPass(true);\n\n    if (access.image && access.imageLayout != access.image->info().layout) {\n      // External release barrier and layout transition in one go\n      transitionImageLayout(*access.image, access.imageSubresources,\n        access.stages, access.access, access.imageLayout,\n        access.image->info().stages, access.image->info().access, false);\n      flushImageLayoutTransitions(cmdBuffer);\n    } else {\n      releaseResources(cmdBuffer, 1u, &access);\n    }\n\n    m_cmd->track(resource, DxvkAccess::Read);\n  }\n\n\n  void DxvkContext::releaseExternalResource(\n    const Rc<DxvkPagedResource>&    resource,\n          VkImageLayout             layout) {\n    endCurrentPass(true);\n\n    DxvkResourceAccess access;\n    access.stages = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;\n    access.access = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT;\n    access.buffer = dynamic_cast<DxvkBuffer*>(resource.ptr());\n    access.image = dynamic_cast<DxvkImage*>(resource.ptr());\n\n    if (access.buffer) {\n      access.bufferOffset = 0u;\n      access.bufferSize = access.buffer->info().size;\n    } else if (access.image) {\n      access.imageSubresources = access.image->getAvailableSubresources();\n      access.imageLayout = layout;\n    }\n\n    // Prepare resource for external use, hence acquire\n    acquireResources(DxvkCmdBuffer::ExecBuffer, 1u, &access);\n  }\n\n\n\n  void DxvkContext::generateMipmaps(\n    const Rc<DxvkImageView>&        imageView,\n          VkFilter                  filter) {\n    if (imageView->info().mipCount <= 1)\n      return;\n    \n    this->endCurrentPass(true);\n\n    // Check whether we can use the single-pass mip gen compute shader. Its main\n    // advantage is that it does not require any internal synchronization as long\n    // as only a single pass is required to process all mips in the view.\n    bool useCs = filter == VK_FILTER_LINEAR\n      && imageView->image()->info().type == VK_IMAGE_TYPE_2D\n      && imageView->image()->info().sampleCount == VK_SAMPLE_COUNT_1_BIT\n      && m_common->metaMipGen().checkFormatSupport(imageView->info().format);\n\n    if (useCs) {\n      DxvkImageUsageInfo usageInfo;\n      usageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT;\n\n      useCs = ensureImageCompatibility(imageView->image(), usageInfo);\n    }\n\n    // If we can't use compute, use plain blits if the view format matches the image\n    // format exactly. Beneficial on hardware that has dedicated 2D engines.\n    bool useHw = !useCs\n      && imageView->info().format == imageView->image()->info().format\n      && imageView->image()->info().sampleCount == VK_SAMPLE_COUNT_1_BIT;\n\n    if (useHw) {\n      DxvkFormatFeatures formatFeatures = m_device->adapter()->getFormatFeatures(imageView->info().format);\n\n      VkFormatFeatureFlags2 features = imageView->image()->info().tiling == VK_IMAGE_TILING_OPTIMAL\n        ? formatFeatures.optimal\n        : formatFeatures.linear;\n\n      useHw = (features & VK_FORMAT_FEATURE_2_BLIT_DST_BIT)\n           && (features & VK_FORMAT_FEATURE_2_BLIT_SRC_BIT);\n    }\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      const char* dstName = imageView->image()->info().debugName;\n\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer, vk::makeLabel(0xe6dcf0,\n        str::format(\"Mip gen (\", dstName ? dstName : \"unknown\", \")\").c_str()));\n    }\n\n    if (useCs)\n      generateMipmapsCs(imageView);\n    else if (useHw)\n      generateMipmapsHw(imageView, filter);\n    else\n      generateMipmapsFb(imageView, filter);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n  }\n  \n  \n  void DxvkContext::invalidateBuffer(\n    const Rc<DxvkBuffer>&           buffer,\n          Rc<DxvkResourceAllocation>&& slice) {\n    Rc<DxvkResourceAllocation> prevAllocation = buffer->assignStorage(std::move(slice));\n    m_cmd->track(std::move(prevAllocation));\n\n    buffer->resetTracking();\n\n    // We also need to update all bindings that the buffer\n    // may be bound to either directly or through views.\n    VkBufferUsageFlags usage = buffer->info().usage &\n      ~(VK_BUFFER_USAGE_TRANSFER_DST_BIT |\n        VK_BUFFER_USAGE_TRANSFER_SRC_BIT |\n        VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);\n\n    // Fast early-out for plain uniform buffers, very common\n    if (likely(usage == VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)) {\n      m_descriptorState.dirtyBuffers(buffer->getShaderStages());\n      return;\n    }\n\n    if (usage & (VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT))\n      m_descriptorState.dirtyBuffers(buffer->getShaderStages());\n\n    if (usage & (VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT))\n      m_descriptorState.dirtyViews(buffer->getShaderStages());\n\n    if (usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT)\n      m_flags.set(DxvkContextFlag::GpDirtyIndexBuffer);\n    \n    if (usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)\n      m_flags.set(DxvkContextFlag::GpDirtyVertexBuffers);\n    \n    if (usage & VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT)\n      m_flags.set(DxvkContextFlag::DirtyDrawBuffer);\n\n    if (usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT)\n      m_flags.set(DxvkContextFlag::GpDirtyXfbBuffers);\n  }\n\n\n  void DxvkContext::ensureBufferAddress(\n    const Rc<DxvkBuffer>&           buffer) {\n    // Really nothing else to do here but set the flag\n    if (buffer->canRelocate()) {\n      buffer->enableStableAddress();\n\n      m_common->memoryManager().lockResourceGpuAddress(buffer->storage());\n    }\n  }\n\n\n  void DxvkContext::invalidateImage(\n    const Rc<DxvkImage>&            image,\n          Rc<DxvkResourceAllocation>&& slice,\n          VkImageLayout             layout) {\n    invalidateImageWithUsage(image, std::move(slice), DxvkImageUsageInfo(), layout);\n  }\n\n\n  void DxvkContext::invalidateImageWithUsage(\n    const Rc<DxvkImage>&            image,\n          Rc<DxvkResourceAllocation>&& slice,\n    const DxvkImageUsageInfo&       usageInfo,\n          VkImageLayout             layout) {\n    VkImageUsageFlags usage = image->info().usage;\n\n    // Invalidate active image descriptors\n    if (usage & (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT))\n      m_descriptorState.dirtyViews(image->getShaderStages());\n\n    // Ensure that the image is in its default layout before invalidation\n    if (usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {\n      bool found = false;\n\n      for (uint32_t i = 0; i < m_state.om.framebufferInfo.numAttachments() && !found; i++)\n        found = m_state.om.framebufferInfo.getAttachment(i).view->image() == image;\n\n      if (found) {\n        m_flags.set(DxvkContextFlag::GpDirtyRenderTargets);\n\n        endCurrentPass(true);\n        flushDeferredClear(*image, image->getAvailableSubresources());\n      }\n    }\n\n    // If there are any pending accesses that may involve\n    // layout transitions, ensure that they are done\n    if (resourceHasAccess(*image, image->getAvailableSubresources(), DxvkAccess::Write, DxvkAccessOp::None)) {\n      endCurrentPass(true);\n      flushBarriers();\n    }\n\n    // Ensure that the image is in its default layout. No need to explicitly\n    // track it here though since we track the storage object instead.\n    if (image->queryLayout(image->getAvailableSubresources()) != image->info().layout) {\n      endCurrentPass(true);\n\n      transitionImageLayout(*image,\n        image->getAvailableSubresources(),\n        image->info().stages, image->info().access,\n        image->info().layout, image->info().stages, image->info().access, false);\n      flushImageLayoutTransitions(DxvkCmdBuffer::ExecBuffer);\n    }\n\n    // Actually replace backing storage and make sure to keep the old one alive\n    Rc<DxvkResourceAllocation> prevAllocation = image->assignStorageWithUsage(std::move(slice), usageInfo);\n    m_cmd->track(std::move(prevAllocation));\n\n    if (usageInfo.stableGpuAddress)\n      m_common->memoryManager().lockResourceGpuAddress(image->storage());\n\n    // If the image is new and uninitialized, submit a layout transition\n    image->trackLayout(image->getAvailableSubresources(), layout);\n\n    if (layout != image->info().layout) {\n      if (layout == VK_IMAGE_LAYOUT_UNDEFINED || layout == VK_IMAGE_LAYOUT_PREINITIALIZED) {\n        transitionImageLayout(*image, image->getAvailableSubresources(),\n          image->info().stages, image->info().access, image->info().layout,\n          image->info().stages, image->info().access, layout == VK_IMAGE_LAYOUT_UNDEFINED);\n        flushImageLayoutTransitions(DxvkCmdBuffer::InitBarriers);\n\n        m_cmd->track(image, DxvkAccess::Read);\n      } else {\n        // Track non-default layout as necessary\n        m_nonDefaultLayoutImages.emplace_back(image);\n      }\n    }\n\n    image->resetTracking();\n  }\n\n\n  bool DxvkContext::ensureImageCompatibility(\n    const Rc<DxvkImage>&            image,\n    const DxvkImageUsageInfo&       usageInfo) {\n    // Check whether image usage, flags and view formats are supported.\n    // If any of these are false, we need to create a new image.\n    bool isUsageAndFormatCompatible = (image->info().usage & usageInfo.usage) == usageInfo.usage\n                                   && (image->info().flags & usageInfo.flags) == usageInfo.flags;\n\n    for (uint32_t i = 0; i < usageInfo.viewFormatCount && isUsageAndFormatCompatible; i++)\n      isUsageAndFormatCompatible &= image->isViewCompatible(usageInfo.viewFormats[i]);\n\n    // Check if we need to insert a barrier and update image properties\n    bool isAccessAndLayoutCompatible = (image->info().stages & usageInfo.stages) == usageInfo.stages\n                                    && (image->info().access & usageInfo.access) == usageInfo.access\n                                    && (!usageInfo.layout || image->info().layout == usageInfo.layout);\n\n    // If everything matches already, no need to do anything. Only ensure\n    // that the stable adress bit is respected if set for the first time.\n    if (isUsageAndFormatCompatible && isAccessAndLayoutCompatible) {\n      bool needsUpdate = (usageInfo.stableGpuAddress && image->canRelocate());\n\n      if (usageInfo.colorSpace != VK_COLOR_SPACE_MAX_ENUM_KHR)\n        needsUpdate |= (usageInfo.colorSpace != image->info().colorSpace);\n\n      if (needsUpdate) {\n        image->assignStorageWithUsage(image->storage(), usageInfo);\n        m_common->memoryManager().lockResourceGpuAddress(image->storage());\n      }\n\n      return true;\n    }\n\n    // Ensure the image is accessible and in its default layout\n    this->endCurrentPass(true);\n\n    DxvkResourceAccess access(*image, image->getAvailableSubresources(),\n      image->info().layout, image->info().stages, image->info().access, false);\n\n    if (isUsageAndFormatCompatible && usageInfo.layout)\n      access.imageLayout = usageInfo.layout;\n\n    acquireResources(DxvkCmdBuffer::ExecBuffer, 1u, &access);\n\n    if (isUsageAndFormatCompatible) {\n      // Emit a barrier. If used in internal passes, this function\n      // must be called *before* emitting dirty checks there.\n      image->assignStorageWithUsage(image->storage(), usageInfo);\n\n      if (usageInfo.stableGpuAddress)\n        m_common->memoryManager().lockResourceGpuAddress(image->storage());\n      return true;\n    }\n\n    // Some images have to stay in their place, we can't do much in that case.\n    if (!image->canRelocate()) {\n      Logger::err(str::format(\"DxvkContext: Cannot relocate image:\",\n        \"\\n  Current usage:   0x\", std::hex, image->info().usage, \", flags: 0x\", image->info().flags, \", \", std::dec, image->info().viewFormatCount, \" view formats\"\n        \"\\n  Requested usage: 0x\", std::hex, usageInfo.usage, \", flags: 0x\", usageInfo.flags, \", \", std::dec, usageInfo.viewFormatCount, \" view formats\"));\n      return false;\n    }\n\n    // Enable mutable format bit as necessary. We do not require\n    // setting this explicitly so that the caller does not have\n    // to check image formats every time.\n    VkImageCreateFlags createFlags = 0u;\n\n    for (uint32_t i = 0; i < usageInfo.viewFormatCount; i++) {\n      if (usageInfo.viewFormats[i] != image->info().format) {\n        createFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;\n        break;\n      }\n    }\n\n    // Ensure that the image can support the requested usage\n    VkFormatFeatureFlagBits2 required = 0u;\n\n    switch (usageInfo.usage) {\n      case VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT:\n        required |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT;\n        break;\n\n      case VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT:\n        required |= VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT;\n        break;\n\n      case VK_IMAGE_USAGE_SAMPLED_BIT:\n        required |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT;\n        break;\n\n      case VK_IMAGE_USAGE_STORAGE_BIT:\n        required |= VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT;\n        break;\n\n      case VK_IMAGE_USAGE_TRANSFER_SRC_BIT:\n        required |= VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT;\n        break;\n\n      case VK_IMAGE_USAGE_TRANSFER_DST_BIT:\n        required |= VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT;\n        break;\n\n      default:\n        break;\n    }\n\n    // Make sure to use the correct set of feature flags for this image\n    auto features = m_device->getFormatFeatures(image->info().format);\n    auto& supported = image->info().tiling == VK_IMAGE_TILING_OPTIMAL\n      ? features.optimal : features.linear;\n\n    if ((supported & required) != required) {\n      // Check if any of the view formats support the required features\n      for (uint32_t i = 0; i < image->info().viewFormatCount; i++) {\n        auto extendedFeatures = m_device->getFormatFeatures(image->info().viewFormats[i]);\n        features.optimal |= extendedFeatures.optimal;\n        features.linear |= extendedFeatures.linear;\n      }\n\n      for (uint32_t i = 0; i < usageInfo.viewFormatCount; i++) {\n        auto extendedFeatures = m_device->getFormatFeatures(usageInfo.viewFormats[i]);\n        features.optimal |= extendedFeatures.optimal;\n        features.linear |= extendedFeatures.linear;\n      }\n\n      if ((supported & required) != required)\n        return false;\n\n      // We're good, just need to enable extended usage\n      createFlags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;\n    }\n\n    // Allocate new backing storage and relocate the image\n    DxvkImageUsageInfo usage = usageInfo;\n    usage.flags |= createFlags;\n\n    auto storage = image->allocateStorageWithUsage(usage, 0u);\n\n    DxvkRelocateImageInfo relocateInfo;\n    relocateInfo.image = image;\n    relocateInfo.storage = storage;\n    relocateInfo.usageInfo = usage;\n\n    relocateResources(0, nullptr, 1, &relocateInfo);\n    return true;\n  }\n\n\n  template<bool Indexed, typename T>\n  void DxvkContext::drawGeneric(\n          uint32_t                  count,\n    const T*                        draws) {\n    if (this->commitGraphicsState<Indexed, false>()) {\n      if (count == 1u) {\n        // Most common case, just emit a single draw\n        if constexpr (Indexed) {\n          m_cmd->cmdDrawIndexed(draws->indexCount, draws->instanceCount,\n            draws->firstIndex, draws->vertexOffset, draws->firstInstance);\n        } else {\n          m_cmd->cmdDraw(draws->vertexCount, draws->instanceCount,\n            draws->firstVertex, draws->firstInstance);\n        }\n\n        m_cmd->addStatCtr(DxvkStatCounter::CmdDrawCalls, 1u);\n      } else if (unlikely(needsDrawBarriers())) {\n        // If the current pipeline has storage resource hazards,\n        // unroll draws and insert a barrier after each one.\n        for (uint32_t i = 0; i < count; i++) {\n          if (i)\n            this->commitGraphicsState<Indexed, false>();\n\n          if constexpr (Indexed) {\n            m_cmd->cmdDrawIndexed(draws[i].indexCount, draws[i].instanceCount,\n              draws[i].firstIndex, draws[i].vertexOffset, draws[i].firstInstance);\n          } else {\n            m_cmd->cmdDraw(draws[i].vertexCount, draws[i].instanceCount,\n              draws[i].firstVertex, draws[i].firstInstance);\n          }\n        }\n\n        m_cmd->addStatCtr(DxvkStatCounter::CmdDrawCalls, count);\n      } else {\n        using MultiDrawInfo = std::conditional_t<Indexed,\n          VkMultiDrawIndexedInfoEXT, VkMultiDrawInfoEXT>;\n\n        // Intentially don't initialize this; we'll probably not use\n        // the full batch size anyway, so doing so would be wasteful.\n        std::array<MultiDrawInfo, DirectMultiDrawBatchSize> batch;\n\n        uint32_t instanceCount = 0u;\n        uint32_t instanceIndex = 0u;\n\n        uint32_t batchSize = 0u;\n\n        for (uint32_t i = 0; i < count; i++) {\n          if (!batchSize) {\n            instanceCount = draws[i].instanceCount;\n            instanceIndex = draws[i].firstInstance;\n          }\n\n          if constexpr (Indexed) {\n            auto& drawInfo = batch[batchSize++];\n            drawInfo.firstIndex = draws[i].firstIndex;\n            drawInfo.indexCount = draws[i].indexCount;\n            drawInfo.vertexOffset = draws[i].vertexOffset;\n          } else {\n            auto& drawInfo = batch[batchSize++];\n            drawInfo.firstVertex = draws[i].firstVertex;\n            drawInfo.vertexCount = draws[i].vertexCount;\n          }\n\n          bool emitDraw = i + 1u == count || batchSize == DirectMultiDrawBatchSize;\n\n          if (!emitDraw) {\n            const auto& next = draws[i + 1u];\n\n            emitDraw = instanceCount != next.instanceCount\n                    || instanceIndex != next.firstInstance;\n          }\n\n          if (emitDraw) {\n            if (m_features.test(DxvkContextFeature::DirectMultiDraw)) {\n              if constexpr (Indexed) {\n                m_cmd->cmdDrawMultiIndexed(batchSize, batch.data(),\n                  instanceCount, instanceIndex);\n              } else {\n                m_cmd->cmdDrawMulti(batchSize, batch.data(),\n                  instanceCount, instanceIndex);\n              }\n            } else {\n              // This path only really exists for consistency reasons; all drivers\n              // we care about support MultiDraw natively, but debug tools may not.\n              if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n                const char* procName = Indexed ? \"vkCmdDrawMultiIndexedEXT\" : \"vkCmdDrawMultiEXT\";\n                m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n                  vk::makeLabel(0u, str::format(procName, \"(\", batchSize, \")\").c_str()));\n              }\n\n              for (uint32_t i = 0; i < batchSize; i++) {\n                const auto& entry = batch[i];\n\n                if constexpr (Indexed) {\n                  m_cmd->cmdDrawIndexed(entry.indexCount, instanceCount,\n                    entry.firstIndex, entry.vertexOffset, instanceIndex);\n                } else {\n                  m_cmd->cmdDraw(entry.vertexCount, instanceCount,\n                    entry.firstVertex, instanceIndex);\n                }\n              }\n\n              if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n                m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n            }\n\n            m_cmd->addStatCtr(DxvkStatCounter::CmdDrawCalls, 1u);\n            m_cmd->addStatCtr(DxvkStatCounter::CmdDrawsMerged, batchSize - 1u);\n\n            batchSize = 0u;\n          }\n        }\n      }\n    }\n  }\n\n\n  template<bool Indexed>\n  void DxvkContext::drawIndirectGeneric(\n          VkDeviceSize              offset,\n          uint32_t                  count,\n          uint32_t                  stride,\n          bool                      unroll) {\n    constexpr VkDeviceSize elementSize = Indexed\n      ? sizeof(VkDrawIndexedIndirectCommand)\n      : sizeof(VkDrawIndirectCommand);\n\n    if (this->commitGraphicsState<Indexed, true>()) {\n      auto argInfo = m_state.id.argBuffer.getSliceInfo();\n\n      if (likely(count == 1u || !unroll || !needsDrawBarriers())) {\n        if (Indexed) {\n          m_cmd->cmdDrawIndexedIndirect(argInfo.buffer,\n            argInfo.offset + offset, count, stride);\n        } else {\n          m_cmd->cmdDrawIndirect(argInfo.buffer,\n            argInfo.offset + offset, count, stride);\n        }\n\n        m_cmd->addStatCtr(DxvkStatCounter::CmdDrawCalls, 1u);\n\n        if (unroll) {\n          // Assume this is an automatically merged draw, if the app\n          // explicitly uses multidraw then don't count it as merged.\n          m_cmd->addStatCtr(DxvkStatCounter::CmdDrawsMerged, count - 1u);\n        }\n\n        if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)\n         || m_state.id.argBuffer.buffer()->hasGfxStores())\n          accessDrawBuffer(offset, count, stride, elementSize);\n      } else {\n        // If the pipeline has order-sensitive stores, submit one\n        // draw at a time and insert barriers in between.\n        for (uint32_t i = 0; i < count; i++) {\n          if (i)\n            this->commitGraphicsState<Indexed, true>();\n\n          if (Indexed) {\n            m_cmd->cmdDrawIndexedIndirect(argInfo.buffer,\n              argInfo.offset + offset, 1u, 0u);\n          } else {\n            m_cmd->cmdDrawIndirect(argInfo.buffer,\n              argInfo.offset + offset, 1u, 0u);\n          }\n\n          if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)\n           || m_state.id.argBuffer.buffer()->hasGfxStores())\n            accessDrawBuffer(offset, 1u, stride, elementSize);\n\n          offset += stride;\n        }\n\n        m_cmd->addStatCtr(DxvkStatCounter::CmdDrawCalls, count);\n      }\n    }\n  }\n\n\n  template<bool Indexed>\n  void DxvkContext::drawIndirectCountGeneric(\n          VkDeviceSize          offset,\n          VkDeviceSize          countOffset,\n          uint32_t              maxCount,\n          uint32_t              stride) {\n    if (this->commitGraphicsState<Indexed, true>()) {\n      auto argInfo = m_state.id.argBuffer.getSliceInfo();\n      auto cntInfo = m_state.id.cntBuffer.getSliceInfo();\n\n      if (Indexed) {\n        m_cmd->cmdDrawIndexedIndirectCount(\n          argInfo.buffer, argInfo.offset + offset,\n          cntInfo.buffer, cntInfo.offset + countOffset,\n          maxCount, stride);\n      } else {\n        m_cmd->cmdDrawIndirectCount(\n          argInfo.buffer, argInfo.offset + offset,\n          cntInfo.buffer, cntInfo.offset + countOffset,\n          maxCount, stride);\n      }\n\n      m_cmd->addStatCtr(DxvkStatCounter::CmdDrawCalls, 1u);\n\n      if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)\n       || m_state.id.argBuffer.buffer()->hasGfxStores()) {\n        accessDrawBuffer(offset, maxCount, stride, Indexed\n          ? sizeof(VkDrawIndexedIndirectCommand)\n          : sizeof(VkDrawIndirectCommand));\n      }\n\n      if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)\n       || m_state.id.cntBuffer.buffer()->hasGfxStores())\n        accessDrawCountBuffer(countOffset);\n    }\n  }\n\n\n  void DxvkContext::resolveImage(\n    const Rc<DxvkImage>&            dstImage,\n    const Rc<DxvkImage>&            srcImage,\n    const VkImageResolve&           region,\n          VkFormat                  format,\n          VkResolveModeFlagBits     mode,\n          VkResolveModeFlagBits     stencilMode) {\n    auto formatInfo = lookupFormatInfo(format);\n\n    // Normalize resolve modes to available subresources\n    VkImageAspectFlags aspects = region.srcSubresource.aspectMask\n                               & region.dstSubresource.aspectMask;\n\n    if (!(aspects & VK_IMAGE_ASPECT_STENCIL_BIT))\n      stencilMode = VK_RESOLVE_MODE_NONE;\n\n    // No-op, but legal\n    if (!mode && !stencilMode)\n      return;\n\n    // Check whether the given resolve modes are supported for render pass resolves\n    bool useFb = false;\n\n    if (formatInfo->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n      const auto& properties = m_device->properties().vk12;\n\n      useFb |= (properties.supportedDepthResolveModes   & mode)        != mode\n            || (properties.supportedStencilResolveModes & stencilMode) != stencilMode;\n\n      if (mode != stencilMode && (formatInfo->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)) {\n        useFb |= (!mode || !stencilMode)\n          ? !properties.independentResolveNone\n          : !properties.independentResolve;\n      }\n    } else {\n      // For color images, only the default mode is supported\n      useFb |= mode != getDefaultResolveMode(formatInfo);\n    }\n\n    // Also fall back to framebuffer path if this is a partial resolve,\n    // or if two depth-stencil images are not format-compatible.\n    if (!useFb) {\n      useFb |= !dstImage->isFullSubresource(region.dstSubresource, region.extent)\n            || !srcImage->isFullSubresource(region.srcSubresource, region.extent);\n\n      if (formatInfo->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n        useFb |= dstImage->info().format != srcImage->info().format;\n    }\n\n    // If the resolve format does not match the base image format, the resolve\n    // attachment path is broken on some drivers so use the framebuffer path.\n    if (m_device->perfHints().renderPassResolveFormatBug) {\n      useFb |= format != srcImage->info().format\n            || format != dstImage->info().format;\n    }\n\n    // Ensure that we can actually use the destination image as an attachment\n    DxvkImageUsageInfo dstUsage = { };\n    dstUsage.viewFormatCount = 1;\n    dstUsage.viewFormats = &format;\n\n    if (formatInfo->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n      dstUsage.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n      dstUsage.stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n      dstUsage.access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n    } else {\n      dstUsage.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n      dstUsage.stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n      dstUsage.access = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n    }\n\n    // Same for the source image, but check for shader usage\n    // instead in case we need to use that path\n    DxvkImageUsageInfo srcUsage = dstUsage;\n\n    if (useFb) {\n      srcUsage.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n      srcUsage.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n      srcUsage.access = VK_ACCESS_SHADER_READ_BIT;\n    }\n\n    bool useHw = !ensureImageCompatibility(dstImage, dstUsage)\n              || !ensureImageCompatibility(srcImage, srcUsage);\n\n    // If possible, fold resolve into active render pass\n    if (!useHw && !useFb && resolveImageInline(dstImage, srcImage, region, format, mode, stencilMode))\n      return;\n\n    this->endCurrentPass(true);\n\n    if (unlikely(useHw)) {\n      // Only used as a fallback if we can't use the images any other way,\n      // format and resolve mode might not match what the app requests.\n      this->resolveImageHw(dstImage, srcImage, region);\n    } else if (unlikely(useFb)) {\n      // Only used for weird resolve modes or partial resolves\n      this->resolveImageFb(dstImage, srcImage,\n        region, format, mode, stencilMode);\n    } else {\n      // Default path, use a resolve attachment\n      this->resolveImageRp(dstImage, srcImage,\n        region, format, mode, stencilMode, true);\n    }\n  }\n\n\n  void DxvkContext::transformImage(\n    const Rc<DxvkImage>&            dstImage,\n    const VkImageSubresourceRange&  dstSubresources,\n          VkImageLayout             srcLayout,\n          VkImageLayout             dstLayout) {\n    this->endCurrentPass(false);\n\n    if (srcLayout != dstLayout) {\n      DxvkResourceAccess access(*dstImage, dstSubresources,\n        dstLayout, dstImage->info().stages, dstImage->info().access,\n        srcLayout == VK_IMAGE_LAYOUT_UNDEFINED);\n\n      acquireResources(DxvkCmdBuffer::ExecBuffer, 1u, &access);\n    }\n  }\n  \n  \n  VkAttachmentStoreOp DxvkContext::determineClearStoreOp(\n          VkAttachmentLoadOp        loadOp) const {\n    if (loadOp == VK_ATTACHMENT_LOAD_OP_NONE)\n      return VK_ATTACHMENT_STORE_OP_NONE;\n\n    if (loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE)\n      return VK_ATTACHMENT_STORE_OP_DONT_CARE;\n\n    return VK_ATTACHMENT_STORE_OP_STORE;\n  }\n\n\n  std::optional<DxvkClearInfo> DxvkContext::batchClear(\n    const Rc<DxvkImageView>&        imageView,\n          int32_t                   attachmentIndex,\n          VkImageAspectFlags        discardAspects,\n          VkImageAspectFlags        clearAspects,\n          VkClearValue              clearValue) {\n    bool hasLoadOpNone = m_device->properties().khrMaintenance7.separateDepthStencilAttachmentAccess;\n\n    DxvkColorAttachmentOps colorOp;\n    colorOp.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;\n    \n    DxvkDepthAttachmentOps depthOp;\n    depthOp.loadOpD = VK_ATTACHMENT_LOAD_OP_LOAD;\n    depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_LOAD;\n\n    if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT)\n      colorOp.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;\n    else if (discardAspects & VK_IMAGE_ASPECT_COLOR_BIT)\n      colorOp.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    \n    if (clearAspects & VK_IMAGE_ASPECT_DEPTH_BIT)\n      depthOp.loadOpD = VK_ATTACHMENT_LOAD_OP_CLEAR;\n    else if (discardAspects & VK_IMAGE_ASPECT_DEPTH_BIT)\n      depthOp.loadOpD = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    else if (hasLoadOpNone)\n      depthOp.loadOpD = VK_ATTACHMENT_LOAD_OP_NONE;\n\n    if (clearAspects & VK_IMAGE_ASPECT_STENCIL_BIT)\n      depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_CLEAR;\n    else if (discardAspects & VK_IMAGE_ASPECT_STENCIL_BIT)\n      depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    else if (hasLoadOpNone)\n      depthOp.loadOpS = VK_ATTACHMENT_LOAD_OP_NONE;\n\n    if (attachmentIndex >= 0 && !m_state.om.framebufferInfo.isWritable(attachmentIndex, clearAspects | discardAspects)) {\n      // Do not fold the clear/discard into the render pass if any of the affected aspects\n      // isn't writable. We can only hit this particular path when starting a render pass,\n      // so we can safely manipulate load layouts here.\n      attachmentIndex = -1;\n    }\n\n    // Completely ignore pure discards here if we can't fold them into the next\n    // render pass, since all we'd do is add an extra barrier for no reason.\n    if (attachmentIndex < 0 && !clearAspects)\n      return std::nullopt;\n\n    if (attachmentIndex < 0) {\n      std::optional<DxvkClearInfo> result;\n\n      auto& clearInfo = result.emplace();\n      clearInfo.view = imageView;\n      clearInfo.loadOp = colorOp.loadOp;\n      clearInfo.clearValue = clearValue;\n      clearInfo.clearAspects = clearAspects;\n      clearInfo.discardAspects = discardAspects;\n\n      if ((clearAspects | discardAspects) & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n        clearInfo.loadOp = depthOp.loadOpD;\n        clearInfo.loadOpS = depthOp.loadOpS;\n      }\n\n      return result;\n    } else {\n      // Perform the operation when starting the next render pass\n      if ((clearAspects | discardAspects) & VK_IMAGE_ASPECT_COLOR_BIT) {\n        uint32_t colorIndex = m_state.om.framebufferInfo.getColorAttachmentIndex(attachmentIndex);\n\n        m_state.om.renderPassOps.colorOps[colorIndex].loadOp = colorOp.loadOp;\n        m_state.om.renderPassOps.colorOps[colorIndex].clearValue = clearValue.color;\n      }\n      \n      if ((clearAspects | discardAspects) & VK_IMAGE_ASPECT_DEPTH_BIT) {\n        m_state.om.renderPassOps.depthOps.loadOpD = depthOp.loadOpD;\n        m_state.om.renderPassOps.depthOps.clearValue.depth = clearValue.depthStencil.depth;\n      }\n      \n      if ((clearAspects | discardAspects) & VK_IMAGE_ASPECT_STENCIL_BIT) {\n        m_state.om.renderPassOps.depthOps.loadOpS = depthOp.loadOpS;\n        m_state.om.renderPassOps.depthOps.clearValue.stencil = clearValue.depthStencil.stencil;\n      }\n\n      return std::nullopt;\n    }\n  }\n\n\n  void DxvkContext::performClears(\n    const DxvkClearBatch&           batch) {\n    auto [entries, count] = batch.getRange();\n\n    // Batch barriers and try to hoist clears if possible\n    small_vector<DxvkResourceAccess, 16> accessBatch;\n\n    for (size_t i = 0u; i < count; i++) {\n      const auto& entry = entries[i];\n\n      VkPipelineStageFlagBits2 clearStages = 0u;\n      VkAccessFlags2 clearAccess = 0u;\n\n      if ((entry.clearAspects | entry.discardAspects) & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n        clearStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n                    |  VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n        clearAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT\n                    |  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;\n      } else {\n        clearStages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        clearAccess |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT\n                    |  VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;\n      }\n\n      accessBatch.emplace_back(*entry.view, clearStages, clearAccess,\n        (entry.clearAspects | entry.discardAspects) == entry.view->info().aspects);\n    }\n\n    DxvkCmdBuffer cmdBuffer = prepareOutOfOrderTransfer(DxvkCmdBuffer::InitBuffer,\n      accessBatch.size(), accessBatch.data());\n\n    syncResources(cmdBuffer, accessBatch.size(), accessBatch.data(), false);\n\n    // Execute clears\n    for (size_t i = 0u; i < count; i++) {\n      const auto& entry = entries[i];\n\n      bool useLateClear = m_device->perfHints().renderPassClearFormatBug\n        && entry.view->info().format != entry.view->image()->info().format;\n\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n        const char* imageName = entry.view->image()->info().debugName;\n        m_cmd->cmdBeginDebugUtilsLabel(cmdBuffer,\n          vk::makeLabel(0xe6f0dc, str::format(\"Clear render target (\", imageName ? imageName : \"unknown\", \")\").c_str()));\n      }\n\n      // Set up a temporary render pass to execute the clear\n      VkRenderingAttachmentInfo attachmentInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n      attachmentInfo.imageView = entry.view->handle();\n      attachmentInfo.imageLayout = entry.view->getLayout();\n      attachmentInfo.clearValue = entry.clearValue;\n\n      VkRenderingAttachmentInfo stencilInfo = attachmentInfo;\n\n      VkExtent3D extent = entry.view->mipLevelExtent(0);\n\n      VkRenderingInfo renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n      renderingInfo.renderArea.extent = { extent.width, extent.height };\n      renderingInfo.layerCount = entry.view->info().layerCount;\n\n      if ((entry.clearAspects | entry.discardAspects) & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n        if (entry.view->info().aspects & VK_IMAGE_ASPECT_DEPTH_BIT) {\n          renderingInfo.pDepthAttachment = &attachmentInfo;\n\n          attachmentInfo.loadOp = entry.loadOp;\n          attachmentInfo.storeOp = determineClearStoreOp(entry.loadOp);\n        }\n\n        if (entry.view->info().aspects & VK_IMAGE_ASPECT_STENCIL_BIT) {\n          renderingInfo.pStencilAttachment = &stencilInfo;\n\n          stencilInfo.loadOp = entry.loadOpS;\n          stencilInfo.storeOp = determineClearStoreOp(entry.loadOpS);\n        }\n      } else {\n        attachmentInfo.loadOp = entry.loadOp;\n        attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\n        if (useLateClear && entry.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR)\n          attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\n        renderingInfo.colorAttachmentCount = 1;\n        renderingInfo.pColorAttachments = &attachmentInfo;\n      }\n\n      m_cmd->cmdBeginRendering(cmdBuffer, &renderingInfo);\n\n      if (useLateClear) {\n        VkClearAttachment clearInfo = { };\n        clearInfo.aspectMask = entry.clearAspects;\n        clearInfo.clearValue = entry.clearValue;\n\n        VkClearRect clearRect = { };\n        clearRect.rect = renderingInfo.renderArea;\n        clearRect.layerCount = renderingInfo.layerCount;\n\n        m_cmd->cmdClearAttachments(cmdBuffer, 1, &clearInfo, 1, &clearRect);\n      }\n\n      m_cmd->cmdEndRendering(cmdBuffer);\n\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n        m_cmd->cmdEndDebugUtilsLabel(cmdBuffer);\n    }\n  }\n\n\n  void DxvkContext::deferClear(\n    const Rc<DxvkImageView>&        imageView,\n          VkImageAspectFlags        clearAspects,\n          VkClearValue              clearValue) {\n    for (auto& entry : m_deferredClears) {\n      if (entry.imageView->matchesView(imageView)) {\n        entry.imageView = imageView;\n        entry.discardAspects &= ~clearAspects;\n        entry.clearAspects |= clearAspects;\n\n        if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT)\n          entry.clearValue.color = clearValue.color;\n        if (clearAspects & VK_IMAGE_ASPECT_DEPTH_BIT)\n          entry.clearValue.depthStencil.depth = clearValue.depthStencil.depth;\n        if (clearAspects & VK_IMAGE_ASPECT_STENCIL_BIT)\n          entry.clearValue.depthStencil.stencil = clearValue.depthStencil.stencil;\n        \n        return;\n      } else if (entry.imageView->checkSubresourceOverlap(imageView)) {\n        this->endCurrentPass(false);\n        break;\n      }\n    }\n\n    m_deferredClears.push_back({ imageView, 0, clearAspects, clearValue });\n  }\n\n\n  void DxvkContext::deferDiscard(\n    const Rc<DxvkImageView>&        imageView,\n          VkImageAspectFlags        discardAspects) {\n    for (auto& entry : m_deferredClears) {\n      if (entry.imageView->matchesView(imageView)) {\n        entry.imageView = imageView;\n        entry.discardAspects |= discardAspects;\n        entry.clearAspects &= ~discardAspects;\n        return;\n      } else if (entry.imageView->checkSubresourceOverlap(imageView)) {\n        this->endCurrentPass(false);\n        break;\n      }\n    }\n\n    m_deferredClears.push_back({ imageView, discardAspects });\n  }\n\n\n  void DxvkContext::hoistInlineClear(\n          DxvkDeferredClear&        clear,\n          VkRenderingAttachmentInfo& attachment,\n          VkImageAspectFlagBits     aspect) {\n    if (clear.clearAspects & aspect) {\n      attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;\n      attachment.clearValue = clear.clearValue;\n\n      clear.clearAspects &= ~aspect;\n    } else if (clear.discardAspects & aspect) {\n      attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\n      clear.discardAspects &= ~aspect;\n    }\n  }\n\n\n  void DxvkContext::flushClearsInline() {\n    small_vector<VkClearAttachment, MaxNumRenderTargets + 1u> attachments;\n\n    for (auto& clear : m_deferredClears) {\n      // If we end up here, the image is guaranteed to be bound and writable.\n      int32_t attachmentIndex = m_state.om.framebufferInfo.findAttachment(clear.imageView);\n\n      if (m_flags.test(DxvkContextFlag::GpRenderPassSecondaryCmd)) {\n        // If the attachment hasn't been used for rendering yet, and if we're inside\n        // a render pass using secondary command buffers, we can fold the clear or\n        // discard into the render pass itself.\n        if ((clear.clearAspects | clear.discardAspects) & VK_IMAGE_ASPECT_COLOR_BIT) {\n          uint32_t colorIndex = m_state.om.framebufferInfo.getColorAttachmentIndex(attachmentIndex);\n\n          if (m_state.om.attachmentMask.getColorAccess(colorIndex) == DxvkAccess::None)\n            hoistInlineClear(clear, m_state.om.renderingInfo.color[colorIndex], VK_IMAGE_ASPECT_COLOR_BIT);\n        } else {\n          if (m_state.om.attachmentMask.getDepthAccess() == DxvkAccess::None)\n            hoistInlineClear(clear, m_state.om.renderingInfo.depth, VK_IMAGE_ASPECT_DEPTH_BIT);\n\n          if (m_state.om.attachmentMask.getStencilAccess() == DxvkAccess::None)\n            hoistInlineClear(clear, m_state.om.renderingInfo.stencil, VK_IMAGE_ASPECT_STENCIL_BIT);\n        }\n      }\n\n      // Ignore discards here, we can't do anything useful with\n      // those without interrupting the render pass again.\n      if (!clear.clearAspects)\n        continue;\n\n      auto& entry = attachments.emplace_back();\n      entry.aspectMask = clear.clearAspects;\n      entry.clearValue = clear.clearValue;\n\n      if (clear.clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) {\n        entry.colorAttachment = m_state.om.framebufferInfo.getColorAttachmentIndex(attachmentIndex);\n        m_state.om.attachmentMask.trackColorWrite(entry.colorAttachment);\n      }\n\n      if (clear.clearAspects & VK_IMAGE_ASPECT_DEPTH_BIT)\n        m_state.om.attachmentMask.trackDepthWrite();\n\n      if (clear.clearAspects & VK_IMAGE_ASPECT_STENCIL_BIT)\n        m_state.om.attachmentMask.trackStencilWrite();\n    }\n\n    if (!attachments.empty()) {\n      VkClearRect clearRect = { };\n      clearRect.rect = m_state.om.renderingInfo.rendering.renderArea;\n      clearRect.layerCount = m_state.om.renderingInfo.rendering.layerCount;\n\n      m_cmd->cmdClearAttachments(DxvkCmdBuffer::ExecBuffer,\n        attachments.size(), attachments.data(), 1u, &clearRect);\n\n      // Full clears require the render area to cover everything\n      m_state.om.renderAreaLo = VkOffset2D { 0, 0 };\n      m_state.om.renderAreaHi = VkOffset2D {\n        int32_t(clearRect.rect.extent.width),\n        int32_t(clearRect.rect.extent.height) };\n    }\n\n    m_deferredClears.clear();\n  }\n\n\n  void DxvkContext::flushClears(\n          bool                      useRenderPass) {\n    DxvkClearBatch clearBatch;\n\n    for (const auto& clear : m_deferredClears) {\n      int32_t attachmentIndex = -1;\n\n      if (useRenderPass && m_state.om.framebufferInfo.isFullSize(clear.imageView))\n        attachmentIndex = m_state.om.framebufferInfo.findAttachment(clear.imageView);\n\n      clearBatch.add(batchClear(clear.imageView, attachmentIndex,\n        clear.discardAspects, clear.clearAspects, clear.clearValue));\n    }\n\n    m_deferredClears.clear();\n\n    if (!clearBatch.empty())\n      performClears(clearBatch);\n  }\n\n\n  void DxvkContext::flushRenderPassDiscards() {\n    if (!m_deferredClears.empty()) {\n      for (size_t i = 0; i < m_state.om.framebufferInfo.numAttachments(); i++) {\n        auto view = m_state.om.framebufferInfo.getAttachment(i).view;\n\n        if (m_deferredResolves.at(i).imageView)\n          continue;\n\n        for (const auto& clear : m_deferredClears) {\n          if (clear.imageView->image() != view->image())\n            continue;\n\n          // Make sure that the cleared and discarded subresources are a superset\n          // of the subresources currently active in the render pass\n          auto clearSubresource = clear.imageView->subresources();\n          auto renderSubresource = view->subresources();\n\n          if (clearSubresource.aspectMask != renderSubresource.aspectMask\n          || !vk::checkSubresourceRangeSuperset(clearSubresource, renderSubresource))\n            continue;\n\n          VkImageAspectFlags aspects = clear.clearAspects | clear.discardAspects;\n          int32_t colorIndex = m_state.om.framebufferInfo.getColorAttachmentIndex(i);\n\n          if (colorIndex < 0) {\n            if ((aspects & VK_IMAGE_ASPECT_DEPTH_BIT) && m_state.om.renderingInfo.rendering.pDepthAttachment)\n              m_state.om.renderingInfo.depth.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n            if ((aspects & VK_IMAGE_ASPECT_STENCIL_BIT) && m_state.om.renderingInfo.rendering.pStencilAttachment)\n              m_state.om.renderingInfo.stencil.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n          } else if (aspects & VK_IMAGE_ASPECT_COLOR_BIT) {\n            if (uint32_t(colorIndex) < m_state.om.renderingInfo.rendering.colorAttachmentCount)\n              m_state.om.renderingInfo.color[colorIndex].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n          }\n        }\n      }\n    }\n  }\n\n\n  void DxvkContext::flushRenderPassResolves() {\n    for (size_t i = 0; i < m_state.om.framebufferInfo.numAttachments(); i++) {\n      auto& resolve = m_deferredResolves.at(i);\n\n      if (!resolve.imageView)\n        continue;\n\n      // We can only fold the resolve into the render pass if all layers are\n      // to be resolved.\n      uint32_t layerMask = (2u << (m_state.om.renderingInfo.rendering.layerCount - 1u)) - 1u;\n\n      if (resolve.layerMask != layerMask)\n        continue;\n\n      // Work out the image layout to use for the attachment based on image usage\n      auto srcImage = m_state.om.framebufferInfo.getAttachment(i).view->image();\n      auto dstImage = resolve.imageView->image();\n\n      auto dstSubresource = resolve.imageView->imageSubresources();\n      bool isDepthStencil = dstSubresource.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);\n\n      VkImageLayout newLayout = dstImage->pickLayout(isDepthStencil\n        ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL\n        : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);\n\n      bool isFullWrite = (resolve.depthMode || !(dstSubresource.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT))\n                      && (resolve.stencilMode || !(dstSubresource.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT));\n\n      // If the application may have used the destination image as shader input in\n      // any way, we need to preserve its contents throughout the render pass and\n      // allocate new backing storage for the resolve attachment itself. This is\n      // only safe to do if we are actually writing all destination subresources.\n      VkPipelineStageFlags graphicsStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT\n                                          | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT\n                                          | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT\n                                          | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT\n                                          | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n\n      bool needsNewBackingStorage = (dstImage->info().stages & graphicsStages)\n        && dstImage->isTracked(m_trackingId, DxvkAccess::Write);\n\n      // We never make MSAA passes unsynchronized, so this should be fine\n      if (needsNewBackingStorage && dstImage->hasGfxStores()) {\n        needsNewBackingStorage = resourceHasAccess(*dstImage, dstSubresource, DxvkAccess::Read, DxvkAccessOp::None)\n                              || resourceHasAccess(*dstImage, dstSubresource, DxvkAccess::Write, DxvkAccessOp::None);\n      }\n\n      // Enable tracking so that we don't unnecessarily hit slow paths in the future\n      if (dstImage->info().stages & graphicsStages)\n        dstImage->trackGfxStores();\n\n      if (needsNewBackingStorage) {\n        auto imageSubresource = dstImage->getAvailableSubresources();\n\n        if (dstSubresource != imageSubresource || !isFullWrite || !dstImage->canRelocate())\n          continue;\n\n        // Allocate and assign new backing storage. Deliberately don't go through\n        // invalidateImageWithUsage here since we know we only need a subset of\n        // state invalidations here and that method may mess with render passes.\n        VkFormat format = resolve.imageView->info().format;\n\n        DxvkImageUsageInfo usageInfo = { };\n        usageInfo.usage = isDepthStencil\n          ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT\n          : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n        usageInfo.viewFormatCount = 1;\n        usageInfo.viewFormats = &format;\n\n        auto newStorage = dstImage->allocateStorageWithUsage(usageInfo, 0u);\n        auto oldStorage = dstImage->assignStorageWithUsage(std::move(newStorage), usageInfo);\n\n        m_descriptorState.dirtyViews(dstImage->getShaderStages());\n\n        dstImage->resetTracking();\n\n        m_cmd->track(std::move(oldStorage));\n      }\n\n      // Record layout transition from default layout to attachment layout\n      VkPipelineStageFlags2 stages = isDepthStencil\n        ? VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT\n        : VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;\n\n      VkAccessFlags2 access = isDepthStencil\n        ? VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT\n        : VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;\n\n      transitionImageLayout(*dstImage, dstSubresource,\n        dstImage->info().stages, dstImage->info().access, newLayout, stages, access,\n        isFullWrite);\n\n      // Record post-resolve barrier. This is not a layout transition.\n      accessImage(DxvkCmdBuffer::ExecBuffer, *dstImage, dstSubresource,\n        newLayout, stages, access,\n        newLayout, dstImage->info().stages, dstImage->info().access,\n        DxvkAccessOp::None);\n\n      if (!isDepthStencil) {\n        uint32_t index = m_state.om.framebufferInfo.getColorAttachmentIndex(i);\n\n        auto& color = m_state.om.renderingInfo.color[index];\n        color.resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT;\n        color.resolveImageView = resolve.imageView->handle();\n        color.resolveImageLayout = newLayout;\n\n        auto& flags = m_state.om.renderingInfo.colorAttachmentFlags[index];\n        flags.flags |= resolve.flags;\n      } else {\n        if (resolve.depthMode) {\n          auto& depth = m_state.om.renderingInfo.depth;\n          depth.resolveMode = resolve.depthMode;\n          depth.resolveImageView = resolve.imageView->handle();\n          depth.resolveImageLayout = newLayout;\n        }\n\n        if (resolve.stencilMode) {\n          auto& stencil = m_state.om.renderingInfo.stencil;\n          stencil.resolveMode = resolve.stencilMode;\n          stencil.resolveImageView = resolve.imageView->handle();\n          stencil.resolveImageLayout = newLayout;\n        }\n      }\n\n      m_cmd->track(dstImage, DxvkAccess::Write);\n      m_cmd->track(srcImage, DxvkAccess::Read);\n\n      // Reset deferred resolve state so we don't do a\n      // redundant resolve after the render pass here\n      resolve = DxvkDeferredResolve();\n    }\n\n    // Transition all resolve attachments to the desired layout\n    flushImageLayoutTransitions(DxvkCmdBuffer::ExecBuffer);\n  }\n\n\n  void DxvkContext::flushResolves() {\n    for (size_t i = 0; i < m_state.om.framebufferInfo.numAttachments(); i++) {\n      auto& resolve = m_deferredResolves.at(i);\n\n      if (!resolve.imageView)\n        continue;\n\n      // Queue up normal resolves here, the render pass has already ended.\n      const auto& attachment = m_state.om.framebufferInfo.getAttachment(i);\n\n      auto srcSubresource = attachment.view->imageSubresources();\n      auto dstSubresource = resolve.imageView->imageSubresources();\n\n      while (resolve.layerMask) {\n        uint32_t layerIndex = bit::tzcnt(resolve.layerMask);\n        uint32_t layerCount = bit::tzcnt(~(resolve.layerMask >> layerIndex));\n\n        VkImageResolve region = { };\n        region.dstSubresource.aspectMask = dstSubresource.aspectMask;\n        region.dstSubresource.mipLevel = dstSubresource.baseMipLevel;\n        region.dstSubresource.baseArrayLayer = dstSubresource.baseArrayLayer + layerIndex;\n        region.dstSubresource.layerCount = layerCount;\n        region.srcSubresource.aspectMask = srcSubresource.aspectMask;\n        region.srcSubresource.mipLevel = srcSubresource.baseMipLevel;\n        region.srcSubresource.baseArrayLayer = srcSubresource.baseArrayLayer + layerIndex;\n        region.srcSubresource.layerCount = layerCount;\n        region.extent = resolve.imageView->mipLevelExtent(0u);\n\n        // We're within a render pass, any pending clears will have\n        // happened after the resolve, so ignore them here.\n        resolveImageRp(resolve.imageView->image(), attachment.view->image(),\n          region, attachment.view->info().format, resolve.depthMode, resolve.stencilMode, false);\n\n        resolve.layerMask &= ~0u << (layerIndex + layerCount);\n      }\n\n      // Reset deferred resolve state\n      resolve = DxvkDeferredResolve();\n    }\n  }\n\n\n  void DxvkContext::finalizeLoadStoreOps() {\n    auto& renderingInfo = m_state.om.renderingInfo;\n\n    // Track attachment access for render pass clears and resolves\n    bool hasClearOrResolve = false;\n\n    for (uint32_t i = 0; i < renderingInfo.rendering.colorAttachmentCount; i++) {\n      if (renderingInfo.color[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) {\n        m_state.om.attachmentMask.trackColorWrite(i);\n        hasClearOrResolve = true;\n      }\n\n      if (renderingInfo.color[i].resolveImageView && renderingInfo.color[i].resolveMode) {\n        m_state.om.attachmentMask.trackColorRead(i);\n        hasClearOrResolve = true;\n      }\n    }\n\n    if (renderingInfo.rendering.pDepthAttachment) {\n      if (renderingInfo.depth.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) {\n        m_state.om.attachmentMask.trackDepthWrite();\n        hasClearOrResolve = true;\n      }\n\n      if (renderingInfo.depth.resolveImageView && renderingInfo.depth.resolveMode) {\n        m_state.om.attachmentMask.trackDepthRead();\n        hasClearOrResolve = true;\n      }\n    }\n\n    if (renderingInfo.rendering.pStencilAttachment) {\n      if (renderingInfo.stencil.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) {\n        m_state.om.attachmentMask.trackStencilWrite();\n        hasClearOrResolve = true;\n      }\n\n      if (renderingInfo.stencil.resolveImageView && renderingInfo.stencil.resolveMode) {\n        m_state.om.attachmentMask.trackStencilRead();\n        hasClearOrResolve = true;\n      }\n    }\n\n    // If we don't have maintenance7 support, we need to pretend that accessing\n    // one of depth or stencil also accesses the other aspect in the same way\n    if (!m_device->properties().khrMaintenance7.separateDepthStencilAttachmentAccess)\n      m_state.om.attachmentMask.unifyDepthStencilAccess();\n\n    // Use attachment access info to set the final load/store ops\n    for (uint32_t i = 0; i < renderingInfo.rendering.colorAttachmentCount; i++) {\n      adjustAttachmentLoadStoreOps(renderingInfo.color[i],\n        m_state.om.attachmentMask.getColorAccess(i));\n    }\n\n    if (renderingInfo.rendering.pDepthAttachment) {\n      adjustAttachmentLoadStoreOps(renderingInfo.depth,\n        m_state.om.attachmentMask.getDepthAccess());\n    }\n\n    if (renderingInfo.rendering.pStencilAttachment) {\n      adjustAttachmentLoadStoreOps(renderingInfo.stencil,\n        m_state.om.attachmentMask.getStencilAccess());\n    }\n\n    // If we can prove that the app has only rendered to a portion of\n    // the image, adjust the render area to the exact rendered region.\n    if (!hasClearOrResolve && m_state.om.renderAreaLo.x < m_state.om.renderAreaHi.x\n                           && m_state.om.renderAreaLo.y < m_state.om.renderAreaHi.y) {\n      renderingInfo.rendering.renderArea.offset = m_state.om.renderAreaLo;\n      renderingInfo.rendering.renderArea.extent = VkExtent2D {\n        uint32_t(m_state.om.renderAreaHi.x - m_state.om.renderAreaLo.x),\n        uint32_t(m_state.om.renderAreaHi.y - m_state.om.renderAreaLo.y) };\n    }\n  }\n\n\n  void DxvkContext::adjustAttachmentLoadStoreOps(\n          VkRenderingAttachmentInfo&  attachment,\n          DxvkAccess                  access) const {\n    if (access == DxvkAccess::None) {\n      // If the attachment is not accessed at all, we can set both the\n      // load and store op to NONE if supported by the implementation.\n      attachment.loadOp = VK_ATTACHMENT_LOAD_OP_NONE;\n      attachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE;\n    } else if (access == DxvkAccess::Read) {\n      // Unlike clears, we don't treat DONT_CARE as a write. If the\n      // attachment isn't written in this pass but is read anyway,\n      // demote the store op to DONT_CARE as well.\n      if (attachment.loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE)\n        attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n      else\n        attachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE;\n    }\n  }\n\n\n  void DxvkContext::updateBuffer(\n    const Rc<DxvkBuffer>&           buffer,\n          VkDeviceSize              offset,\n          VkDeviceSize              size,\n    const void*                     data) {\n    DxvkResourceAccess access(*buffer, offset, size,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n\n    DxvkCmdBuffer cmdBuffer = prepareOutOfOrderTransfer(DxvkCmdBuffer::InitBuffer, 1u, &access);\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer)\n      endCurrentPass(true);\n\n    syncResources(cmdBuffer, 1u, &access);\n\n    auto bufferSlice = buffer->getSliceInfo(offset, size);\n\n    m_cmd->cmdUpdateBuffer(cmdBuffer,\n      bufferSlice.buffer,\n      bufferSlice.offset,\n      bufferSlice.size,\n      data);\n  }\n  \n  \n  void DxvkContext::uploadBuffer(\n    const Rc<DxvkBuffer>&           buffer,\n    const Rc<DxvkBuffer>&           source,\n          VkDeviceSize              sourceOffset) {\n    auto bufferSlice = buffer->getSliceInfo();\n    auto sourceSlice = source->getSliceInfo(sourceOffset, buffer->info().size);\n\n    VkBufferCopy2 copyRegion = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 };\n    copyRegion.srcOffset = sourceSlice.offset;\n    copyRegion.dstOffset = bufferSlice.offset;\n    copyRegion.size      = bufferSlice.size;\n\n    VkCopyBufferInfo2 copyInfo = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 };\n    copyInfo.srcBuffer = sourceSlice.buffer;\n    copyInfo.dstBuffer = bufferSlice.buffer;\n    copyInfo.regionCount = 1;\n    copyInfo.pRegions = &copyRegion;\n\n    m_cmd->cmdCopyBuffer(DxvkCmdBuffer::SdmaBuffer, &copyInfo);\n\n    accessBufferTransfer(*buffer, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n\n    m_cmd->track(source, DxvkAccess::Read);\n    m_cmd->track(buffer, DxvkAccess::Write);\n  }\n\n\n  void DxvkContext::uploadImage(\n    const Rc<DxvkImage>&            image,\n    const Rc<DxvkBuffer>&           source,\n          VkDeviceSize              sourceOffset,\n          VkDeviceSize              subresourceAlignment,\n          VkFormat                  format) {\n    // Always use framebuffer path for depth-stencil images since we know\n    // they are writeable and can't use Vulkan transfer queues. Stencil\n    // data is interleaved and needs to be decoded manually anyway.\n    bool useFb = !formatsAreBufferCopyCompatible(image->info().format, format)\n              || image->info().sampleCount != VK_SAMPLE_COUNT_1_BIT;\n\n    if (useFb)\n      uploadImageFb(image, source, sourceOffset, subresourceAlignment, format);\n    else\n      uploadImageHw(image, source, sourceOffset, subresourceAlignment);\n  }\n\n\n  void DxvkContext::setViewports(\n          uint32_t            viewportCount,\n    const DxvkViewport*       viewports) {\n    for (uint32_t i = 0; i < viewportCount; i++) {\n      m_state.vp.viewports[i] = viewports[i].viewport;\n      m_state.vp.scissorRects[i] = viewports[i].scissor;\n\n      // Vulkan viewports are not allowed to have a width or\n      // height of zero, so we fall back to a dummy viewport\n      // and instead set an empty scissor rect, which is legal.\n      if (viewports[i].viewport.width <= 0.0f || viewports[i].viewport.height == 0.0f) {\n        m_state.vp.viewports[i] = VkViewport {\n          0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f };\n        m_state.vp.scissorRects[i] = VkRect2D {\n          VkOffset2D { 0, 0 },\n          VkExtent2D { 0, 0 } };\n      }\n    }\n\n    m_state.vp.viewportCount = viewportCount;\n    m_flags.set(DxvkContextFlag::GpDirtyViewport);\n  }\n  \n  \n  void DxvkContext::setBlendConstants(\n          DxvkBlendConstants  blendConstants) {\n    if (m_state.dyn.blendConstants != blendConstants) {\n      m_state.dyn.blendConstants = blendConstants;\n      m_flags.set(DxvkContextFlag::GpDirtyBlendConstants);\n    }\n  }\n  \n  \n  void DxvkContext::setDepthBias(\n          DxvkDepthBias       depthBias) {\n    if (m_state.dyn.depthBias != depthBias) {\n      m_state.dyn.depthBias = depthBias;\n      m_flags.set(DxvkContextFlag::GpDirtyDepthBias);\n    }\n  }\n\n\n  void DxvkContext::setDepthBiasRepresentation(\n          DxvkDepthBiasRepresentation  depthBiasRepresentation) {\n    if (m_state.dyn.depthBiasRepresentation != depthBiasRepresentation) {\n      m_state.dyn.depthBiasRepresentation = depthBiasRepresentation;\n      m_flags.set(DxvkContextFlag::GpDirtyDepthBias);\n    }\n  }\n\n\n  void DxvkContext::setDepthBounds(\n          DxvkDepthBounds     depthBounds) {\n    if (m_state.dyn.depthBounds != depthBounds) {\n      m_state.dyn.depthBounds = depthBounds;\n      m_flags.set(DxvkContextFlag::GpDirtyDepthBounds);\n    }\n  }\n  \n  \n  void DxvkContext::setStencilReference(\n          uint32_t            reference) {\n    if (m_state.dyn.stencilReference != reference) {\n      m_state.dyn.stencilReference = reference;\n      m_flags.set(DxvkContextFlag::GpDirtyStencilRef);\n    }\n  }\n  \n  \n  void DxvkContext::setInputAssemblyState(const DxvkInputAssemblyState& ia) {\n    m_state.gp.state.ia = DxvkIaInfo(\n      ia.primitiveTopology(),\n      ia.primitiveRestart(),\n      ia.patchVertexCount());\n    \n    m_flags.set(DxvkContextFlag::GpDirtyPipelineState);\n  }\n  \n  \n  void DxvkContext::setInputLayout(\n          uint32_t             attributeCount,\n    const DxvkVertexInput*     attributes,\n          uint32_t             bindingCount,\n    const DxvkVertexInput*     bindings) {\n    m_flags.set(\n      DxvkContextFlag::GpDirtyPipelineState,\n      DxvkContextFlag::GpDirtyVertexBuffers);\n\n    for (uint32_t i = 0; i < bindingCount; i++) {\n      auto binding = bindings[i].binding();\n\n      m_state.gp.state.ilBindings[i] = DxvkIlBinding(\n        binding.binding, 0,\n        binding.inputRate,\n        binding.divisor);\n      m_state.vi.vertexExtents[i] = binding.extent;\n    }\n\n    for (uint32_t i = bindingCount; i < m_state.gp.state.il.bindingCount(); i++) {\n      m_state.gp.state.ilBindings[i] = DxvkIlBinding();\n      m_state.vi.vertexExtents[i] = 0;\n    }\n\n    for (uint32_t i = 0; i < attributeCount; i++) {\n      auto attribute = attributes[i].attribute();\n\n      m_state.gp.state.ilAttributes[i] = DxvkIlAttribute(\n        attribute.location,\n        attribute.binding,\n        attribute.format,\n        attribute.offset);\n    }\n\n    for (uint32_t i = attributeCount; i < m_state.gp.state.il.attributeCount(); i++)\n      m_state.gp.state.ilAttributes[i] = DxvkIlAttribute();\n\n    m_state.gp.state.il = DxvkIlInfo(attributeCount, bindingCount);\n  }\n\n\n  void DxvkContext::setRasterizerState(const DxvkRasterizerState& rs) {\n    VkCullModeFlags cullMode = rs.cullMode();\n    VkFrontFace frontFace = rs.frontFace();\n\n    if (m_state.dyn.cullMode != cullMode || m_state.dyn.frontFace != frontFace) {\n      m_state.dyn.cullMode = cullMode;\n      m_state.dyn.frontFace = frontFace;\n\n      m_flags.set(DxvkContextFlag::GpDirtyRasterizerState);\n    }\n\n    if (unlikely(rs.sampleCount() != m_state.gp.state.rs.sampleCount())) {\n      m_flags.set(DxvkContextFlag::GpDirtySampleLocations);\n\n      if (!m_state.gp.state.ms.sampleCount())\n        m_flags.set(DxvkContextFlag::GpDirtyMultisampleState);\n\n      if (!m_features.test(DxvkContextFeature::VariableMultisampleRate))\n        m_flags.set(DxvkContextFlag::GpRenderPassNeedsFlush);\n    }\n\n    DxvkRsInfo rsInfo(\n      rs.depthClip(),\n      rs.polygonMode(),\n      rs.sampleCount(),\n      rs.conservativeMode(),\n      rs.flatShading(),\n      rs.lineMode());\n\n    if (!m_state.gp.state.rs.eq(rsInfo)) {\n      m_flags.set(DxvkContextFlag::GpDirtyPipelineState,\n                  DxvkContextFlag::GpDirtyDepthClip);\n      m_state.gp.state.rs = rsInfo;\n    }\n  }\n  \n  \n  void DxvkContext::setMultisampleState(const DxvkMultisampleState& ms) {\n    m_state.gp.state.ms = DxvkMsInfo(\n      m_state.gp.state.ms.sampleCount(),\n      ms.sampleMask(),\n      ms.alphaToCoverage());\n\n    m_flags.set(\n      DxvkContextFlag::GpDirtyPipelineState,\n      DxvkContextFlag::GpDirtyMultisampleState);\n  }\n  \n  \n  void DxvkContext::setDepthStencilState(const DxvkDepthStencilState& ds) {\n    if (m_state.dyn.depthStencilState.depthTest() != ds.depthTest()\n     || m_state.dyn.depthStencilState.depthWrite() != ds.depthWrite()\n     || m_state.dyn.depthStencilState.depthCompareOp() != ds.depthCompareOp())\n      m_flags.set(DxvkContextFlag::GpDirtyDepthTest);\n\n    if (m_state.dyn.depthStencilState.stencilTest() != ds.stencilTest()\n     || !m_state.dyn.depthStencilState.stencilOpFront().eq(ds.stencilOpFront())\n     || !m_state.dyn.depthStencilState.stencilOpBack().eq(ds.stencilOpBack()))\n      m_flags.set(DxvkContextFlag::GpDirtyStencilTest);\n\n    m_state.dyn.depthStencilState = ds;\n  }\n  \n  \n  void DxvkContext::setLogicOpState(const DxvkLogicOpState& lo) {\n    m_state.gp.state.om = DxvkOmInfo(\n      lo.logicOpEnable(),\n      lo.logicOp(),\n      m_state.gp.state.om.feedbackLoop());\n    \n    m_flags.set(DxvkContextFlag::GpDirtyPipelineState);\n  }\n  \n  \n  void DxvkContext::setBlendMode(\n          uint32_t            attachment,\n    const DxvkBlendMode&      blendMode) {\n    m_state.gp.state.omBlend[attachment] = DxvkOmAttachmentBlend(\n      blendMode.blendEnable(),\n      blendMode.colorSrcFactor(),\n      blendMode.colorDstFactor(),\n      blendMode.colorBlendOp(),\n      blendMode.alphaSrcFactor(),\n      blendMode.alphaDstFactor(),\n      blendMode.alphaBlendOp(),\n      blendMode.writeMask());\n\n    m_flags.set(DxvkContextFlag::GpDirtyPipelineState);\n  }\n\n\n  void DxvkContext::setBarrierControl(DxvkBarrierControlFlags control) {\n    // If any currently relevant control flags change, play it safe and force\n    // a barrier the next time we encounter a write-after-write hazard, even\n    // if the same set of flags is restored by that time. Only check graphics\n    // flags inside a render pass to avoid performance regressions when an\n    // application uses this feature but we already have an app profile.\n    // Barriers get flushed when beginning or ending a render pass anyway.\n    DxvkBarrierControlFlags mask = m_flags.test(DxvkContextFlag::GpRenderPassActive)\n      ? DxvkBarrierControlFlags(DxvkBarrierControl::GraphicsAllowReadWriteOverlap)\n      : DxvkBarrierControlFlags(DxvkBarrierControl::ComputeAllowReadWriteOverlap,\n                                DxvkBarrierControl::ComputeAllowWriteOnlyOverlap);\n\n    if (!((m_barrierControl ^ control) & mask).isClear()) {\n      m_flags.set(DxvkContextFlag::ForceWriteAfterWriteSync);\n\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n        popDebugRegion(util::DxvkDebugLabelType::InternalBarrierControl);\n    }\n\n    m_barrierControl = control;\n  }\n\n\n  void DxvkContext::updatePageTable(\n    const DxvkSparseBindInfo&   bindInfo,\n          DxvkSparseBindFlags   flags) {\n    // Split command buffers here so that we execute\n    // the sparse binding operation at the right time\n    if (!flags.test(DxvkSparseBindFlag::SkipSynchronization))\n      this->splitCommands();\n\n    DxvkSparsePageAllocator* srcAllocator = bindInfo.srcAllocator.ptr();\n    DxvkSparsePageTable* dstPageTable = bindInfo.dstResource->getSparsePageTable();\n    DxvkSparsePageTable* srcPageTable = nullptr;\n\n    if (bindInfo.srcResource != nullptr)\n      srcPageTable = bindInfo.srcResource->getSparsePageTable();\n\n    // In order to support copies properly, we need to buffer the new\n    // mappings first before we apply them to the destination resource.\n    size_t bindCount = bindInfo.binds.size();\n    std::vector<DxvkSparseMapping> mappings(bindCount);\n\n    for (size_t i = 0; i < bindCount; i++) {\n      DxvkSparseBind bind = bindInfo.binds[i];\n\n      switch (bind.mode) {\n        case DxvkSparseBindMode::Null:\n          // The mapping array is already default-initialized\n          // so we don't actually need to do anything here\n          break;\n\n        case DxvkSparseBindMode::Bind:\n          mappings[i] = srcAllocator->acquirePage(bind.srcPage);\n          break;\n\n        case DxvkSparseBindMode::Copy:\n          mappings[i] = srcPageTable->getMapping(bind.srcPage);\n          break;\n      }\n    }\n\n    // Process the actual page table updates here and resolve\n    // our internal structures to Vulkan resource and memory\n    // handles. The rest will be done at submission time.\n    for (size_t i = 0; i < bindCount; i++) {\n      DxvkSparseBind bind = bindInfo.binds[i];\n      DxvkSparseMapping mapping = std::move(mappings[i]);\n\n      DxvkSparsePageInfo pageInfo = dstPageTable->getPageInfo(bind.dstPage);\n\n      switch (pageInfo.type) {\n        case DxvkSparsePageType::None:\n          break;\n\n        case DxvkSparsePageType::Buffer: {\n          DxvkSparseBufferBindKey key;\n          key.buffer = dstPageTable->getBufferHandle();\n          key.offset = pageInfo.buffer.offset;\n          key.size   = pageInfo.buffer.length;\n\n          m_cmd->bindBufferMemory(key, mapping.getMemoryInfo());\n        } break;\n\n        case DxvkSparsePageType::Image: {\n          DxvkSparseImageBindKey key;\n          key.image = dstPageTable->getImageHandle();\n          key.subresource = pageInfo.image.subresource;\n          key.offset = pageInfo.image.offset;\n          key.extent = pageInfo.image.extent;\n\n          m_cmd->bindImageMemory(key, mapping.getMemoryInfo());\n        } break;\n\n        case DxvkSparsePageType::ImageMipTail: {\n          DxvkSparseImageOpaqueBindKey key;\n          key.image  = dstPageTable->getImageHandle();\n          key.offset = pageInfo.mipTail.resourceOffset;\n          key.size   = pageInfo.mipTail.resourceLength;\n          key.flags  = 0;\n\n          m_cmd->bindImageOpaqueMemory(key, mapping.getMemoryInfo());\n        } break;\n      }\n\n      // Update the page table mapping for tracking purposes\n      if (pageInfo.type != DxvkSparsePageType::None)\n        dstPageTable->updateMapping(m_cmd.ptr(), bind.dstPage, std::move(mapping));\n    }\n\n    m_cmd->track(bindInfo.dstResource, DxvkAccess::Write);\n  }\n\n\n  void DxvkContext::signalGpuEvent(const Rc<DxvkEvent>& event) {\n    this->endCurrentPass(true);\n    \n    Rc<DxvkGpuEvent> gpuEvent = m_common->eventPool().allocEvent();\n    event->assignGpuEvent(gpuEvent);\n\n    // Supported client APIs can't access device memory in a defined manner\n    // without triggering a queue submission first, so we really only need\n    // to wait for prior commands, especially queries, to complete.\n    VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;\n\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.memoryBarrierCount = 1;\n    depInfo.pMemoryBarriers = &barrier;\n\n    m_cmd->cmdSetEvent(gpuEvent->handle(), &depInfo);\n    m_cmd->track(std::move(gpuEvent));\n  }\n  \n\n  void DxvkContext::launchCuKernelNVX(\n    const VkCuLaunchInfoNVX& nvxLaunchInfo,\n    const std::vector<std::pair<Rc<DxvkBuffer>, DxvkAccessFlags>>& buffers,\n    const std::vector<std::pair<Rc<DxvkImage>,  DxvkAccessFlags>>& images) {\n    // The resources in the std::vectors above are called-out\n    // explicitly in the API for barrier and tracking purposes\n    // since they're being used bindlessly.\n    this->endCurrentPass(true);\n\n    std::vector<DxvkResourceAccess> accessBatch;\n\n    for (auto& r : buffers) {\n      accessBatch.emplace_back(*r.first, 0u, r.first->info().size,\n        VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT);\n    }\n\n    for (auto& r : images) {\n      accessBatch.emplace_back(*r.first, r.first->getAvailableSubresources(), r.first->info().layout,\n        VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, false);\n    }\n\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    if (m_features.test(DxvkContextFeature::DescriptorHeap)) {\n      // HACK: We want to use the driver-managed descriptor heap here, but\n      // there is no spec-legal way to bind it, not even by starting a new\n      // command buffer. For now, work around the problem by calling a\n      // legacy descriptor set function.\n      m_cmd->cmdBindDescriptorSets(DxvkCmdBuffer::ExecBuffer,\n        VK_PIPELINE_BIND_POINT_COMPUTE, VK_NULL_HANDLE, 0, 0, nullptr);\n      m_cmd->invalidateDescriptorHeapBinding();\n    }\n\n    m_cmd->cmdLaunchCuKernel(nvxLaunchInfo);\n  }\n  \n  \n  void DxvkContext::writeTimestamp(const Rc<DxvkQuery>& query) {\n    m_queryManager.writeTimestamp(m_cmd, query);\n  }\n\n\n  void DxvkContext::signal(const Rc<sync::Signal>& signal, uint64_t value) {\n    m_cmd->queueSignal(signal, value);\n  }\n\n\n  void DxvkContext::waitFence(const Rc<DxvkFence>& fence, uint64_t value) {\n    m_cmd->waitFence(fence, value);\n  }\n\n\n  void DxvkContext::signalFence(const Rc<DxvkFence>& fence, uint64_t value) {\n    m_cmd->signalFence(fence, value);\n  }\n\n\n  bool DxvkContext::needsDrawBarriers() {\n    return m_state.gp.flags.test(DxvkGraphicsPipelineFlag::UnrollMergedDraws)\n      && !m_barrierControl.test(DxvkBarrierControl::GraphicsAllowReadWriteOverlap);\n  }\n\n\n  void DxvkContext::beginRenderPassDebugRegion() {\n    VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;\n\n    bool hasFeedbackLoop = false;\n    bool hasColorAttachments = false;\n    bool hasDepthAttachment = m_state.om.renderTargets.depth.view != nullptr;\n\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++)\n      hasColorAttachments |= m_state.om.renderTargets.color[i].view != nullptr;\n\n    std::stringstream label;\n\n    if (hasColorAttachments == hasDepthAttachment)\n      label << \"Render\";\n    else if (hasColorAttachments)\n      label << \"Color\";\n    else if (hasDepthAttachment)\n      label << \"Depth\";\n\n    label << \" pass \" << uint32_t(++m_renderPassIndex) << \" (\";\n\n    hasColorAttachments = false;\n\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      if (m_state.om.renderTargets.color[i].view) {\n        const char* imageName = m_state.om.renderTargets.color[i].view->image()->info().debugName;\n        label << (hasColorAttachments ? \", \" : \"\") << i << \": \" << (imageName ? imageName : \"unknown\");\n\n        hasColorAttachments = true;\n        sampleCount = m_state.om.renderTargets.color[i].view->image()->info().sampleCount;\n\n        hasFeedbackLoop = hasFeedbackLoop ||\n          (m_state.om.renderTargets.color[i].view->image()->info().usage & VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT);\n      }\n    }\n\n    if (m_state.om.renderTargets.depth.view) {\n      if (hasColorAttachments)\n        label << \", \";\n\n      const char* imageName = m_state.om.renderTargets.depth.view->image()->info().debugName;\n      label << \"DS:\" << (imageName ? imageName : \"unknown\");\n\n      sampleCount = m_state.om.renderTargets.depth.view->image()->info().sampleCount;\n\n      hasFeedbackLoop = hasFeedbackLoop ||\n        (m_state.om.renderTargets.depth.view->image()->info().usage & VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT);\n    }\n\n    if (!hasColorAttachments && !hasDepthAttachment)\n      label << \"No attachments\";\n\n    if (sampleCount > VK_SAMPLE_COUNT_1_BIT)\n      label << \", \" << uint32_t(sampleCount) << \"x MSAA\";\n\n    label << \")\";\n\n    uint32_t color = sampleCount > VK_SAMPLE_COUNT_1_BIT ? 0xf0dcf0 : 0xf0e6dc;\n\n    if (hasFeedbackLoop)\n      color = 0xdceff0;\n\n    pushDebugRegion(vk::makeLabel(color, label.str().c_str()),\n      util::DxvkDebugLabelType::InternalRenderPass);\n  }\n\n\n  template<VkPipelineBindPoint BindPoint>\n  void DxvkContext::beginBarrierControlDebugRegion() {\n    if (hasDebugRegion(util::DxvkDebugLabelType::InternalBarrierControl))\n      return;\n\n    const char* label = nullptr;\n\n    if (BindPoint == VK_PIPELINE_BIND_POINT_COMPUTE) {\n      if (m_barrierControl.test(DxvkBarrierControl::ComputeAllowReadWriteOverlap))\n        label = \"Relaxed sync\";\n      else if (m_barrierControl.test(DxvkBarrierControl::ComputeAllowWriteOnlyOverlap))\n        label = \"Relaxed sync (write-only)\";\n    } else {\n      if (m_barrierControl.test(DxvkBarrierControl::GraphicsAllowReadWriteOverlap))\n        label = \"Relaxed sync\";\n    }\n\n    if (label) {\n      pushDebugRegion(vk::makeLabel(0x9bded9, label),\n        util::DxvkDebugLabelType::InternalBarrierControl);\n    }\n  }\n\n\n  void DxvkContext::beginDebugLabel(const VkDebugUtilsLabelEXT& label) {\n    if (m_features.test(DxvkContextFeature::DebugUtils))\n      pushDebugRegion(label, util::DxvkDebugLabelType::External);\n  }\n\n\n  void DxvkContext::endDebugLabel() {\n    if (m_features.test(DxvkContextFeature::DebugUtils))\n      popDebugRegion(util::DxvkDebugLabelType::External);\n  }\n\n\n  void DxvkContext::insertDebugLabel(const VkDebugUtilsLabelEXT& label) {\n    if (m_features.test(DxvkContextFeature::DebugUtils))\n      m_cmd->cmdInsertDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer, label);\n  }\n\n\n  void DxvkContext::setDebugName(const Rc<DxvkPagedResource>& resource, const char* name) {\n    if (m_features.test(DxvkContextFeature::DebugUtils))\n      resource->setDebugName(name);\n  }\n  \n  \n  void DxvkContext::blitImageFb(\n          Rc<DxvkImageView>     dstView,\n    const VkOffset3D*           dstOffsets,\n          Rc<DxvkImageView>     srcView,\n    const VkOffset3D*           srcOffsets,\n          VkFilter              filter) {\n    this->invalidateState();\n\n    bool dstIsDepthStencil = dstView->info().aspects & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);\n\n    dstView = ensureImageViewCompatibility(dstView, dstIsDepthStencil\n      ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT\n      : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);\n    srcView = ensureImageViewCompatibility(srcView, VK_IMAGE_USAGE_SAMPLED_BIT);\n\n    if (!dstView || !srcView) {\n      Logger::err(str::format(\"DxvkContext: blitImageFb: Resources not supported\"));\n      return;\n    }\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      const char* dstName = dstView->image()->info().debugName;\n      const char* srcName = srcView->image()->info().debugName;\n\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xf0dcdc, str::format(\"Blit (\",\n          dstName ? dstName : \"unknown\", \", \",\n          srcName ? srcName : \"unknown\", \")\").c_str()));\n    }\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*dstView, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,\n      VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, false);\n    accessBatch.emplace_back(*srcView, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,\n      VK_ACCESS_2_SHADER_READ_BIT, false);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    // Sort out image offsets so that dstOffset[0] points\n    // to the top-left corner of the target area\n    std::array<VkOffset3D, 2> srcOffsetsAdjusted = { srcOffsets[0], srcOffsets[1] };\n    std::array<VkOffset3D, 2> dstOffsetsAdjusted = { dstOffsets[0], dstOffsets[1] };\n\n    if (dstOffsetsAdjusted[0].x > dstOffsetsAdjusted[1].x) {\n      std::swap(dstOffsetsAdjusted[0].x, dstOffsetsAdjusted[1].x);\n      std::swap(srcOffsetsAdjusted[0].x, srcOffsetsAdjusted[1].x);\n    }\n\n    if (dstOffsetsAdjusted[0].y > dstOffsetsAdjusted[1].y) {\n      std::swap(dstOffsetsAdjusted[0].y, dstOffsetsAdjusted[1].y);\n      std::swap(srcOffsetsAdjusted[0].y, srcOffsetsAdjusted[1].y);\n    }\n\n    if (dstOffsetsAdjusted[0].z > dstOffsetsAdjusted[1].z) {\n      std::swap(dstOffsetsAdjusted[0].z, dstOffsetsAdjusted[1].z);\n      std::swap(srcOffsetsAdjusted[0].z, srcOffsetsAdjusted[1].z);\n    }\n    \n    VkExtent3D dstExtent = {\n      uint32_t(dstOffsetsAdjusted[1].x - dstOffsetsAdjusted[0].x),\n      uint32_t(dstOffsetsAdjusted[1].y - dstOffsetsAdjusted[0].y),\n      uint32_t(dstOffsetsAdjusted[1].z - dstOffsetsAdjusted[0].z) };\n\n    // Determine resolve mode for when the source is multisampled. If\n    // there is no stretching going on, do a regular resolve.\n    auto resolveMode = filter == VK_FILTER_NEAREST\n      ? DxvkMetaBlitResolveMode::FilterNearest\n      : DxvkMetaBlitResolveMode::FilterLinear;\n\n    if (srcView->image()->info().sampleCount != VK_SAMPLE_COUNT_1_BIT) {\n      bool isSameExtent = (std::abs(dstOffsets[1].x - dstOffsets[0].x) == std::abs(srcOffsets[1].x - srcOffsets[0].x))\n                       && (std::abs(dstOffsets[1].y - dstOffsets[0].y) == std::abs(srcOffsets[1].y - srcOffsets[0].y))\n                       && (std::abs(dstOffsets[1].z - dstOffsets[0].z) == std::abs(srcOffsets[1].z - srcOffsets[0].z));\n\n      if (isSameExtent)\n        resolveMode = DxvkMetaBlitResolveMode::ResolveAverage;\n    }\n\n    DxvkMetaBlitPipeline pipeInfo = m_common->metaBlit().getPipeline(\n      dstView->info().viewType, dstView->info().format,\n      srcView->image()->info().sampleCount,\n      dstView->image()->info().sampleCount, resolveMode);\n\n    VkViewport viewport = { };\n    viewport.x = float(dstOffsetsAdjusted[0].x);\n    viewport.y = float(dstOffsetsAdjusted[0].y);\n    viewport.width = float(dstExtent.width);\n    viewport.height = float(dstExtent.height);\n    viewport.minDepth = 0.0f;\n    viewport.maxDepth = 1.0f;\n\n    VkRect2D scissor = { };\n    scissor.offset = { dstOffsetsAdjusted[0].x, dstOffsetsAdjusted[0].y  };\n    scissor.extent = { dstExtent.width, dstExtent.height };\n\n    // Begin render pass\n    VkRenderingAttachmentInfo attachmentInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n    attachmentInfo.imageView = dstView->handle();\n    attachmentInfo.imageLayout = dstView->getLayout();\n    attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\n    VkRenderingInfo renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n    renderingInfo.renderArea = scissor;\n    renderingInfo.layerCount = dstView->info().layerCount;\n    renderingInfo.colorAttachmentCount = 1;\n    renderingInfo.pColorAttachments = &attachmentInfo;\n\n    m_cmd->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderingInfo);\n    m_cmd->cmdSetViewport(1, &viewport);\n    m_cmd->cmdSetScissor(1, &scissor);\n\n    // Set up source image view\n    Rc<DxvkSampler> sampler = createBlitSampler(filter);\n\n    DxvkDescriptorWrite imageDescriptor = { };\n    imageDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n    imageDescriptor.descriptor = srcView->getDescriptor();\n    \n    // Compute shader parameters for the operation\n    VkExtent3D srcExtent = srcView->mipLevelExtent(0);\n\n    DxvkMetaBlitPushConstants pushConstants = { };\n    pushConstants.layerCount = dstView->info().layerCount;\n    pushConstants.sampler = sampler->getDescriptor().samplerIndex;\n    if (srcView->image()->info().sampleCount == VK_SAMPLE_COUNT_1_BIT) {\n      pushConstants.srcCoord0 = {\n        float(srcOffsetsAdjusted[0].x) / float(srcExtent.width),\n        float(srcOffsetsAdjusted[0].y) / float(srcExtent.height),\n        float(srcOffsetsAdjusted[0].z) / float(srcExtent.depth) };\n      pushConstants.srcCoord1 = {\n        float(srcOffsetsAdjusted[1].x) / float(srcExtent.width),\n        float(srcOffsetsAdjusted[1].y) / float(srcExtent.height),\n        float(srcOffsetsAdjusted[1].z) / float(srcExtent.depth) };\n    } else {\n      // Src coords are in texels rather than 0.0 - 1.0\n      pushConstants.srcCoord0 = {\n        float(srcOffsetsAdjusted[0].x),\n        float(srcOffsetsAdjusted[0].y),\n        float(srcOffsetsAdjusted[0].z) };\n      pushConstants.srcCoord1 = {\n        float(srcOffsetsAdjusted[1].x),\n        float(srcOffsetsAdjusted[1].y),\n        float(srcOffsetsAdjusted[1].z) };\n    }\n\n    m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, pipeInfo.pipeline);\n\n    m_cmd->bindResources(DxvkCmdBuffer::ExecBuffer,\n      pipeInfo.layout, 1u, &imageDescriptor,\n      sizeof(pushConstants), &pushConstants);\n\n    m_cmd->cmdDraw(3, pushConstants.layerCount, 0, 0);\n    m_cmd->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n\n    m_cmd->track(std::move(sampler));\n  }\n\n\n  void DxvkContext::blitImageHw(\n    const Rc<DxvkImageView>&    dstView,\n    const VkOffset3D*           dstOffsets,\n    const Rc<DxvkImageView>&    srcView,\n    const VkOffset3D*           srcOffsets,\n          VkFilter              filter) {\n    // Prepare the two images for transfer ops if necessary\n    auto dstLayout = dstView->image()->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n    auto srcLayout = srcView->image()->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*dstView->image(), dstView->imageSubresources(),\n      dstLayout, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT, false);\n    accessBatch.emplace_back(*srcView->image(), srcView->imageSubresources(),\n      srcLayout, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT, false);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    // Perform the blit operation\n    VkImageBlit2 blitRegion = { VK_STRUCTURE_TYPE_IMAGE_BLIT_2 };\n    blitRegion.srcSubresource = vk::pickSubresourceLayers(srcView->imageSubresources(), 0);\n    blitRegion.dstSubresource = vk::pickSubresourceLayers(dstView->imageSubresources(), 0);\n\n    for (uint32_t i = 0; i < 2; i++) {\n      blitRegion.srcOffsets[i] = srcOffsets[i];\n      blitRegion.dstOffsets[i] = dstOffsets[i];\n    }\n\n    VkBlitImageInfo2 blitInfo = { VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2 };\n    blitInfo.srcImage = srcView->image()->handle();\n    blitInfo.srcImageLayout = srcLayout;\n    blitInfo.dstImage = dstView->image()->handle();\n    blitInfo.dstImageLayout = dstLayout;\n    blitInfo.regionCount = 1;\n    blitInfo.pRegions = &blitRegion;\n    blitInfo.filter = filter;\n\n    m_cmd->cmdBlitImage(&blitInfo);\n  }\n\n\n  template<bool ToImage>\n  void DxvkContext::copyImageBufferData(\n          DxvkCmdBuffer         cmd,\n    const Rc<DxvkImage>&        image,\n    const VkImageSubresourceLayers& imageSubresource,\n          VkOffset3D            imageOffset,\n          VkExtent3D            imageExtent,\n          VkImageLayout         imageLayout,\n    const DxvkResourceBufferInfo& bufferSlice,\n          VkDeviceSize          bufferRowAlignment,\n          VkDeviceSize          bufferSliceAlignment) {\n    auto formatInfo = image->formatInfo();\n    auto layers = imageSubresource.layerCount;\n\n    VkDeviceSize bufferOffset = bufferSlice.offset;\n\n    // Do one copy region per layer in case the buffer memory layout is weird\n    if (bufferSliceAlignment || formatInfo->flags.test(DxvkFormatFlag::MultiPlane))\n      layers = 1;\n\n    for (uint32_t i = 0; i < imageSubresource.layerCount; i += layers) {\n      auto aspectOffset = bufferOffset;\n\n      for (auto aspects = imageSubresource.aspectMask; aspects; ) {\n        auto aspect = vk::getNextAspect(aspects);\n        auto elementSize = formatInfo->elementSize;\n\n        VkBufferImageCopy2 copyRegion = { VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 };\n        copyRegion.imageSubresource.aspectMask = aspect;\n        copyRegion.imageSubresource.baseArrayLayer = imageSubresource.baseArrayLayer + i;\n        copyRegion.imageSubresource.layerCount = layers;\n        copyRegion.imageSubresource.mipLevel = imageSubresource.mipLevel;\n        copyRegion.imageOffset = imageOffset;\n        copyRegion.imageExtent = imageExtent;\n\n        if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n          auto plane = &formatInfo->planes[vk::getPlaneIndex(aspect)];\n          copyRegion.imageOffset.x /= plane->blockSize.width;\n          copyRegion.imageOffset.y /= plane->blockSize.height;\n          copyRegion.imageExtent.width  /= plane->blockSize.width;\n          copyRegion.imageExtent.height /= plane->blockSize.height;\n          elementSize = plane->elementSize;\n        }\n\n        // Fix up some edge cases with unaligned mips of block-compressed images\n        VkExtent3D maxExtent = image->mipLevelExtent(imageSubresource.mipLevel, aspect);\n\n        copyRegion.imageExtent = VkExtent3D {\n          std::min(copyRegion.imageExtent.width,  maxExtent.width  - copyRegion.imageOffset.x),\n          std::min(copyRegion.imageExtent.height, maxExtent.height - copyRegion.imageOffset.y),\n          std::min(copyRegion.imageExtent.depth,  maxExtent.depth  - copyRegion.imageOffset.z) };\n\n        // Vulkan can't really express row pitch in the same way that client APIs\n        // may expect, so we'll need to do some heroics here and hope that it works\n        VkExtent3D blockCount = util::computeBlockCount(copyRegion.imageExtent, formatInfo->blockSize);\n        VkDeviceSize rowPitch = blockCount.width * elementSize;\n\n        if (bufferRowAlignment > elementSize)\n          rowPitch = bufferRowAlignment >= rowPitch ? bufferRowAlignment : align(rowPitch, bufferRowAlignment);\n\n        VkDeviceSize slicePitch = blockCount.height * rowPitch;\n\n        if (image->info().type == VK_IMAGE_TYPE_3D && bufferSliceAlignment > elementSize)\n          slicePitch = bufferSliceAlignment >= slicePitch ? bufferSliceAlignment : align(slicePitch, bufferSliceAlignment);\n\n        copyRegion.bufferOffset      = aspectOffset;\n        copyRegion.bufferRowLength   = formatInfo->blockSize.width * rowPitch / elementSize;\n        copyRegion.bufferImageHeight = formatInfo->blockSize.height * slicePitch / rowPitch;\n\n        // Perform the actual copy\n        if constexpr (ToImage) {\n          VkCopyBufferToImageInfo2 copyInfo = { VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 };\n          copyInfo.srcBuffer = bufferSlice.buffer;\n          copyInfo.dstImage = image->handle();\n          copyInfo.dstImageLayout = imageLayout;\n          copyInfo.regionCount = 1;\n          copyInfo.pRegions = &copyRegion;\n\n          m_cmd->cmdCopyBufferToImage(cmd, &copyInfo);\n        } else {\n          VkCopyImageToBufferInfo2 copyInfo = { VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2 };\n          copyInfo.srcImage = image->handle();\n          copyInfo.srcImageLayout = imageLayout;\n          copyInfo.dstBuffer = bufferSlice.buffer;\n          copyInfo.regionCount = 1;\n          copyInfo.pRegions = &copyRegion;\n\n          m_cmd->cmdCopyImageToBuffer(cmd, &copyInfo);\n        }\n\n        aspectOffset += blockCount.depth * slicePitch;\n      }\n\n      // Advance to next layer. This is non-trivial for multi-plane formats\n      // since plane data for each layer is expected to be packed.\n      VkDeviceSize layerPitch = aspectOffset - bufferOffset;\n\n      if (bufferSliceAlignment)\n        layerPitch = bufferSliceAlignment >= layerPitch ? bufferSliceAlignment : align(layerPitch, bufferSliceAlignment);\n\n      bufferOffset += layerPitch;\n    }\n  }\n\n\n  void DxvkContext::copyBufferToImageHw(\n    const Rc<DxvkImage>&        image,\n    const VkImageSubresourceLayers& imageSubresource,\n          VkOffset3D            imageOffset,\n          VkExtent3D            imageExtent,\n    const Rc<DxvkBuffer>&       buffer,\n          VkDeviceSize          bufferOffset,\n          VkDeviceSize          bufferRowAlignment,\n          VkDeviceSize          bufferSliceAlignment) {\n    VkDeviceSize dataSize = imageSubresource.layerCount * util::computeImageDataSize(\n      image->info().format, imageExtent, imageSubresource.aspectMask);\n\n    // We may copy to only one aspect at a time, but pipeline\n    // barriers need to have all available aspect bits set\n    auto dstFormatInfo = image->formatInfo();\n    auto dstLayout = image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n\n    auto dstSubresource = vk::makeSubresourceRange(imageSubresource);\n    dstSubresource.aspectMask = dstFormatInfo->aspectMask;\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*buffer, bufferOffset, dataSize,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT);\n\n    auto& imageAccess = accessBatch.emplace_back(*image, dstSubresource, dstLayout,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT,\n      image->isFullSubresource(imageSubresource, imageExtent));\n    imageAccess.imageOffset = imageOffset;\n    imageAccess.imageExtent = imageExtent;\n\n    DxvkCmdBuffer cmdBuffer = prepareOutOfOrderTransfer(DxvkCmdBuffer::InitBuffer,\n      accessBatch.size(), accessBatch.data());\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer)\n      endCurrentPass(true);\n\n    syncResources(cmdBuffer, accessBatch.size(), accessBatch.data());\n\n    auto bufferSlice = buffer->getSliceInfo(bufferOffset, dataSize);\n\n    copyImageBufferData<true>(cmdBuffer,\n      image, imageSubresource, imageOffset, imageExtent, dstLayout,\n      bufferSlice, bufferRowAlignment, bufferSliceAlignment);\n  }\n\n\n  void DxvkContext::copyBufferToImageFb(\n    const Rc<DxvkImage>&        image,\n    const VkImageSubresourceLayers& imageSubresource,\n          VkOffset3D            imageOffset,\n          VkExtent3D            imageExtent,\n    const Rc<DxvkBuffer>&       buffer,\n          VkDeviceSize          bufferOffset,\n          VkDeviceSize          bufferRowAlignment,\n          VkDeviceSize          bufferSliceAlignment,\n          VkFormat              bufferFormat) {\n    this->endCurrentPass(true);\n    this->invalidateState();\n\n    bool isDepthStencil = imageSubresource.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);\n\n    // Ensure we can read the source image\n    DxvkImageUsageInfo imageUsage = { };\n    imageUsage.usage = isDepthStencil\n      ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT\n      : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\n    if (!ensureImageCompatibility(image, imageUsage)) {\n      Logger::err(str::format(\"DxvkContext: copyBufferToImageFb: Unsupported image:\"\n        \"\\n  format: \", image->info().format));\n    }\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      const char* dstName = image->info().debugName;\n      const char* srcName = buffer->info().debugName;\n\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xf0dcdc, str::format(\"Upload image (\",\n          dstName ? dstName : \"unknown\", \", \",\n          srcName ? srcName : \"unknown\", \")\").c_str()));\n    }\n\n    auto formatInfo = lookupFormatInfo(bufferFormat);\n\n    if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n      Logger::err(str::format(\"DxvkContext: Planar formats not supported for shader-based buffer to image copies\"));\n      return;\n    }\n\n    VkDeviceSize rowPitch = imageExtent.width * formatInfo->elementSize;\n\n    if (bufferRowAlignment > formatInfo->elementSize)\n      rowPitch = bufferRowAlignment >= rowPitch ? bufferRowAlignment : align(rowPitch, bufferRowAlignment);\n\n    VkDeviceSize slicePitch = imageExtent.height * rowPitch;\n\n    if (bufferSliceAlignment > formatInfo->elementSize)\n      slicePitch = bufferSliceAlignment >= slicePitch ? bufferSliceAlignment : align(slicePitch, bufferSliceAlignment);\n\n    if ((rowPitch % formatInfo->elementSize) || (slicePitch % formatInfo->elementSize)) {\n      Logger::err(str::format(\"DxvkContext: Pitches \", rowPitch, \",\", slicePitch, \" not a multiple of element size \", formatInfo->elementSize, \" for format \", bufferFormat));\n      return;\n    }\n\n    // Create texel buffer view to read from\n    DxvkBufferViewKey bufferViewInfo = { };\n    bufferViewInfo.format = sanitizeTexelBufferFormat(bufferFormat);\n    bufferViewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n    bufferViewInfo.offset = bufferOffset;\n    bufferViewInfo.size = slicePitch * imageExtent.depth * imageSubresource.layerCount;\n\n    Rc<DxvkBufferView> bufferView = buffer->createView(bufferViewInfo);\n\n    // Create image view to render to\n    bool discard = image->isFullSubresource(imageSubresource, imageExtent);\n\n    DxvkImageViewKey imageViewInfo = { };\n    imageViewInfo.viewType = image->info().type == VK_IMAGE_TYPE_1D\n      ? VK_IMAGE_VIEW_TYPE_1D_ARRAY\n      : VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n    imageViewInfo.format = image->info().format;\n    imageViewInfo.usage = isDepthStencil\n      ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT\n      : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n    imageViewInfo.aspects = image->formatInfo()->aspectMask;\n    imageViewInfo.mipIndex = imageSubresource.mipLevel;\n    imageViewInfo.mipCount = 1u;\n    imageViewInfo.layerIndex = imageSubresource.baseArrayLayer;\n    imageViewInfo.layerCount = imageSubresource.layerCount;\n\n    if (image->info().type == VK_IMAGE_TYPE_3D) {\n      imageViewInfo.layerIndex = imageOffset.z;\n      imageViewInfo.layerCount = imageExtent.depth;\n    }\n\n    Rc<DxvkImageView> imageView = image->createView(imageViewInfo);\n\n    VkPipelineStageFlags stages = isDepthStencil\n      ? VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT\n      : VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n\n    VkAccessFlags access = isDepthStencil\n      ? VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT\n      : VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*bufferView, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT);\n    accessBatch.emplace_back(*imageView, stages, access, discard);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    // Bind image for rendering\n    VkRenderingAttachmentInfo attachment = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n    attachment.imageView = imageView->handle();\n    attachment.imageLayout = imageView->getLayout();\n    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\n    // Don't bother optimizing load ops for the partial copy case,\n    // that should basically never happen to begin with\n    if (imageSubresource.aspectMask != image->formatInfo()->aspectMask)\n      attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;\n\n    VkViewport viewport = { };\n    viewport.x = imageOffset.x;\n    viewport.y = imageOffset.y;\n    viewport.width = imageExtent.width;\n    viewport.height = imageExtent.height;\n    viewport.maxDepth = 1.0f;\n\n    VkRect2D scissor = { };\n    scissor.offset = { imageOffset.x, imageOffset.y };\n    scissor.extent = { imageExtent.width, imageExtent.height };\n\n    VkRenderingInfo renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n    renderingInfo.renderArea = scissor;\n    renderingInfo.layerCount = imageViewInfo.layerCount;\n\n    if (image->formatInfo()->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {\n      renderingInfo.colorAttachmentCount = 1;\n      renderingInfo.pColorAttachments = &attachment;\n    }\n\n    if (image->formatInfo()->aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)\n      renderingInfo.pDepthAttachment = &attachment;\n\n    if (image->formatInfo()->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)\n      renderingInfo.pStencilAttachment = &attachment;\n\n    m_cmd->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderingInfo);\n\n    // Set up viewport and scissor state\n    m_cmd->cmdSetViewport(1, &viewport);\n    m_cmd->cmdSetScissor(1, &scissor);\n\n    // Get pipeline and descriptor set layout. All pipelines\n    // will be using the same pipeline layout here.\n    bool needsBitwiseStencilCopy = !m_device->features().extShaderStencilExport\n      && (imageSubresource.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT);\n\n    // If we have a depth aspect, this will give us either the depth-only\n    // pipeline or one that can write all the given aspects\n    DxvkMetaCopyPipeline pipeline = m_common->metaCopy().getCopyBufferToImagePipeline(\n      image->info().format, bufferFormat, imageSubresource.aspectMask,\n      image->info().sampleCount);\n\n    DxvkDescriptorWrite bufferDescriptor = { };\n    bufferDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;\n    bufferDescriptor.descriptor = bufferView->getDescriptor(false);\n\n    DxvkBufferImageCopyArgs pushConst = { };\n    pushConst.imageOffset = imageOffset;\n    pushConst.bufferOffset = 0u;\n    pushConst.imageExtent = imageExtent;\n    pushConst.bufferImageWidth = rowPitch / formatInfo->elementSize;\n    pushConst.bufferImageHeight = slicePitch / rowPitch;\n\n    if (imageSubresource.aspectMask != VK_IMAGE_ASPECT_STENCIL_BIT || !needsBitwiseStencilCopy) {\n      m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n        VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline);\n\n      m_cmd->bindResources(DxvkCmdBuffer::ExecBuffer,\n        pipeline.layout, 1u, &bufferDescriptor,\n        sizeof(pushConst), &pushConst);\n\n      m_cmd->cmdDraw(3, renderingInfo.layerCount, 0, 0);\n    }\n\n    if (needsBitwiseStencilCopy) {\n      // On systems that do not support stencil export, we need to clear\n      // stencil to 0 and then \"write\" each individual bit by discarding\n      // fragments where that bit is not set.\n      pipeline = m_common->metaCopy().getCopyBufferToImagePipeline(\n        image->info().format, bufferFormat, VK_IMAGE_ASPECT_STENCIL_BIT,\n        image->info().sampleCount);\n\n      if (imageSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {\n        VkClearAttachment clear = { };\n        clear.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;\n\n        VkClearRect clearRect = { };\n        clearRect.rect = scissor;\n        clearRect.baseArrayLayer = 0;\n        clearRect.layerCount = renderingInfo.layerCount;\n\n        m_cmd->cmdClearAttachments(DxvkCmdBuffer::ExecBuffer, 1, &clear, 1, &clearRect);\n      }\n\n      m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n        VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline);\n\n      for (uint32_t i = 0; i < 8; i++) {\n        pushConst.stencilBitIndex = i;\n\n        m_cmd->bindResources(DxvkCmdBuffer::ExecBuffer,\n          pipeline.layout, i ? 0u : 1u, &bufferDescriptor,\n          sizeof(pushConst), &pushConst);\n\n        m_cmd->cmdSetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, 1u << i);\n        m_cmd->cmdDraw(3, renderingInfo.layerCount, 0, 0);\n      }\n    }\n\n    m_cmd->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n  }\n\n\n  void DxvkContext::copyImageToBufferHw(\n    const Rc<DxvkBuffer>&       buffer,\n          VkDeviceSize          bufferOffset,\n          VkDeviceSize          bufferRowAlignment,\n          VkDeviceSize          bufferSliceAlignment,\n    const Rc<DxvkImage>&        image,\n          VkImageSubresourceLayers imageSubresource,\n          VkOffset3D            imageOffset,\n          VkExtent3D            imageExtent) {\n    this->endCurrentPass(true);\n\n    VkDeviceSize dataSize = imageSubresource.layerCount * util::computeImageDataSize(\n      image->info().format, imageExtent, imageSubresource.aspectMask);\n\n    auto bufferSlice = buffer->getSliceInfo(bufferOffset, dataSize);\n\n    // We may copy to only one aspect of a depth-stencil image,\n    // but pipeline barriers need to have all aspect bits set\n    auto srcFormatInfo = image->formatInfo();\n\n    auto srcSubresource = imageSubresource;\n    srcSubresource.aspectMask = srcFormatInfo->aspectMask;\n\n    // Select a suitable image layout for the transfer op\n    VkImageLayout srcLayout = image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*buffer, bufferOffset, dataSize,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n\n    auto& dstAccess = accessBatch.emplace_back(*image,\n      vk::makeSubresourceRange(srcSubresource), srcLayout,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT, false);\n    dstAccess.imageOffset = imageOffset;\n    dstAccess.imageExtent = imageExtent;\n\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    this->copyImageBufferData<false>(DxvkCmdBuffer::ExecBuffer,\n      image, imageSubresource, imageOffset, imageExtent, srcLayout,\n      bufferSlice, bufferRowAlignment, bufferSliceAlignment);\n  }\n\n\n  void DxvkContext::copyImageToBufferCs(\n    const Rc<DxvkBuffer>&       buffer,\n          VkDeviceSize          bufferOffset,\n          VkDeviceSize          bufferRowAlignment,\n          VkDeviceSize          bufferSliceAlignment,\n          VkFormat              bufferFormat,\n    const Rc<DxvkImage>&        image,\n          VkImageSubresourceLayers imageSubresource,\n          VkOffset3D            imageOffset,\n          VkExtent3D            imageExtent) {\n    this->endCurrentPass(true);\n    this->invalidateState();\n\n    // Ensure we can read the source image\n    DxvkImageUsageInfo imageUsage = { };\n    imageUsage.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n\n    ensureImageCompatibility(image, imageUsage);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      const char* dstName = buffer->info().debugName;\n      const char* srcName = image->info().debugName;\n\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xf0dcdc, str::format(\"Readback image (\",\n          dstName ? dstName : \"unknown\", \", \",\n          srcName ? srcName : \"unknown\", \")\").c_str()));\n    }\n\n    auto formatInfo = lookupFormatInfo(bufferFormat);\n\n    if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n      Logger::err(str::format(\"DxvkContext: Planar formats not supported for shader-based image to buffer copies\"));\n      return;\n    }\n\n    VkDeviceSize rowPitch = imageExtent.width * formatInfo->elementSize;\n\n    if (bufferRowAlignment > formatInfo->elementSize)\n      rowPitch = bufferRowAlignment >= rowPitch ? bufferRowAlignment : align(rowPitch, bufferRowAlignment);\n\n    VkDeviceSize slicePitch = imageExtent.height * rowPitch;\n\n    if (bufferSliceAlignment > formatInfo->elementSize)\n      slicePitch = bufferSliceAlignment >= slicePitch ? bufferSliceAlignment : align(slicePitch, bufferSliceAlignment);\n\n    if ((rowPitch % formatInfo->elementSize) || (slicePitch % formatInfo->elementSize)) {\n      Logger::err(str::format(\"DxvkContext: Pitches \", rowPitch, \",\", slicePitch, \" not a multiple of element size \", formatInfo->elementSize, \" for format \", bufferFormat));\n      return;\n    }\n\n    // Create texel buffer view to write to\n    DxvkBufferViewKey bufferViewInfo = { };\n    bufferViewInfo.format = sanitizeTexelBufferFormat(bufferFormat);\n    bufferViewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;\n    bufferViewInfo.offset = bufferOffset;\n    bufferViewInfo.size = slicePitch * imageExtent.depth * imageSubresource.layerCount;\n\n    Rc<DxvkBufferView> bufferView = buffer->createView(bufferViewInfo);\n\n    // Transition image to a layout we can use for reading as necessary\n    VkImageLayout imageLayout = (image->formatInfo()->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n      ? image->pickLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL)\n      : image->pickLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*bufferView, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT);\n\n    auto& srcAccess = accessBatch.emplace_back(*image, vk::makeSubresourceRange(imageSubresource),\n      imageLayout, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, false);\n    srcAccess.imageOffset = imageOffset;\n    srcAccess.imageExtent = imageExtent;\n\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    // Retrieve pipeline\n    VkImageViewType viewType = image->info().type == VK_IMAGE_TYPE_1D\n      ? VK_IMAGE_VIEW_TYPE_1D_ARRAY\n      : VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n\n    if (image->info().type == VK_IMAGE_TYPE_3D)\n      viewType = VK_IMAGE_VIEW_TYPE_3D;\n\n    DxvkMetaCopyPipeline pipeline = m_common->metaCopy().getCopyImageToBufferPipeline(viewType, bufferFormat);\n\n    // Create image views  for the main and stencil aspects\n    std::array<DxvkDescriptorWrite, 3> descriptors = { };\n\n    DxvkImageViewKey imageViewInfo;\n    imageViewInfo.viewType = viewType;\n    imageViewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    imageViewInfo.format = image->info().format;\n    imageViewInfo.layout = imageLayout;\n    imageViewInfo.mipIndex = imageSubresource.mipLevel;\n    imageViewInfo.mipCount = 1;\n    imageViewInfo.layerIndex = imageSubresource.baseArrayLayer;\n    imageViewInfo.layerCount = imageSubresource.layerCount;\n\n    auto& bufferDescriptor = descriptors[0u];\n    bufferDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;\n    bufferDescriptor.descriptor = bufferView->getDescriptor(false);\n\n    auto& imagePlane0Descriptor = descriptors[1u];\n    imagePlane0Descriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\n    if ((imageViewInfo.aspects = (imageSubresource.aspectMask & (VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_PLANE_0_BIT))))\n      imagePlane0Descriptor.descriptor = image->createView(imageViewInfo)->getDescriptor();\n\n    auto& imagePlane1Descriptor = descriptors[2u];\n    imagePlane1Descriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\n    if ((imageViewInfo.aspects = (imageSubresource.aspectMask & (VK_IMAGE_ASPECT_STENCIL_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT))))\n      imagePlane1Descriptor.descriptor = image->createView(imageViewInfo)->getDescriptor();\n\n    // Compute number of workgroups\n    VkExtent3D workgroupCount = imageExtent;\n    workgroupCount.depth *= imageSubresource.layerCount;\n    workgroupCount = util::computeBlockCount(workgroupCount, { 16, 16, 1 });\n\n    // Set up shader arguments and dispatch shader\n    DxvkBufferImageCopyArgs pushConst = { };\n    pushConst.imageOffset = imageOffset;\n    pushConst.bufferOffset = 0u;\n    pushConst.imageExtent = imageExtent;\n    pushConst.bufferImageWidth = rowPitch / formatInfo->elementSize;\n    pushConst.bufferImageHeight = slicePitch / rowPitch;\n\n    m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.pipeline);\n\n    m_cmd->bindResources(DxvkCmdBuffer::ExecBuffer,\n      pipeline.layout, descriptors.size(), descriptors.data(),\n      sizeof(pushConst), &pushConst);\n\n    m_cmd->cmdDispatch(DxvkCmdBuffer::ExecBuffer,\n      workgroupCount.width,\n      workgroupCount.height,\n      workgroupCount.depth);\n\n    m_flags.set(DxvkContextFlag::ForceWriteAfterWriteSync);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n}\n\n\n  void DxvkContext::clearImageViewFb(\n    const Rc<DxvkImageView>&    imageView,\n          VkOffset3D            offset,\n          VkExtent3D            extent,\n          VkImageAspectFlags    aspect,\n          VkClearValue          value) {\n    // Ensure that the clear region is actually within the bounds of the given view\n    VkExtent3D mipExtent = imageView->mipLevelExtent(0);\n\n    offset.x = std::clamp(offset.x, 0, int32_t(mipExtent.width));\n    offset.y = std::clamp(offset.y, 0, int32_t(mipExtent.height));\n\n    extent.width  = std::clamp(extent.width,  0u, mipExtent.width  - uint32_t(offset.x));\n    extent.height = std::clamp(extent.height, 0u, mipExtent.height - uint32_t(offset.y));\n\n    if (unlikely(!extent.width || !extent.height))\n      return;\n\n    // Use regular render target clear path if we're clearing the\n    // entire view to hit some additional optimizations.\n    if (extent == imageView->mipLevelExtent(0u)) {\n      clearRenderTarget(imageView, aspect, value, 0u);\n      return;\n    }\n\n    // Find out if the render target view is currently bound, so that\n    // we can avoid spilling the render pass if it is. Don't try to\n    // use the render pass if the framebuffer size is too small.\n    this->updateRenderTargets();\n\n    int32_t attachmentIndex = -1;\n\n    DxvkFramebufferSize fbSize = m_state.om.framebufferInfo.size();\n\n    if (uint32_t(offset.x) + extent.width <= fbSize.width\n     && uint32_t(offset.y) + extent.height <= fbSize.height)\n      attachmentIndex = m_state.om.framebufferInfo.findAttachment(imageView);\n\n    if (attachmentIndex >= 0 && !m_state.om.framebufferInfo.isWritable(attachmentIndex, aspect))\n      attachmentIndex = -1;\n\n    if (attachmentIndex < 0) {\n      this->endCurrentPass(true);\n\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n        const char* dstName = imageView->image()->info().debugName;\n\n        m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer, vk::makeLabel(0xf0dcdc,\n          str::format(\"Clear view (\", dstName ? dstName : \"unknown\", \")\").c_str()));\n      }\n\n      // Use render area to limit the actual clear region\n      VkRenderingAttachmentInfo attachmentInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n      attachmentInfo.imageView = imageView->handle();\n      attachmentInfo.imageLayout = imageView->getLayout();\n      attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;\n      attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n      attachmentInfo.clearValue = value;\n\n      VkRenderingAttachmentInfo stencilInfo = attachmentInfo;\n\n      VkRenderingInfo renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n      renderingInfo.renderArea.offset = { offset.x,     offset.y };\n      renderingInfo.renderArea.extent = { extent.width, extent.height };\n      renderingInfo.layerCount = imageView->info().layerCount;\n\n      VkPipelineStageFlags clearStages = 0;\n      VkAccessFlags clearAccess = 0;\n\n      if (imageView->info().aspects & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n        clearStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n                    |  VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n        clearAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n\n        if (imageView->info().aspects & VK_IMAGE_ASPECT_DEPTH_BIT)\n          renderingInfo.pDepthAttachment = &attachmentInfo;\n\n        if (imageView->info().aspects & VK_IMAGE_ASPECT_STENCIL_BIT)\n          renderingInfo.pStencilAttachment = &stencilInfo;\n\n        if (imageView->info().aspects != aspect) {\n          if (m_device->properties().khrMaintenance7.separateDepthStencilAttachmentAccess) {\n            if (!(aspect & VK_IMAGE_ASPECT_DEPTH_BIT)) {\n              attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_NONE;\n              attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_NONE;\n            }\n\n            if (!(aspect & VK_IMAGE_ASPECT_STENCIL_BIT)) {\n              stencilInfo.loadOp = VK_ATTACHMENT_LOAD_OP_NONE;\n              stencilInfo.storeOp = VK_ATTACHMENT_STORE_OP_NONE;\n            }\n          } else {\n            clearAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;\n\n            if (!(aspect & VK_IMAGE_ASPECT_DEPTH_BIT)) {\n              attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;\n              attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n            }\n\n            if (!(aspect & VK_IMAGE_ASPECT_STENCIL_BIT)) {\n              stencilInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;\n              stencilInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n            }\n          }\n        }\n      } else {\n        clearStages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        clearAccess |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\n        renderingInfo.colorAttachmentCount = 1;\n        renderingInfo.pColorAttachments = &attachmentInfo;\n      }\n\n      DxvkResourceAccess access(*imageView, clearStages, clearAccess, false);\n      syncResources(DxvkCmdBuffer::ExecBuffer, 1u, &access);\n\n      m_cmd->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderingInfo);\n      m_cmd->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n        m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n    } else {\n      // Make sure the render pass is active so\n      // that we can actually perform the clear\n      this->beginRenderPass();\n\n      if (findOverlappingDeferredClear(*imageView->image(), imageView->imageSubresources()))\n        flushClearsInline();\n\n      // Inline clears may affect render area\n      m_state.om.renderAreaLo = VkOffset2D {\n        std::min(m_state.om.renderAreaLo.x, offset.x),\n        std::min(m_state.om.renderAreaLo.y, offset.y) };\n      m_state.om.renderAreaHi = VkOffset2D {\n        std::max(m_state.om.renderAreaHi.x, int32_t(offset.x + extent.width)),\n        std::max(m_state.om.renderAreaHi.y, int32_t(offset.y + extent.height)) };\n\n      // Check whether we can fold the clear into the curret render pass. This is the\n      // case when the framebuffer size matches the clear size, even if the clear itself\n      // does not cover the entire view, and if the view has not been accessed yet.\n      bool hoistClear = m_flags.test(DxvkContextFlag::GpRenderPassSecondaryCmd)\n        && extent.width == fbSize.width && extent.height == fbSize.height;\n\n      DxvkDeferredClear clearInfo = { };\n      clearInfo.imageView = imageView;\n      clearInfo.clearAspects = aspect;\n      clearInfo.clearValue = value;\n\n      uint32_t colorIndex = 0u;\n\n      if (aspect & VK_IMAGE_ASPECT_COLOR_BIT) {\n        colorIndex = m_state.om.framebufferInfo.getColorAttachmentIndex(attachmentIndex);\n\n        if (hoistClear && m_state.om.attachmentMask.getColorAccess(colorIndex) == DxvkAccess::None)\n          hoistInlineClear(clearInfo, m_state.om.renderingInfo.color[colorIndex], VK_IMAGE_ASPECT_COLOR_BIT);\n      } else {\n        if (hoistClear && m_state.om.attachmentMask.getDepthAccess() == DxvkAccess::None)\n          hoistInlineClear(clearInfo, m_state.om.renderingInfo.depth, VK_IMAGE_ASPECT_DEPTH_BIT);\n\n        if (hoistClear && m_state.om.attachmentMask.getStencilAccess() == DxvkAccess::None)\n          hoistInlineClear(clearInfo, m_state.om.renderingInfo.stencil, VK_IMAGE_ASPECT_STENCIL_BIT);\n      }\n\n      // If we hoisted everything, there's nothing left to do\n      if (!clearInfo.clearAspects)\n        return;\n\n      if (clearInfo.clearAspects & VK_IMAGE_ASPECT_COLOR_BIT)\n        m_state.om.attachmentMask.trackColorWrite(colorIndex);\n\n      if (clearInfo.clearAspects & VK_IMAGE_ASPECT_DEPTH_BIT)\n        m_state.om.attachmentMask.trackDepthWrite();\n\n      if (clearInfo.clearAspects & VK_IMAGE_ASPECT_STENCIL_BIT)\n        m_state.om.attachmentMask.trackStencilWrite();\n\n      // Perform the actual clear operation\n      VkClearAttachment clear = { };\n      clear.aspectMask = aspect;\n      clear.colorAttachment = colorIndex;\n      clear.clearValue = value;\n\n      VkClearRect clearRect = { };\n      clearRect.rect.offset.x = offset.x;\n      clearRect.rect.offset.y = offset.y;\n      clearRect.rect.extent.width = extent.width;\n      clearRect.rect.extent.height = extent.height;\n      clearRect.layerCount = imageView->info().layerCount;\n\n      m_cmd->cmdClearAttachments(DxvkCmdBuffer::ExecBuffer, 1, &clear, 1, &clearRect);\n    }\n  }\n\n  \n  void DxvkContext::clearImageViewCs(\n    const Rc<DxvkImageView>&    imageView,\n          VkOffset3D            offset,\n          VkExtent3D            extent,\n          VkClearValue          value) {\n    DxvkResourceAccess access(*imageView, VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT,\n      imageView->image()->isFullSubresource(vk::pickSubresourceLayers(imageView->imageSubresources(), 0u), extent));\n\n    DxvkCmdBuffer cmdBuffer = prepareOutOfOrderTransfer(DxvkCmdBuffer::InitBuffer, 1u, &access);\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer) {\n      endCurrentPass(true);\n      invalidateState();\n    }\n\n    syncResources(cmdBuffer, 1u, &access);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      const char* dstName = imageView->image()->info().debugName;\n\n      m_cmd->cmdBeginDebugUtilsLabel(cmdBuffer, vk::makeLabel(0xf0dcdc,\n        str::format(\"Clear view (\", dstName ? dstName : \"unknown\", \")\").c_str()));\n    }\n\n    // Query pipeline objects to use for this clear operation\n    DxvkMetaClearPipeline pipeInfo = m_common->metaClear().getClearImagePipeline(\n      imageView->type(), lookupFormatInfo(imageView->info().format)->flags);\n\n    // Create a descriptor set pointing to the view\n    DxvkDescriptorWrite imageDescriptor = { };\n    imageDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n    imageDescriptor.descriptor = imageView->getDescriptor();\n    \n    // Prepare shader arguments\n    DxvkMetaClearArgs pushArgs = { };\n    pushArgs.clearValue = value.color;\n    pushArgs.offset = offset;\n    pushArgs.extent = extent;\n\n    VkExtent3D workgroups = util::computeBlockCount(\n      pushArgs.extent, pipeInfo.workgroupSize);\n\n    if (imageView->type() == VK_IMAGE_VIEW_TYPE_1D_ARRAY)\n      workgroups.height = imageView->subresources().layerCount;\n    else if (imageView->type() == VK_IMAGE_VIEW_TYPE_2D_ARRAY)\n      workgroups.depth = imageView->subresources().layerCount;\n\n    m_cmd->cmdBindPipeline(cmdBuffer,\n      VK_PIPELINE_BIND_POINT_COMPUTE, pipeInfo.pipeline);\n\n    m_cmd->bindResources(cmdBuffer, pipeInfo.layout,\n      1u, &imageDescriptor, sizeof(pushArgs), &pushArgs);\n\n    m_cmd->cmdDispatch(cmdBuffer,\n      workgroups.width, workgroups.height, workgroups.depth);\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer)\n      m_flags.set(DxvkContextFlag::ForceWriteAfterWriteSync);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(cmdBuffer);\n  }\n\n  \n  void DxvkContext::copyImageHw(\n    const Rc<DxvkImage>&        dstImage,\n          VkImageSubresourceLayers dstSubresource,\n          VkOffset3D            dstOffset,\n    const Rc<DxvkImage>&        srcImage,\n          VkImageSubresourceLayers srcSubresource,\n          VkOffset3D            srcOffset,\n          VkExtent3D            extent) {\n    auto dstSubresourceRange = vk::makeSubresourceRange(dstSubresource);\n    auto srcSubresourceRange = vk::makeSubresourceRange(srcSubresource);\n\n    auto srcFormatInfo = srcImage->formatInfo();\n    auto dstFormatInfo = dstImage->formatInfo();\n\n    // If we copy between disjoint regions of the same image subresources,\n    // make sure that we only do one single transition to GENERAL.\n    bool hasOvelap = dstImage == srcImage && vk::checkSubresourceRangeOverlap(dstSubresourceRange, srcSubresourceRange);\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n\n    VkImageLayout dstImageLayout = VK_IMAGE_LAYOUT_GENERAL;\n    VkImageLayout srcImageLayout = VK_IMAGE_LAYOUT_GENERAL;\n\n    if (hasOvelap) {\n      VkImageSubresourceRange overlapSubresourceRange = { };\n      overlapSubresourceRange.aspectMask = srcSubresource.aspectMask | dstSubresource.aspectMask;\n      overlapSubresourceRange.baseArrayLayer = std::min(srcSubresource.baseArrayLayer, dstSubresource.baseArrayLayer);\n      overlapSubresourceRange.baseMipLevel = srcSubresource.mipLevel;\n      overlapSubresourceRange.layerCount = std::max(srcSubresource.baseArrayLayer, dstSubresource.baseArrayLayer)\n        + dstSubresource.layerCount - overlapSubresourceRange.baseArrayLayer;\n      overlapSubresourceRange.levelCount = 1u;\n\n      accessBatch.emplace_back(*dstImage, overlapSubresourceRange, dstImageLayout,\n        VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT, false);\n    } else {\n      dstImageLayout = dstImage->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n      srcImageLayout = srcImage->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);\n\n      auto& dstAccess = accessBatch.emplace_back(*dstImage, dstSubresourceRange, dstImageLayout,\n        VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT,\n        dstImage->isFullSubresource(dstSubresource, extent));\n      dstAccess.imageOffset = srcOffset;\n      dstAccess.imageExtent = extent;\n\n      auto& srcAccess = accessBatch.emplace_back(*srcImage, srcSubresourceRange, srcImageLayout,\n        VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT, false);\n      srcAccess.imageOffset = srcOffset;\n      srcAccess.imageExtent = extent;\n    }\n\n    // Try to do the copy out of order to avoid barrier spam\n    DxvkCmdBuffer cmdBuffer = prepareOutOfOrderTransfer(\n      DxvkCmdBuffer::InitBuffer, accessBatch.size(), accessBatch.data());\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer)\n      this->endCurrentPass(true);\n\n    syncResources(cmdBuffer, accessBatch.size(), accessBatch.data());\n\n    auto dstAspects = dstSubresource.aspectMask;\n    auto srcAspects = srcSubresource.aspectMask;\n\n    while (dstAspects && srcAspects) {\n      VkImageCopy2 copyRegion = { VK_STRUCTURE_TYPE_IMAGE_COPY_2 };\n      copyRegion.srcSubresource = srcSubresource;\n      copyRegion.srcSubresource.aspectMask = vk::getNextAspect(srcAspects);\n      copyRegion.srcOffset = srcOffset;\n      copyRegion.dstSubresource = dstSubresource;\n      copyRegion.dstSubresource.aspectMask = vk::getNextAspect(dstAspects);\n      copyRegion.dstOffset = dstOffset;\n      copyRegion.extent = extent;\n\n      if (srcFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n        auto plane = &srcFormatInfo->planes[vk::getPlaneIndex(copyRegion.srcSubresource.aspectMask)];\n        copyRegion.srcOffset.x /= plane->blockSize.width;\n        copyRegion.srcOffset.y /= plane->blockSize.height;\n      }\n\n      if (dstFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n        auto plane = &dstFormatInfo->planes[vk::getPlaneIndex(copyRegion.dstSubresource.aspectMask)];\n        copyRegion.dstOffset.x /= plane->blockSize.width;\n        copyRegion.dstOffset.y /= plane->blockSize.height;\n        copyRegion.extent.width /= plane->blockSize.width;\n        copyRegion.extent.height /= plane->blockSize.height;\n      }\n\n      VkCopyImageInfo2 copyInfo = { VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2 };\n      copyInfo.srcImage = srcImage->handle();\n      copyInfo.srcImageLayout = srcImageLayout;\n      copyInfo.dstImage = dstImage->handle();\n      copyInfo.dstImageLayout = dstImageLayout;\n      copyInfo.regionCount = 1;\n      copyInfo.pRegions = &copyRegion;\n\n      m_cmd->cmdCopyImage(cmdBuffer, &copyInfo);\n    }\n  }\n\n  \n  void DxvkContext::copyImageFb(\n    const Rc<DxvkImage>&        dstImage,\n          VkImageSubresourceLayers dstSubresource,\n          VkOffset3D            dstOffset,\n    const Rc<DxvkImage>&        srcImage,\n          VkImageSubresourceLayers srcSubresource,\n          VkOffset3D            srcOffset,\n          VkExtent3D            extent) {\n    this->endCurrentPass(true);\n\n    DxvkMetaCopyFormats viewFormats = m_common->metaCopy().getCopyImageFormats(\n      dstImage->info().format, dstSubresource.aspectMask,\n      srcImage->info().format, srcSubresource.aspectMask);\n    \n    // Guarantee that we can render to or sample the images\n    DxvkImageUsageInfo dstUsage = { };\n    dstUsage.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n    dstUsage.viewFormatCount = 1;\n    dstUsage.viewFormats = &viewFormats.dstFormat;\n\n    if (dstImage->formatInfo()->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n      dstUsage.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n    DxvkImageUsageInfo srcUsage = { };\n    srcUsage.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    srcUsage.viewFormatCount = 1;\n    srcUsage.viewFormats = &viewFormats.srcFormat;\n\n    if (!ensureImageCompatibility(dstImage, dstUsage)\n     || !ensureImageCompatibility(srcImage, srcUsage)) {\n      Logger::err(str::format(\"DxvkContext: copyImageFb: Unsupported images:\"\n        \"\\n  dst format: \", dstImage->info().format,\n        \"\\n  src format: \", srcImage->info().format));\n      return;\n    }\n\n    this->invalidateState();\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      const char* dstName = dstImage->info().debugName;\n      const char* srcName = srcImage->info().debugName;\n\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xf0dcdc, str::format(\"Copy image (\",\n          dstName ? dstName : \"unknown\", \", \",\n          srcName ? srcName : \"unknown\", \")\").c_str()));\n    }\n\n    auto dstSubresourceRange = vk::makeSubresourceRange(dstSubresource);\n    auto srcSubresourceRange = vk::makeSubresourceRange(srcSubresource);\n\n    // Create source and destination image views\n    DxvkMetaCopyViews views(\n      dstImage, dstSubresource, viewFormats.dstFormat,\n      srcImage, srcSubresource, viewFormats.srcFormat);\n\n    VkAccessFlags dstAccess = views.srcImageView->getLayout();\n    VkImageLayout dstLayout = views.dstImageView->getLayout();\n\n    // Flag used to determine whether we can do an UNDEFINED transition\n    bool doDiscard = dstImage->isFullSubresource(dstSubresource, extent);\n\n    // This function can process both color and depth-stencil images, so\n    // some things change a lot depending on the destination image type\n    VkPipelineStageFlags dstStages;\n\n    if (dstSubresource.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {\n      dstStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n      dstAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\n      if (!doDiscard)\n        dstAccess |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;\n    } else {\n      dstStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n                | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n      dstAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n\n      if (!doDiscard)\n        dstAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;\n    }\n\n    // Might have to transition source image as well\n    VkImageLayout srcLayout = (srcSubresource.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT)\n      ? srcImage->pickLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)\n      : srcImage->pickLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL);\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*dstImage, dstSubresourceRange, dstLayout,\n      dstStages, dstAccess, doDiscard);\n    accessBatch.emplace_back(*srcImage, srcSubresourceRange, srcLayout,\n      VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, false);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    // Create pipeline for the copy operation\n    DxvkMetaCopyPipeline pipeInfo = m_common->metaCopy().getCopyImagePipeline(\n      views.srcImageView->info().viewType, viewFormats.dstFormat, dstImage->info().sampleCount);\n\n    // Create and initialize descriptor set\n    std::array<DxvkDescriptorWrite, 2> descriptors = { };\n\n    auto& imagePlane0Descriptor = descriptors[0u];\n    imagePlane0Descriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n    imagePlane0Descriptor.descriptor = views.srcImageView->getDescriptor();\n\n    auto& imagePlane1Descriptor = descriptors[1u];\n    imagePlane1Descriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\n    if (views.srcStencilView)\n      imagePlane1Descriptor.descriptor = views.srcStencilView->getDescriptor();\n\n    // Set up render state    \n    VkViewport viewport;\n    viewport.x = float(dstOffset.x);\n    viewport.y = float(dstOffset.y);\n    viewport.width = float(extent.width);\n    viewport.height = float(extent.height);\n    viewport.minDepth = 0.0f;\n    viewport.maxDepth = 1.0f;\n\n    VkRect2D scissor;\n    scissor.offset = { dstOffset.x, dstOffset.y };\n    scissor.extent = { extent.width, extent.height };\n\n    VkRenderingAttachmentInfo attachmentInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n    attachmentInfo.imageView = views.dstImageView->handle();\n    attachmentInfo.imageLayout = dstLayout;\n    attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\n    // Don't bother optimizing load ops for the partial copy case, shouldn't happen\n    if (dstSubresource.aspectMask != dstImage->formatInfo()->aspectMask)\n      attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;\n\n    VkRenderingInfo renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n    renderingInfo.renderArea = scissor;\n    renderingInfo.layerCount = dstSubresource.layerCount;\n\n    VkImageAspectFlags dstAspects = dstImage->formatInfo()->aspectMask;\n\n    if (dstAspects & VK_IMAGE_ASPECT_COLOR_BIT) {\n      renderingInfo.colorAttachmentCount = 1;\n      renderingInfo.pColorAttachments = &attachmentInfo;\n    } else {\n      if (dstAspects & VK_IMAGE_ASPECT_DEPTH_BIT)\n        renderingInfo.pDepthAttachment = &attachmentInfo;\n      if (dstAspects & VK_IMAGE_ASPECT_STENCIL_BIT)\n        renderingInfo.pStencilAttachment = &attachmentInfo;\n    }\n\n    VkOffset2D srcCoordOffset = {\n      srcOffset.x - dstOffset.x,\n      srcOffset.y - dstOffset.y };\n\n    // Perform the actual copy operation\n    m_cmd->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderingInfo);\n\n    m_cmd->cmdSetViewport(1, &viewport);\n    m_cmd->cmdSetScissor(1, &scissor);\n\n    m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, pipeInfo.pipeline);\n\n    m_cmd->bindResources(DxvkCmdBuffer::ExecBuffer,\n      pipeInfo.layout, descriptors.size(), descriptors.data(),\n      sizeof(srcCoordOffset), &srcCoordOffset);\n\n    m_cmd->cmdDraw(3, dstSubresource.layerCount, 0, 0);\n    m_cmd->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n  }\n\n\n  bool DxvkContext::copyImageClear(\n    const Rc<DxvkImage>&        dstImage,\n          VkImageSubresourceLayers dstSubresource,\n          VkOffset3D            dstOffset,\n          VkExtent3D            dstExtent,\n    const Rc<DxvkImage>&        srcImage,\n          VkImageSubresourceLayers srcSubresource) {\n    this->endCurrentPass(true);\n\n    // If the source image has a pending deferred clear, we can\n    // implement the copy by clearing the destination image to\n    // the same clear value.\n    const VkImageUsageFlags attachmentUsage\n      = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n      | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n    if (!(dstImage->info().usage & attachmentUsage)\n     || !(srcImage->info().usage & attachmentUsage))\n      return false;\n\n    // Ignore 3D images since those are complicated to handle\n    if (dstImage->info().type == VK_IMAGE_TYPE_3D\n     || srcImage->info().type == VK_IMAGE_TYPE_3D)\n      return false;\n\n    // Find a pending clear that overlaps with the source image\n    const DxvkDeferredClear* clear = findDeferredClear(*srcImage, vk::makeSubresourceRange(srcSubresource));\n\n    if (!clear)\n      return false;\n\n    // Create a view for the destination image with the general\n    // properties ofthe source image view used for the clear\n    DxvkImageViewKey viewInfo = clear->imageView->info();\n    viewInfo.viewType = dstImage->info().type == VK_IMAGE_TYPE_1D\n      ? VK_IMAGE_VIEW_TYPE_1D_ARRAY\n      : VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n    viewInfo.mipIndex = dstSubresource.mipLevel;\n    viewInfo.mipCount = 1;\n    viewInfo.layerIndex = dstSubresource.baseArrayLayer;\n    viewInfo.layerCount = dstSubresource.layerCount;\n\n    // That is, if the formats are actually compatible\n    // so that we can safely use the same clear value\n    if (!dstImage->isViewCompatible(viewInfo.format))\n      return false;\n\n    // Ignore mismatched size for now, needs more testing since we'd\n    // need to prepare the image first and then call clearImageViewFb\n    if (dstImage->mipLevelExtent(dstSubresource.mipLevel, dstSubresource.aspectMask) != dstExtent)\n      return false;\n\n    auto view = dstImage->createView(viewInfo);\n\n    deferClear(view, srcSubresource.aspectMask, clear->clearValue);\n    return true;\n  }\n\n\n  template<bool ToBuffer>\n  void DxvkContext::copySparsePages(\n    const Rc<DxvkPagedResource>& sparse,\n          uint32_t              pageCount,\n    const uint32_t*             pages,\n    const Rc<DxvkBuffer>&       buffer,\n          VkDeviceSize          offset) {\n    auto pageTable = sparse->getSparsePageTable();\n\n    if (pageTable->getBufferHandle()) {\n      this->copySparseBufferPages<ToBuffer>(\n        static_cast<DxvkBuffer*>(sparse.ptr()),\n        pageCount, pages, buffer, offset);\n    } else {\n      this->copySparseImagePages<ToBuffer>(\n        static_cast<DxvkImage*>(sparse.ptr()),\n        pageCount, pages, buffer, offset);\n    }\n  }\n\n\n  template<bool ToBuffer>\n  void DxvkContext::copySparseBufferPages(\n    const Rc<DxvkBuffer>&       sparse,\n          uint32_t              pageCount,\n    const uint32_t*             pages,\n    const Rc<DxvkBuffer>&       buffer,\n          VkDeviceSize          offset) {\n    std::vector<VkBufferCopy2> regions;\n    regions.reserve(pageCount);\n\n    auto pageTable = sparse->getSparsePageTable();\n\n    auto sparseSlice = sparse->getSliceInfo();\n    auto bufferSlice = buffer->getSliceInfo(offset, SparseMemoryPageSize * pageCount);\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*sparse, 0u, sparseSlice.size, VK_PIPELINE_STAGE_2_TRANSFER_BIT,\n      ToBuffer ? VK_ACCESS_2_TRANSFER_READ_BIT : VK_ACCESS_2_TRANSFER_WRITE_BIT);\n    accessBatch.emplace_back(*buffer, offset, bufferSlice.size, VK_PIPELINE_STAGE_2_TRANSFER_BIT,\n      ToBuffer ? VK_ACCESS_2_TRANSFER_WRITE_BIT : VK_ACCESS_2_TRANSFER_READ_BIT);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    for (uint32_t i = 0; i < pageCount; i++) {\n      auto pageInfo = pageTable->getPageInfo(pages[i]);\n\n      if (pageInfo.type == DxvkSparsePageType::Buffer) {\n        VkDeviceSize sparseOffset = pageInfo.buffer.offset;\n        VkDeviceSize bufferOffset = bufferSlice.offset + SparseMemoryPageSize * i;\n\n        VkBufferCopy2 copy = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 };\n        copy.srcOffset = ToBuffer ? sparseOffset : bufferOffset;\n        copy.dstOffset = ToBuffer ? bufferOffset : sparseOffset;\n        copy.size = pageInfo.buffer.length;\n\n        regions.push_back(copy);\n      }\n    }\n\n    VkCopyBufferInfo2 info = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 };\n    info.srcBuffer = ToBuffer ? sparseSlice.buffer: bufferSlice.buffer;\n    info.dstBuffer = ToBuffer ? bufferSlice.buffer: sparseSlice.buffer;\n    info.regionCount = uint32_t(regions.size());\n    info.pRegions = regions.data();\n\n    if (info.regionCount)\n      m_cmd->cmdCopyBuffer(DxvkCmdBuffer::ExecBuffer, &info);\n  }\n\n\n  template<bool ToBuffer>\n  void DxvkContext::copySparseImagePages(\n    const Rc<DxvkImage>&        sparse,\n          uint32_t              pageCount,\n    const uint32_t*             pages,\n    const Rc<DxvkBuffer>&       buffer,\n          VkDeviceSize          offset) {\n    std::vector<VkBufferImageCopy2> regions;\n    regions.reserve(pageCount);\n\n    auto pageTable = sparse->getSparsePageTable();\n    auto pageExtent = pageTable->getProperties().pageRegionExtent;\n\n    auto bufferSlice = buffer->getSliceInfo(offset, SparseMemoryPageSize * pageCount);\n\n    VkImageLayout transferLayout = sparse->pickLayout(ToBuffer\n      ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n      : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*sparse, sparse->getAvailableSubresources(), transferLayout,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, ToBuffer ? VK_ACCESS_2_TRANSFER_READ_BIT : VK_ACCESS_2_TRANSFER_WRITE_BIT, false);\n    accessBatch.emplace_back(*buffer, offset, bufferSlice.size, VK_PIPELINE_STAGE_2_TRANSFER_BIT,\n      ToBuffer ? VK_ACCESS_2_TRANSFER_WRITE_BIT : VK_ACCESS_2_TRANSFER_READ_BIT);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    for (uint32_t i = 0; i < pageCount; i++) {\n      auto pageInfo = pageTable->getPageInfo(pages[i]);\n\n      if (pageInfo.type == DxvkSparsePageType::Image) {\n        VkBufferImageCopy2 copy = { VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 };\n        copy.bufferOffset = bufferSlice.offset + SparseMemoryPageSize * i;\n        copy.bufferRowLength = pageExtent.width;\n        copy.bufferImageHeight = pageExtent.height;\n        copy.imageSubresource = vk::makeSubresourceLayers(pageInfo.image.subresource);\n        copy.imageOffset = pageInfo.image.offset;\n        copy.imageExtent = pageInfo.image.extent;\n\n        regions.push_back(copy);\n      }\n    }\n\n    if (ToBuffer) {\n      VkCopyImageToBufferInfo2 info = { VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2 };\n      info.srcImage = sparse->handle();\n      info.srcImageLayout = transferLayout;\n      info.dstBuffer = bufferSlice.buffer;\n      info.regionCount = regions.size();\n      info.pRegions = regions.data();\n\n      if (info.regionCount)\n        m_cmd->cmdCopyImageToBuffer(DxvkCmdBuffer::ExecBuffer, &info);\n    } else {\n      VkCopyBufferToImageInfo2 info = { VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 };\n      info.srcBuffer = bufferSlice.buffer;\n      info.dstImage = sparse->handle();\n      info.dstImageLayout = transferLayout;\n      info.regionCount = regions.size();\n      info.pRegions = regions.data();\n\n      if (info.regionCount)\n        m_cmd->cmdCopyBufferToImage(DxvkCmdBuffer::ExecBuffer, &info);\n    }\n  }\n\n\n  void DxvkContext::generateMipmapsHw(\n    const Rc<DxvkImageView>&        imageView,\n          VkFilter                  filter) {\n    auto image = imageView->image();\n\n    VkImageSubresourceRange subresources = imageView->imageSubresources();\n\n    VkImageSubresourceRange srcSubresource = subresources;\n    srcSubresource.levelCount = 1u;\n\n    VkImageSubresourceRange dstSubresource = subresources;\n    dstSubresource.baseMipLevel += 1u;\n    dstSubresource.levelCount -= 1u;\n\n    VkImageLayout srcLayout = image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);\n    VkImageLayout dstLayout = image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n\n    for (uint32_t i = 0u; i < subresources.levelCount - 1u; i++) {\n      // Transition and discard all levels that will be written in the first iteration,\n      // then just synchronize access as normal. Requires that the destination mip count\n      // is left intact for the first iteration.\n      small_vector<DxvkResourceAccess, 2u> accessBatch;\n      accessBatch.emplace_back(*image, srcSubresource, srcLayout, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT, false);\n      accessBatch.emplace_back(*image, dstSubresource, dstLayout, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT, !i);\n      syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n      dstSubresource.levelCount = 1u;\n\n      VkExtent3D dstSize = image->mipLevelExtent(dstSubresource.baseMipLevel);\n      VkExtent3D srcSize = image->mipLevelExtent(srcSubresource.baseMipLevel);\n\n      VkImageBlit2 blit = { VK_STRUCTURE_TYPE_IMAGE_BLIT_2 };\n      blit.dstSubresource = vk::pickSubresourceLayers(dstSubresource, 0u);\n      blit.srcSubresource = vk::pickSubresourceLayers(srcSubresource, 0u);\n      blit.dstOffsets[1u] = { int32_t(dstSize.width), int32_t(dstSize.height), int32_t(dstSize.depth) };\n      blit.srcOffsets[1u] = { int32_t(srcSize.width), int32_t(srcSize.height), int32_t(srcSize.depth) };\n\n      VkBlitImageInfo2 info = { VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2 };\n      info.dstImage = image->handle();\n      info.dstImageLayout = dstLayout;\n      info.srcImage = image->handle();\n      info.srcImageLayout = srcLayout;\n      info.regionCount = 1u;\n      info.pRegions = &blit;\n      info.filter = filter;\n\n      m_cmd->cmdBlitImage(&info);\n\n      srcSubresource.baseMipLevel += 1u;\n      dstSubresource.baseMipLevel += 1u;\n    }\n  }\n\n\n  void DxvkContext::generateMipmapsFb(\n    const Rc<DxvkImageView>&        imageView,\n          VkFilter                  filter) {\n    this->invalidateState();\n\n    // Make sure we can both render to and read from the image\n    VkFormat viewFormat = imageView->info().format;\n\n    DxvkImageUsageInfo usageInfo;\n    usageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;\n    usageInfo.viewFormatCount = 1;\n    usageInfo.viewFormats = &viewFormat;\n\n    if (!ensureImageCompatibility(imageView->image(), usageInfo)) {\n      Logger::err(str::format(\"DxvkContext: generateMipmaps: Unsupported operation:\"\n        \"\\n  view format:  \", imageView->info().format,\n        \"\\n  image format: \", imageView->image()->info().format));\n      return;\n    }\n\n    // Create image views, etc.\n    DxvkMetaMipGenViews mipGenerator(imageView, VK_PIPELINE_BIND_POINT_GRAPHICS);\n\n    // Common descriptor set properties that we use to\n    // bind the source image view to the fragment shader\n    Rc<DxvkSampler> sampler = createBlitSampler(filter);\n\n    DxvkDescriptorWrite imageDescriptor = { };\n    imageDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\n    // Retrieve a compatible pipeline to use for rendering\n    auto resolveMode = filter == VK_FILTER_NEAREST\n      ? DxvkMetaBlitResolveMode::FilterNearest\n      : DxvkMetaBlitResolveMode::FilterLinear;\n\n    DxvkMetaBlitPipeline pipeInfo = m_common->metaBlit().getPipeline(\n      mipGenerator.getSrcViewType(), imageView->info().format,\n      VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_1_BIT, resolveMode);\n\n    for (uint32_t i = 0; i < mipGenerator.getPassCount(); i++) {\n      auto srcView = mipGenerator.getSrcView(i);\n      auto dstView = mipGenerator.getDstView(i);\n\n      imageDescriptor.descriptor = srcView->getDescriptor();\n\n      // Width, height and layer count for the current pass\n      VkExtent3D passExtent = mipGenerator.computePassExtent(i);\n\n      // Set up viewport and scissor rect\n      VkViewport viewport;\n      viewport.x        = 0.0f;\n      viewport.y        = 0.0f;\n      viewport.width    = float(passExtent.width);\n      viewport.height   = float(passExtent.height);\n      viewport.minDepth = 0.0f;\n      viewport.maxDepth = 1.0f;\n\n      VkRect2D scissor;\n      scissor.offset    = { 0, 0 };\n      scissor.extent    = { passExtent.width, passExtent.height };\n\n      // Set up rendering info\n      VkRenderingAttachmentInfo attachmentInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n      attachmentInfo.imageView = dstView->handle();\n      attachmentInfo.imageLayout = dstView->getLayout();\n      attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n      attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\n      VkRenderingInfo renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n      renderingInfo.renderArea = scissor;\n      renderingInfo.layerCount = passExtent.depth;\n      renderingInfo.colorAttachmentCount = 1;\n      renderingInfo.pColorAttachments = &attachmentInfo;\n\n      // Set up push constants\n      DxvkMetaBlitPushConstants pushConstants = { };\n      pushConstants.srcCoord0  = { 0.0f, 0.0f, 0.0f };\n      pushConstants.srcCoord1  = { 1.0f, 1.0f, 1.0f };\n      pushConstants.layerCount = passExtent.depth;\n      pushConstants.sampler = sampler->getDescriptor().samplerIndex;\n\n      // Emit per-subresource barriers\n      small_vector<DxvkResourceAccess, 2u> accessBatch;\n      accessBatch.emplace_back(*srcView, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, false);\n      accessBatch.emplace_back(*dstView,  VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, true);\n      syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n      m_cmd->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderingInfo);\n\n      m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n        VK_PIPELINE_BIND_POINT_GRAPHICS, pipeInfo.pipeline);\n\n      m_cmd->cmdSetViewport(1, &viewport);\n      m_cmd->cmdSetScissor(1, &scissor);\n\n      m_cmd->bindResources(DxvkCmdBuffer::ExecBuffer,\n        pipeInfo.layout, 1u, &imageDescriptor,\n        sizeof(pushConstants), &pushConstants);\n\n      m_cmd->cmdDraw(3, passExtent.depth, 0, 0);\n      m_cmd->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n    }\n\n    m_cmd->track(std::move(sampler));\n  }\n\n\n  void DxvkContext::generateMipmapsCs(\n    const Rc<DxvkImageView>&        imageView) {\n    this->invalidateState();\n\n    // Number of descriptors to bind, first is for source mip\n    constexpr uint32_t MaxDescriptorCount = DxvkMetaMipGenObjects::MipCount * 2u + 1u;\n\n    // Maximum image dimension that the mip gen shader can handle in on\n    // pass. If the image is any larger, we need to emit extra passes.\n    constexpr uint32_t MaxSinglePassSize = (2u << (DxvkMetaMipGenObjects::MipCount * 2u)) - 1u;\n\n    // Linear filtering only with the current implementation\n    Rc<DxvkSampler> sampler = createBlitSampler(VK_FILTER_LINEAR);\n\n    // Create views. These are actually going to be usable even in a two-pass\n    // scenario since the src view is *always* sampled rather than storage.\n    DxvkMetaMipGenViews mipGenerator(imageView, VK_PIPELINE_BIND_POINT_COMPUTE);\n\n    uint32_t layerCount = imageView->info().layerCount;\n\n    // Check if we need to run two passes. This is only going to be the case if\n    // the view's top level is larger than the largest mip would be in an image\n    // where we downsample *twice* the per-step mip count, because the shader can\n    // read back one mip and process it in the last running workgroup. However,\n    // this only works on the actual mip tail of the base image, regardless of\n    // which views are accessed, so we also need to make sure that the pre-pass\n    // only processes a single batch of mips.\n    VkExtent3D baseExtent = imageView->mipLevelExtent(0u);\n\n    bool requiresPrepass = std::max(baseExtent.width, baseExtent.height) > MaxSinglePassSize\n      && mipGenerator.getPassCount() > DxvkMetaMipGenObjects::MipCount;\n\n    // Allocate scratch memory for per-layer workgroup counters and zero it on\n    // the out-of-order init command buffer to avoid having to synchronize\n    uint32_t counterDwordCount = layerCount * (requiresPrepass ? 2u : 1u);\n\n    DxvkResourceBufferInfo counterMemory = allocateScratchMemory(\n      sizeof(uint32_t), sizeof(uint32_t) * counterDwordCount);\n\n    m_cmd->cmdFillBuffer(DxvkCmdBuffer::InitBuffer,\n      counterMemory.buffer, counterMemory.offset, counterMemory.size, 0u);\n\n    VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;\n    barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;\n    barrier.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;\n    barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT;\n\n    m_initBarriers.addMemoryBarrier(barrier);\n\n    // Bind pipeline already, no reason to do it twice if we need a pre-pass\n    auto pipeline = m_common->metaMipGen().getPipeline(imageView->info().format);\n\n    m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.pipeline);\n\n    uint32_t basePass = 0u;\n\n    while (basePass < mipGenerator.getPassCount()) {\n      small_vector<DxvkResourceAccess, MaxDescriptorCount> accessBatch;\n\n      DxvkMetaMipGenPushConstants pushData = { };\n      pushData.atomicCounterVa = counterMemory.gpuAddress;\n      pushData.samplerIndex = sampler->getDescriptor().samplerIndex;\n\n      if (requiresPrepass && !basePass) {\n        // It is beneficial to run this on the maximum mip count here because\n        // sampling the top-level mip tends to be the primary bottleneck. We\n        // already ensured that all these views are included anyway.\n        pushData.mipCount = DxvkMetaMipGenObjects::MipCount;\n      } else {\n        // If we can, just do a single pass.\n        pushData.mipCount = mipGenerator.getPassCount() - basePass;\n      }\n\n      // Bind views only for this pass, set null descriptors for unused mips.\n      std::array<DxvkDescriptorWrite, MaxDescriptorCount> descriptors = { };\n\n      size_t n = 0u;\n\n      auto& descriptor = descriptors.at(n++);\n      descriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n      descriptor.descriptor = mipGenerator.getSrcView(basePass)->getDescriptor();\n\n      accessBatch.emplace_back(*mipGenerator.getSrcView(basePass),\n        VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, false);\n\n      for (uint32_t i = 0u; i < pushData.mipCount; i++) {\n        auto& descriptor = descriptors.at(n++);\n        descriptor.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n        descriptor.descriptor = mipGenerator.getDstView(basePass + i)->getDescriptor();\n\n        // Don't actually bother discarding mips here, there's *probably*\n        // no good reason to introduce an extra sync point just for that.\n        accessBatch.emplace_back(*mipGenerator.getDstView(basePass + i),\n          VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,\n          VK_ACCESS_2_SHADER_WRITE_BIT | VK_ACCESS_2_SHADER_READ_BIT, false);\n      }\n\n      while (n < descriptors.size()) {\n        auto& descriptor = descriptors.at(n++);\n        descriptor.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n      }\n\n      m_cmd->bindResources(DxvkCmdBuffer::ExecBuffer,\n        pipeline.layout, descriptors.size(), descriptors.data(),\n        sizeof(pushData), &pushData);\n\n      syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n      // Dispatch size must always match that of the bottom mip of one\n      // pass, even if that mip is not included in the view at all\n      VkExtent3D dispatchSize = imageView->mipLevelExtent(DxvkMetaMipGenObjects::MipCount);\n\n      m_cmd->cmdDispatch(DxvkCmdBuffer::ExecBuffer,\n        dispatchSize.width, dispatchSize.height, layerCount);\n\n      // Advance some stuff for the next pass\n      basePass += pushData.mipCount;\n\n      pushData.atomicCounterVa += layerCount * sizeof(uint32_t);\n    }\n\n    m_cmd->track(std::move(sampler));\n  }\n\n\n  void DxvkContext::resolveImageHw(\n    const Rc<DxvkImage>&            dstImage,\n    const Rc<DxvkImage>&            srcImage,\n    const VkImageResolve&           region) {\n    auto dstSubresourceRange = vk::makeSubresourceRange(region.dstSubresource);\n    auto srcSubresourceRange = vk::makeSubresourceRange(region.srcSubresource);\n\n    // We only support resolving to the entire image\n    // area, so we might as well discard its contents\n    VkImageLayout dstLayout = dstImage->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n    VkImageLayout srcLayout = srcImage->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*dstImage, dstSubresourceRange, dstLayout,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT,\n      dstImage->isFullSubresource(region.dstSubresource, region.extent));\n    accessBatch.emplace_back(*srcImage, srcSubresourceRange, srcLayout,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT, false);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    VkResolveImageModeInfoKHR resolveMode = { VK_STRUCTURE_TYPE_RESOLVE_IMAGE_MODE_INFO_KHR };\n\n    VkImageResolve2 resolveRegion = { VK_STRUCTURE_TYPE_IMAGE_RESOLVE_2 };\n    resolveRegion.srcSubresource = region.srcSubresource;\n    resolveRegion.srcOffset = region.srcOffset;\n    resolveRegion.dstSubresource = region.dstSubresource;\n    resolveRegion.dstOffset = region.dstOffset;\n    resolveRegion.extent = region.extent;\n\n    VkResolveImageInfo2 resolveInfo = { VK_STRUCTURE_TYPE_RESOLVE_IMAGE_INFO_2 };\n    resolveInfo.srcImage = srcImage->handle();\n    resolveInfo.srcImageLayout = srcLayout;\n    resolveInfo.dstImage = dstImage->handle();\n    resolveInfo.dstImageLayout = dstLayout;\n    resolveInfo.regionCount = 1;\n    resolveInfo.pRegions = &resolveRegion;\n\n    if (srcImage->formatInfo()->flags.test(DxvkFormatFlag::ColorSpaceSrgb)\n     && m_device->features().khrMaintenance10.maintenance10\n     && m_device->properties().khrMaintenance10.resolveSrgbFormatSupportsTransferFunctionControl) {\n      resolveMode.pNext = std::exchange(resolveInfo.pNext, &resolveMode);\n      resolveMode.flags = VK_RESOLVE_IMAGE_ENABLE_TRANSFER_FUNCTION_BIT_KHR;\n    }\n\n    m_cmd->cmdResolveImage(&resolveInfo);\n  }\n\n\n  void DxvkContext::resolveImageRp(\n    const Rc<DxvkImage>&            dstImage,\n    const Rc<DxvkImage>&            srcImage,\n    const VkImageResolve&           region,\n          VkFormat                  format,\n          VkResolveModeFlagBits     mode,\n          VkResolveModeFlagBits     stencilMode,\n          bool                      flushClears) {\n    auto dstSubresourceRange = vk::makeSubresourceRange(region.dstSubresource);\n    auto srcSubresourceRange = vk::makeSubresourceRange(region.srcSubresource);\n\n    bool isDepthStencil = (dstImage->formatInfo()->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      const char* dstName = dstImage->info().debugName;\n      const char* srcName = srcImage->info().debugName;\n\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xf0dcdc, str::format(\"Resolve pass (\",\n          dstName ? dstName : \"unknown\", \", \",\n          srcName ? srcName : \"unknown\", \")\").c_str()));\n    }\n\n    // Transition both images to usable layouts if necessary. For the source image\n    // we can be fairly lenient when dealing with writable depth-stencil layouts.\n    VkImageLayout writableLayout = isDepthStencil\n      ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL\n      : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\n    VkImageLayout dstLayout = dstImage->pickLayout(writableLayout);\n    VkImageLayout srcLayout = srcImage->info().layout;\n\n    if (srcLayout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL\n     && srcLayout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL)\n      srcLayout = srcImage->pickLayout(writableLayout);\n\n    // For some reason, someone somewhere decided that even depth-stencil\n    // resolves are performed with COLOR_ATTACHMENT stage and access\n    VkPipelineStageFlags2 stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;\n\n    VkAccessFlags2 srcAccess = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;\n    VkAccessFlags2 dstAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*srcImage, srcSubresourceRange, srcLayout, stages, srcAccess, false);\n    accessBatch.emplace_back(*dstImage, dstSubresourceRange, dstLayout, stages, dstAccess, true);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data(), flushClears);\n\n    // Create a pair of views for the attachment resolve\n    DxvkMetaResolveViews views(dstImage, region.dstSubresource,\n      srcImage, region.srcSubresource, format);\n\n    VkRenderingAttachmentFlagsInfoKHR attachmentFlags = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_FLAGS_INFO_KHR };\n\n    VkRenderingAttachmentInfo attachment = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n    attachment.imageView = views.srcView->handle();\n    attachment.imageLayout = srcLayout;\n    attachment.resolveMode = mode;\n    attachment.resolveImageView = views.dstView->handle();\n    attachment.resolveImageLayout = dstLayout;\n    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;\n    attachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE;\n\n    if (srcImage->formatInfo()->flags.test(DxvkFormatFlag::ColorSpaceSrgb)\n     && m_device->features().khrMaintenance10.maintenance10\n     && m_device->properties().khrMaintenance10.resolveSrgbFormatSupportsTransferFunctionControl) {\n      attachmentFlags.pNext = std::exchange(attachment.pNext, &attachmentFlags);\n      attachmentFlags.flags = VK_RENDERING_ATTACHMENT_RESOLVE_ENABLE_TRANSFER_FUNCTION_BIT_KHR;\n    }\n\n    VkRenderingAttachmentInfo stencilAttachment = attachment;\n    stencilAttachment.resolveMode = stencilMode;\n\n    VkExtent3D extent = dstImage->mipLevelExtent(region.dstSubresource.mipLevel);\n\n    VkRenderingInfo renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n    renderingInfo.renderArea.offset = VkOffset2D { 0, 0 };\n    renderingInfo.renderArea.extent = VkExtent2D { extent.width, extent.height };\n    renderingInfo.layerCount = region.dstSubresource.layerCount;\n\n    if (isDepthStencil) {\n      if (dstImage->formatInfo()->aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)\n        renderingInfo.pDepthAttachment = &attachment;\n\n      if (dstImage->formatInfo()->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)\n        renderingInfo.pStencilAttachment = &stencilAttachment;\n    } else {\n      renderingInfo.colorAttachmentCount = 1u;\n      renderingInfo.pColorAttachments = &attachment;\n    }\n\n    m_cmd->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderingInfo);\n    m_cmd->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n  }\n\n\n  void DxvkContext::resolveImageFb(\n    const Rc<DxvkImage>&            dstImage,\n    const Rc<DxvkImage>&            srcImage,\n    const VkImageResolve&           region,\n          VkFormat                  format,\n          VkResolveModeFlagBits     depthMode,\n          VkResolveModeFlagBits     stencilMode) {\n    this->invalidateState();\n\n    auto dstSubresourceRange = vk::makeSubresourceRange(region.dstSubresource);\n    auto srcSubresourceRange = vk::makeSubresourceRange(region.srcSubresource);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      const char* dstName = dstImage->info().debugName;\n      const char* srcName = srcImage->info().debugName;\n\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xf0dcdc, str::format(\"Resolve (\",\n          dstName ? dstName : \"unknown\", \", \",\n          srcName ? srcName : \"unknown\", \")\").c_str()));\n    }\n\n    // Create image views for the resolve operation\n    VkFormat dstFormat = format ? format : dstImage->info().format;\n    VkFormat srcFormat = format ? format : srcImage->info().format;\n\n    DxvkMetaCopyViews views(\n      dstImage, region.dstSubresource, dstFormat,\n      srcImage, region.srcSubresource, srcFormat);\n\n    // Discard the destination image if we're fully writing it,\n    // and transition the image layout if necessary\n    VkImageLayout dstLayout = views.dstImageView->getLayout();\n    VkImageLayout srcLayout = views.srcImageView->getLayout();\n\n    bool doDiscard = dstImage->isFullSubresource(region.dstSubresource, region.extent);\n\n    if (region.dstSubresource.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)\n      doDiscard &= depthMode != VK_RESOLVE_MODE_NONE;\n    if (region.dstSubresource.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)\n      doDiscard &= stencilMode != VK_RESOLVE_MODE_NONE;\n\n    VkPipelineStageFlags dstStages;\n    VkAccessFlags dstAccess;\n\n    if (region.dstSubresource.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {\n      dstStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n      dstAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\n      if (!doDiscard)\n        dstAccess |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;\n    } else {\n      dstStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n                | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n      dstAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n\n      if (!doDiscard)\n        dstAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;\n    }\n\n    small_vector<DxvkResourceAccess, 2u> accessBatch;\n    accessBatch.emplace_back(*dstImage, dstSubresourceRange, dstLayout, dstStages, dstAccess, doDiscard);\n    accessBatch.emplace_back(*srcImage, srcSubresourceRange, srcLayout,\n      VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, false);\n    syncResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    // Create a framebuffer and pipeline for the resolve op\n    DxvkMetaResolvePipeline pipeInfo = m_common->metaResolve().getPipeline(\n      dstFormat, srcImage->info().sampleCount, depthMode, stencilMode);\n\n    // Create and initialize descriptor set\n    std::array<DxvkDescriptorWrite, 2> descriptors = { };\n\n    auto& imagePlane0Descriptor = descriptors[0u];\n    imagePlane0Descriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n    imagePlane0Descriptor.descriptor = views.srcImageView->getDescriptor();\n\n    auto& imagePlane1Descriptor = descriptors[1u];\n    imagePlane1Descriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\n    if (views.srcStencilView)\n      imagePlane1Descriptor.descriptor = views.srcStencilView->getDescriptor();\n\n    // Set up render state    \n    VkViewport viewport = { };\n    viewport.x        = float(region.dstOffset.x);\n    viewport.y        = float(region.dstOffset.y);\n    viewport.width    = float(region.extent.width);\n    viewport.height   = float(region.extent.height);\n    viewport.minDepth = 0.0f;\n    viewport.maxDepth = 1.0f;\n\n    VkRect2D scissor = { };\n    scissor.offset = { region.dstOffset.x,  region.dstOffset.y   };\n    scissor.extent = { region.extent.width, region.extent.height };\n\n    VkRenderingAttachmentInfo attachmentInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n    attachmentInfo.imageView = views.dstImageView->handle();\n    attachmentInfo.imageLayout = dstLayout;\n    attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\n    VkRenderingInfo renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n    renderingInfo.renderArea = scissor;\n    renderingInfo.layerCount = region.dstSubresource.layerCount;\n    \n    VkImageAspectFlags dstAspects = dstImage->formatInfo()->aspectMask;\n\n    if (dstAspects & VK_IMAGE_ASPECT_COLOR_BIT) {\n      renderingInfo.colorAttachmentCount = 1;\n      renderingInfo.pColorAttachments = &attachmentInfo;\n    } else {\n      if (dstAspects & VK_IMAGE_ASPECT_DEPTH_BIT)\n        renderingInfo.pDepthAttachment = &attachmentInfo;\n      if (dstAspects & VK_IMAGE_ASPECT_STENCIL_BIT)\n        renderingInfo.pStencilAttachment = &attachmentInfo;\n    }\n\n    // Perform the actual resolve operation\n    VkOffset2D srcOffset = {\n      region.srcOffset.x - region.dstOffset.x,\n      region.srcOffset.y - region.dstOffset.y };\n    \n    m_cmd->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderingInfo);\n\n    m_cmd->cmdSetViewport(1, &viewport);\n    m_cmd->cmdSetScissor(1, &scissor);\n\n    m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, pipeInfo.pipeline);\n\n    m_cmd->bindResources(DxvkCmdBuffer::ExecBuffer,\n      pipeInfo.layout, descriptors.size(), descriptors.data(),\n      sizeof(srcOffset), &srcOffset);\n\n    m_cmd->cmdDraw(3, region.dstSubresource.layerCount, 0, 0);\n\n    m_cmd->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n  }\n\n\n  bool DxvkContext::resolveImageClear(\n    const Rc<DxvkImage>&            dstImage,\n    const Rc<DxvkImage>&            srcImage,\n    const VkImageResolve&           region,\n          VkFormat                  format) {\n    // If the destination image is only partially written, ignore\n    if (dstImage->mipLevelExtent(region.dstSubresource.mipLevel, region.dstSubresource.aspectMask) != region.extent)\n      return false;\n\n    // Find a pending clear that overlaps with the source image\n    const DxvkDeferredClear* clear = findDeferredClear(*srcImage, vk::makeSubresourceRange(region.srcSubresource));\n\n    if (!clear)\n      return false;\n\n    // The clear format must match the resolve format, or\n    // otherwise we cannot reuse the clear value\n    if (clear->imageView->info().format != format)\n      return false;\n\n    // Ensure that we can actually clear the image as intended. We can be\n    // aggressive here since we know the destination image has a format\n    // that can be used for rendering.\n    bool isDepthStencil = region.dstSubresource.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);\n\n    VkImageUsageFlagBits usageBit = isDepthStencil\n      ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT\n      : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\n    DxvkImageUsageInfo usage = { };\n    usage.usage = usageBit;\n    usage.viewFormatCount = 1;\n    usage.viewFormats = &format;\n\n    if (!ensureImageCompatibility(dstImage, usage))\n      return false;\n\n    // End current render pass and prepare the destination image\n    endCurrentPass(true);\n\n    // Create an image view that we can use to perform the clear\n    DxvkImageViewKey key = { };\n    key.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    key.usage = usageBit;\n    key.format = format;\n    key.aspects = region.dstSubresource.aspectMask;\n    key.layerIndex = region.dstSubresource.baseArrayLayer;\n    key.layerCount = region.dstSubresource.layerCount;\n    key.mipIndex = region.dstSubresource.mipLevel;\n    key.mipCount = 1u;\n\n    if (isDepthStencil)\n      key.aspects = dstImage->formatInfo()->aspectMask;\n\n    deferClear(dstImage->createView(key), region.dstSubresource.aspectMask, clear->clearValue);\n    return true;\n  }\n\n\n  bool DxvkContext::resolveImageInline(\n    const Rc<DxvkImage>&            dstImage,\n    const Rc<DxvkImage>&            srcImage,\n    const VkImageResolve&           region,\n          VkFormat                  format,\n          VkResolveModeFlagBits     depthMode,\n          VkResolveModeFlagBits     stencilMode) {\n    // Ignore any non-2D images due to the added complexity, and the\n    // source image is going to be a multisampled 2D image anyway.\n    if (dstImage->info().type != VK_IMAGE_TYPE_2D)\n      return false;\n\n    // Check if we can implement the resolve as a clear first\n    VkImageResolve clearRegion = region;\n\n    if (!depthMode) {\n      clearRegion.dstSubresource.aspectMask &= ~VK_IMAGE_ASPECT_DEPTH_BIT;\n      clearRegion.srcSubresource.aspectMask &= ~VK_IMAGE_ASPECT_DEPTH_BIT;\n    }\n\n    if (!stencilMode) {\n      clearRegion.dstSubresource.aspectMask &= ~VK_IMAGE_ASPECT_STENCIL_BIT;\n      clearRegion.srcSubresource.aspectMask &= ~VK_IMAGE_ASPECT_STENCIL_BIT;\n    }\n\n    if (resolveImageClear(dstImage, srcImage, clearRegion, format))\n      return true;\n\n    // Need an active render pass with secondary command buffers to\n    // fold resolve operations into it\n    if (!m_flags.test(DxvkContextFlag::GpRenderPassSecondaryCmd))\n      return false;\n\n    // Otherwise, if any clears are queued up for the source image,\n    // that clear operation needs to be performed first.\n    if (findOverlappingDeferredClear(*srcImage, vk::makeSubresourceRange(region.srcSubresource)))\n      flushClearsInline();\n\n    // Check whether we're dealing with a color or depth attachment, one\n    // of those flags needs to be set for resolve to even make sense\n    VkImageUsageFlags usage = srcImage->info().usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);\n\n    if (!usage)\n      return false;\n\n    // We can't support partial resolves here\n    if (region.srcOffset.x || region.srcOffset.y || region.srcOffset.z\n     || region.dstOffset.x || region.dstOffset.y || region.dstOffset.z\n     || region.extent != dstImage->mipLevelExtent(region.dstSubresource.mipLevel, region.dstSubresource.aspectMask)\n     || region.extent.width != m_state.om.renderingInfo.rendering.renderArea.extent.width\n     || region.extent.height != m_state.om.renderingInfo.rendering.renderArea.extent.height)\n      return false;\n\n    // The array layer we're dealing with relative to the source image,\n    // if layered resolves are split across multiple resolve calls.\n    uint32_t relativeLayer = 0u;\n\n    DxvkImageViewKey dstKey = { };\n    dstKey.usage = VkImageUsageFlagBits(usage);\n    dstKey.aspects = region.dstSubresource.aspectMask;\n    dstKey.mipIndex = region.dstSubresource.mipLevel;\n    dstKey.mipCount = 1u;\n\n    if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)\n      dstKey.aspects = lookupFormatInfo(format)->aspectMask;\n\n    // Need the source image to be bound with a superset of the subresources that\n    // we're resolving. The resolve format is allowed to differ in sRGB-ness only.\n    int32_t attachmentIndex = -1;\n\n    for (uint32_t i = 0; i < m_state.om.framebufferInfo.numAttachments(); i++) {\n      const auto& attachment = m_state.om.framebufferInfo.getAttachment(i);\n\n      if (attachment.view->image() == srcImage) {\n        VkImageSubresourceRange subresources = attachment.view->imageSubresources();\n\n        if ((subresources.aspectMask & region.srcSubresource.aspectMask)\n         && (subresources.baseMipLevel == region.srcSubresource.mipLevel)\n         && (subresources.levelCount == 1u)\n         && (subresources.baseArrayLayer <= region.srcSubresource.baseArrayLayer)\n         && (subresources.baseArrayLayer + subresources.layerCount >= region.srcSubresource.baseArrayLayer + region.srcSubresource.layerCount)\n         && formatsAreResolveCompatible(format, attachment.view->info().format)) {\n          relativeLayer = region.srcSubresource.baseArrayLayer - subresources.baseArrayLayer;\n\n          dstKey.viewType = attachment.view->info().viewType;\n          dstKey.format = attachment.view->info().format;\n\n          attachmentIndex = i;\n          break;\n        }\n      }\n    }\n\n    if (attachmentIndex < 0)\n      return false;\n\n    // Need to check if the source image is actually currently bound for rendering\n    if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {\n      uint32_t index = m_state.om.framebufferInfo.getColorAttachmentIndex(attachmentIndex);\n\n      if (index >= m_state.om.renderingInfo.rendering.colorAttachmentCount\n       || !m_state.om.renderingInfo.color[index].imageView)\n        return false;\n    } else {\n      if ((!m_state.om.renderingInfo.rendering.pDepthAttachment || !m_state.om.renderingInfo.depth.imageView)\n       && (!m_state.om.renderingInfo.rendering.pStencilAttachment || !m_state.om.renderingInfo.stencil.imageView))\n        return false;\n    }\n\n    // If we can't properly map bound array layers to the detination image,\n    // skip. This could e.g. be the case if we're trying to resolve relative\n    // layer 1 into destination layer 0.\n    if (relativeLayer > region.dstSubresource.baseArrayLayer)\n      return false;\n\n    dstKey.layerIndex = region.dstSubresource.baseArrayLayer - relativeLayer;\n    dstKey.layerCount = m_state.om.renderingInfo.rendering.layerCount;\n\n    if (dstKey.layerIndex + dstKey.layerCount > dstImage->info().numLayers)\n      return false;\n\n    // Create view to bind as the attachment\n    Rc<DxvkImageView> dstView = dstImage->createView(dstKey);\n\n    // Detect duplicate resolves, and error out if we\n    // have already set a different resolve attachment\n    auto& resolve = m_deferredResolves.at(attachmentIndex);\n\n    if (resolve.imageView && resolve.imageView != dstView)\n      return false;\n\n    resolve.imageView = dstView;\n    resolve.layerMask |= 1u << relativeLayer;\n    resolve.depthMode = depthMode;\n    resolve.stencilMode = stencilMode;\n\n    // Ensure that the resolve happens in linear space only if the resolve format is sRGB\n    if (dstView->formatInfo()->flags.test(DxvkFormatFlag::ColorSpaceSrgb)\n     && m_device->properties().khrMaintenance10.resolveSrgbFormatSupportsTransferFunctionControl) {\n      resolve.flags = lookupFormatInfo(format)->flags.test(DxvkFormatFlag::ColorSpaceSrgb)\n        ? VK_RENDERING_ATTACHMENT_RESOLVE_ENABLE_TRANSFER_FUNCTION_BIT_KHR\n        : VK_RENDERING_ATTACHMENT_RESOLVE_SKIP_TRANSFER_FUNCTION_BIT_KHR;\n    }\n\n    // Ensure resolves get flushed before the next draw\n    m_flags.set(DxvkContextFlag::GpRenderPassNeedsFlush);\n    return true;\n  }\n\n\n  void DxvkContext::uploadImageFb(\n    const Rc<DxvkImage>&            image,\n    const Rc<DxvkBuffer>&           source,\n          VkDeviceSize              sourceOffset,\n          VkDeviceSize              subresourceAlignment,\n          VkFormat                  format) {\n    if (!format)\n      format = image->info().format;\n\n    for (uint32_t m = 0; m < image->info().mipLevels; m++) {\n      VkExtent3D mipExtent = image->mipLevelExtent(m);\n\n      VkDeviceSize mipSize = util::computeImageDataSize(\n        format, mipExtent, image->formatInfo()->aspectMask);\n\n      for (uint32_t l = 0; l < image->info().numLayers; l++) {\n        VkImageSubresourceLayers layers = { };\n        layers.aspectMask = image->formatInfo()->aspectMask;\n        layers.mipLevel = m;\n        layers.baseArrayLayer = l;\n        layers.layerCount = 1;\n\n        copyBufferToImageFb(image, layers,\n          VkOffset3D { 0, 0, 0 }, mipExtent,\n          source, sourceOffset, 0, 0, format);\n\n        sourceOffset += align(mipSize, subresourceAlignment);\n      }\n    }\n  }\n\n\n  void DxvkContext::uploadImageHw(\n    const Rc<DxvkImage>&            image,\n    const Rc<DxvkBuffer>&           source,\n          VkDeviceSize              sourceOffset,\n          VkDeviceSize              subresourceAlignment) {\n    // Initialize all subresources of the image at once\n    VkImageLayout transferLayout = image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n\n    addImageInitTransition(*image, image->getAvailableSubresources(),\n      transferLayout, VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n    flushImageLayoutTransitions(DxvkCmdBuffer::SdmaBuffer);\n\n    // Copy image data, one mip at a time\n    VkDeviceSize dataOffset = sourceOffset;\n\n    for (uint32_t m = 0; m < image->info().mipLevels; m++) {\n      VkExtent3D mipExtent = image->mipLevelExtent(m);\n\n      VkDeviceSize mipSize = util::computeImageDataSize(\n        image->info().format, mipExtent, image->formatInfo()->aspectMask);\n\n      for (uint32_t l = 0; l < image->info().numLayers; l++) {\n        VkImageSubresourceLayers layers = { };\n        layers.aspectMask = image->formatInfo()->aspectMask;\n        layers.mipLevel = m;\n        layers.baseArrayLayer = l;\n        layers.layerCount = 1;\n\n        copyImageBufferData<true>(DxvkCmdBuffer::SdmaBuffer,\n          image, layers, VkOffset3D { 0, 0, 0 }, mipExtent, transferLayout,\n          source->getSliceInfo(dataOffset, mipSize), 0, 0);\n\n        dataOffset += align(mipSize, subresourceAlignment);\n      }\n    }\n\n    accessImageTransfer(*image, image->getAvailableSubresources(), transferLayout,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);\n\n    image->trackLayout(image->getAvailableSubresources(), image->info().layout);\n\n    m_cmd->track(source, DxvkAccess::Read);\n    m_cmd->track(image, DxvkAccess::Write);\n  }\n\n\n  void DxvkContext::beginRenderPass() {\n    if (!m_flags.test(DxvkContextFlag::GpRenderPassActive)) {\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n        popDebugRegion(util::DxvkDebugLabelType::InternalBarrierControl);\n\n      prepareShaderReadableImages(true);\n\n      // Make sure all graphics state gets reapplied on the next draw\n      m_descriptorState.clearStages(VK_SHADER_STAGE_COMPUTE_BIT);\n      m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS);\n\n      m_flags.set(\n        DxvkContextFlag::GpRenderPassActive,\n        DxvkContextFlag::GpDirtyPipelineState,\n        DxvkContextFlag::GpDirtyVertexBuffers,\n        DxvkContextFlag::GpDirtyIndexBuffer,\n        DxvkContextFlag::GpDirtyXfbBuffers,\n        DxvkContextFlag::GpDirtyBlendConstants,\n        DxvkContextFlag::GpDirtyStencilTest,\n        DxvkContextFlag::GpDirtyStencilRef,\n        DxvkContextFlag::GpDirtyMultisampleState,\n        DxvkContextFlag::GpDirtyRasterizerState,\n        DxvkContextFlag::GpDirtySampleLocations,\n        DxvkContextFlag::GpDirtyViewport,\n        DxvkContextFlag::GpDirtyDepthBias,\n        DxvkContextFlag::GpDirtyDepthBounds,\n        DxvkContextFlag::GpDirtyDepthClip,\n        DxvkContextFlag::GpDirtyDepthTest);\n\n      m_flags.clr(\n        DxvkContextFlag::GpRenderPassSuspended,\n        DxvkContextFlag::GpIndependentSets);\n\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n        beginRenderPassDebugRegion();\n\n      this->renderPassBindFramebuffer(\n        m_state.om.framebufferInfo,\n        m_state.om.renderPassOps);\n\n      // Don't discard image contents if we have\n      // to spill the current render pass\n      this->resetRenderPassOps(\n        m_state.om.renderTargets,\n        m_state.om.renderPassOps);\n      \n      // Begin occlusion queries\n      m_queryManager.beginQueries(m_cmd, VK_QUERY_TYPE_OCCLUSION);\n      m_queryManager.beginQueries(m_cmd, VK_QUERY_TYPE_PIPELINE_STATISTICS);\n    }\n  }\n  \n  \n  void DxvkContext::endRenderPass(bool suspend) {\n    if (m_flags.test(DxvkContextFlag::GpRenderPassActive)) {\n      bool unsynchronized = m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized);\n\n      if (unsynchronized) {\n        // Do not allow any graphics shader hazards across render passes\n        m_flags.set(DxvkContextFlag::ForceWriteAfterWriteSync);\n      }\n\n      m_flags.clr(DxvkContextFlag::GpRenderPassActive,\n                  DxvkContextFlag::GpRenderPassSideEffects,\n                  DxvkContextFlag::GpRenderPassNeedsFlush,\n                  DxvkContextFlag::GpRenderPassUnsynchronized);\n\n      this->pauseTransformFeedback();\n\n      m_queryManager.endQueries(m_cmd, VK_QUERY_TYPE_OCCLUSION);\n      m_queryManager.endQueries(m_cmd, VK_QUERY_TYPE_PIPELINE_STATISTICS);\n\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n        popDebugRegion(util::DxvkDebugLabelType::InternalBarrierControl);\n\n      this->renderPassUnbindFramebuffer();\n\n      if (suspend)\n        m_flags.set(DxvkContextFlag::GpRenderPassSuspended);\n\n      // For unsynchronized passes, we have full barrier tracking info for all\n      // draws that were performed. Otherwise, emit a barrier to prevent hazards\n      // with resources that were not tracked due to perf reasons.\n      if (!unsynchronized) {\n        if (m_renderPassBarrierSrc.stages) {\n          accessMemory(DxvkCmdBuffer::ExecBuffer,\n            m_renderPassBarrierSrc.stages, m_renderPassBarrierSrc.access,\n            m_renderPassBarrierDst.stages, m_renderPassBarrierDst.access);\n\n          m_renderPassBarrierSrc = DxvkGlobalPipelineBarrier();\n        }\n\n        flushBarriers();\n      }\n\n      flushResolves();\n\n      if (!suspend)\n        prepareShaderReadableImages(false);\n\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n        popDebugRegion(util::DxvkDebugLabelType::InternalRenderPass);\n    } else if (!suspend) {\n      // We may be ending a previously suspended render pass\n      m_flags.clr(DxvkContextFlag::GpRenderPassSuspended);\n\n      // Ensure that all shader readable images are ready\n      // to be read and in their default layout\n      prepareShaderReadableImages(false);\n    }\n  }\n\n\n  void DxvkContext::endCurrentPass(bool suspend) {\n    endRenderPass(suspend);\n    endComputePass();\n  }\n\n\n  void DxvkContext::acquireRenderTargets(\n    const DxvkFramebufferInfo&  framebufferInfo,\n          DxvkRenderPassOps&    ops) {\n    m_rtAccess.clear();\n\n    // Try to perform the layout transitions on the init command buffer to avoid\n    // unnecessary synchronization, especially around unsynchronized render passes\n    // with a clear. We can't use the regular prepareOutOfOrderTransfer function\n    // here because that will ignore bound render targets.\n    DxvkCmdBuffer cmdBuffer = DxvkCmdBuffer::InitBuffer;\n\n    // Transition all images to the render layout as necessary\n    const auto& depthAttachment = framebufferInfo.getDepthTarget();\n\n    if (depthAttachment.view) {\n      VkImageLayout layout = depthAttachment.view->getLayout();\n\n      VkPipelineStageFlags2 depthStages =\n        VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT |\n        VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT;\n      VkAccessFlags2 depthAccess = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT;\n\n      if (layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL)\n        depthAccess |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n\n      if (layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {\n        depthStages |= m_device->getShaderPipelineStages();\n        depthAccess |= VK_ACCESS_2_SHADER_READ_BIT;\n      }\n\n      VkImageLayout currentLayout = depthAttachment.view->image()->queryLayout(depthAttachment.view->imageSubresources());\n\n      if (currentLayout == VK_IMAGE_LAYOUT_UNDEFINED) {\n        if (ops.depthOps.loadOpD == VK_ATTACHMENT_LOAD_OP_LOAD)\n          ops.depthOps.loadOpD = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n        if (ops.depthOps.loadOpS == VK_ATTACHMENT_LOAD_OP_LOAD)\n          ops.depthOps.loadOpS = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n      }\n\n      VkImageAspectFlags preserveAspects = depthAttachment.view->info().aspects;\n\n      if (ops.depthOps.loadOpD == VK_ATTACHMENT_LOAD_OP_CLEAR\n       || ops.depthOps.loadOpD == VK_ATTACHMENT_LOAD_OP_DONT_CARE)\n        preserveAspects &= ~VK_IMAGE_ASPECT_DEPTH_BIT;\n\n      if (ops.depthOps.loadOpS == VK_ATTACHMENT_LOAD_OP_CLEAR\n       || ops.depthOps.loadOpS == VK_ATTACHMENT_LOAD_OP_DONT_CARE)\n        preserveAspects &= ~VK_IMAGE_ASPECT_STENCIL_BIT;\n\n      m_rtAccess.emplace_back(*depthAttachment.view, depthStages, depthAccess, !preserveAspects);\n\n      if (!prepareOutOfOrderTransition(*depthAttachment.view->image()))\n        cmdBuffer = DxvkCmdBuffer::ExecBuffer;\n    }\n\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      const auto& colorAttachment = framebufferInfo.getColorTarget(i);\n\n      if (colorAttachment.view) {\n        VkAccessFlags2 colorAccess = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT\n                                   | VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT;\n\n        VkPipelineStageFlags2 colorStages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;\n\n        VkImageLayout currentLayout = colorAttachment.view->image()->queryLayout(colorAttachment.view->imageSubresources());\n\n        if (currentLayout == VK_IMAGE_LAYOUT_UNDEFINED && ops.colorOps[i].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD)\n          ops.colorOps[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\n        bool discard = ops.colorOps[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR\n                    || ops.colorOps[i].loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\n        m_rtAccess.emplace_back(*colorAttachment.view, colorStages, colorAccess, discard);\n\n        if (!prepareOutOfOrderTransition(*colorAttachment.view->image()))\n          cmdBuffer = DxvkCmdBuffer::ExecBuffer;\n      }\n    }\n\n    // For regularly synchronized render passes, emit barriers here. We need to do this\n    // even if there are no layout transitions, since we don't track resource usage during\n    // render passes.\n    if (!m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized))\n      flushBarriers();\n\n    // Ignore clears, we should already have processed all of them.\n    acquireResources(cmdBuffer, m_rtAccess.size(), m_rtAccess.data(), false);\n  }\n\n\n  void DxvkContext::releaseRenderTargets() {\n    releaseResources(DxvkCmdBuffer::ExecBuffer, m_rtAccess.size(), m_rtAccess.data());\n\n    m_rtAccess.clear();\n  }\n\n\n  bool DxvkContext::renderPassStartUnsynchronized() {\n    // Don't even try if there is a depth buffer bound, this is most likely\n    // either a shadow pass or an otherwise expensive main render pass.\n    //\n    // This will create some false positives, but that is okay.\n    if (m_state.om.framebufferInfo.getDepthTarget().view)\n      return false;\n\n    // Same for MSAA. This is actually necessary for correctness as well\n    // since handling render pass resolves gets all sorts of problematic,\n    // and those cases are currently not handled.\n    if (m_state.gp.state.ms.sampleCount() != VK_SAMPLE_COUNT_1_BIT)\n      return false;\n\n    // Otherwise, we are probably good. Post-processing and UI passes don't\n    // typically have a lot of draws, or a lot of resources per draw.\n    return true;\n  }\n\n\n  void DxvkContext::renderPassBindFramebuffer(\n    const DxvkFramebufferInfo&  framebufferInfo,\n          DxvkRenderPassOps&    ops) {\n    const DxvkFramebufferSize fbSize = framebufferInfo.size();\n\n    acquireRenderTargets(framebufferInfo, ops);\n\n    m_state.om.attachmentMask.clear();\n\n    VkCommandBufferInheritanceRenderingInfo renderingInheritance = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO };\n    VkCommandBufferInheritanceInfo inheritance = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, &renderingInheritance };\n\n    uint32_t colorInfoCount = 0;\n    uint32_t lateClearCount = 0;\n\n    std::array<VkFormat, MaxNumRenderTargets> colorFormats = { };\n    std::array<VkClearAttachment, MaxNumRenderTargets> lateClears = { };\n\n    bool hasMipmappedRt = false;\n\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      const auto& colorTarget = framebufferInfo.getColorTarget(i);\n\n      auto& colorInfo = m_state.om.renderingInfo.color[i];\n      colorInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n\n      if (colorTarget.view != nullptr) {\n        colorFormats[i] = colorTarget.view->info().format;\n\n        colorInfo.imageView = colorTarget.view->handle();\n        colorInfo.imageLayout = colorTarget.view->getLayout();\n        colorInfo.loadOp = ops.colorOps[i].loadOp;\n        colorInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\n        renderingInheritance.rasterizationSamples = colorTarget.view->image()->info().sampleCount;\n\n        if (ops.colorOps[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) {\n          colorInfo.clearValue.color = ops.colorOps[i].clearValue;\n\n          if (m_device->perfHints().renderPassClearFormatBug\n           && colorTarget.view->info().format != colorTarget.view->image()->info().format) {\n            colorInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\n            auto& clear = lateClears[lateClearCount++];\n            clear.colorAttachment = i;\n            clear.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n            clear.clearValue.color = ops.colorOps[i].clearValue;\n          }\n        }\n\n        colorInfoCount = i + 1;\n\n        hasMipmappedRt |= colorTarget.view->image()->info().mipLevels > 1u;\n\n        if ((colorTarget.view->image()->info().usage & VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT)\n         && m_device->features().khrUnifiedImageLayouts.unifiedImageLayouts) {\n          auto& feedbackLoopInfo = m_state.om.renderingInfo.colorFeedbackLoop[i];\n          feedbackLoopInfo = { VK_STRUCTURE_TYPE_ATTACHMENT_FEEDBACK_LOOP_INFO_EXT };\n          feedbackLoopInfo.pNext = std::exchange(colorInfo.pNext, &feedbackLoopInfo);\n          feedbackLoopInfo.feedbackLoopEnable = true;\n        }\n\n        // Attachment flags are used to control sRGB resolves\n        if (m_device->features().khrMaintenance10.maintenance10) {\n          auto& flagInfo = m_state.om.renderingInfo.colorAttachmentFlags[i];\n          flagInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_FLAGS_INFO_KHR };\n          flagInfo.pNext = std::exchange(colorInfo.pNext, &flagInfo);\n        }\n      }\n    }\n\n    VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED;\n    VkImageAspectFlags depthStencilAspects = 0;\n    VkImageAspectFlags depthStencilWritable = 0;\n\n    auto& depthInfo = m_state.om.renderingInfo.depth;\n    depthInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n\n    const auto& depthTarget = framebufferInfo.getDepthTarget();\n\n    if (depthTarget.view) {\n      depthStencilFormat = depthTarget.view->info().format;\n      depthStencilAspects = depthTarget.view->info().aspects;\n      depthStencilWritable = vk::getWritableAspectsForLayout(depthTarget.view->info().layout);\n\n      if (!m_device->properties().khrMaintenance7.separateDepthStencilAttachmentAccess && depthStencilWritable)\n        depthStencilWritable = depthStencilAspects;\n\n      depthInfo.imageView = depthTarget.view->handle();\n      depthInfo.imageLayout = depthTarget.view->getLayout();\n      depthInfo.loadOp = ops.depthOps.loadOpD;\n      depthInfo.storeOp = (depthStencilWritable & VK_IMAGE_ASPECT_DEPTH_BIT)\n        ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_NONE;\n\n      if (ops.depthOps.loadOpD == VK_ATTACHMENT_LOAD_OP_CLEAR) {\n        depthInfo.clearValue.depthStencil.depth = ops.depthOps.clearValue.depth;\n        depthInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n      }\n\n      renderingInheritance.rasterizationSamples = depthTarget.view->image()->info().sampleCount;\n\n      if ((depthTarget.view->image()->info().usage & VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT)\n       && m_device->features().khrUnifiedImageLayouts.unifiedImageLayouts) {\n        auto& feedbackLoopInfo = m_state.om.renderingInfo.depthStencilFeedbackLoop;\n        feedbackLoopInfo = { VK_STRUCTURE_TYPE_ATTACHMENT_FEEDBACK_LOOP_INFO_EXT };\n        feedbackLoopInfo.pNext = std::exchange(depthInfo.pNext, &feedbackLoopInfo);\n        feedbackLoopInfo.feedbackLoopEnable = true;\n      }\n    }\n\n    auto& stencilInfo = m_state.om.renderingInfo.stencil;\n    stencilInfo = depthInfo;\n\n    if (depthTarget.view) {\n      stencilInfo.loadOp = ops.depthOps.loadOpS;\n      stencilInfo.storeOp = (depthStencilWritable & VK_IMAGE_ASPECT_STENCIL_BIT)\n        ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_NONE;\n\n      if (ops.depthOps.loadOpS == VK_ATTACHMENT_LOAD_OP_CLEAR) {\n        stencilInfo.clearValue.depthStencil.stencil = ops.depthOps.clearValue.stencil;\n        stencilInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n      }\n    }\n\n    auto& renderingInfo = m_state.om.renderingInfo.rendering;\n    renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n    renderingInfo.renderArea.offset = VkOffset2D { 0, 0 };\n    renderingInfo.renderArea.extent = VkExtent2D { fbSize.width, fbSize.height };\n    renderingInfo.layerCount = fbSize.layers;\n\n    if (colorInfoCount) {\n      renderingInfo.colorAttachmentCount = colorInfoCount;\n      renderingInfo.pColorAttachments = m_state.om.renderingInfo.color.data();\n      renderingInheritance.colorAttachmentCount = colorInfoCount;\n      renderingInheritance.pColorAttachmentFormats = colorFormats.data();\n    }\n\n    if (depthStencilAspects & VK_IMAGE_ASPECT_DEPTH_BIT) {\n      renderingInfo.pDepthAttachment = &depthInfo;\n      renderingInheritance.depthAttachmentFormat = depthStencilFormat;\n    }\n\n    if (depthStencilAspects & VK_IMAGE_ASPECT_STENCIL_BIT) {\n      renderingInfo.pStencilAttachment = &stencilInfo;\n      renderingInheritance.stencilAttachmentFormat = depthStencilFormat;\n    }\n\n    // Reset render area tracking, will be adjusted when drawing with viewports.\n    m_state.om.renderAreaLo = VkOffset2D { int32_t(fbSize.width), int32_t(fbSize.height) };\n    m_state.om.renderAreaHi = VkOffset2D { 0, 0 };\n\n    if (lateClearCount)\n      std::swap(m_state.om.renderAreaLo, m_state.om.renderAreaHi);\n\n    // On drivers that don't natively support secondary command buffers, only use\n    // them to enable MSAA resolve attachments. Also ignore render passes with only\n    // one color attachment here since those tend to only have a small number of\n    // draws and we are almost certainly going to use the output anyway.\n    bool useSecondaryCmdBuffer = !m_device->perfHints().preferPrimaryCmdBufs\n      && renderingInheritance.rasterizationSamples > VK_SAMPLE_COUNT_1_BIT;\n\n    if (m_device->perfHints().preferRenderPassOps) {\n      useSecondaryCmdBuffer = renderingInheritance.rasterizationSamples > VK_SAMPLE_COUNT_1_BIT;\n\n      if (!m_device->perfHints().preferPrimaryCmdBufs)\n        useSecondaryCmdBuffer |= depthStencilAspects || colorInfoCount > 1u || !hasMipmappedRt;\n    }\n\n    if (useSecondaryCmdBuffer) {\n      // Begin secondary command buffer on tiling GPUs so that subsequent\n      // resolve, discard and clear commands can modify render pass ops.\n      m_flags.set(DxvkContextFlag::GpRenderPassSecondaryCmd);\n\n      renderingInfo.flags = VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT;\n\n      m_cmd->beginSecondaryCommandBuffer(inheritance);\n    } else {\n      // Begin rendering right away on regular GPUs\n      m_cmd->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderingInfo);\n    }\n\n    if (lateClearCount) {\n      VkClearRect clearRect = { };\n      clearRect.rect.extent.width   = fbSize.width;\n      clearRect.rect.extent.height  = fbSize.height;\n      clearRect.layerCount          = fbSize.layers;\n\n      m_cmd->cmdClearAttachments(DxvkCmdBuffer::ExecBuffer, lateClearCount, lateClears.data(), 1, &clearRect);\n    }\n\n    for (uint32_t i = 0; i < framebufferInfo.numAttachments(); i++) {\n      const auto& attachment = framebufferInfo.getAttachment(i);\n      m_cmd->track(attachment.view->image(), DxvkAccess::Write);\n\n      if (attachment.view->isMultisampled()) {\n        VkImageSubresourceRange subresources = attachment.view->imageSubresources();\n\n        if (subresources.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n          subresources.aspectMask = vk::getWritableAspectsForLayout(attachment.view->info().layout);\n\n        m_implicitResolves.invalidate(*attachment.view->image(), subresources);\n      }\n    }\n\n    m_cmd->addStatCtr(DxvkStatCounter::CmdRenderPassCount, 1u);\n  }\n  \n  \n  void DxvkContext::renderPassUnbindFramebuffer() {\n    if (m_flags.test(DxvkContextFlag::GpRenderPassSecondaryCmd)) {\n      m_flags.clr(DxvkContextFlag::GpRenderPassSecondaryCmd);\n      VkCommandBuffer cmdBuffer = m_cmd->endSecondaryCommandBuffer();\n\n      // Record scoped rendering commands with potentially\n      // modified store or resolve ops here\n      flushRenderPassResolves();\n      flushRenderPassDiscards();\n\n      // Need information about clears, discards and resolve\n      // to set up the final load and store ops for the pass\n      finalizeLoadStoreOps();\n\n      auto& renderingInfo = m_state.om.renderingInfo.rendering;\n      m_cmd->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderingInfo);\n      m_cmd->cmdExecuteCommands(1, &cmdBuffer);\n    }\n\n    // End actual rendering command\n    m_cmd->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n\n    // Emit render target barriers\n    releaseRenderTargets();\n  }\n  \n  \n  void DxvkContext::resetRenderPassOps(\n    const DxvkRenderTargets&    renderTargets,\n          DxvkRenderPassOps&    renderPassOps) {\n    renderPassOps.depthOps = DxvkDepthAttachmentOps {\n      VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_LOAD_OP_LOAD };\n    \n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++)\n      renderPassOps.colorOps[i] = DxvkColorAttachmentOps { VK_ATTACHMENT_LOAD_OP_LOAD };\n  }\n  \n  \n  void DxvkContext::startTransformFeedback() {\n    if (!m_flags.test(DxvkContextFlag::GpXfbActive)) {\n      m_flags.set(DxvkContextFlag::GpXfbActive);\n\n      VkBuffer     ctrBuffers[MaxNumXfbBuffers];\n      VkDeviceSize ctrOffsets[MaxNumXfbBuffers];\n\n      for (uint32_t i = 0; i < MaxNumXfbBuffers; i++) {\n        m_state.xfb.activeCounters[i] = m_state.xfb.counters[i];\n        auto bufferSlice = m_state.xfb.activeCounters[i].getSliceInfo();\n\n        ctrBuffers[i] = bufferSlice.buffer;\n        ctrOffsets[i] = bufferSlice.offset;\n\n        if (bufferSlice.buffer) {\n          // Just in case someone is mad enough to write to a\n          // transform feedback buffer from a shader as well\n          m_flags.set(DxvkContextFlag::ForceWriteAfterWriteSync);\n\n          accessBuffer(DxvkCmdBuffer::ExecBuffer, m_state.xfb.activeCounters[i],\n            VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,\n            VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT |\n            VK_ACCESS_2_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,\n            DxvkAccessOp::None);\n\n          m_cmd->track(m_state.xfb.activeCounters[i].buffer(), DxvkAccess::Write);\n        }\n      }\n      \n      m_cmd->cmdBeginTransformFeedback(\n        0, MaxNumXfbBuffers, ctrBuffers, ctrOffsets);\n      \n      m_queryManager.beginQueries(m_cmd,\n        VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT);\n    }\n  }\n\n\n  void DxvkContext::pauseTransformFeedback() {\n    if (m_flags.test(DxvkContextFlag::GpXfbActive)) {\n      m_flags.clr(DxvkContextFlag::GpXfbActive);\n      \n      VkBuffer     ctrBuffers[MaxNumXfbBuffers];\n      VkDeviceSize ctrOffsets[MaxNumXfbBuffers];\n\n      for (uint32_t i = 0; i < MaxNumXfbBuffers; i++) {\n        auto bufferSlice = m_state.xfb.activeCounters[i].getSliceInfo();\n\n        ctrBuffers[i] = bufferSlice.buffer;\n        ctrOffsets[i] = bufferSlice.offset;\n\n        m_state.xfb.activeCounters[i] = DxvkBufferSlice();\n      }\n\n      m_queryManager.endQueries(m_cmd, \n        VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT);\n      \n      m_cmd->cmdEndTransformFeedback(\n        0, MaxNumXfbBuffers, ctrBuffers, ctrOffsets);\n    }\n  }\n\n\n  void DxvkContext::unbindComputePipeline() {\n    m_flags.set(DxvkContextFlag::CpDirtyPipelineState);\n    m_flags.clr(DxvkContextFlag::CpHasPushData);\n\n    m_state.cp.pipeline = nullptr;\n  }\n  \n  \n  bool DxvkContext::updateComputePipelineState() {\n    if (unlikely(m_state.gp.pipeline != nullptr))\n      this->unbindGraphicsPipeline();\n\n    m_flags.clr(DxvkContextFlag::CpHasPushData);\n\n    // Look up pipeline object based on the bound compute shader\n    auto newPipeline = lookupComputePipeline(m_state.cp.shaders);\n    m_state.cp.pipeline = newPipeline;\n\n    if (unlikely(!newPipeline))\n      return false;\n\n    auto newLayout = newPipeline->getLayout()->getLayout(DxvkPipelineLayoutType::Merged);\n\n    if (unlikely(newPipeline->getSpecConstantMask() != m_state.cp.constants.mask))\n      this->resetSpecConstants<VK_PIPELINE_BIND_POINT_COMPUTE>(newPipeline->getSpecConstantMask());\n\n    if (m_flags.test(DxvkContextFlag::CpDirtySpecConstants))\n      this->updateSpecConstants<VK_PIPELINE_BIND_POINT_COMPUTE>();\n\n    // Look up Vulkan pipeline handle for the given compute state\n    auto pipelineHandle = newPipeline->getPipelineHandle(m_state.cp.state);\n    \n    if (unlikely(!pipelineHandle))\n      return false;\n\n    m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_COMPUTE, pipelineHandle);\n\n    // Mark compute resources and push constants as dirty\n    m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT);\n\n    auto pushData = newLayout->getPushData();\n\n    if (!pushData.isEmpty()) {\n      m_flags.set(DxvkContextFlag::CpHasPushData,\n                  DxvkContextFlag::DirtyPushData);\n    }\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      m_cmd->cmdInsertDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xf0dca2, newPipeline->debugName()));\n    }\n\n    if (!m_features.test(DxvkContextFeature::DescriptorHeap)) {\n      // Bind global sampler set if needed and if the previous pipeline did not use it\n      if (newLayout->usesSamplerHeap())\n        updateSamplerSet<VK_PIPELINE_BIND_POINT_COMPUTE>(newLayout);\n    }\n\n    m_flags.clr(DxvkContextFlag::CpDirtyPipelineState);\n    return true;\n  }\n  \n  \n  void DxvkContext::unbindGraphicsPipeline() {\n    m_flags.set(DxvkContextFlag::GpDirtyPipeline,\n                DxvkContextFlag::GpDirtyPipelineState,\n                DxvkContextFlag::GpDirtyVertexBuffers,\n                DxvkContextFlag::GpDirtyIndexBuffer,\n                DxvkContextFlag::GpDirtyXfbBuffers,\n                DxvkContextFlag::GpDirtyBlendConstants,\n                DxvkContextFlag::GpDirtyStencilTest,\n                DxvkContextFlag::GpDirtyStencilRef,\n                DxvkContextFlag::GpDirtyMultisampleState,\n                DxvkContextFlag::GpDirtyRasterizerState,\n                DxvkContextFlag::GpDirtySampleLocations,\n                DxvkContextFlag::GpDirtyViewport,\n                DxvkContextFlag::GpDirtyDepthBias,\n                DxvkContextFlag::GpDirtyDepthBounds,\n                DxvkContextFlag::GpDirtyDepthClip,\n                DxvkContextFlag::GpDirtyDepthTest);\n\n    m_flags.clr(DxvkContextFlag::GpHasPushData);\n\n    m_state.gp.pipeline = nullptr;\n  }\n  \n  \n  bool DxvkContext::updateGraphicsPipeline() {\n    if (unlikely(m_state.cp.pipeline != nullptr))\n      this->unbindComputePipeline();\n\n    auto newPipeline = lookupGraphicsPipeline(m_state.gp.shaders);\n    m_state.gp.pipeline = newPipeline;\n\n    if (unlikely(!newPipeline)) {\n      m_state.gp.flags = DxvkGraphicsPipelineFlags();\n      return false;\n    }\n\n    if (m_features.test(DxvkContextFeature::TrackGraphicsPipeline))\n      m_cmd->trackGraphicsPipeline(newPipeline);\n\n    if (unlikely(newPipeline->getSpecConstantMask() != m_state.gp.constants.mask))\n      this->resetSpecConstants<VK_PIPELINE_BIND_POINT_GRAPHICS>(newPipeline->getSpecConstantMask());\n\n    DxvkGraphicsPipelineFlags oldFlags = m_state.gp.flags;\n    DxvkGraphicsPipelineFlags newFlags = newPipeline->flags();\n    DxvkGraphicsPipelineFlags diffFlags = oldFlags ^ newFlags;\n\n    m_state.gp.flags = newFlags;\n\n    if (diffFlags.test(DxvkGraphicsPipelineFlag::HasSampleMaskExport))\n      m_flags.set(DxvkContextFlag::GpDirtyMultisampleState);\n\n    m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS);\n\n    m_flags.clr(DxvkContextFlag::GpDirtyPipeline);\n    return true;\n  }\n  \n  \n  bool DxvkContext::updateGraphicsPipelineState() {\n    auto oldPipelineLayoutType = getActivePipelineLayoutType(VK_PIPELINE_BIND_POINT_GRAPHICS);\n\n    // Check which dynamic states need to be active. States that\n    // are not dynamic will be invalidated in the command buffer.\n    m_flags.clr(DxvkContextFlag::GpDynamicBlendConstants,\n                DxvkContextFlag::GpDynamicDepthBias,\n                DxvkContextFlag::GpDynamicDepthBounds,\n                DxvkContextFlag::GpDynamicDepthClip,\n                DxvkContextFlag::GpDynamicDepthTest,\n                DxvkContextFlag::GpDynamicStencilTest,\n                DxvkContextFlag::GpDynamicMultisampleState,\n                DxvkContextFlag::GpDynamicRasterizerState,\n                DxvkContextFlag::GpDynamicSampleLocations,\n                DxvkContextFlag::GpHasPushData,\n                DxvkContextFlag::GpIndependentSets);\n    \n    m_flags.set(m_state.gp.state.useDynamicBlendConstants()\n      ? DxvkContextFlag::GpDynamicBlendConstants\n      : DxvkContextFlag::GpDirtyBlendConstants);\n    \n    m_flags.set((!m_state.gp.flags.test(DxvkGraphicsPipelineFlag::HasRasterizerDiscard))\n      ? DxvkContextFlags(DxvkContextFlag::GpDynamicRasterizerState,\n                         DxvkContextFlag::GpDynamicDepthBias)\n      : DxvkContextFlags(DxvkContextFlag::GpDirtyRasterizerState,\n                         DxvkContextFlag::GpDirtyDepthBias));\n\n    // Retrieve and bind actual Vulkan pipeline handle\n    auto pipelineInfo = m_state.gp.pipeline->getPipelineHandle(m_state.gp.state);\n\n    if (unlikely(!pipelineInfo.handle))\n      return false;\n\n    m_cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineInfo.handle);\n\n    // Update attachment usage info based on the pipeline state\n    m_state.om.attachmentMask.merge(pipelineInfo.attachments);\n\n    // For pipelines created from graphics pipeline libraries, we need to\n    // apply a bunch of dynamic state that is otherwise static or unused\n    if (pipelineInfo.type == DxvkGraphicsPipelineType::BasePipeline) {\n      m_flags.set(DxvkContextFlag::GpDynamicDepthBias,\n                  DxvkContextFlag::GpDynamicDepthTest,\n                  DxvkContextFlag::GpDynamicStencilTest,\n                  DxvkContextFlag::GpIndependentSets);\n\n      if (m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable)\n        m_flags.set(DxvkContextFlag::GpDynamicDepthClip);\n\n      if (m_device->features().core.features.depthBounds)\n        m_flags.set(DxvkContextFlag::GpDynamicDepthBounds);\n\n      if (m_device->features().extExtendedDynamicState3.extendedDynamicState3RasterizationSamples\n       && m_device->features().extExtendedDynamicState3.extendedDynamicState3SampleMask) {\n        m_flags.set(m_state.gp.flags.test(DxvkGraphicsPipelineFlag::HasSampleRateShading)\n          ? DxvkContextFlag::GpDynamicMultisampleState\n          : DxvkContextFlag::GpDirtyMultisampleState);\n      }\n\n      if (m_device->canUseSampleLocations(0u))\n        m_flags.set(DxvkContextFlag::GpDynamicSampleLocations);\n    } else {\n      if (m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable)\n        m_flags.set(DxvkContextFlag::GpDirtyDepthClip);\n\n      if (m_device->features().core.features.depthBounds) {\n        m_flags.set(m_state.gp.state.useDynamicDepthBounds()\n          ? DxvkContextFlag::GpDynamicDepthBounds\n          : DxvkContextFlag::GpDirtyDepthBounds);\n      }\n\n      if (m_device->canUseSampleLocations(0u)) {\n        m_flags.set(m_state.gp.state.useSampleLocations()\n          ? DxvkContextFlag::GpDynamicSampleLocations\n          : DxvkContextFlag::GpDirtySampleLocations);\n      }\n\n      m_flags.set(m_state.gp.state.useDynamicDepthTest()\n        ? DxvkContextFlag::GpDynamicDepthTest\n        : DxvkContextFlag::GpDirtyDepthTest);\n\n      m_flags.set(m_state.gp.state.useDynamicStencilTest()\n        ? DxvkContextFlags(DxvkContextFlag::GpDynamicStencilTest)\n        : DxvkContextFlags(DxvkContextFlag::GpDirtyStencilTest,\n                           DxvkContextFlag::GpDirtyStencilRef));\n\n      m_flags.set(\n        DxvkContextFlag::GpDirtyMultisampleState);\n    }\n\n    // If necessary, dirty descriptor sets due to layout incompatibilities\n    auto newPipelineLayoutType = getActivePipelineLayoutType(VK_PIPELINE_BIND_POINT_GRAPHICS);\n\n    if (newPipelineLayoutType != oldPipelineLayoutType)\n      m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS);\n\n    // Also update push constant status when we know the final layout\n    auto layout = m_state.gp.pipeline->getLayout()->getLayout(newPipelineLayoutType);\n    auto pushData = layout->getPushData();\n\n    if (!pushData.isEmpty()) {\n      m_flags.set(DxvkContextFlag::GpHasPushData,\n                  DxvkContextFlag::DirtyPushData);\n    }\n\n    // Emit barrier based on pipeline properties, in order to avoid\n    // accidental write-after-read hazards after the render pass.\n    DxvkGlobalPipelineBarrier srcBarrier = m_state.gp.pipeline->getGlobalBarrier(m_state.gp.state);\n    m_renderPassBarrierSrc.stages |= srcBarrier.stages;\n    m_renderPassBarrierSrc.access |= srcBarrier.access;\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      uint32_t color = getGraphicsPipelineDebugColor();\n\n      m_cmd->cmdInsertDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(color, m_state.gp.pipeline->debugName()));\n    }\n\n    if (!m_features.test(DxvkContextFeature::DescriptorHeap)) {\n      // If the new pipeline uses the global sampler set when the\n      // previous one didn't, re-bind it to the static set index 0.\n      if (layout->usesSamplerHeap())\n        updateSamplerSet<VK_PIPELINE_BIND_POINT_GRAPHICS>(layout);\n    }\n\n    m_flags.clr(DxvkContextFlag::GpDirtyPipelineState);\n    return true;\n  }\n\n\n  uint32_t DxvkContext::getGraphicsPipelineDebugColor() const {\n    if (m_state.gp.flags.test(DxvkGraphicsPipelineFlag::HasStorageDescriptors))\n      return 0xf0a2dc;\n\n    if (m_state.gp.flags.test(DxvkGraphicsPipelineFlag::HasTransformFeedback))\n      return 0xa2f0dc;\n\n    return 0xa2dcf0;\n  }\n\n\n  template<VkPipelineBindPoint BindPoint>\n  void DxvkContext::resetSpecConstants(\n          uint32_t                newMask) {\n    auto& scInfo  = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.state.sc  : m_state.cp.state.sc;\n    auto& scState = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.constants : m_state.cp.constants;\n\n    // Set all constants to 0 that were used by the previous pipeline\n    // but are not used by the old one. Any stale data could otherwise\n    // lead to unnecessary pipeline variants being created.\n    for (auto i : bit::BitMask(scState.mask & ~newMask))\n      scInfo.specConstants[i] = 0;\n\n    scState.mask = newMask;\n\n    auto flag = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS\n      ? DxvkContextFlag::GpDirtySpecConstants\n      : DxvkContextFlag::CpDirtySpecConstants;\n\n    if (newMask)\n      m_flags.set(flag);\n    else\n      m_flags.clr(flag);\n  }\n\n\n  template<VkPipelineBindPoint BindPoint>\n  void DxvkContext::updateSpecConstants() {\n    auto& scInfo  = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.state.sc  : m_state.cp.state.sc;\n    auto& scState = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS ? m_state.gp.constants : m_state.cp.constants;\n\n    for (auto i : bit::BitMask(scState.mask))\n      scInfo.specConstants[i] = scState.data[i];\n\n    if (BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS) {\n      m_flags.clr(DxvkContextFlag::GpDirtySpecConstants);\n      m_flags.set(DxvkContextFlag::GpDirtyPipelineState);\n    } else {\n      m_flags.clr(DxvkContextFlag::CpDirtySpecConstants);\n      m_flags.set(DxvkContextFlag::CpDirtyPipelineState);\n    }\n  }\n\n\n  void DxvkContext::invalidateState() {\n    this->unbindComputePipeline();\n    this->unbindGraphicsPipeline();\n  }\n\n  template<VkPipelineBindPoint BindPoint>\n  void DxvkContext::updateSamplerSet(const DxvkPipelineLayout* layout) {\n    if (m_features.test(DxvkContextFeature::DescriptorBuffer)) {\n      const uint32_t     bufferIndex = 0u;\n      const VkDeviceSize bufferOffset = 0u;\n\n      m_cmd->cmdSetDescriptorBufferOffsetsEXT(DxvkCmdBuffer::ExecBuffer,\n        BindPoint, layout->getPipelineLayout(), 0u, 1u,\n        &bufferIndex, &bufferOffset);\n    } else {\n      VkDescriptorSet set = m_device->getSamplerDescriptorSet().set;\n\n      m_cmd->cmdBindDescriptorSets(DxvkCmdBuffer::ExecBuffer,\n        BindPoint, layout->getPipelineLayout(), 0u, 1u, &set);\n    }\n  }\n\n\n  template<VkPipelineBindPoint BindPoint, bool AlwaysTrack>\n  bool DxvkContext::updateResourceBindings(const DxvkPipelineBindings* layout) {\n    if (m_features.test(DxvkContextFeature::DescriptorHeap)) {\n      if (!updateDescriptorHeapBindings<BindPoint, DxvkBindingModel::DescriptorHeap, AlwaysTrack>(layout))\n        return false;\n    } else if (m_features.test(DxvkContextFeature::DescriptorBuffer)) {\n      if (!updateDescriptorHeapBindings<BindPoint, DxvkBindingModel::DescriptorBuffer, AlwaysTrack>(layout))\n        return false;\n    } else {\n      updateDescriptorSetsBindings<BindPoint, AlwaysTrack>(layout);\n    }\n\n    updatePushDataBindings<BindPoint, AlwaysTrack>(layout);\n    return true;\n  }\n\n\n  template<VkPipelineBindPoint BindPoint, bool AlwaysTrack>\n  void DxvkContext::updateDescriptorSetsBindings(const DxvkPipelineBindings* layout) {\n    constexpr bool TrackBindings = AlwaysTrack || BindPoint == VK_PIPELINE_BIND_POINT_COMPUTE;\n\n    DxvkPipelineLayoutType pipelineLayoutType = getActivePipelineLayoutType(BindPoint);\n    const auto* pipelineLayout = layout->getLayout(pipelineLayoutType);\n\n    // Ensure that the arrays we write descriptor info to are big enough\n    if (unlikely(layout->getDescriptorCount() > m_legacyDescriptors.infos.size()))\n      this->resizeDescriptorArrays(layout->getDescriptorCount());\n\n    // Find out which sets we actually need to update based on the pipeline\n    // layout. This may be an empty mask if only unrelated resources were\n    // changed, but we have no way of knowing that up-front.\n    uint32_t dirtySetMask = layout->getDirtySetMask(pipelineLayoutType, m_descriptorState);\n\n    if (likely(dirtySetMask)) {\n      // On 32-bit wine, vkUpdateDescriptorSets has significant overhead due\n      // to struct conversion, so we should use descriptor update templates.\n      // For 64-bit applications, using templates is slower on some drivers.\n      constexpr bool useDescriptorTemplates = env::is32BitHostPlatform();\n\n      std::array<VkDescriptorSet, DxvkDescriptorSets::SetCount> sets = { };\n      m_descriptorPool->alloc(m_trackingId, pipelineLayout, dirtySetMask, sets.data());\n\n      uint32_t descriptorCount = 0;\n\n      for (auto setIndex : bit::BitMask(dirtySetMask)) {\n        auto range = layout->getAllDescriptorsInSet(pipelineLayoutType, setIndex);\n\n        for (uint32_t j = 0; j < range.bindingCount; j++) {\n          const auto& binding = range.bindings[j];\n\n          if (!useDescriptorTemplates) {\n            auto& descriptorWrite = m_legacyDescriptors.writes[descriptorCount];\n            descriptorWrite.dstSet = sets[setIndex];\n            descriptorWrite.dstBinding = binding.getBinding();\n            descriptorWrite.dstArrayElement = binding.getArrayIndex();\n            descriptorWrite.descriptorType = binding.getDescriptorType();\n          }\n\n          auto& descriptorInfo = m_legacyDescriptors.infos[descriptorCount++];\n\n          if (binding.isUniformBuffer()) {\n            const auto& slice = m_uniformBuffers[binding.getResourceIndex()];\n\n            if (slice.length()) {\n              auto bufferInfo = slice.getSliceInfo();\n              descriptorInfo.buffer.buffer = bufferInfo.buffer;\n              descriptorInfo.buffer.offset = bufferInfo.offset;\n              descriptorInfo.buffer.range = bufferInfo.size;\n\n              trackUniformBufferBinding<TrackBindings>(binding, slice);\n            } else {\n              descriptorInfo.buffer.buffer = VK_NULL_HANDLE;\n              descriptorInfo.buffer.offset = 0;\n              descriptorInfo.buffer.range = VK_WHOLE_SIZE;\n            }\n          } else {\n            switch (binding.getDescriptorType()) {\n              case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: {\n                const auto& res = m_resources[binding.getResourceIndex()];\n                const DxvkDescriptor* descriptor = nullptr;\n\n                if (res.imageView)\n                  descriptor = res.imageView->getDescriptor(binding.getViewType());\n\n                if (descriptor) {\n                  if (likely(!res.imageView->isMultisampled() || binding.isMultisampled())) {\n                    descriptorInfo = descriptor->legacy;\n\n                    trackImageViewBinding<TrackBindings, false>(binding, *res.imageView);\n                  } else if (m_device->config().enableImplicitResolves) {\n                    auto view = m_implicitResolves.getResolveView(*res.imageView, m_trackingId);\n                    descriptorInfo = view->getDescriptor(binding.getViewType())->legacy;\n\n                    m_cmd->track(view->image(), DxvkAccess::Read);\n                  } else {\n                    descriptorInfo.image.sampler = VK_NULL_HANDLE;\n                    descriptorInfo.image.imageView = VK_NULL_HANDLE;\n                    descriptorInfo.image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n                  }\n                } else {\n                  descriptorInfo.image.sampler = VK_NULL_HANDLE;\n                  descriptorInfo.image.imageView = VK_NULL_HANDLE;\n                  descriptorInfo.image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n                }\n              } break;\n\n              case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {\n                const auto& res = m_resources[binding.getResourceIndex()];\n                const DxvkDescriptor* descriptor = nullptr;\n\n                if (res.imageView)\n                  descriptor = res.imageView->getDescriptor(binding.getViewType());\n\n                if (descriptor) {\n                  descriptorInfo = descriptor->legacy;\n\n                  trackImageViewBinding<TrackBindings, true>(binding, *res.imageView);\n                } else {\n                  descriptorInfo.image.sampler = VK_NULL_HANDLE;\n                  descriptorInfo.image.imageView = VK_NULL_HANDLE;\n                  descriptorInfo.image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n                }\n              } break;\n\n              case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: {\n                const auto& res = m_resources[binding.getResourceIndex()];\n                const DxvkDescriptor* descriptor = nullptr;\n\n                if (res.bufferView)\n                  descriptor = res.bufferView->getDescriptor(false);\n\n                if (descriptor) {\n                  descriptorInfo = descriptor->legacy;\n\n                  trackBufferViewBinding<TrackBindings, false>(binding, *res.bufferView);\n                } else {\n                  descriptorInfo.bufferView = VK_NULL_HANDLE;\n                }\n              } break;\n\n              case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: {\n                const auto& res = m_resources[binding.getResourceIndex()];\n                const DxvkDescriptor* descriptor = nullptr;\n\n                if (res.bufferView)\n                  descriptor = res.bufferView->getDescriptor(false);\n\n                if (descriptor) {\n                  descriptorInfo = descriptor->legacy;\n\n                  trackBufferViewBinding<TrackBindings, true>(binding, *res.bufferView);\n                } else {\n                  descriptorInfo.bufferView = VK_NULL_HANDLE;\n                }\n              } break;\n\n              case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: {\n                const auto& res = m_resources[binding.getResourceIndex()];\n                const DxvkDescriptor* descriptor = nullptr;\n\n                if (res.bufferView)\n                  descriptor = res.bufferView->getDescriptor(true);\n\n                if (descriptor) {\n                  descriptorInfo = descriptor->legacy;\n\n                  trackBufferViewBinding<TrackBindings, false>(binding, *res.bufferView);\n                } else {\n                  descriptorInfo.buffer.buffer = VK_NULL_HANDLE;\n                  descriptorInfo.buffer.offset = 0;\n                  descriptorInfo.buffer.range = VK_WHOLE_SIZE;\n                }\n              } break;\n\n              case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: {\n                const auto& res = m_resources[binding.getResourceIndex()];\n                const DxvkDescriptor* descriptor = nullptr;\n\n                if (res.bufferView)\n                  descriptor = res.bufferView->getDescriptor(true);\n\n                if (descriptor) {\n                  descriptorInfo = descriptor->legacy;\n\n                  trackBufferViewBinding<TrackBindings, true>(binding, *res.bufferView);\n                } else {\n                  descriptorInfo.buffer.buffer = VK_NULL_HANDLE;\n                  descriptorInfo.buffer.offset = 0;\n                  descriptorInfo.buffer.range = VK_WHOLE_SIZE;\n                }\n              } break;\n\n              default:\n                /* nothing to do */;\n            }\n          }\n        }\n\n        if (useDescriptorTemplates) {\n          m_cmd->updateDescriptorSetWithTemplate(sets[setIndex],\n            pipelineLayout->getDescriptorSetLayout(setIndex)->getSetUpdateTemplate(),\n            m_legacyDescriptors.infos.data());\n          descriptorCount = 0;\n        }\n      }\n\n      // Update all descriptors in one go to avoid API call overhead\n      if (!useDescriptorTemplates) {\n        m_cmd->updateDescriptorSets(descriptorCount,\n          m_legacyDescriptors.writes.data());\n      }\n\n      do {\n        // Similarly, bind consecutive descriptor set ranges at once.\n        uint32_t first = bit::bsf(dirtySetMask);\n\n        // Add the lowest set bit to the mask and count the number of\n        // additional zeroes we created to get the final set count\n        uint32_t countMask = dirtySetMask + (dirtySetMask & -dirtySetMask);\n        uint32_t count = bit::bsf(countMask) - first;\n\n        // Global sampler set will always be bound to index 0\n        uint32_t setIndex = first + uint32_t(pipelineLayout->usesSamplerHeap());\n\n        m_cmd->cmdBindDescriptorSets(DxvkCmdBuffer::ExecBuffer,\n          BindPoint, pipelineLayout->getPipelineLayout(),\n          setIndex, count, &sets[first]);\n\n        dirtySetMask &= countMask;\n      } while (dirtySetMask);\n    }\n  }\n\n\n  template<VkPipelineBindPoint BindPoint, DxvkBindingModel Model, bool AlwaysTrack>\n  bool DxvkContext::updateDescriptorHeapBindings(const DxvkPipelineBindings* layout) {\n    constexpr bool TrackBindings = AlwaysTrack || BindPoint == VK_PIPELINE_BIND_POINT_COMPUTE;\n    using HeapOffset = std::conditional_t<Model == DxvkBindingModel::DescriptorHeap, uint32_t, VkDeviceSize>;\n\n    DxvkPipelineLayoutType pipelineLayoutType = getActivePipelineLayoutType(BindPoint);\n    const auto* pipelineLayout = layout->getLayout(pipelineLayoutType);\n\n    // Check if there's anything to do; the mask can be empty\n    // in case only unrelated bindings have been updated.\n    uint32_t dirtySetMask = layout->getDirtySetMask(pipelineLayoutType, m_descriptorState);\n\n    if (unlikely(!dirtySetMask))\n      return true;\n\n    // Make sure we have enough space for the set. If this fails, the caller\n    // has to make sure that no secondary command buffer is active. If we\n    // allocate a new descriptor range and potentially re-bind the heap, we\n    // need to re-allocate all sets too.\n    if (!m_cmd->canAllocateDescriptors(pipelineLayout)) {\n      m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS | VK_SHADER_STAGE_COMPUTE_BIT);\n\n      if (!m_cmd->createDescriptorRange())\n        return false;\n\n      if (Model != DxvkBindingModel::DescriptorHeap) {\n        if (pipelineLayout->usesSamplerHeap())\n          updateSamplerSet<BindPoint>(pipelineLayout);\n      }\n\n      dirtySetMask = layout->getDirtySetMask(pipelineLayoutType, m_descriptorState);\n    }\n\n    std::array<uint32_t, DxvkDescriptorSets::SetCount> bufferIndices = { };\n    std::array<HeapOffset, DxvkDescriptorSets::SetCount> heapOffsets = { };\n\n    if constexpr (Model == DxvkBindingModel::DescriptorHeap) {\n      // Make sure the heaps are actually valid and usable\n      m_cmd->ensureDescriptorHeapBinding();\n    } else {\n      // The resource heap is always bound at index 1\n      for (auto& index : bufferIndices)\n        index = 1u;\n    }\n\n    // Scratch memory for descriptor updates\n    for (auto setIndex : bit::BitMask(dirtySetMask)) {\n      auto range = layout->getAllDescriptorsInSet(pipelineLayoutType, setIndex);\n\n      auto setLayout = pipelineLayout->getDescriptorSetLayout(setIndex);\n\n      // Allocate descriptor set in memory and query heap offset\n      auto setStorage = m_cmd->allocateDescriptors(setLayout);\n      heapOffsets[setIndex] = setStorage.offset >> pipelineLayout->getDescriptorOffsetShift();\n\n      // Allocate descriptor update entry to write descriptor pointers to\n      auto e = m_descriptorWorker.allocEntry(setLayout, setStorage.mapPtr, range.bindingCount,\n        layout->getUniformBuffersInSet(pipelineLayoutType, setIndex).bindingCount);\n\n      size_t bufferCount = 0u;\n\n      for (uint32_t j = 0; j < range.bindingCount; j++) {\n        const auto& binding = range.bindings[j];\n\n        if (binding.isUniformBuffer()) {\n          const auto& slice = m_uniformBuffers[binding.getResourceIndex()];\n          auto sliceInfo = slice.getSliceInfo();\n\n          auto& buffer = e.buffers[bufferCount++];\n          buffer.gpuAddress = sliceInfo.gpuAddress;\n          buffer.size = sliceInfo.size;\n          buffer.indexInSet = j;\n          buffer.descriptorType = uint16_t(binding.getDescriptorType());\n\n          if (likely(sliceInfo.size))\n            trackUniformBufferBinding<TrackBindings>(binding, slice);\n        } else {\n          const auto& res = m_resources[binding.getResourceIndex()];\n\n          switch (binding.getDescriptorType()) {\n            case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: {\n              if (res.imageView && likely(e.descriptors[j] = res.imageView->getDescriptor(binding.getViewType()))) {\n                if (likely(!res.imageView->isMultisampled() || binding.isMultisampled())) {\n                  trackImageViewBinding<TrackBindings, false>(binding, *res.imageView);\n                  break;\n                } else if (m_device->config().enableImplicitResolves) {\n                  auto view = m_implicitResolves.getResolveView(*res.imageView, m_trackingId);\n\n                  if (likely(e.descriptors[j] = view->getDescriptor(binding.getViewType()))) {\n                    m_cmd->track(view->image(), DxvkAccess::Read);\n                    break;\n                  }\n                }\n              }\n\n              e.descriptors[j] = m_device->getDescriptorProperties().getNullDescriptor(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);\n            } break;\n\n            case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {\n              if (res.imageView && likely(e.descriptors[j] = res.imageView->getDescriptor(binding.getViewType()))) {\n                trackImageViewBinding<TrackBindings, true>(binding, *res.imageView);\n                break;\n              }\n\n              e.descriptors[j] = m_device->getDescriptorProperties().getNullDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);\n            } break;\n\n            case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: {\n              if (res.bufferView && likely(e.descriptors[j] = res.bufferView->getDescriptor(false))) {\n                trackBufferViewBinding<TrackBindings, false>(binding, *res.bufferView);\n                break;\n              }\n\n              e.descriptors[j] = m_device->getDescriptorProperties().getNullDescriptor(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);\n            } break;\n\n            case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: {\n              if (res.bufferView && likely(e.descriptors[j] = res.bufferView->getDescriptor(false))) {\n                trackBufferViewBinding<TrackBindings, true>(binding, *res.bufferView);\n                break;\n              }\n\n              e.descriptors[j] = m_device->getDescriptorProperties().getNullDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);\n            } break;\n\n            case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: {\n              if (res.bufferView && likely(e.descriptors[j] = res.bufferView->getDescriptor(true))) {\n                trackBufferViewBinding<TrackBindings, false>(binding, *res.bufferView);\n                break;\n              }\n\n              e.descriptors[j] = m_device->getDescriptorProperties().getNullDescriptor(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);\n            } break;\n\n            case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: {\n              if (res.bufferView && likely(e.descriptors[j] = res.bufferView->getDescriptor(true))) {\n                trackBufferViewBinding<TrackBindings, true>(binding, *res.bufferView);\n                break;\n              }\n\n              e.descriptors[j] = m_device->getDescriptorProperties().getNullDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);\n            } break;\n\n            default:\n              /* Nothing to do */;\n          }\n        }\n      }\n    }\n\n    do {\n      // Bind consecutive descriptor ranges at once\n      uint32_t first = bit::bsf(dirtySetMask);\n\n      // Add the lowest set bit to the mask and count the number of\n      // additional zeroes we created to get the final set count\n      uint32_t countMask = dirtySetMask + (dirtySetMask & -dirtySetMask);\n      uint32_t count = bit::bsf(countMask) - first;\n\n      if constexpr (Model == DxvkBindingModel::DescriptorHeap) {\n        // Descriptor set offsets are stored in-order at offset 0\n        VkPushDataInfoEXT pushInfo = { VK_STRUCTURE_TYPE_PUSH_DATA_INFO_EXT };\n        pushInfo.offset = sizeof(uint32_t) * first;\n        pushInfo.data.address = &heapOffsets[first];\n        pushInfo.data.size = sizeof(uint32_t) * count;\n\n        m_cmd->cmdPushData(DxvkCmdBuffer::ExecBuffer, &pushInfo);\n      } else {\n        // Global sampler set will always be bound to index 0 if used\n        uint32_t setIndex = first + uint32_t(pipelineLayout->usesSamplerHeap());\n\n        m_cmd->cmdSetDescriptorBufferOffsetsEXT(DxvkCmdBuffer::ExecBuffer,\n          BindPoint, pipelineLayout->getPipelineLayout(), setIndex, count,\n          &bufferIndices[first], &heapOffsets[first]);\n      }\n\n      dirtySetMask &= countMask;\n    } while (dirtySetMask);\n\n    return true;\n  }\n\n\n  template<VkPipelineBindPoint BindPoint, bool AlwaysTrack>\n  void DxvkContext::updatePushDataBindings(const DxvkPipelineBindings* layout) {\n    constexpr bool TrackBindings = AlwaysTrack || BindPoint == VK_PIPELINE_BIND_POINT_COMPUTE;\n\n    DxvkPipelineLayoutType pipelineLayoutType = getActivePipelineLayoutType(BindPoint);\n\n    if (m_descriptorState.hasDirtyVas(layout->getNonemptyStageMask())) {\n      auto range = layout->getVaBindings(pipelineLayoutType);\n\n      if (range.bindingCount)\n        m_flags.set(DxvkContextFlag::DirtyPushData);\n\n      for (uint32_t i = 0u; i < range.bindingCount; i++) {\n        const auto& binding = range.bindings[i];\n        const auto& res = m_resources[binding.getResourceIndex()];\n\n        VkDeviceAddress va = 0u;\n\n        if (binding.isUniformBuffer()) {\n          const auto& slice = m_uniformBuffers[binding.getResourceIndex()];\n\n          if (slice.length()) {\n            va = slice.getSliceInfo().gpuAddress;\n\n            trackUniformBufferBinding<TrackBindings>(binding, slice);\n          }\n        } else {\n          if (res.bufferView) {\n            va = res.bufferView->getSliceInfo().gpuAddress;\n\n            trackBufferViewBinding<TrackBindings, true>(binding, *res.bufferView);\n          }\n        }\n\n        std::memcpy(&m_state.pc.resourceData[binding.getBlockOffset()], &va, sizeof(va));\n      }\n    }\n\n    if (m_descriptorState.hasDirtySamplers(layout->getNonemptyStageMask())) {\n      auto range = layout->getSamplers(pipelineLayoutType);\n\n      if (range.bindingCount)\n        m_flags.set(DxvkContextFlag::DirtyPushData);\n\n      for (uint32_t i = 0u; i < range.bindingCount; i++) {\n        const auto& binding = range.bindings[i];\n        const auto& sampler = m_samplers[binding.getResourceIndex()];\n\n        uint16_t index = 0u;\n\n        if (likely(sampler)) {\n          index = sampler->getDescriptor().samplerIndex;\n          m_cmd->track(sampler);\n        }\n\n        std::memcpy(&m_state.pc.resourceData[binding.getBlockOffset()], &index, sizeof(index));\n      }\n    }\n  }\n\n\n  void DxvkContext::updateComputeShaderResources() {\n    this->updateResourceBindings<VK_PIPELINE_BIND_POINT_COMPUTE, true>(\n      m_state.cp.pipeline->getLayout());\n\n    m_descriptorState.clearStages(VK_SHADER_STAGE_COMPUTE_BIT);\n  }\n  \n  \n  bool DxvkContext::updateGraphicsShaderResources() {\n    bool status;\n\n    if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)) {\n      // Enable full resource tracking inside unsynchronized render passes. This\n      // does come at a hefty CPU performance cost, so at least optimize the code\n      // in such a way that we skip all the checks for GFX stores.\n      status = updateResourceBindings<VK_PIPELINE_BIND_POINT_GRAPHICS, true>(m_state.gp.pipeline->getLayout());\n    } else {\n      // In regularly synchronized passes, only track resources with GFX stores.\n      status = updateResourceBindings<VK_PIPELINE_BIND_POINT_GRAPHICS, false>(m_state.gp.pipeline->getLayout());\n    }\n\n    if (!status)\n      return false;\n\n    m_descriptorState.clearStages(VK_SHADER_STAGE_ALL_GRAPHICS);\n    return true;\n  }\n  \n  \n  DxvkFramebufferInfo DxvkContext::makeFramebufferInfo(\n    const DxvkRenderTargets&      renderTargets) {\n    return DxvkFramebufferInfo(renderTargets, m_device->getDefaultFramebufferSize());\n  }\n\n\n  void DxvkContext::updateRenderTargets() {\n    if (m_flags.test(DxvkContextFlag::GpDirtyRenderTargets)) {\n      m_flags.clr(DxvkContextFlag::GpDirtyRenderTargets);\n\n      if (m_flags.test(DxvkContextFlag::GpRenderPassActive) && !m_flags.test(DxvkContextFlag::GpRenderPassNeedsFlush)) {\n        // Only interrupt an active render pass if the render targets have actually\n        // changed since the last update. There are cases where client APIs cannot\n        // know in advance that consecutive draws use the same set of render targets.\n        if (m_state.om.renderTargets == m_state.om.framebufferInfo.attachments())\n          return;\n      }\n\n      // End active render pass and reset load/store ops for the new render targets.\n      DxvkFramebufferInfo fbInfo = makeFramebufferInfo(m_state.om.renderTargets);\n\n      this->endCurrentPass(true);\n\n      this->resetRenderPassOps(\n        m_state.om.renderTargets,\n        m_state.om.renderPassOps);\n\n      // Update relevant graphics pipeline state\n      m_state.gp.state.ms.setSampleCount(fbInfo.getSampleCount());\n      m_state.gp.state.rt = fbInfo.getRtInfo();\n\n      for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n        const auto& attachment = fbInfo.getColorTarget(i).view;\n\n        VkComponentMapping mapping = VkComponentMapping();\n\n        if (attachment)\n          mapping = util::invertComponentMapping(attachment->info().unpackSwizzle());\n\n        m_state.gp.state.omSwizzle[i] = DxvkOmAttachmentSwizzle(mapping);\n      }\n\n      m_state.om.framebufferInfo = std::move(fbInfo);\n\n      m_flags.set(DxvkContextFlag::GpDirtyPipelineState);\n    } else if (m_flags.test(DxvkContextFlag::GpRenderPassNeedsFlush)) {\n      // End render pass to flush pending resolves\n      this->endCurrentPass(true);\n    }\n  }\n\n\n  bool DxvkContext::flushDeferredClear(\n    const DxvkImage&              image,\n    const VkImageSubresourceRange& subresources) {\n    if (!(image.info().usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)))\n      return false;\n\n    DxvkClearBatch clearBatch;\n\n    size_t srcIndex = 0u;\n    size_t dstIndex = 0u;\n\n    while (srcIndex < m_deferredClears.size()) {\n      auto& entry = m_deferredClears[srcIndex];\n\n      if ((entry.imageView->image() == &image)\n       && ((entry.clearAspects | entry.discardAspects) | subresources.aspectMask)\n       && (vk::checkSubresourceRangeOverlap(entry.imageView->imageSubresources(), subresources))) {\n        clearBatch.add(batchClear(entry.imageView, -1, entry.discardAspects, entry.clearAspects, entry.clearValue));\n        srcIndex += 1u;\n       } else {\n        if (dstIndex < srcIndex)\n          m_deferredClears[dstIndex] = std::move(m_deferredClears[srcIndex]);\n\n        dstIndex += 1u;\n        srcIndex += 1u;\n      }\n    }\n\n    if (dstIndex < srcIndex) {\n      m_deferredClears.resize(dstIndex);\n\n      // Need to call this *after* removing the clear from the\n      // list since this will try to run clears out-of-order.\n      performClears(clearBatch);\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n\n  DxvkDeferredClear* DxvkContext::findDeferredClear(\n    const DxvkImage&              image,\n    const VkImageSubresourceRange& subresources) {\n    for (auto& entry : m_deferredClears) {\n      if ((entry.imageView->image() == &image) && ((subresources.aspectMask & entry.clearAspects) == subresources.aspectMask)\n       && (vk::checkSubresourceRangeSuperset(entry.imageView->imageSubresources(), subresources)))\n        return &entry;\n    }\n\n    return nullptr;\n  }\n\n\n  DxvkDeferredClear* DxvkContext::findOverlappingDeferredClear(\n    const DxvkImage&              image,\n    const VkImageSubresourceRange& subresources) {\n    for (auto& entry : m_deferredClears) {\n      if ((entry.imageView->image() == &image)\n       && ((entry.clearAspects | entry.discardAspects) | subresources.aspectMask)\n       && (vk::checkSubresourceRangeOverlap(entry.imageView->imageSubresources(), subresources)))\n        return &entry;\n    }\n\n    return nullptr;\n  }\n\n\n  DxvkDeferredResolve* DxvkContext::findOverlappingDeferredResolve(\n    const DxvkImage&              image,\n    const VkImageSubresourceRange& subresources) {\n    for (auto& entry : m_deferredResolves) {\n      if (entry.imageView && entry.imageView->image() == &image\n       && (vk::checkSubresourceRangeOverlap(entry.imageView->imageSubresources(), subresources)))\n        return &entry;\n    }\n\n    return nullptr;\n  }\n\n\n  bool DxvkContext::isBoundAsRenderTarget(\n    const DxvkImage&              image,\n    const VkImageSubresourceRange& subresources) {\n    for (uint32_t i = 0; i < m_state.om.framebufferInfo.numAttachments(); i++) {\n      auto& view = m_state.om.framebufferInfo.getAttachment(i).view;\n\n      if (view && view->image() == &image && vk::checkSubresourceRangeOverlap(view->imageSubresources(), subresources))\n        return true;\n    }\n\n    return false;\n  }\n\n\n  void DxvkContext::updateIndexBufferBinding() {\n    m_flags.clr(DxvkContextFlag::GpDirtyIndexBuffer);\n\n    if (likely(m_state.vi.indexBuffer.length())) {\n      auto bufferInfo = m_state.vi.indexBuffer.getSliceInfo();\n\n      VkDeviceSize align = m_state.vi.indexType == VK_INDEX_TYPE_UINT16 ? 2 : 4;\n      VkDeviceSize length = bufferInfo.size & ~(align - 1);\n\n      m_cmd->cmdBindIndexBuffer2(\n        bufferInfo.buffer, bufferInfo.offset,\n        length, m_state.vi.indexType);\n\n      if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)\n       || m_state.vi.indexBuffer.buffer()->hasGfxStores()) {\n        accessBuffer(DxvkCmdBuffer::ExecBuffer, m_state.vi.indexBuffer,\n          VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_INDEX_READ_BIT, DxvkAccessOp::None);\n      }\n\n      m_renderPassBarrierSrc.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;\n      m_renderPassBarrierSrc.access |= VK_ACCESS_INDEX_READ_BIT;\n\n      m_cmd->track(m_state.vi.indexBuffer.buffer(), DxvkAccess::Read);\n    } else {\n      // Bind null index buffer to read all zeroes, not too useful but well-defined\n      m_cmd->cmdBindIndexBuffer2(VK_NULL_HANDLE, 0, VK_WHOLE_SIZE, m_state.vi.indexType);\n    }\n  }\n  \n  \n  void DxvkContext::updateVertexBufferBindings() {\n    m_flags.clr(DxvkContextFlag::GpDirtyVertexBuffers);\n\n    if (unlikely(!m_state.gp.state.il.bindingCount()))\n      return;\n    \n    std::array<VkBuffer,     MaxNumVertexBindings> buffers;\n    std::array<VkDeviceSize, MaxNumVertexBindings> offsets;\n    std::array<VkDeviceSize, MaxNumVertexBindings> lengths;\n    std::array<VkDeviceSize, MaxNumVertexBindings> strides;\n    \n    bool oldDynamicStrides = m_flags.test(DxvkContextFlag::GpDynamicVertexStrides);\n    bool newDynamicStrides = true;\n\n    // Set buffer handles and offsets for active bindings\n    for (uint32_t i = 0; i < m_state.gp.state.il.bindingCount(); i++) {\n      uint32_t binding = m_state.gp.state.ilBindings[i].binding();\n      \n      if (likely(m_state.vi.vertexBuffers[binding].length())) {\n        auto vbo = m_state.vi.vertexBuffers[binding].getSliceInfo();\n        \n        buffers[i] = vbo.buffer;\n        offsets[i] = vbo.offset;\n        lengths[i] = vbo.size;\n        strides[i] = m_state.vi.vertexStrides[binding];\n\n        if (strides[i]) {\n          // Dynamic strides are only allowed if the stride is not smaller\n          // than highest attribute offset + format size for given binding\n          newDynamicStrides &= strides[i] >= m_state.vi.vertexExtents[i];\n        }\n\n        if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)\n         || m_state.vi.vertexBuffers[binding].buffer()->hasGfxStores()) {\n          accessBuffer(DxvkCmdBuffer::ExecBuffer, m_state.vi.vertexBuffers[binding],\n            VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, DxvkAccessOp::None);\n        }\n\n        m_cmd->track(m_state.vi.vertexBuffers[binding].buffer(), DxvkAccess::Read);\n      } else {\n        buffers[i] = VK_NULL_HANDLE;\n        offsets[i] = 0;\n        lengths[i] = 0;\n        strides[i] = 0;\n      }\n    }\n\n    // If vertex strides are static or if we are switching between static or\n    // dynamic strides, we'll have to apply them to the pipeline state and\n    // also sort out our state flags\n    if (unlikely(!oldDynamicStrides) || unlikely(!newDynamicStrides)) {\n      m_flags.clr(DxvkContextFlag::GpDynamicVertexStrides);\n\n      for (uint32_t i = 0; i < m_state.gp.state.il.bindingCount(); i++) {\n        uint32_t stride = newDynamicStrides ? 0 : strides[i];\n\n        if (m_state.gp.state.ilBindings[i].stride() != stride) {\n          m_state.gp.state.ilBindings[i].setStride(stride);\n          m_flags.set(DxvkContextFlag::GpDirtyPipelineState);\n        }\n      }\n\n      if (newDynamicStrides)\n        m_flags.set(DxvkContextFlag::GpDynamicVertexStrides);\n    }\n\n    // Vertex bindigs get remapped when compiling the\n    // pipeline, so this actually does the right thing\n    m_cmd->cmdBindVertexBuffers(0, m_state.gp.state.il.bindingCount(),\n      buffers.data(), offsets.data(), lengths.data(),\n      newDynamicStrides ? strides.data() : nullptr);\n  }\n  \n  \n  void DxvkContext::updateTransformFeedbackBuffers() {\n    const auto& gsInfo = m_state.gp.shaders.gs->metadata();\n\n    VkBuffer     xfbBuffers[MaxNumXfbBuffers];\n    VkDeviceSize xfbOffsets[MaxNumXfbBuffers];\n    VkDeviceSize xfbLengths[MaxNumXfbBuffers];\n\n    for (size_t i = 0; i < MaxNumXfbBuffers; i++) {\n      auto bufferSlice = m_state.xfb.buffers[i].getSliceInfo();\n      \n      xfbBuffers[i] = bufferSlice.buffer;\n      xfbOffsets[i] = bufferSlice.offset;\n      xfbLengths[i] = bufferSlice.size;\n\n      if (!bufferSlice.buffer)\n        xfbBuffers[i] = m_common->dummyResources().bufferInfo().buffer;\n\n      if (bufferSlice.buffer) {\n        Rc<DxvkBuffer> buffer = m_state.xfb.buffers[i].buffer();\n        buffer->setXfbVertexStride(gsInfo.xfbStrides[i]);\n\n        accessBuffer(DxvkCmdBuffer::ExecBuffer, m_state.xfb.buffers[i],\n          VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,\n          VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, DxvkAccessOp::None);\n\n        m_cmd->track(std::move(buffer), DxvkAccess::Write);\n      }\n    }\n\n    m_cmd->cmdBindTransformFeedbackBuffers(\n      0, MaxNumXfbBuffers,\n      xfbBuffers, xfbOffsets, xfbLengths);\n  }\n\n\n  void DxvkContext::updateTransformFeedbackState() {\n    if (m_flags.test(DxvkContextFlag::GpDirtyXfbBuffers)) {\n      m_flags.clr(DxvkContextFlag::GpDirtyXfbBuffers);\n\n      this->pauseTransformFeedback();\n      this->updateTransformFeedbackBuffers();\n    }\n\n    this->startTransformFeedback();\n  }\n\n  \n  void DxvkContext::updateDynamicState() {\n    if (unlikely(m_flags.test(DxvkContextFlag::GpDirtyViewport))) {\n      m_flags.clr(DxvkContextFlag::GpDirtyViewport);\n\n      // Clamp scissor against rendering area. Not doing so is technically\n      // out of spec, even if this doesn't get validated. This also solves\n      // problems with potentially invalid scissor rects.\n      std::array<VkRect2D, DxvkLimits::MaxNumViewports> clampedScissors;\n      DxvkFramebufferSize renderSize = m_state.om.framebufferInfo.size();\n\n      for (uint32_t i = 0; i < m_state.vp.viewportCount; i++) {\n        const auto& viewport = m_state.vp.viewports[i];\n        const auto& scissor = m_state.vp.scissorRects[i];\n\n        // Need to floor scissor to viewport region to match D3D rules\n        VkOffset2D lo = {\n          std::max(scissor.offset.x, int32_t(std::max(0.0f, viewport.x))),\n          std::max(scissor.offset.y, int32_t(std::max(0.0f, std::min(viewport.y, viewport.y + viewport.height)))) };\n\n        VkOffset2D hi = {\n          std::min(int32_t(renderSize.width),  int32_t(std::max(0.0f, viewport.x + viewport.width))),\n          std::min(int32_t(renderSize.height), int32_t(std::max(0.0f, std::max(viewport.y, viewport.y + viewport.height)))) };\n\n        hi.x = std::max(hi.x, lo.x);\n        hi.y = std::max(hi.y, lo.y);\n\n        auto& dst = clampedScissors[i];\n        dst.offset = lo;\n        dst.extent = VkExtent2D {\n          std::min(scissor.extent.width,  uint32_t(hi.x - lo.x)),\n          std::min(scissor.extent.height, uint32_t(hi.y - lo.y)) };\n\n        // Extend render area based on the final scissor rect\n        m_state.om.renderAreaLo = {\n          std::min(m_state.om.renderAreaLo.x, dst.offset.x),\n          std::min(m_state.om.renderAreaLo.y, dst.offset.y) };\n        m_state.om.renderAreaHi = {\n          std::max(m_state.om.renderAreaHi.x, int32_t(dst.offset.x + dst.extent.width)),\n          std::max(m_state.om.renderAreaHi.y, int32_t(dst.offset.y + dst.extent.height)) };\n      }\n\n      m_cmd->cmdSetViewport(m_state.vp.viewportCount, m_state.vp.viewports.data());\n      m_cmd->cmdSetScissor(m_state.vp.viewportCount, clampedScissors.data());\n    }\n\n    if (unlikely(m_flags.all(DxvkContextFlag::GpDirtyDepthClip,\n                             DxvkContextFlag::GpDynamicDepthClip))) {\n      m_flags.clr(DxvkContextFlag::GpDirtyDepthClip);\n\n      m_cmd->cmdSetDepthClipState(m_state.gp.state.rs.depthClipEnable());\n    }\n\n    if (unlikely(m_flags.all(DxvkContextFlag::GpDirtyMultisampleState,\n                             DxvkContextFlag::GpDynamicMultisampleState))) {\n      m_flags.clr(DxvkContextFlag::GpDirtyMultisampleState);\n\n      // Infer actual sample count from both the multisample state\n      // and rasterizer state, just like during pipeline creation\n      VkSampleCountFlagBits sampleCount = VkSampleCountFlagBits(m_state.gp.state.ms.sampleCount());\n\n      if (!sampleCount) {\n        sampleCount = m_state.gp.state.rs.sampleCount()\n          ? VkSampleCountFlagBits(m_state.gp.state.rs.sampleCount())\n          : VK_SAMPLE_COUNT_1_BIT;\n      }\n\n      VkSampleMask sampleMask = m_state.gp.state.ms.sampleMask() & ((1u << sampleCount) - 1u);\n      m_cmd->cmdSetMultisampleState(sampleCount, sampleMask);\n\n      if (m_device->features().extExtendedDynamicState3.extendedDynamicState3AlphaToCoverageEnable\n       && !m_state.gp.flags.test(DxvkGraphicsPipelineFlag::HasSampleMaskExport))\n        m_cmd->cmdSetAlphaToCoverageState(m_state.gp.state.ms.enableAlphaToCoverage());\n    }\n\n    if (unlikely(m_flags.all(DxvkContextFlag::GpDirtySampleLocations,\n                             DxvkContextFlag::GpDynamicSampleLocations))) {\n      m_flags.clr(DxvkContextFlag::GpDirtySampleLocations);\n\n      // While technically undefined behaviour according to the Vulkan spec, we do not track\n      // whether an image has been rendered to using centered or default sample locations.\n      // On AMD hardware, it seems like samples may be reordered depending on their position,\n      // and the interpretation of depth-stencil image contents can change depending on the\n      // sample locations used for rendering said content. We can generally expect games to\n      // render most of its content with regular sample positions and only draw a small portion\n      // with centered positions, so we would want the default interpretation to use default\n      // sample positions anyway, e.g. for the purpose of copies or resolves.\n      VkSampleCountFlagBits msSampleCount = VkSampleCountFlagBits(m_state.gp.state.ms.sampleCount());\n      VkSampleCountFlagBits rsSampleCount = VkSampleCountFlagBits(m_state.gp.state.rs.sampleCount());\n\n      if (!msSampleCount)\n        msSampleCount = rsSampleCount ? rsSampleCount : VK_SAMPLE_COUNT_1_BIT;\n\n      bool center = m_state.gp.state.useSampleLocations();\n      bool enable = m_device->canUseSampleLocations(msSampleCount);\n\n      if (enable && m_state.om.renderTargets.depth.view) {\n        auto flags = m_state.om.renderTargets.depth.view->image()->info().flags;\n        enable = bool(flags & VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT);\n      }\n\n      VkSampleLocationsInfoEXT locations = util::setupSampleLocations(msSampleCount, center);\n      m_cmd->cmdSetSampleLocations(enable && center, &locations);\n    }\n\n    if (unlikely(m_flags.all(DxvkContextFlag::GpDirtyBlendConstants,\n                             DxvkContextFlag::GpDynamicBlendConstants))) {\n      m_flags.clr(DxvkContextFlag::GpDirtyBlendConstants);\n      m_cmd->cmdSetBlendConstants(&m_state.dyn.blendConstants.r);\n    }\n\n    if (m_flags.all(DxvkContextFlag::GpDirtyRasterizerState,\n                    DxvkContextFlag::GpDynamicRasterizerState)) {\n      m_flags.clr(DxvkContextFlag::GpDirtyRasterizerState);\n\n      m_cmd->cmdSetRasterizerState(\n        m_state.dyn.cullMode,\n        m_state.dyn.frontFace);\n    }\n\n    if (unlikely(m_flags.all(DxvkContextFlag::GpDirtyDepthTest,\n                             DxvkContextFlag::GpDynamicDepthTest))) {\n      m_flags.clr(DxvkContextFlag::GpDirtyDepthTest);\n\n      auto dsReadOnlyAspects = m_state.gp.state.rt.getDepthStencilReadOnlyAspects();\n      bool writable = !(dsReadOnlyAspects & VK_IMAGE_ASPECT_DEPTH_BIT);\n\n      if (m_state.dyn.depthStencilState.depthTest()) {\n        bool written = writable &&\n          m_state.dyn.depthStencilState.depthTest() &&\n          m_state.dyn.depthStencilState.depthWrite();\n\n        m_cmd->cmdSetDepthTest(VK_TRUE);\n        m_cmd->cmdSetDepthWrite(written);\n        m_cmd->cmdSetDepthCompareOp(m_state.dyn.depthStencilState.depthCompareOp());\n\n        if (written)\n          m_state.om.attachmentMask.trackDepthWrite();\n\n        m_state.om.attachmentMask.trackDepthRead();\n      } else {\n        m_cmd->cmdSetDepthTest(VK_FALSE);\n        m_cmd->cmdSetDepthWrite(VK_FALSE);\n        m_cmd->cmdSetDepthCompareOp(VK_COMPARE_OP_ALWAYS);\n      }\n    }\n\n    if (m_flags.all(DxvkContextFlag::GpDirtyStencilTest,\n                    DxvkContextFlag::GpDynamicStencilTest)) {\n      m_flags.clr(DxvkContextFlag::GpDirtyStencilTest);\n\n      if (m_state.dyn.depthStencilState.stencilTest()) {\n        auto dsReadOnlyAspects = m_state.gp.state.rt.getDepthStencilReadOnlyAspects();\n        bool writable = !(dsReadOnlyAspects & VK_IMAGE_ASPECT_STENCIL_BIT);\n\n        auto front = convertStencilOp(m_state.dyn.depthStencilState.stencilOpFront(), writable);\n        auto back = convertStencilOp(m_state.dyn.depthStencilState.stencilOpBack(), writable);\n\n        m_cmd->cmdSetStencilTest(VK_TRUE);\n\n        m_cmd->cmdSetStencilOp(VK_STENCIL_FACE_FRONT_BIT, front);\n        m_cmd->cmdSetStencilOp(VK_STENCIL_FACE_BACK_BIT, back);\n\n        m_cmd->cmdSetStencilCompareMask(VK_STENCIL_FACE_FRONT_BIT, front.compareMask);\n        m_cmd->cmdSetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back.compareMask);\n\n        m_cmd->cmdSetStencilWriteMask(VK_STENCIL_FACE_FRONT_BIT, front.writeMask);\n        m_cmd->cmdSetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back.writeMask);\n\n        if (front.writeMask | back.writeMask)\n          m_state.om.attachmentMask.trackStencilWrite();\n\n        m_state.om.attachmentMask.trackStencilRead();\n      } else {\n        VkStencilOpState state = { };\n        state.compareOp = VK_COMPARE_OP_ALWAYS;\n\n        m_cmd->cmdSetStencilTest(VK_FALSE);\n        m_cmd->cmdSetStencilOp(VK_STENCIL_FACE_FRONT_AND_BACK, state);\n        m_cmd->cmdSetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, 0u);\n        m_cmd->cmdSetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, 0u);\n      }\n    }\n\n    if (m_flags.all(DxvkContextFlag::GpDirtyStencilRef,\n                    DxvkContextFlag::GpDynamicStencilTest)) {\n      m_flags.clr(DxvkContextFlag::GpDirtyStencilRef);\n\n      m_cmd->cmdSetStencilReference(VK_STENCIL_FRONT_AND_BACK,\n        m_state.dyn.stencilReference);\n    }\n    \n    if (m_flags.all(DxvkContextFlag::GpDirtyDepthBias,\n                    DxvkContextFlag::GpDynamicDepthBias)) {\n      m_flags.clr(DxvkContextFlag::GpDirtyDepthBias);\n\n      if (m_device->features().extDepthBiasControl.depthBiasControl) {\n        VkDepthBiasRepresentationInfoEXT depthBiasRepresentation = { VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT };\n        depthBiasRepresentation.depthBiasRepresentation = m_state.dyn.depthBiasRepresentation.depthBiasRepresentation;\n        depthBiasRepresentation.depthBiasExact          = m_state.dyn.depthBiasRepresentation.depthBiasExact;\n\n        VkDepthBiasInfoEXT depthBiasInfo = { VK_STRUCTURE_TYPE_DEPTH_BIAS_INFO_EXT };\n        depthBiasInfo.pNext                   = &depthBiasRepresentation;\n        depthBiasInfo.depthBiasConstantFactor = m_state.dyn.depthBias.depthBiasConstant;\n        depthBiasInfo.depthBiasClamp          = m_state.dyn.depthBias.depthBiasClamp;\n        depthBiasInfo.depthBiasSlopeFactor    = m_state.dyn.depthBias.depthBiasSlope;\n\n        m_cmd->cmdSetDepthBias2(&depthBiasInfo);\n      } else {\n        m_cmd->cmdSetDepthBias(\n          m_state.dyn.depthBias.depthBiasConstant,\n          m_state.dyn.depthBias.depthBiasClamp,\n          m_state.dyn.depthBias.depthBiasSlope);\n      }\n    }\n    \n    if (m_flags.all(DxvkContextFlag::GpDirtyDepthBounds,\n                    DxvkContextFlag::GpDynamicDepthBounds)) {\n      m_flags.clr(DxvkContextFlag::GpDirtyDepthBounds);\n\n      m_cmd->cmdSetDepthBounds(\n        m_state.dyn.depthBounds.minDepthBounds,\n        m_state.dyn.depthBounds.maxDepthBounds);\n\n      if (m_state.dyn.depthBounds.minDepthBounds > 0.0f\n       || m_state.dyn.depthBounds.maxDepthBounds < 1.0f)\n        m_state.om.attachmentMask.trackDepthRead();\n    }\n  }\n\n\n  template<VkPipelineBindPoint BindPoint>\n  void DxvkContext::updatePushData() {\n    m_flags.clr(DxvkContextFlag::DirtyPushData);\n\n    // Optimized pipelines may have push constants trimmed, so look\n    // up the exact layout used for the currently bound pipeline.\n    auto layoutType = getActivePipelineLayoutType(BindPoint);\n\n    auto layout = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS\n      ? m_state.gp.pipeline->getLayout()->getLayout(layoutType)\n      : m_state.cp.pipeline->getLayout()->getLayout(layoutType);\n\n    auto pushData = layout->getPushData();\n\n    if (unlikely(pushData.isEmpty()))\n      return;\n\n    // If all push data comes from resource updates, it is already in\n    // the correct layout, otherwise gather data into a temporary array.\n    std::array<char, MaxTotalPushDataSize> localData;\n\n    VkPushDataInfoEXT pushInfo = { VK_STRUCTURE_TYPE_PUSH_DATA_INFO_EXT };\n    pushInfo.offset = pushData.getOffset();\n    pushInfo.data.address = &m_state.pc.resourceData[pushData.getOffset()];\n    pushInfo.data.size = pushData.getSize();\n\n    if ((bit::tzcnt(pushData.getResourceDwordMask() + 1u) * 4u) < pushData.getSize()) {\n      pushInfo.data.address = &localData[pushData.getOffset()];\n\n      for (auto i : bit::BitMask(layout->getPushDataMask())) {\n        auto block = layout->getPushDataBlock(i);\n        auto blockSize = block.getSize();\n\n        auto srcOffset = computePushDataBlockOffset(i);\n        auto dstOffset = block.getOffset();\n\n        auto constantData = &m_state.pc.constantData[srcOffset];\n        auto resourceData = &m_state.pc.resourceData[dstOffset];\n\n        auto dstData = &localData[dstOffset];\n\n        uint32_t rangeOffset = 0u;\n\n        // Copy chunks of dwords either from the constant data array or\n        // the resource data array, depending on the resource mask.\n        uint64_t resourceMask = block.getResourceDwordMask();\n\n        while (resourceMask) {\n          uint32_t dwordIndex = bit::tzcnt(resourceMask);\n          uint32_t dwordCount = bit::tzcnt(resourceMask + (resourceMask & -resourceMask));\n\n          uint32_t byteIndex = dwordIndex * sizeof(uint32_t);\n          uint32_t byteCount = dwordCount * sizeof(uint32_t);\n\n          std::memcpy(&dstData[rangeOffset],\n            &constantData[rangeOffset], byteIndex);\n\n          std::memcpy(&dstData[rangeOffset + byteIndex],\n            &resourceData[rangeOffset + byteIndex],\n            byteCount - byteIndex);\n\n          resourceMask >>= dwordCount;\n          rangeOffset += byteCount;\n        }\n\n        std::memcpy(&dstData[rangeOffset],\n          &constantData[rangeOffset], blockSize - rangeOffset);\n      }\n    }\n\n    if (m_features.test(DxvkContextFeature::DescriptorHeap)) {\n      m_cmd->cmdPushData(DxvkCmdBuffer::ExecBuffer, &pushInfo);\n    } else {\n      m_cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer,\n        layout->getPipelineLayout(), pushData.getStageMask(),\n        pushInfo.offset, pushInfo.data.size, pushInfo.data.address);\n    }\n  }\n\n\n  void DxvkContext::beginComputePass() {\n    m_flags.set(DxvkContextFlag::CpComputePassActive,\n                DxvkContextFlag::DirtyDrawBuffer);\n\n    // Mark compute descriptors as dirty so that hazards are checked properly\n    // between dispatches even when none of the resources were re-bound. This\n    // can happen when a bound resource got written by a transfer op.\n    m_descriptorState.clearStages(VK_SHADER_STAGE_ALL_GRAPHICS);\n    m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT);\n  }\n\n\n  void DxvkContext::endComputePass() {\n    m_flags.clr(DxvkContextFlag::CpComputePassActive);\n  }\n\n\n  template<bool Indirect, bool Resolve>\n  bool DxvkContext::commitComputeState() {\n    this->endRenderPass(false);\n\n    if (!m_flags.test(DxvkContextFlag::CpComputePassActive))\n      this->beginComputePass();\n\n    if (m_flags.any(DxvkContextFlag::CpDirtyPipelineState,\n                    DxvkContextFlag::CpDirtySpecConstants)) {\n      if (unlikely(!this->updateComputePipelineState()))\n        return false;\n    }\n\n    if (this->checkComputeHazards<Indirect>()) {\n      this->flushBarriers();\n\n      // Dirty descriptors if this hasn't happened yet for\n      // whatever reason in order to re-emit barriers\n      m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT);\n    }\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      this->beginBarrierControlDebugRegion<VK_PIPELINE_BIND_POINT_COMPUTE>();\n\n    if (m_descriptorState.hasDirtyResources(VK_SHADER_STAGE_COMPUTE_BIT)) {\n      this->updateComputeShaderResources();\n\n      if (unlikely(Resolve && m_implicitResolves.hasPendingResolves())) {\n        this->flushImplicitResolves();\n        return this->commitComputeState<Indirect, false>();\n      }\n    }\n\n    if (m_flags.all(DxvkContextFlag::CpHasPushData,\n                    DxvkContextFlag::DirtyPushData))\n      this->updatePushData<VK_PIPELINE_BIND_POINT_COMPUTE>();\n\n    return true;\n  }\n  \n  \n  template<bool Indexed, bool Indirect, bool Resolve>\n  bool DxvkContext::commitGraphicsState() {\n    if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) {\n      if (unlikely(!this->updateGraphicsPipeline()))\n        return false;\n    }\n\n    // End render pass if there are pending resolves\n    if (m_flags.any(DxvkContextFlag::GpDirtyRenderTargets,\n                    DxvkContextFlag::GpRenderPassNeedsFlush))\n      this->updateRenderTargets();\n\n    if (m_flags.test(DxvkContextFlag::GpXfbActive)) {\n      // If transform feedback is active and there is a chance that we might\n      // need to rebind the pipeline, we need to end transform feedback and\n      // issue a barrier. End the render pass to do that. Ignore dirty vertex\n      // buffers here since non-dynamic vertex strides are such an extreme\n      // edge case that it's likely irrelevant in practice.\n      if (m_flags.any(DxvkContextFlag::GpDirtyPipelineState,\n                      DxvkContextFlag::GpDirtySpecConstants,\n                      DxvkContextFlag::GpDirtyXfbBuffers)) {\n        this->endCurrentPass(true);\n        this->flushBarriers();\n      }\n    }\n\n    // If a depth-stencil image is bound used with non-default sample locations,\n    // make sure that the image actually has the compat flag set.\n    if (unlikely(m_state.gp.state.useSampleLocations())) {\n      if (m_state.om.renderTargets.depth.view) {\n        VkSampleCountFlagBits samples = m_state.om.renderTargets.depth.view->image()->info().sampleCount;\n\n        if (m_device->canUseSampleLocations(samples)) {\n          auto flags = m_state.om.renderTargets.depth.view->image()->info().flags;\n\n          if (!(flags & VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT)) {\n            this->endCurrentPass(true);\n\n            DxvkImageUsageInfo usage = { };\n            usage.flags = VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT;\n\n            ensureImageCompatibility(m_state.om.renderTargets.depth.view->image(), usage);\n          }\n        }\n      }\n    }\n\n    // Check whether we can actually start the render pass as unsynchronized.\n    if (!m_flags.test(DxvkContextFlag::GpRenderPassActive)) {\n      if (renderPassStartUnsynchronized()) {\n        m_flags.set(DxvkContextFlag::GpRenderPassUnsynchronized);\n        m_unsynchronizedDrawCount = 0u;\n      }\n\n      // Dirty all relevant state and descriptor sets if there is no active\n      // render pass so that we actually check all bound resources for hazards\n      // as necessary. Otherwise, descriptor state will only be dirtied once\n      // we actually start the render pass, which may be too late.\n      m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS);\n\n      m_flags.set(DxvkContextFlag::DirtyDrawBuffer,\n                  DxvkContextFlag::GpDirtyIndexBuffer,\n                  DxvkContextFlag::GpDirtyVertexBuffers,\n                  DxvkContextFlag::GpDirtyXfbBuffers);\n    }\n\n    if (m_flags.any(DxvkContextFlag::GpRenderPassSideEffects,\n                    DxvkContextFlag::GpRenderPassUnsynchronized)\n     || m_state.gp.flags.any(DxvkGraphicsPipelineFlag::HasStorageDescriptors,\n                             DxvkGraphicsPipelineFlag::HasTransformFeedback)) {\n      // If either the current pipeline has side effects or if there are pending\n      // writes from previous draws, check for hazards. This also tracks any\n      // resources written for the first time, but does not emit any barriers\n      // on its own so calling this outside a render pass is safe. This also\n      // implicitly dirties all state for which we need to track resource access.\n      //\n      // If the render pass is currently unsynchronized, we need to manually\n      // flush barriers afterwards. We will have done full resource tracking,\n      // so skipping the global barrier is still fine in that case.\n      if (this->checkGraphicsHazards<Indexed, Indirect>()) {\n        this->endCurrentPass(true);\n        this->flushBarriers();\n      }\n\n      // The render pass flag gets reset when the render pass ends, so set it late\n      if (m_state.gp.flags.any(DxvkGraphicsPipelineFlag::HasStorageDescriptors,\n                               DxvkGraphicsPipelineFlag::HasTransformFeedback))\n        m_flags.set(DxvkContextFlag::GpRenderPassSideEffects);\n\n      if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)) {\n        // In case we entered an unsynchronized render pass with no pending writes,\n        // i.e. if we issued a barrier before the pass, we can trivially revert the\n        // pass to the regularly synchronized mode if the render pass happens to be\n        // big. This isn't common, but Ashes of the Singularity hits this case.\n        //\n        // Doing this is safe even in case shader writes are used, because we keep\n        // the actual tracking info intact. On the other hand, we cannot safely do\n        // this if there are any pending writes without issuing a barrier.\n        if ((++m_unsynchronizedDrawCount == MaxUnsynchronizedDraws) && m_execBarriers.hasPendingAccess(vk::AccessWriteMask))\n          m_flags.clr(DxvkContextFlag::GpRenderPassUnsynchronized);\n      }\n    }\n\n    // Start the render pass. This must happen before any render state\n    // is set up so that we can safely use secondary command buffers.\n    if (!m_flags.test(DxvkContextFlag::GpRenderPassActive))\n      this->beginRenderPass();\n\n    // If there are any pending clears, record them now\n    if (unlikely(!m_deferredClears.empty()))\n      flushClearsInline();\n\n    if (m_flags.test(DxvkContextFlag::GpRenderPassSideEffects)) {\n      // Make sure that the debug label for barrier control\n      // always starts within an active render pass\n      if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n        this->beginBarrierControlDebugRegion<VK_PIPELINE_BIND_POINT_GRAPHICS>();\n    }\n\n    if (m_flags.test(DxvkContextFlag::GpDirtyIndexBuffer) && Indexed)\n      this->updateIndexBufferBinding();\n    \n    if (m_flags.test(DxvkContextFlag::GpDirtyVertexBuffers))\n      this->updateVertexBufferBindings();\n    \n    if (m_flags.test(DxvkContextFlag::GpDirtySpecConstants))\n      this->updateSpecConstants<VK_PIPELINE_BIND_POINT_GRAPHICS>();\n\n    if (m_flags.test(DxvkContextFlag::GpDirtyPipelineState)) {\n      if (unlikely(!this->updateGraphicsPipelineState()))\n        return false;\n    }\n    \n    if (m_descriptorState.hasDirtyResources(VK_SHADER_STAGE_ALL_GRAPHICS)) {\n      if (unlikely(!this->updateGraphicsShaderResources())) {\n        // This can only happen if we were inside a secondary command buffer.\n        // Technically it would be sufficient to only restart the secondary\n        // command buffer, but this should almost never happen in practice\n        // anyway so avoid the complexity and just suspend the render pass.\n        this->endCurrentPass(true);\n\n        m_cmd->createDescriptorRange();\n\n        return this->commitGraphicsState<Indexed, Indirect>();\n      }\n\n      if (unlikely(Resolve && m_implicitResolves.hasPendingResolves())) {\n        // If implicit resolves are required for any of the shader bindings, we need\n        // to discard all the state setup that we've done so far and try again\n        this->endCurrentPass(true);\n        this->flushImplicitResolves();\n\n        return this->commitGraphicsState<Indexed, Indirect, false>();\n      }\n    }\n    \n    if (m_state.gp.flags.test(DxvkGraphicsPipelineFlag::HasTransformFeedback))\n      this->updateTransformFeedbackState();\n    \n    this->updateDynamicState();\n    \n    if (m_flags.all(DxvkContextFlag::GpHasPushData, DxvkContextFlag::DirtyPushData))\n      this->updatePushData<VK_PIPELINE_BIND_POINT_GRAPHICS>();\n\n    if (m_flags.test(DxvkContextFlag::DirtyDrawBuffer) && Indirect)\n      this->trackDrawBuffer();\n\n    return true;\n  }\n  \n  \n  template<VkPipelineBindPoint BindPoint>\n  bool DxvkContext::checkResourceHazards(\n    const DxvkPipelineBindings*     layout) {\n    // For performance reasons, we generall want to skip tracking resources that are\n    // not written from any graphics shaders, but in unsynchronized passes we have\n    // to check everything anyway.\n    bool checkEverything = BindPoint == VK_PIPELINE_BIND_POINT_COMPUTE\n      || m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized);\n\n    // Iterate over all resources that are actively being written by the shader pipeline.\n    // On graphics, this must not exit early since extra resource tracking is required.\n    { auto range = layout->getReadWriteResources();\n\n      if (range.bindingCount) {\n        bool requiresBarrier = false;\n\n        for (uint32_t j = 0u; j < range.bindingCount; j++) {\n          const auto& binding = range.bindings[j];\n          const auto& slot = m_resources[binding.getResourceIndex()];\n\n          switch (binding.getDescriptorType()) {\n            case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:\n            case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: {\n              if (slot.bufferView) {\n                if (checkEverything || slot.bufferView->buffer()->hasGfxStores()) {\n                  requiresBarrier = requiresBarrier || checkBufferViewBarrier<BindPoint>(\n                    slot.bufferView, binding.getAccess(), binding.getAccessOp());\n                } else {\n                  requiresBarrier = !slot.bufferView->buffer()->trackGfxStores() || requiresBarrier;\n                }\n              }\n            } break;\n\n            case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {\n              if (slot.imageView) {\n                if (checkEverything || slot.imageView->hasGfxStores()) {\n                  requiresBarrier = requiresBarrier || checkImageViewBarrier<BindPoint>(\n                    slot.imageView, binding.getAccess(), binding.getAccessOp());\n                } else {\n                  requiresBarrier = !slot.imageView->image()->trackGfxStores() || requiresBarrier;\n                }\n              }\n            } break;\n\n            default:\n              /* nothing to do */;\n          }\n\n          // On compute, we may exit immediately since no additional tracking is required.\n          if (checkEverything && requiresBarrier)\n            return true;\n        }\n\n        // Once we've processed all written resources, we can exit on graphics as well.\n        // If we ever transition an unsynchronized pass to a synchronized pass, we will\n        // have to issue a barrier since we may have skipped per-resource store tracking.\n        if (!checkEverything && requiresBarrier)\n          return true;\n      }\n    }\n\n    // Only check read-only resources if there are any pending writes targeting them.\n    VkAccessFlags2 shaderReadAccess = VK_ACCESS_2_UNIFORM_READ_BIT\n      | VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_SAMPLED_READ_BIT\n      | VK_ACCESS_2_SHADER_STORAGE_READ_BIT;\n\n    if (!m_execBarriers.hasPendingAccess(vk::AccessWriteMask)\n     || !m_execBarriers.hasTargetAccess(shaderReadAccess))\n      return false;\n\n    // For read-only resources, it is sufficient to check dirty bindings since\n    // any resource previously bound as read-only cannot have been written by\n    // the same pipeline.\n    VkShaderStageFlags dirtyStageMask = m_descriptorState.getDirtyStageMask(\n      DxvkDescriptorClass::Buffer | DxvkDescriptorClass::View | DxvkDescriptorClass::Va);\n    dirtyStageMask &= layout->getNonemptyStageMask();\n\n    for (auto stageIndex : bit::BitMask(uint32_t(dirtyStageMask))) {\n      VkShaderStageFlagBits stage = VkShaderStageFlagBits(1u << stageIndex);\n\n      // Check any view-based resource for hazards\n      auto range = layout->getReadOnlyResourcesForStage(stage);\n\n      for (uint32_t j = 0; j < range.bindingCount; j++) {\n        const auto& binding = range.bindings[j];\n\n        switch (binding.getDescriptorType()) {\n          case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:\n          case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:\n            if (binding.isUniformBuffer()) {\n              const auto& slot = m_uniformBuffers[binding.getResourceIndex()];\n\n              if (slot.length() && (checkEverything || slot.buffer()->hasGfxStores())) {\n                if (checkBufferBarrier<BindPoint>(slot, binding.getAccess(), DxvkAccessOp::None))\n                  return true;\n              }\n              break;\n            }\n            [[fallthrough]];\n\n          case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:\n          case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: {\n            const auto& slot = m_resources[binding.getResourceIndex()];\n\n            if (slot.bufferView && (checkEverything || slot.bufferView->buffer()->hasGfxStores())) {\n              if (checkBufferViewBarrier<BindPoint>(slot.bufferView, binding.getAccess(), DxvkAccessOp::None))\n                return true;\n            }\n          } break;\n\n          case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:\n          case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {\n            const auto& slot = m_resources[binding.getResourceIndex()];\n\n            if (slot.imageView && (checkEverything || slot.imageView->hasGfxStores())) {\n              if (checkImageViewBarrier<BindPoint>(slot.imageView, binding.getAccess(), DxvkAccessOp::None))\n                return true;\n            }\n          } break;\n\n          default:\n            /* nothing to do */;\n        }\n      }\n    }\n\n    return false;\n  }\n  \n\n  template<bool Indirect>\n  bool DxvkContext::checkComputeHazards() {\n    // Exit early if we know that there cannot be any hazards to avoid\n    // some overhead after barriers are flushed. This is common.\n    if (m_barrierTracker.empty())\n      return false;\n\n    bool requiresBarrier = checkResourceHazards<VK_PIPELINE_BIND_POINT_COMPUTE>(m_state.cp.pipeline->getLayout());\n\n    if (Indirect && !requiresBarrier) {\n      requiresBarrier = checkBufferBarrier<VK_PIPELINE_BIND_POINT_COMPUTE>(\n        m_state.id.argBuffer, VK_ACCESS_INDIRECT_COMMAND_READ_BIT, DxvkAccessOp::None);\n    }\n\n    return requiresBarrier;\n  }\n\n\n  template<bool Indexed, bool Indirect>\n  bool DxvkContext::checkGraphicsHazards() {\n    // If the current pipeline does not have any stores, we only need to iterate\n    // over all resources if any stores are in the current barrier set. If the\n    // render pass has side effects, we know that there are writes already.\n    if (!m_flags.test(DxvkContextFlag::GpRenderPassSideEffects)\n     && !m_state.gp.flags.any(DxvkGraphicsPipelineFlag::HasStorageDescriptors,\n                              DxvkGraphicsPipelineFlag::HasTransformFeedback)\n     && !m_execBarriers.hasLayoutTransitions()\n     && !m_execBarriers.hasPendingAccess(vk::AccessWriteMask))\n      return false;\n\n    // Check shader resources on every draw to handle WAW hazards, and to make\n    // sure that writes are handled properly.\n    bool requiresBarrier = checkResourceHazards<VK_PIPELINE_BIND_POINT_GRAPHICS>(m_state.gp.pipeline->getLayout());\n\n    // Transform feedback buffer writes won't overlap, so we also only need to\n    // check those if dirty.\n    if (m_flags.test(DxvkContextFlag::GpDirtyXfbBuffers)\n     && m_state.gp.flags.test(DxvkGraphicsPipelineFlag::HasTransformFeedback)) {\n      for (uint32_t i = 0; i < MaxNumXfbBuffers; i++) {\n        const auto& xfbBufferSlice = m_state.xfb.buffers[i];\n        const auto& xfbCounterSlice = m_state.xfb.activeCounters[i];\n\n        if (xfbBufferSlice.length()) {\n          requiresBarrier |= !xfbBufferSlice.buffer()->trackGfxStores();\n          requiresBarrier |= checkBufferBarrier<VK_PIPELINE_BIND_POINT_GRAPHICS>(\n            xfbBufferSlice, VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, DxvkAccessOp::None);\n\n          if (xfbCounterSlice.length()) {\n            requiresBarrier |= !xfbCounterSlice.buffer()->trackGfxStores();\n            requiresBarrier |= checkBufferBarrier<VK_PIPELINE_BIND_POINT_GRAPHICS>(xfbCounterSlice,\n              VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT |\n              VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,\n              DxvkAccessOp::None);\n          }\n        }\n      }\n    }\n\n    // From now on, we only have read-only resources to check and can\n    // exit early if we find a hazard.\n    if (requiresBarrier || !m_execBarriers.hasPendingAccess(vk::AccessWriteMask))\n      return requiresBarrier;\n\n    bool unsynchronizedPass = m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized);\n\n    if (m_execBarriers.hasTargetAccess(VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT) && Indirect) {\n      std::array<DxvkBufferSlice*, 2> slices = {{\n        &m_state.id.argBuffer,\n        &m_state.id.cntBuffer,\n      }};\n\n      for (uint32_t i = 0; i < slices.size(); i++) {\n        if (slices[i]->length() && (unsynchronizedPass || slices[i]->buffer()->hasGfxStores())) {\n          if (checkBufferBarrier<VK_PIPELINE_BIND_POINT_GRAPHICS>(*slices[i],\n              VK_ACCESS_INDIRECT_COMMAND_READ_BIT, DxvkAccessOp::None))\n            return true;\n        }\n      }\n    }\n\n    // Only checking index and vertex buffers when dirty works because client\n    // APIs prohibit binding them for reading and writing at the same time.\n    // This does not extend to indirect draw buffers.\n    if (m_execBarriers.hasTargetAccess(VK_ACCESS_2_INDEX_READ_BIT) && Indexed\n     && m_flags.test(DxvkContextFlag::GpDirtyIndexBuffer)) {\n      const auto& indexBufferSlice = m_state.vi.indexBuffer;\n\n      if (indexBufferSlice.length() && (unsynchronizedPass || indexBufferSlice.buffer()->hasGfxStores())) {\n        if (checkBufferBarrier<VK_PIPELINE_BIND_POINT_GRAPHICS>(indexBufferSlice,\n            VK_ACCESS_INDEX_READ_BIT, DxvkAccessOp::None))\n          return true;\n      }\n    }\n\n    if (m_execBarriers.hasTargetAccess(VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT)\n     && m_flags.test(DxvkContextFlag::GpDirtyVertexBuffers)) {\n      uint32_t bindingCount = m_state.gp.state.il.bindingCount();\n\n      for (uint32_t i = 0; i < bindingCount; i++) {\n        uint32_t binding = m_state.gp.state.ilBindings[i].binding();\n        const auto& vertexBufferSlice = m_state.vi.vertexBuffers[binding];\n\n        if (vertexBufferSlice.length() && (unsynchronizedPass || vertexBufferSlice.buffer()->hasGfxStores())) {\n          if (checkBufferBarrier<VK_PIPELINE_BIND_POINT_GRAPHICS>(vertexBufferSlice,\n              VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, DxvkAccessOp::None))\n            return true;\n        }\n      }\n    }\n\n    return false;\n  }\n\n\n  template<VkPipelineBindPoint BindPoint>\n  bool DxvkContext::checkBufferBarrier(\n    const DxvkBufferSlice&          bufferSlice,\n          VkAccessFlags             access,\n          DxvkAccessOp              accessOp) {\n    return checkResourceBarrier<BindPoint>([this, &bufferSlice, accessOp] (DxvkAccess access) {\n      return resourceHasAccess(*bufferSlice.buffer(),\n        bufferSlice.offset(), bufferSlice.length(), access, accessOp);\n    }, access);\n  }\n\n\n  template<VkPipelineBindPoint BindPoint>\n  bool DxvkContext::checkBufferViewBarrier(\n    const Rc<DxvkBufferView>&       bufferView,\n          VkAccessFlags             access,\n          DxvkAccessOp              accessOp) {\n    return checkResourceBarrier<BindPoint>([this, &bufferView, accessOp] (DxvkAccess access) {\n      return resourceHasAccess(*bufferView, access, accessOp);\n    }, access);\n  }\n\n\n  template<VkPipelineBindPoint BindPoint>\n  bool DxvkContext::checkImageViewBarrier(\n    const Rc<DxvkImageView>&        imageView,\n          VkAccessFlags             access,\n          DxvkAccessOp              accessOp) {\n    return checkResourceBarrier<BindPoint>([this, &imageView, accessOp] (DxvkAccess access) {\n      return resourceHasAccess(*imageView, access, accessOp);\n    }, access);\n  }\n\n\n  void DxvkContext::emitMemoryBarrier(\n          VkPipelineStageFlags      srcStages,\n          VkAccessFlags             srcAccess,\n          VkPipelineStageFlags      dstStages,\n          VkAccessFlags             dstAccess) {\n    VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    barrier.srcStageMask = srcStages;\n    barrier.srcAccessMask = srcAccess;\n    barrier.dstStageMask = dstStages;\n    barrier.dstAccessMask = dstAccess;\n\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.memoryBarrierCount = 1;\n    depInfo.pMemoryBarriers = &barrier;\n\n    m_cmd->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n  }\n\n\n  void DxvkContext::trackDrawBuffer() {\n    if (m_flags.test(DxvkContextFlag::DirtyDrawBuffer)) {\n      m_flags.clr(DxvkContextFlag::DirtyDrawBuffer);\n\n      m_renderPassBarrierSrc.stages |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;\n      m_renderPassBarrierSrc.access |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT;\n\n      if (m_state.id.argBuffer.length())\n        m_cmd->track(m_state.id.argBuffer.buffer(), DxvkAccess::Read);\n\n      if (m_state.id.cntBuffer.length())\n        m_cmd->track(m_state.id.cntBuffer.buffer(), DxvkAccess::Read);\n    }\n  }\n\n\n  bool DxvkContext::tryInvalidateDeviceLocalBuffer(\n      const Rc<DxvkBuffer>&           buffer,\n            VkDeviceSize              copySize) {\n    // We can only discard if the full buffer gets written, and we will only discard\n    // small buffers in order to not waste significant amounts of memory.\n    if (copySize != buffer->info().size || copySize > 0x40000)\n      return false;\n\n    // Check if the buffer is safe to move at all\n    if (!buffer->canRelocate())\n      return false;\n\n    // Suspend the current render pass if transform feedback is active prior to\n    // invalidating the buffer, since otherwise we may invalidate a bound buffer.\n    if ((buffer->info().usage & VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT)\n     && (m_flags.test(DxvkContextFlag::GpXfbActive)))\n      this->endCurrentPass(true);\n\n    this->invalidateBuffer(buffer, buffer->allocateStorage());\n    return true;\n  }\n\n\n  Rc<DxvkImageView> DxvkContext::ensureImageViewCompatibility(\n    const Rc<DxvkImageView>&        view,\n          VkImageUsageFlagBits      usage) {\n    // Return existing view if it already compatible with the image\n    VkFormat viewFormat = view->info().format;\n\n    bool isFormatCompatible = view->image()->isViewCompatible(viewFormat);\n    bool isUsageCompatible = (view->image()->info().usage & usage) == usage;\n\n    if (isFormatCompatible && isUsageCompatible) {\n      if (view->info().usage & usage)\n        return view;\n\n      // Just create a new view with the correct usage flag\n      DxvkImageViewKey viewInfo = view->info();\n      viewInfo.usage = usage;\n      return view->image()->createView(viewInfo);\n    } else {\n      // Actually need to relocate the image\n      DxvkImageUsageInfo usageInfo = { };\n      usageInfo.usage = usage;\n\n      if (!isFormatCompatible) {\n        usageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;\n        usageInfo.viewFormatCount = 1u;\n        usageInfo.viewFormats = &viewFormat;\n      }\n\n      if (!ensureImageCompatibility(view->image(), usageInfo))\n        return nullptr;\n\n      DxvkImageViewKey viewInfo = view->info();\n      viewInfo.usage = usage;\n      return view->image()->createView(viewInfo);\n    }\n  }\n\n\n  void DxvkContext::relocateResources(\n          size_t                    bufferCount,\n    const DxvkRelocateBufferInfo*   bufferInfos,\n          size_t                    imageCount,\n    const DxvkRelocateImageInfo*    imageInfos) {\n    if (!bufferCount && !imageCount)\n      return;\n\n    // Ensure that all images are in their default layout and are\n    // safely accessible, but ignore any uninitialized subresources.\n    std::vector<DxvkResourceAccess> accessBatch;\n\n    for (size_t i = 0u; i < bufferCount; i++) {\n      const auto& e = bufferInfos[i];\n\n      accessBatch.emplace_back(*e.buffer, 0u, e.buffer->info().size,\n        e.buffer->info().stages, e.buffer->info().access);\n    }\n\n    for (size_t i = 0u; i < imageCount; i++) {\n      const auto& e = imageInfos[i];\n\n      if (e.image->isInitialized(e.image->getAvailableSubresources())) {\n        accessBatch.emplace_back(*e.image, e.image->getAvailableSubresources(),\n          e.image->info().layout, e.image->info().stages, e.image->info().access, false);\n      } else {\n        VkImageAspectFlags aspects = e.image->formatInfo()->aspectMask;\n        VkImageSubresource subresource = { };\n\n        while (aspects) {\n          subresource.aspectMask = vk::getNextAspect(aspects);\n\n          for (subresource.mipLevel = 0u; subresource.mipLevel < e.image->info().mipLevels; subresource.mipLevel++) {\n            for (subresource.arrayLayer = 0u; subresource.arrayLayer < e.image->info().numLayers; subresource.arrayLayer++) {\n              if (e.image->isInitialized(subresource)) {\n                accessBatch.emplace_back(*e.image, vk::makeSubresourceRange(subresource),\n                  e.image->info().layout, e.image->info().stages, e.image->info().access, false);\n              }\n            }\n          }\n        }\n      }\n    }\n\n    acquireResources(DxvkCmdBuffer::ExecBuffer, accessBatch.size(), accessBatch.data());\n\n    // Use low-level barriers while processing actual copies\n    small_vector<VkImageMemoryBarrier2, 16> imageBarriers;\n\n    VkMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    memoryBarrier.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;\n    memoryBarrier.dstAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;\n\n    for (size_t i = 0; i < bufferCount; i++) {\n      const auto& info = bufferInfos[i];\n\n      memoryBarrier.srcStageMask |= info.buffer->info().stages;\n      memoryBarrier.srcAccessMask |= info.buffer->info().access;\n    }\n\n    for (size_t i = 0; i < imageCount; i++) {\n      const auto& info = imageInfos[i];\n      auto oldStorage = info.image->storage();\n\n      // The source image may only be partially initialized. Ignore any subresources\n      // that aren't, but try to do process as much in one go as possible.\n      VkImageSubresourceRange availableSubresources = info.image->getAvailableSubresources();\n\n      uint32_t mipStep = info.image->isInitialized(availableSubresources)\n        ? availableSubresources.levelCount : 1u;\n\n      for (uint32_t m = 0; m < availableSubresources.levelCount; m += mipStep) {\n        VkImageSubresourceRange subresourceRange = availableSubresources;\n        subresourceRange.baseMipLevel = m;\n        subresourceRange.levelCount = mipStep;\n\n        uint32_t layerStep = info.image->isInitialized(subresourceRange)\n          ? availableSubresources.layerCount : 1u;\n\n        for (uint32_t l = 0; l < availableSubresources.layerCount; l += layerStep) {\n          subresourceRange.baseArrayLayer = l;\n          subresourceRange.layerCount = layerStep;\n\n          if (info.image->isInitialized(subresourceRange)) {\n            VkImageMemoryBarrier2 dstBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n            dstBarrier.srcStageMask = info.image->info().stages;\n            dstBarrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;\n            dstBarrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;\n            dstBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n            dstBarrier.newLayout = info.image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n            dstBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n            dstBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n            dstBarrier.image = info.storage->getImageInfo().image;\n            dstBarrier.subresourceRange = subresourceRange;\n\n            if (info.image->info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) {\n              dstBarrier.subresourceRange.baseArrayLayer = 0u;\n              dstBarrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;\n            }\n\n            imageBarriers.push_back(dstBarrier);\n\n            VkImageMemoryBarrier2 srcBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n            srcBarrier.srcStageMask = info.image->info().stages;\n            srcBarrier.srcAccessMask = info.image->info().access;\n            srcBarrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;\n            srcBarrier.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT;\n            srcBarrier.oldLayout = info.image->info().layout;\n            srcBarrier.newLayout = info.image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);\n            srcBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n            srcBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n            srcBarrier.image = oldStorage->getImageInfo().image;\n            srcBarrier.subresourceRange = subresourceRange;\n\n            if (info.image->info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) {\n              srcBarrier.subresourceRange.baseArrayLayer = 0u;\n              srcBarrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;\n            }\n\n            imageBarriers.push_back(srcBarrier);\n          }\n        }\n      }\n    }\n\n    // Submit all pending barriers in one go\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n\n    if (!imageBarriers.empty()) {\n      depInfo.imageMemoryBarrierCount = imageBarriers.size();\n      depInfo.pImageMemoryBarriers = imageBarriers.data();\n    }\n\n    if (memoryBarrier.srcStageMask) {\n      depInfo.memoryBarrierCount = 1u;\n      depInfo.pMemoryBarriers = &memoryBarrier;\n    }\n\n    m_cmd->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n\n    // Set up post-copy barriers\n    depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n\n    memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    memoryBarrier.srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;\n    memoryBarrier.srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;\n\n    imageBarriers.clear();\n\n    // Copy and invalidate all buffers\n    for (size_t i = 0; i < bufferCount; i++) {\n      const auto& info = bufferInfos[i];\n      auto oldStorage = info.buffer->storage();\n\n      DxvkResourceBufferInfo dstInfo = info.storage->getBufferInfo();\n      DxvkResourceBufferInfo srcInfo = oldStorage->getBufferInfo();\n\n      VkBufferCopy2 region = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 };\n      region.dstOffset = dstInfo.offset;\n      region.srcOffset = srcInfo.offset;\n      region.size = info.buffer->info().size;\n\n      VkCopyBufferInfo2 copy = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 };\n      copy.dstBuffer = dstInfo.buffer;\n      copy.srcBuffer = srcInfo.buffer;\n      copy.regionCount = 1;\n      copy.pRegions = &region;\n\n      invalidateBuffer(info.buffer, Rc<DxvkResourceAllocation>(info.storage));\n\n      m_cmd->cmdCopyBuffer(DxvkCmdBuffer::ExecBuffer, &copy);\n      m_cmd->track(info.buffer, DxvkAccess::Move);\n\n      memoryBarrier.dstStageMask |= info.buffer->info().stages;\n      memoryBarrier.dstAccessMask |= info.buffer->info().access;\n    }\n\n    // Copy and invalidate all images\n    for (size_t i = 0; i < imageCount; i++) {\n      const auto& info = imageInfos[i];\n      auto oldStorage = info.image->storage();\n\n      VkImageLayout finalLayout = info.usageInfo.layout ? info.usageInfo.layout : info.image->info().layout;\n\n      DxvkResourceImageInfo dstInfo = info.storage->getImageInfo();\n      DxvkResourceImageInfo srcInfo = oldStorage->getImageInfo();\n\n      VkImageSubresourceRange availableSubresources = info.image->getAvailableSubresources();\n\n      // Iterate over all subresources and compute copy regions. We need\n      // one region per mip or plane, so size the local array accordingly.\n      small_vector<VkImageCopy2, 16> imageRegions;\n\n      uint32_t planeCount = 1;\n\n      auto formatInfo = info.image->formatInfo();\n\n      if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane))\n        planeCount = vk::getPlaneCount(formatInfo->aspectMask);\n\n      for (uint32_t p = 0; p < planeCount; p++) {\n        for (uint32_t m = 0; m < info.image->info().mipLevels; m++) {\n          VkImageSubresourceRange subresourceRange = availableSubresources;\n          subresourceRange.baseMipLevel = m;\n          subresourceRange.levelCount = 1u;\n\n          uint32_t layerStep = info.image->isInitialized(subresourceRange)\n            ? subresourceRange.layerCount : 1u;\n\n          for (uint32_t l = 0; l < subresourceRange.layerCount; l += layerStep) {\n            subresourceRange.baseArrayLayer = l;\n            subresourceRange.layerCount = layerStep;\n\n            if (info.image->isInitialized(subresourceRange)) {\n              VkImageCopy2 region = { VK_STRUCTURE_TYPE_IMAGE_COPY_2 };\n              region.dstSubresource.aspectMask = formatInfo->aspectMask;\n              region.dstSubresource.mipLevel = m;\n              region.dstSubresource.baseArrayLayer = l;\n              region.dstSubresource.layerCount = layerStep;\n              region.srcSubresource = region.dstSubresource;\n              region.extent = info.image->mipLevelExtent(m);\n\n              if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n                region.dstSubresource.aspectMask = vk::getPlaneAspect(p);\n                region.srcSubresource.aspectMask = vk::getPlaneAspect(p);\n\n                region.extent.width /= formatInfo->planes[p].blockSize.width;\n                region.extent.height /= formatInfo->planes[p].blockSize.height;\n              }\n\n              imageRegions.push_back(region);\n\n              // Emit image barrier for this region. We could in theory transition the\n              // entire image in one go as long as all subresources are initialized,\n              // but there is usually no reason to do so.\n              VkImageMemoryBarrier2 dstBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n              dstBarrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;\n              dstBarrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT;\n              dstBarrier.dstStageMask = info.image->info().stages;\n              dstBarrier.dstAccessMask = info.image->info().access;\n              dstBarrier.oldLayout = info.image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n              dstBarrier.newLayout = finalLayout;\n              dstBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n              dstBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n              dstBarrier.image = info.storage->getImageInfo().image;\n              dstBarrier.subresourceRange = vk::makeSubresourceRange(region.dstSubresource);\n\n              if (info.image->info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) {\n                dstBarrier.subresourceRange.baseArrayLayer = 0u;\n                dstBarrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;\n              }\n\n              imageBarriers.push_back(dstBarrier);\n            }\n          }\n        }\n      }\n\n      VkCopyImageInfo2 copy = { VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2 };\n      copy.dstImage = dstInfo.image;\n      copy.dstImageLayout = info.image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n      copy.srcImage = srcInfo.image;\n      copy.srcImageLayout = info.image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);\n      copy.regionCount = imageRegions.size();\n      copy.pRegions = imageRegions.data();\n\n      invalidateImageWithUsage(info.image, Rc<DxvkResourceAllocation>(info.storage), info.usageInfo, finalLayout);\n\n      m_cmd->cmdCopyImage(DxvkCmdBuffer::ExecBuffer, &copy);\n      m_cmd->track(info.image, DxvkAccess::Move);\n    }\n\n    if (!imageBarriers.empty()) {\n      depInfo.imageMemoryBarrierCount = imageBarriers.size();\n      depInfo.pImageMemoryBarriers = imageBarriers.data();\n    }\n\n    if (memoryBarrier.dstStageMask) {\n      depInfo.memoryBarrierCount = 1u;\n      depInfo.pMemoryBarriers = &memoryBarrier;\n    }\n\n    m_cmd->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n  }\n\n\n  void DxvkContext::relocateQueuedResources() {\n    // Limit the number and size of resources to process per submission to\n    // something reasonable. We don't know if we are transferring over PCIe.\n    constexpr static uint32_t MaxRelocationsPerSubmission = 128u;\n    constexpr static uint32_t MaxRelocatedMemoryPerSubmission = 16u << 20;\n\n    auto resourceList = m_common->memoryManager().pollRelocationList(\n      MaxRelocationsPerSubmission, MaxRelocatedMemoryPerSubmission);\n\n    if (resourceList.empty())\n      return;\n\n    std::vector<DxvkRelocateBufferInfo> bufferInfos;\n    std::vector<DxvkRelocateImageInfo> imageInfos;\n\n    // Iterate over resource list and try to create and assign new allocations\n    // for them based on the mode selected by the allocator. Failures here are\n    // not fatal, but may lead to weird behaviour down the line - ignore for now.\n    for (const auto& e : resourceList) {\n      auto storage = e.resource->relocateStorage(e.mode);\n\n      if (!storage)\n        continue;\n\n      Rc<DxvkImage> image = dynamic_cast<DxvkImage*>(e.resource.ptr());\n      Rc<DxvkBuffer> buffer = dynamic_cast<DxvkBuffer*>(e.resource.ptr());\n\n      if (image) {\n        auto& e = imageInfos.emplace_back();\n        e.image = std::move(image);\n        e.storage = std::move(storage);\n      } else if (buffer) {\n        auto& e = bufferInfos.emplace_back();\n        e.buffer = std::move(buffer);\n        e.storage = std::move(storage);\n      }\n    }\n\n    if (bufferInfos.empty() && imageInfos.empty())\n      return;\n\n    // If there are any resources to relocate, we have to stall the transfer\n    // queue so that subsequent resource uploads do not overlap with resource\n    // copies on the graphics timeline.\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils))) {\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xc0a2f0, \"Memory defrag\"));\n    }\n\n    relocateResources(\n      bufferInfos.size(), bufferInfos.data(),\n      imageInfos.size(), imageInfos.data());\n\n    m_cmd->setSubmissionBarrier();\n\n    if (unlikely(m_features.test(DxvkContextFeature::DebugUtils)))\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n  }\n\n\n  Rc<DxvkSampler> DxvkContext::createBlitSampler(\n          VkFilter                    filter) {\n    DxvkSamplerKey samplerKey;\n    samplerKey.setFilter(filter, filter,\n      VK_SAMPLER_MIPMAP_MODE_NEAREST);\n    samplerKey.setAddressModes(\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);\n\n    return m_device->createSampler(samplerKey);\n  }\n\n\n  DxvkGraphicsPipeline* DxvkContext::lookupGraphicsPipeline(\n    const DxvkGraphicsPipelineShaders&  shaders) {\n    auto idx = shaders.hash() % m_gpLookupCache.size();\n    \n    if (unlikely(!m_gpLookupCache[idx] || !shaders.eq(m_gpLookupCache[idx]->shaders())))\n      m_gpLookupCache[idx] = m_common->pipelineManager().createGraphicsPipeline(shaders);\n\n    return m_gpLookupCache[idx];\n  }\n\n\n  DxvkComputePipeline* DxvkContext::lookupComputePipeline(\n    const DxvkComputePipelineShaders&   shaders) {\n    auto idx = shaders.hash() % m_cpLookupCache.size();\n    \n    if (unlikely(!m_cpLookupCache[idx] || !shaders.eq(m_cpLookupCache[idx]->shaders())))\n      m_cpLookupCache[idx] = m_common->pipelineManager().createComputePipeline(shaders);\n\n    return m_cpLookupCache[idx];\n  }\n\n\n  Rc<DxvkBuffer> DxvkContext::createZeroBuffer(\n          VkDeviceSize              size) {\n    if (m_zeroBuffer && m_zeroBuffer->info().size >= size) {\n      m_cmd->track(m_zeroBuffer, DxvkAccess::Read);\n      return m_zeroBuffer;\n    }\n\n    DxvkBufferCreateInfo bufInfo;\n    bufInfo.size    = align<VkDeviceSize>(size, 1 << 20);\n    bufInfo.usage   = VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                    | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n    bufInfo.stages  = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    bufInfo.access  = VK_ACCESS_TRANSFER_WRITE_BIT\n                    | VK_ACCESS_TRANSFER_READ_BIT;\n    bufInfo.debugName = \"Zero buffer\";\n\n    m_zeroBuffer = m_device->createBuffer(bufInfo,\n      VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n    auto slice = m_zeroBuffer->getSliceInfo();\n\n    // FillBuffer is allowed even on transfer queues. Execute it on the barrier\n    // command buffer to ensure that subsequent transfer commands can see it.\n    m_cmd->cmdFillBuffer(DxvkCmdBuffer::SdmaBarriers,\n      slice.buffer, slice.offset, slice.size, 0);\n\n    accessMemory(DxvkCmdBuffer::SdmaBarriers,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT,\n      VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT);\n\n    if (m_device->hasDedicatedTransferQueue()) {\n      accessMemory(DxvkCmdBuffer::InitBarriers,\n        VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_NONE,\n        VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_ACCESS_2_TRANSFER_READ_BIT);\n    }\n\n    m_cmd->track(m_zeroBuffer, DxvkAccess::Write);\n    return m_zeroBuffer;\n  }\n\n\n  void DxvkContext::freeZeroBuffer() {\n    constexpr uint64_t ZeroBufferLifetime = 4096u;\n\n    // Don't free the zero buffer if it is still kept alive by a prior\n    // submission anyway\n    if (!m_zeroBuffer || m_zeroBuffer->isInUse(DxvkAccess::Write))\n      return;\n\n    // Delete zero buffer if it hasn't been actively used in a while\n    if (m_zeroBuffer->getTrackId() + ZeroBufferLifetime < m_trackingId)\n      m_zeroBuffer = nullptr;\n  }\n\n\n  void DxvkContext::resizeDescriptorArrays(\n          uint32_t                  bindingCount) {\n    m_legacyDescriptors.infos.resize(bindingCount);\n    m_legacyDescriptors.writes.resize(bindingCount);\n\n    for (uint32_t i = 0; i < bindingCount; i++) {\n      auto& info = m_legacyDescriptors.infos[i];\n\n      auto& write = m_legacyDescriptors.writes[i];\n      write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };\n      write.descriptorCount = 1;\n      write.descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM;\n      write.pImageInfo = &info.image;\n      write.pBufferInfo = &info.buffer;\n      write.pTexelBufferView = &info.bufferView;\n    }\n  }\n\n\n  void DxvkContext::flushImplicitResolves() {\n    endCurrentPass(true);\n\n    DxvkImplicitResolveOp op;\n\n    while (m_implicitResolves.extractResolve(op)) {\n      // Always do a SAMPLE_ZERO resolve here since that's less expensive and closer to what\n      // happens on native AMD anyway. Need to use a shader in case we are dealing with a\n      // non-integer color image since render pass resolves only support AVERAGE..\n      // We know that the source image is shader readable and that the resolve image can be\n      // rendered to, so reduce the image compatibility checks to a minimum here.\n      bool useRp = (getDefaultResolveMode(op.resolveFormat) == VK_RESOLVE_MODE_SAMPLE_ZERO_BIT) &&\n        (op.inputImage->info().usage & (VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT));\n\n      if (useRp) {\n        resolveImageRp(op.resolveImage, op.inputImage, op.resolveRegion,\n          op.resolveFormat, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT, true);\n      } else {\n        resolveImageFb(op.resolveImage, op.inputImage, op.resolveRegion,\n          op.resolveFormat, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT, VK_RESOLVE_MODE_SAMPLE_ZERO_BIT);\n      }\n    }\n  }\n\n\n  void DxvkContext::beginCurrentCommands() {\n    beginActiveDebugRegions();\n\n    // The current state of the internal command buffer is\n    // undefined, so we have to bind and set up everything\n    // before any draw or dispatch command is recorded.\n    m_flags.clr(\n      DxvkContextFlag::GpRenderPassActive,\n      DxvkContextFlag::GpXfbActive,\n      DxvkContextFlag::GpIndependentSets,\n      DxvkContextFlag::CpComputePassActive);\n\n    m_flags.set(\n      DxvkContextFlag::GpDirtyRenderTargets,\n      DxvkContextFlag::GpDirtyPipeline,\n      DxvkContextFlag::GpDirtyPipelineState,\n      DxvkContextFlag::GpDirtyVertexBuffers,\n      DxvkContextFlag::GpDirtyIndexBuffer,\n      DxvkContextFlag::GpDirtyXfbBuffers,\n      DxvkContextFlag::GpDirtyBlendConstants,\n      DxvkContextFlag::GpDirtyStencilTest,\n      DxvkContextFlag::GpDirtyStencilRef,\n      DxvkContextFlag::GpDirtyMultisampleState,\n      DxvkContextFlag::GpDirtyRasterizerState,\n      DxvkContextFlag::GpDirtySampleLocations,\n      DxvkContextFlag::GpDirtyViewport,\n      DxvkContextFlag::GpDirtyDepthBias,\n      DxvkContextFlag::GpDirtyDepthBounds,\n      DxvkContextFlag::GpDirtyDepthClip,\n      DxvkContextFlag::GpDirtyDepthTest,\n      DxvkContextFlag::CpDirtyPipelineState,\n      DxvkContextFlag::DirtyDrawBuffer);\n\n    m_descriptorState.dirtyStages(\n      VK_SHADER_STAGE_ALL_GRAPHICS |\n      VK_SHADER_STAGE_COMPUTE_BIT);\n\n    m_state.gp.pipeline = nullptr;\n    m_state.cp.pipeline = nullptr;\n\n    m_cmd->setTrackingId(++m_trackingId);\n\n    if (m_features.any(DxvkContextFeature::DescriptorHeap,\n                       DxvkContextFeature::DescriptorBuffer)) {\n      m_cmd->setDescriptorHeap(m_descriptorHeap);\n    } else {\n      m_cmd->setDescriptorPool(m_descriptorPool);\n    }\n  }\n\n\n  void DxvkContext::endCurrentCommands() {\n    endCurrentPass(true);\n\n    prepareSharedImages();\n\n    m_sdmaAcquires.finalize(m_cmd);\n    m_sdmaBarriers.finalize(m_cmd);\n    m_initAcquires.finalize(m_cmd);\n    m_initBarriers.finalize(m_cmd);\n    m_execBarriers.finalize(m_cmd);\n\n    m_barrierTracker.clear();\n\n    endActiveDebugRegions();\n  }\n\n\n  void DxvkContext::splitCommands() {\n    // This behaves the same as a pair of endRecording and\n    // beginRecording calls, except that we keep the same\n    // command list object for subsequent commands.\n    this->endCurrentCommands();\n\n    m_cmd->next();\n\n    this->beginCurrentCommands();\n  }\n\n\n  void DxvkContext::discardRenderTarget(\n    const DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources) {\n    // We can only retroactively change store ops if we are currently\n    // inside a render pass that uses secondary command buffers.\n    if (!m_flags.test(DxvkContextFlag::GpRenderPassSecondaryCmd))\n      return;\n\n    // Handling 3D is possible, but annoying, so skip it\n    if (image.info().type == VK_IMAGE_TYPE_3D)\n      return;\n\n    for (uint32_t i = 0; i < m_state.om.framebufferInfo.numAttachments(); i++) {\n      auto& view = m_state.om.framebufferInfo.getAttachment(i).view;\n\n      if (view->image() != &image)\n        continue;\n\n      // If the given subresource range fully contains any bound render target,\n      // retroactively change the corresponding store op to DONT_CARE.\n      auto viewSubresources = view->imageSubresources();\n\n      if (vk::checkSubresourceRangeSuperset(subresources, viewSubresources))\n        deferDiscard(view, subresources.aspectMask);\n    }\n  }\n\n\n  void DxvkContext::flushImageLayoutTransitions(\n          DxvkCmdBuffer             cmdBuffer) {\n    if (m_imageLayoutTransitions.empty())\n      return;\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer) {\n      VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n      depInfo.imageMemoryBarrierCount = m_imageLayoutTransitions.size();\n      depInfo.pImageMemoryBarriers = m_imageLayoutTransitions.data();\n\n      m_cmd->cmdPipelineBarrier(cmdBuffer, &depInfo);\n    } else {\n      // If we're recording into an out-of-order command buffer, batch\n      // layout transitions into a dedicated command buffer in order to\n      // avoid pipeline stalls.\n      DxvkCmdBuffer barrierBuffer = cmdBuffer;\n\n      if (cmdBuffer == DxvkCmdBuffer::InitBuffer)\n        barrierBuffer = DxvkCmdBuffer::InitBarriers;\n      if (cmdBuffer == DxvkCmdBuffer::SdmaBuffer)\n        barrierBuffer = DxvkCmdBuffer::SdmaBarriers;\n\n      auto& batch = getBarrierBatch(barrierBuffer);\n\n      for (const auto& barrier : m_imageLayoutTransitions)\n        batch.addImageBarrier(barrier);\n    }\n\n    m_imageLayoutTransitions.clear();\n  }\n\n\n  void DxvkContext::addImageLayoutTransition(\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          VkImageLayout             srcLayout,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          VkImageLayout             dstLayout,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess) {\n    if (srcLayout == dstLayout && srcStages == dstStages)\n      return;\n\n    auto& barrier = m_imageLayoutTransitions.emplace_back();\n    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;\n    barrier.srcStageMask = srcStages;\n    barrier.srcAccessMask = srcAccess;\n    barrier.dstStageMask = dstStages;\n    barrier.dstAccessMask = dstAccess;\n    barrier.oldLayout = srcLayout;\n    barrier.newLayout = dstLayout;\n    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.image = image.handle();\n    barrier.subresourceRange = subresources;\n\n    if (image.info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) {\n      barrier.subresourceRange.baseArrayLayer = 0u;\n      barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;\n    }\n  }\n\n\n  void DxvkContext::addImageLayoutTransition(\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          VkImageLayout             dstLayout,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess,\n          bool                      discard) {\n    // If discard is false, this assumes that the image is in its default\n    // layout and ready to be accessed via its standard access patterns.\n    VkImageLayout srcLayout = image.info().layout;\n\n    if (discard) {\n      // Only discard if the image is either uninitialized or if it is\n      // GPU-writable. Discarding is most likely not useful otherwise.\n      constexpr VkImageUsageFlags WritableFlags =\n        VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |\n        VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |\n        VK_IMAGE_USAGE_STORAGE_BIT;\n\n      if ((image.info().usage & WritableFlags) || !image.isInitialized(subresources))\n        srcLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    }\n\n    addImageLayoutTransition(image, subresources,\n      srcLayout, dstStages, 0,\n      dstLayout, dstStages, dstAccess);\n  }\n\n\n  void DxvkContext::addImageInitTransition(\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          VkImageLayout             dstLayout,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess) {\n    addImageLayoutTransition(image, subresources,\n      VK_IMAGE_LAYOUT_UNDEFINED, 0, 0,\n      dstLayout, dstStages, dstAccess);\n  }\n\n\n  void DxvkContext::trackNonDefaultImageLayout(\n            DxvkImage&                image) {\n    for (const auto& e : m_nonDefaultLayoutImages) {\n      if (e == &image)\n        return;\n    }\n\n    m_nonDefaultLayoutImages.emplace_back(&image);\n  }\n\n\n  bool DxvkContext::overlapsRenderTarget(\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources) {\n    if (!(image.info().usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)))\n      return false;\n\n    if (image.info().usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {\n      const auto& view = m_state.om.renderTargets.depth.view;\n\n      if (view && view->image() == &image) {\n        VkImageSubresourceRange viewSubresources = view->imageSubresources();\n\n        if ((subresources.aspectMask & viewSubresources.aspectMask)\n         && vk::checkSubresourceRangeOverlap(subresources, viewSubresources))\n          return true;\n      }\n    }\n\n    if (image.info().usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {\n      for (uint32_t i = 0u; i < MaxNumRenderTargets; i++) {\n        const auto& view = m_state.om.renderTargets.color[i].view;\n\n        if (view && view->image() == &image) {\n          VkImageSubresourceRange viewSubresources = view->imageSubresources();\n\n          if ((subresources.aspectMask & viewSubresources.aspectMask)\n           && vk::checkSubresourceRangeOverlap(subresources, viewSubresources))\n            return true;\n        }\n      }\n    }\n\n    return false;\n  }\n\n\n  bool DxvkContext::restoreImageLayout(\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          bool                      keepAttachments) {\n    // Note that this method does not flush layout transitions\n    m_cmd->track(&image, DxvkAccess::Read);\n\n    if (likely(!keepAttachments || !overlapsRenderTarget(image, subresources))) {\n      // Transition entire image to its default limit in one go\n      transitionImageLayout(image, subresources,\n        image.info().stages, image.info().access, image.info().layout,\n        image.info().stages, image.info().access, false);\n      return true;\n    } else {\n      // Iterate over each plane and mip level, and check whether\n      // there is any overlap with bound render targets\n      VkImageAspectFlags aspects = image.formatInfo()->aspectMask;\n\n      while (aspects) {\n        VkImageSubresourceRange range = { };\n        range.aspectMask = vk::getNextAspect(aspects);\n\n        for (uint32_t m = 0u; m < subresources.levelCount; m++) {\n          range.baseMipLevel = subresources.baseMipLevel + m;\n          range.levelCount = 1u;\n          range.baseArrayLayer = subresources.baseArrayLayer;\n          range.layerCount = subresources.layerCount;\n\n          if (overlapsRenderTarget(image, range)) {\n            // Scan and transition array layers one by one\n            for (uint32_t l = 0u; l < subresources.layerCount; l++) {\n              range.baseArrayLayer = subresources.baseArrayLayer + l;\n              range.layerCount = 1u;\n\n              if (!overlapsRenderTarget(image, range)) {\n                transitionImageLayout(image, range,\n                  image.info().stages, image.info().access, image.info().layout,\n                  image.info().stages, image.info().access, false);\n              }\n            }\n          } else {\n            // Transition entire mip level at once\n            transitionImageLayout(image, range,\n              image.info().stages, image.info().access, image.info().layout,\n              image.info().stages, image.info().access, false);\n          }\n        }\n      }\n\n      return false;\n    }\n  }\n\n\n  template<typename Pred>\n  void DxvkContext::restoreImageLayouts(const Pred& pred, bool keepAttachments) {\n    if (m_nonDefaultLayoutImages.empty())\n      return;\n\n    size_t src = 0u;\n    size_t dst = 0u;\n\n    while (src < m_nonDefaultLayoutImages.size()) {\n      bool fullyRestored = false;\n\n      if (pred(*m_nonDefaultLayoutImages[src])) {\n        fullyRestored = restoreImageLayout(*m_nonDefaultLayoutImages[src],\n          m_nonDefaultLayoutImages[src]->getAvailableSubresources(),\n          keepAttachments);\n      }\n\n      if (!fullyRestored) {\n        if (dst < src)\n          m_nonDefaultLayoutImages[dst] = std::move(m_nonDefaultLayoutImages[src]);\n\n        dst += 1u;\n      }\n\n      src += 1u;\n    }\n\n    if (dst < src) {\n      m_nonDefaultLayoutImages.resize(dst);\n\n      flushImageLayoutTransitions(DxvkCmdBuffer::ExecBuffer);\n    }\n  }\n\n\n  void DxvkContext::prepareShaderReadableImages(bool renderPass) {\n    // Flush pending clears and emit barriers before potentially\n    // emitting layout transitions affecting the same images.\n    if (!m_deferredClears.empty()) {\n      flushClears(renderPass);\n      flushBarriers();\n    }\n\n    // Just transition all images for the time being, we can make this\n    // more granular for tilers as necessary. When changing this, we will\n    // also need to ensure that we don't keep destroyed images alive.\n    if (!m_nonDefaultLayoutImages.empty()) {\n      flushBarriers();\n\n      restoreImageLayouts([] (DxvkImage& image) {\n        return true;\n      }, renderPass);\n    }\n  }\n\n\n  void DxvkContext::prepareSharedImages() {\n    // Only flush clears for shared images, and restore layouts\n    bool hasSharedClear = false;\n\n    for (const auto& image : m_nonDefaultLayoutImages) {\n      if (image->info().shared)\n        hasSharedClear = flushDeferredClear(*image, image->getAvailableSubresources());\n    }\n\n    if (hasSharedClear)\n      flushBarriers();\n\n    restoreImageLayouts([] (DxvkImage& image) {\n      return image.info().shared;\n    }, false);\n  }\n\n\n  bool DxvkContext::transitionImageLayout(\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          VkImageLayout             dstLayout,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess,\n          bool                      discard) {\n    // If all subresources are in the correct layout, we don't need to do anything.\n    // If we're discarding a render target, re-initialize the image since doing so\n    // may lead to more efficient compression in some cases.\n    VkImageUsageFlags rtUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n                              | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n    VkImageLayout srcLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n    if (!discard || !(image.info().usage & rtUsage))\n      srcLayout = image.queryLayout(subresources);\n\n    if (likely(srcLayout == dstLayout))\n      return false;\n\n    if (srcLayout == VK_IMAGE_LAYOUT_MAX_ENUM) {\n      VkImageAspectFlags aspects = subresources.aspectMask;\n      VkImageSubresource subresource = { };\n\n      while (aspects) {\n        subresource.aspectMask = vk::getNextAspect(aspects);\n        subresource.mipLevel = subresources.baseMipLevel;\n\n        for (uint32_t m = 0u; m < subresources.levelCount; m++) {\n          subresource.arrayLayer = subresources.baseArrayLayer;\n\n          for (uint32_t l = 0u; l < subresources.layerCount; l++) {\n            srcLayout = image.queryLayout(subresource);\n\n            if (srcLayout != dstLayout) {\n              addImageLayoutTransition(image, vk::makeSubresourceRange(subresource),\n                srcLayout, srcStages, srcAccess,\n                dstLayout, dstStages, dstAccess);\n            }\n\n            subresource.arrayLayer += 1u;\n          }\n\n          subresource.mipLevel += 1u;\n        }\n      }\n    } else {\n      addImageLayoutTransition(image, subresources,\n        srcLayout, srcStages, srcAccess,\n        dstLayout, dstStages, dstAccess);\n    }\n\n    if (dstLayout != image.info().layout)\n      trackNonDefaultImageLayout(image);\n\n    image.trackLayout(subresources, dstLayout);\n\n    // Need to track for writes here even if\n    // the actual access itself is read-only\n    m_cmd->track(&image, DxvkAccess::Write);\n    return true;\n  }\n\n\n  void DxvkContext::acquireResources(\n          DxvkCmdBuffer             cmdBuffer,\n          size_t                    count,\n    const DxvkResourceAccess*       batch,\n          bool                      flushClears) {\n    if (cmdBuffer == DxvkCmdBuffer::InitBuffer)\n      cmdBuffer = DxvkCmdBuffer::InitBarriers;\n    else if (cmdBuffer == DxvkCmdBuffer::SdmaBuffer)\n      cmdBuffer = DxvkCmdBuffer::SdmaBarriers;\n\n    // For out-of-order operations we know that the resource can't have\n    // any pending accesses that would require synchronization.\n    bool needsFlush = cmdBuffer != DxvkCmdBuffer::ExecBuffer;\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer && flushClears) {\n      // If we need to flush any clears, we need to so *before* modifying any\n      // global state since clears themselves may need to issue barriers.\n      for (size_t i = 0u; i < count; i++) {\n        if (batch[i].image)\n          needsFlush |= flushDeferredClear(*batch[i].image, batch[i].image->getAvailableSubresources());\n      }\n    }\n\n    // Even if we have to perform the current operation on the main command buffer,\n    // we can still try to move layout transitions that may be necessary to an\n    // out-of-order command buffer in order to avoid additional barriers.\n    bool promoteTransitions = m_imageLayoutTransitions.empty();\n\n    // Flush any barriers affecting the resources\n    VkPipelineStageFlags2 srcStages = 0u;\n    VkPipelineStageFlags2 dstStages = 0u;\n\n    VkAccessFlags2 srcAccess = 0u;\n    VkAccessFlags2 dstAccess = 0u;\n\n    for (size_t i = 0u; i < count; i++) {\n      const auto& e = batch[i];\n\n      DxvkAccess access = (e.access & vk::AccessWriteMask)\n        ? DxvkAccess::Write\n        : DxvkAccess::Read;\n\n      if (e.buffer) {\n        if (!needsFlush) {\n          needsFlush = resourceHasAccess(*e.buffer, e.bufferOffset, e.bufferSize,\n            DxvkAccess::Write, DxvkAccessOp::None);\n\n          if (!needsFlush && (e.access & vk::AccessWriteMask)) {\n            needsFlush = resourceHasAccess(*e.buffer, e.bufferOffset, e.bufferSize,\n              DxvkAccess::Read, DxvkAccessOp::None);\n          }\n        }\n\n        if (unlikely(e.stages & ~e.buffer->info().stages)\n         || unlikely(e.access & ~e.buffer->info().access)) {\n          srcStages |= e.buffer->info().stages;\n          srcAccess |= e.buffer->info().access;\n          dstStages |= e.stages;\n          dstAccess |= e.access;\n        }\n\n        m_cmd->track(e.buffer, access);\n      } else if (e.image) {\n        if (!needsFlush) {\n          if (!e.imageExtent.width) {\n            if (!needsFlush) {\n              needsFlush = resourceHasAccess(*e.image, e.imageSubresources,\n                DxvkAccess::Write, DxvkAccessOp::None);\n\n              if (!needsFlush && (e.access & vk::AccessWriteMask)) {\n                needsFlush = resourceHasAccess(*e.image, e.imageSubresources,\n                  DxvkAccess::Read, DxvkAccessOp::None);\n              }\n            }\n          } else {\n            VkImageSubresourceLayers layers = vk::pickSubresourceLayers(e.imageSubresources, 0u);\n\n            needsFlush = resourceHasAccess(*e.image, layers,\n              e.imageOffset, e.imageExtent, DxvkAccess::Write, DxvkAccessOp::None);\n\n            if (!needsFlush && (e.access & vk::AccessWriteMask)) {\n              needsFlush = resourceHasAccess(*e.image, layers,\n                e.imageOffset, e.imageExtent, DxvkAccess::Read, DxvkAccessOp::None);\n            }\n          }\n        }\n\n        if (unlikely(e.stages & ~e.image->info().stages)\n         || unlikely(e.access & ~e.image->info().access)) {\n          srcStages |= e.image->info().stages;\n          srcAccess |= e.image->info().access;\n          dstStages |= e.stages;\n          dstAccess |= e.access;\n        }\n\n        bool canPromote = !e.image->isTracked(m_trackingId, DxvkAccess::Write);\n\n        bool hasTransition = transitionImageLayout(*e.image, e.imageSubresources,\n          e.image->info().stages, e.image->info().access,\n          e.imageLayout, e.stages, e.access, e.discard);\n\n        if (hasTransition && !canPromote)\n          promoteTransitions = false;\n\n        m_cmd->track(e.image, access);\n      }\n    }\n\n    // If the resource can be accessed in ways that regular barriers\n    // don't handle, emit a global memory barrier to sort it out\n    if (unlikely(srcStages | dstStages)) {\n      accessMemory(cmdBuffer, srcStages, srcAccess, dstStages, dstAccess);\n      needsFlush = true;\n    }\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer && needsFlush)\n      flushBarriers();\n\n    // Move layout transitions and discards to init command buffer if possible\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer && promoteTransitions)\n      cmdBuffer = DxvkCmdBuffer::InitBarriers;\n\n    flushImageLayoutTransitions(cmdBuffer);\n  }\n\n\n  void DxvkContext::releaseResources(\n          DxvkCmdBuffer             cmdBuffer,\n          size_t                    count,\n    const DxvkResourceAccess*       batch) {\n    for (size_t i = 0u; i < count; i++) {\n      const auto& e = batch[i];\n\n      if (e.buffer) {\n        accessBuffer(cmdBuffer, *e.buffer, e.bufferOffset, e.bufferSize,\n          e.stages, e.access, e.buffer->info().stages, e.buffer->info().access,\n          DxvkAccessOp::None);\n      } else if (e.image) {\n        if (!e.imageExtent.width) {\n          accessImage(cmdBuffer, *e.image, e.imageSubresources,\n            e.imageLayout, e.stages, e.access, e.imageLayout,\n            e.image->info().stages, e.image->info().access,\n            DxvkAccessOp::None);\n        } else {\n          accessImageRegion(cmdBuffer, *e.image,\n            vk::pickSubresourceLayers(e.imageSubresources, 0u),\n            e.imageOffset, e.imageExtent, e.imageLayout,\n            e.stages, e.access, e.imageLayout,\n            e.image->info().stages, e.image->info().access,\n            DxvkAccessOp::None);\n        }\n      }\n    }\n  }\n\n\n  void DxvkContext::syncResources(\n          DxvkCmdBuffer             cmdBuffer,\n          size_t                    count,\n    const DxvkResourceAccess*       batch,\n          bool                      flushClears) {\n    acquireResources(cmdBuffer, count, batch, flushClears);\n    releaseResources(cmdBuffer, count, batch);\n  }\n\n\n  void DxvkContext::accessMemory(\n          DxvkCmdBuffer             cmdBuffer,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess) {\n    auto& batch = getBarrierBatch(cmdBuffer);\n\n    VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    barrier.srcStageMask = srcStages;\n    barrier.srcAccessMask = srcAccess;\n    barrier.dstStageMask = dstStages;\n    barrier.dstAccessMask = dstAccess;\n\n    batch.addMemoryBarrier(barrier);\n  }\n\n\n  void DxvkContext::accessImage(\n          DxvkCmdBuffer             cmdBuffer,\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          VkImageLayout             srcLayout,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          DxvkAccessOp              accessOp) {\n    accessImage(cmdBuffer, image, subresources,\n      srcLayout, srcStages, srcAccess,\n      image.info().layout,\n      image.info().stages,\n      image.info().access,\n      accessOp);\n  }\n\n\n  void DxvkContext::accessImage(\n          DxvkCmdBuffer             cmdBuffer,\n    const DxvkImageView&            imageView,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          DxvkAccessOp              accessOp) {\n    accessImage(cmdBuffer, *imageView.image(),\n      imageView.imageSubresources(),\n      imageView.image()->info().layout,\n      srcStages, srcAccess, accessOp);\n  }\n\n\n  void DxvkContext::accessImage(\n          DxvkCmdBuffer             cmdBuffer,\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          VkImageLayout             srcLayout,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          VkImageLayout             dstLayout,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess,\n          DxvkAccessOp              accessOp) {\n    auto& batch = getBarrierBatch(cmdBuffer);\n\n    VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n    barrier.srcStageMask = srcStages;\n    barrier.srcAccessMask = srcAccess;\n    barrier.dstStageMask = dstStages;\n    barrier.dstAccessMask = dstAccess;\n    barrier.oldLayout = srcLayout;\n    barrier.newLayout = dstLayout;\n    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.image = image.handle();\n    barrier.subresourceRange = subresources;\n\n    // maintenance9 changed semantics for barriers involving 3D images\n    if (image.info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) {\n      barrier.subresourceRange.baseArrayLayer = 0u;\n      barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;\n    }\n\n    batch.addImageBarrier(barrier);\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer) {\n      bool hasWrite = (srcAccess & vk::AccessWriteMask) || (srcLayout != dstLayout);\n      bool hasRead = (srcAccess & vk::AccessReadMask);\n\n      uint32_t layerCount = image.info().numLayers;\n\n      DxvkAddressRange range;\n      range.resource = image.getResourceId();\n      range.accessOp = accessOp;\n\n      if (subresources.levelCount == 1u || subresources.layerCount == layerCount) {\n        range.rangeStart = image.getSubresourceStartAddress(\n          subresources.baseMipLevel, subresources.baseArrayLayer);\n        range.rangeEnd = image.getSubresourceEndAddress(\n          subresources.baseMipLevel + subresources.levelCount - 1u,\n          subresources.baseArrayLayer + subresources.layerCount - 1u);\n\n        if (hasWrite)\n          m_barrierTracker.insertRange(range, DxvkAccess::Write);\n        if (hasRead)\n          m_barrierTracker.insertRange(range, DxvkAccess::Read);\n      } else {\n        for (uint32_t i = subresources.baseMipLevel; i < subresources.baseMipLevel + subresources.levelCount; i++) {\n          range.rangeStart = image.getSubresourceStartAddress(i, subresources.baseArrayLayer);\n          range.rangeEnd = image.getSubresourceEndAddress(i, subresources.baseArrayLayer + subresources.layerCount - 1u);\n\n          if (hasWrite)\n            m_barrierTracker.insertRange(range, DxvkAccess::Write);\n          if (hasRead)\n            m_barrierTracker.insertRange(range, DxvkAccess::Read);\n        }\n      }\n    }\n  }\n\n\n  void DxvkContext::accessImageRegion(\n          DxvkCmdBuffer             cmdBuffer,\n          DxvkImage&                image,\n    const VkImageSubresourceLayers& subresources,\n          VkOffset3D                offset,\n          VkExtent3D                extent,\n          VkImageLayout             srcLayout,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          DxvkAccessOp              accessOp) {\n    accessImageRegion(cmdBuffer, image, subresources,\n      offset, extent, srcLayout, srcStages, srcAccess,\n      image.info().layout, image.info().stages, image.info().access,\n      accessOp);\n  }\n\n\n  void DxvkContext::accessImageRegion(\n          DxvkCmdBuffer             cmdBuffer,\n          DxvkImage&                image,\n    const VkImageSubresourceLayers& subresources,\n          VkOffset3D                offset,\n          VkExtent3D                extent,\n          VkImageLayout             srcLayout,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          VkImageLayout             dstLayout,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess,\n          DxvkAccessOp              accessOp) {\n    // If the image layout needs to change, access the entire image\n    if (srcLayout != dstLayout) {\n      accessImage(cmdBuffer, image, vk::makeSubresourceRange(subresources),\n        srcLayout, srcStages, srcAccess,\n        dstLayout, dstStages, dstAccess, accessOp);\n      return;\n    }\n\n    // No layout transition, just emit a plain memory barrier\n    auto& batch = getBarrierBatch(cmdBuffer);\n\n    VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    barrier.srcStageMask = srcStages;\n    barrier.srcAccessMask = srcAccess;\n    barrier.dstStageMask = dstStages;\n    barrier.dstAccessMask = dstAccess;\n\n    batch.addMemoryBarrier(barrier);\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer) {\n      bool hasWrite = (srcAccess & vk::AccessWriteMask) || (srcLayout != dstLayout);\n      bool hasRead = (srcAccess & vk::AccessReadMask);\n\n      DxvkAddressRange range;\n      range.resource = image.getResourceId();\n      range.accessOp = accessOp;\n\n      if (extent == image.mipLevelExtent(subresources.mipLevel)) {\n        range.rangeStart = image.getSubresourceStartAddress(subresources.mipLevel, subresources.baseArrayLayer);\n        range.rangeEnd = image.getSubresourceEndAddress(subresources.mipLevel, subresources.baseArrayLayer + subresources.layerCount - 1u);\n\n        if (hasWrite)\n          m_barrierTracker.insertRange(range, DxvkAccess::Write);\n        if (hasRead)\n          m_barrierTracker.insertRange(range, DxvkAccess::Read);\n      } else {\n        VkOffset3D maxCoord = offset;\n        maxCoord.x += extent.width - 1u;\n        maxCoord.y += extent.height - 1u;\n        maxCoord.z += extent.depth - 1u;\n\n        for (uint32_t i = subresources.baseArrayLayer; i < subresources.baseArrayLayer + subresources.layerCount; i++) {\n          range.rangeStart = image.getSubresourceAddressAt(subresources.mipLevel, i, offset);\n          range.rangeEnd = image.getSubresourceAddressAt(subresources.mipLevel, i, maxCoord);\n\n          if (hasWrite)\n            m_barrierTracker.insertRange(range, DxvkAccess::Write);\n          if (hasRead)\n            m_barrierTracker.insertRange(range, DxvkAccess::Read);\n        }\n      }\n    }\n  }\n\n\n  void DxvkContext::accessImageTransfer(\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          VkImageLayout             srcLayout,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess) {\n    auto& transferBatch = getBarrierBatch(DxvkCmdBuffer::SdmaBuffer);\n    auto& graphicsBatch = getBarrierBatch(DxvkCmdBuffer::InitBarriers);\n\n    if (m_device->hasDedicatedTransferQueue()) {\n      VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n      barrier.srcStageMask = srcStages;\n      barrier.srcAccessMask = srcAccess;\n      barrier.dstStageMask = srcStages;\n      barrier.dstAccessMask = VK_ACCESS_2_NONE;\n      barrier.oldLayout = srcLayout;\n      barrier.newLayout = image.info().layout;\n      barrier.srcQueueFamilyIndex = m_device->queues().transfer.queueFamily;\n      barrier.dstQueueFamilyIndex = m_device->queues().graphics.queueFamily;\n      barrier.image = image.handle();\n      barrier.subresourceRange = subresources;\n\n      if (image.info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) {\n        barrier.subresourceRange.baseArrayLayer = 0u;\n        barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;\n      }\n\n      transferBatch.addImageBarrier(barrier);\n\n      barrier.srcAccessMask = VK_ACCESS_2_NONE;\n      barrier.dstStageMask = image.info().stages;\n      barrier.dstAccessMask = image.info().access;\n\n      graphicsBatch.addImageBarrier(barrier);\n    } else {\n      VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n      barrier.srcStageMask = srcStages;\n      barrier.srcAccessMask = srcAccess;\n      barrier.dstStageMask = image.info().stages;\n      barrier.dstAccessMask = image.info().access;\n      barrier.oldLayout = srcLayout;\n      barrier.newLayout = image.info().layout;\n      barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n      barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n      barrier.image = image.handle();\n      barrier.subresourceRange = subresources;\n\n      if (image.info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) {\n        barrier.subresourceRange.baseArrayLayer = 0u;\n        barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;\n      }\n\n      transferBatch.addImageBarrier(barrier);\n    }\n  }\n\n\n  void DxvkContext::accessBuffer(\n          DxvkCmdBuffer             cmdBuffer,\n          DxvkBuffer&               buffer,\n          VkDeviceSize              offset,\n          VkDeviceSize              size,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          DxvkAccessOp              accessOp) {\n    accessBuffer(cmdBuffer, buffer, offset, size,\n      srcStages, srcAccess,\n      buffer.info().stages,\n      buffer.info().access,\n      accessOp);\n  }\n\n\n  void DxvkContext::accessBuffer(\n          DxvkCmdBuffer             cmdBuffer,\n          DxvkBuffer&               buffer,\n          VkDeviceSize              offset,\n          VkDeviceSize              size,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess,\n          DxvkAccessOp              accessOp) {\n    if (unlikely(!size))\n      return;\n\n    auto& batch = getBarrierBatch(cmdBuffer);\n\n    VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    barrier.srcStageMask = srcStages;\n    barrier.srcAccessMask = srcAccess;\n    barrier.dstStageMask = dstStages;\n    barrier.dstAccessMask = dstAccess;\n\n    batch.addMemoryBarrier(barrier);\n\n    if (cmdBuffer == DxvkCmdBuffer::ExecBuffer) {\n      DxvkAddressRange range;\n      range.resource = buffer.getResourceId();\n      range.accessOp = accessOp;\n      range.rangeStart = offset;\n      range.rangeEnd = offset + size - 1;\n\n      if (srcAccess & vk::AccessWriteMask)\n        m_barrierTracker.insertRange(range, DxvkAccess::Write);\n      if (srcAccess & vk::AccessReadMask)\n        m_barrierTracker.insertRange(range, DxvkAccess::Read);\n    }\n  }\n\n\n  void DxvkContext::accessBuffer(\n          DxvkCmdBuffer             cmdBuffer,\n    const DxvkBufferSlice&          bufferSlice,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          DxvkAccessOp              accessOp) {\n    accessBuffer(cmdBuffer,\n      *bufferSlice.buffer(),\n      bufferSlice.offset(),\n      bufferSlice.length(),\n      srcStages, srcAccess,\n      accessOp);\n  }\n\n\n  void DxvkContext::accessBuffer(\n          DxvkCmdBuffer             cmdBuffer,\n    const DxvkBufferSlice&          bufferSlice,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess,\n          DxvkAccessOp              accessOp) {\n    accessBuffer(cmdBuffer,\n      *bufferSlice.buffer(),\n      bufferSlice.offset(),\n      bufferSlice.length(),\n      srcStages, srcAccess,\n      dstStages, dstAccess,\n      accessOp);\n  }\n\n\n  void DxvkContext::accessBuffer(\n          DxvkCmdBuffer             cmdBuffer,\n          DxvkBufferView&           bufferView,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          DxvkAccessOp              accessOp) {\n    accessBuffer(cmdBuffer,\n      *bufferView.buffer(),\n      bufferView.info().offset,\n      bufferView.info().size,\n      srcStages, srcAccess, accessOp);\n  }\n\n\n  void DxvkContext::accessBuffer(\n          DxvkCmdBuffer             cmdBuffer,\n          DxvkBufferView&           bufferView,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess,\n          VkPipelineStageFlags2     dstStages,\n          VkAccessFlags2            dstAccess,\n          DxvkAccessOp              accessOp) {\n    accessBuffer(cmdBuffer,\n      *bufferView.buffer(),\n      bufferView.info().offset,\n      bufferView.info().size,\n      srcStages, srcAccess,\n      dstStages, dstAccess,\n      accessOp);\n  }\n\n\n  void DxvkContext::accessBufferTransfer(\n          DxvkBuffer&               buffer,\n          VkPipelineStageFlags2     srcStages,\n          VkAccessFlags2            srcAccess) {\n    auto& transferBatch = getBarrierBatch(DxvkCmdBuffer::SdmaBuffer);\n    auto& graphicsBatch = getBarrierBatch(DxvkCmdBuffer::InitBarriers);\n\n    if (m_device->hasDedicatedTransferQueue()) {\n      // No queue ownership transfer necessary since buffers all\n      // use SHARING_MODE_CONCURRENT, but we need a split barrier.\n      VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n      barrier.srcStageMask = srcStages;\n      barrier.srcAccessMask = srcAccess;\n      barrier.dstStageMask = srcStages;\n      barrier.dstAccessMask = VK_ACCESS_2_NONE;\n\n      transferBatch.addMemoryBarrier(barrier);\n\n      barrier.srcStageMask = srcStages;\n      barrier.srcAccessMask = VK_ACCESS_2_NONE;\n      barrier.dstStageMask = buffer.info().stages;\n      barrier.dstAccessMask = buffer.info().access;\n\n      graphicsBatch.addMemoryBarrier(barrier);\n    } else {\n      VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n      barrier.srcStageMask = srcStages;\n      barrier.srcAccessMask = srcAccess;\n      barrier.dstStageMask = buffer.info().stages;\n      barrier.dstAccessMask = buffer.info().access;\n\n      transferBatch.addMemoryBarrier(barrier);\n    }\n  }\n\n\n  void DxvkContext::accessDrawBuffer(\n          VkDeviceSize              offset,\n          uint32_t                  count,\n          uint32_t                  stride,\n          uint32_t                  size) {\n    uint32_t dataSize = count ? (count - 1u) * stride + size : 0u;\n\n    accessBuffer(DxvkCmdBuffer::ExecBuffer,\n      *m_state.id.argBuffer.buffer(),\n      m_state.id.argBuffer.offset() + offset, dataSize,\n      VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT,\n      VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT_KHR,\n      DxvkAccessOp::None);\n  }\n\n\n  void DxvkContext::accessDrawCountBuffer(\n          VkDeviceSize              offset) {\n    accessBuffer(DxvkCmdBuffer::ExecBuffer,\n      *m_state.id.cntBuffer.buffer(),\n      m_state.id.cntBuffer.offset() + offset, sizeof(uint32_t),\n      VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT,\n      VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT_KHR,\n      DxvkAccessOp::None);\n  }\n\n\n  void DxvkContext::flushBarriers() {\n    m_execBarriers.flush(m_cmd);\n    m_barrierTracker.clear();\n\n    m_flags.clr(DxvkContextFlag::ForceWriteAfterWriteSync);\n  }\n\n\n  bool DxvkContext::resourceHasAccess(\n          DxvkBuffer&               buffer,\n          VkDeviceSize              offset,\n          VkDeviceSize              size,\n          DxvkAccess                access,\n          DxvkAccessOp              accessOp) {\n    if (unlikely(!size))\n      return false;\n\n    DxvkAddressRange range;\n    range.resource = buffer.getResourceId();\n    range.accessOp = accessOp;\n    range.rangeStart = offset;\n    range.rangeEnd = offset + size - 1;\n\n    return m_barrierTracker.findRange(range, access);\n  }\n\n\n  bool DxvkContext::resourceHasAccess(\n          DxvkBufferView&           bufferView,\n          DxvkAccess                access,\n          DxvkAccessOp              accessOp) {\n    return resourceHasAccess(*bufferView.buffer(),\n      bufferView.info().offset,\n      bufferView.info().size, access, accessOp);\n  }\n\n\n  bool DxvkContext::resourceHasAccess(\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          DxvkAccess                access,\n          DxvkAccessOp              accessOp) {\n    uint32_t layerCount = image.info().numLayers;\n\n    // Subresources are enumerated in such a way that array layers of\n    // one mip form a consecutive address range, and we do not track\n    // individual image aspects. This is useful since image views for\n    // rendering and compute can only access one mip level.\n    DxvkAddressRange range;\n    range.resource = image.getResourceId();\n    range.accessOp = accessOp;\n    range.rangeStart = image.getSubresourceStartAddress(\n      subresources.baseMipLevel, subresources.baseArrayLayer);\n    range.rangeEnd = image.getSubresourceEndAddress(\n      subresources.baseMipLevel + subresources.levelCount - 1u,\n      subresources.baseArrayLayer + subresources.layerCount - 1u);\n\n    // Probe all subresources first, only check individual mip levels\n    // if there are overlaps and if we are checking a subset of array\n    // layers of multiple mips.\n    bool dirty = m_barrierTracker.findRange(range, access);\n\n    if (!dirty || subresources.levelCount == 1u || subresources.layerCount == layerCount)\n      return dirty;\n\n    for (uint32_t i = subresources.baseMipLevel; i < subresources.baseMipLevel + subresources.levelCount && !dirty; i++) {\n      range.rangeStart = image.getSubresourceStartAddress(i, subresources.baseArrayLayer);\n      range.rangeEnd = image.getSubresourceEndAddress(i, subresources.baseArrayLayer + subresources.layerCount - 1u);\n\n      dirty = m_barrierTracker.findRange(range, access);\n    }\n\n    return dirty;\n  }\n\n\n  bool DxvkContext::resourceHasAccess(\n          DxvkImage&                image,\n    const VkImageSubresourceLayers& subresources,\n          VkOffset3D                offset,\n          VkExtent3D                extent,\n          DxvkAccess                access,\n          DxvkAccessOp              accessOp) {\n    DxvkAddressRange range;\n    range.resource = image.getResourceId();\n    range.accessOp = accessOp;\n\n    // If there are multiple subresources, check whether any of them have been\n    // touched before checking individual regions. If the given region covers\n    // the entire image, there is also no need to check more granularly.\n    bool isFullSize = image.mipLevelExtent(subresources.mipLevel) == extent;\n\n    if (subresources.layerCount > 1u || isFullSize) {\n      range.rangeStart = image.getSubresourceStartAddress(subresources.mipLevel, subresources.baseArrayLayer);\n      range.rangeEnd = image.getSubresourceEndAddress(subresources.mipLevel, subresources.baseArrayLayer + subresources.layerCount - 1u);\n\n      bool dirty = m_barrierTracker.findRange(range, access);\n\n      if (!dirty || isFullSize)\n        return dirty;\n    }\n\n    // Check given image region in each subresource\n    VkOffset3D maxCoord = offset;\n    maxCoord.x += extent.width - 1u;\n    maxCoord.y += extent.height - 1u;\n    maxCoord.z += extent.depth - 1u;\n\n    for (uint32_t i = subresources.baseArrayLayer; i < subresources.baseArrayLayer + subresources.layerCount; i++) {\n      range.rangeStart = image.getSubresourceAddressAt(subresources.mipLevel, i, offset);\n      range.rangeEnd = image.getSubresourceAddressAt(subresources.mipLevel, i, maxCoord);\n\n      if (m_barrierTracker.findRange(range, access))\n        return true;\n    }\n\n    return false;\n  }\n\n\n  bool DxvkContext::resourceHasAccess(\n          DxvkImageView&            imageView,\n          DxvkAccess                access,\n          DxvkAccessOp              accessOp) {\n    return resourceHasAccess(*imageView.image(), imageView.imageSubresources(), access, accessOp);\n  }\n\n\n  DxvkBarrierBatch& DxvkContext::getBarrierBatch(\n          DxvkCmdBuffer             cmdBuffer) {\n    switch (cmdBuffer) {\n      default:\n      case DxvkCmdBuffer::ExecBuffer: return m_execBarriers;\n      case DxvkCmdBuffer::InitBuffer: return m_initBarriers;\n      case DxvkCmdBuffer::InitBarriers: return m_initAcquires;\n      case DxvkCmdBuffer::SdmaBuffer: return m_sdmaBarriers;\n      case DxvkCmdBuffer::SdmaBarriers: return m_sdmaAcquires;\n    }\n  }\n\n\n  DxvkCmdBuffer DxvkContext::prepareOutOfOrderTransfer(\n          DxvkCmdBuffer             cmdBuffer,\n          size_t                    accessCount,\n    const DxvkResourceAccess*       accessBatch) {\n    for (size_t i = 0u; i < accessCount; i++) {\n      const auto& e = accessBatch[i];\n\n      DxvkAccess access = (e.access & vk::AccessWriteMask)\n        ? DxvkAccess::Write\n        : DxvkAccess::Read;\n\n      if (e.buffer) {\n        if (!prepareOutOfOrderTransfer(*e.buffer, e.bufferOffset, e.bufferSize, access))\n          return DxvkCmdBuffer::ExecBuffer;\n      } else if (e.image) {\n        if (!prepareOutOfOrderTransfer(*e.image, e.imageSubresources, e.discard, access))\n          return DxvkCmdBuffer::ExecBuffer;\n      }\n    }\n\n    return cmdBuffer;\n  }\n\n\n  bool DxvkContext::prepareOutOfOrderTransfer(\n          DxvkBuffer&               buffer,\n          VkDeviceSize              offset,\n          VkDeviceSize              size,\n          DxvkAccess                access) {\n    // Sparse resources can alias, need to ignore.\n    if (unlikely(buffer.info().flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT))\n      return false;\n\n    // If the resource hasn't been used yet or both uses are reads,\n    // we can use this buffer in the init command buffer\n    if (!buffer.isTracked(m_trackingId, access))\n      return true;\n\n    // Otherwise, our only option is to discard. We can only do that if\n    // we're writing the full buffer. Therefore, the resource being read\n    // should always be checked first to avoid unnecessary discards.\n    if (access != DxvkAccess::Write || size < buffer.info().size || offset)\n      return false;\n\n    // Check if the buffer can actually be discarded at all.\n    if (!buffer.canRelocate())\n      return false;\n\n    // Ignore large buffers to keep memory overhead in check. Use a higher\n    // threshold when a render pass is active to avoid interrupting it.\n    VkDeviceSize threshold = !m_flags.test(DxvkContextFlag::GpRenderPassActive)\n      ? MaxDiscardSizeInRp\n      : MaxDiscardSize;\n\n    if (size > threshold)\n      return false;\n\n    // If the buffer is used for transform feedback in any way, we have to stop\n    // the render pass anyway, but we can at least avoid an extra barrier.\n    if (unlikely(m_flags.test(DxvkContextFlag::GpXfbActive))) {\n      VkBufferUsageFlags xfbUsage = VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT\n                                  | VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;\n\n      if (buffer.info().usage & xfbUsage)\n        this->endCurrentPass(true);\n    }\n\n    // Actually allocate and assign new backing storage\n    this->invalidateBuffer(&buffer, buffer.allocateStorage());\n    return true;\n  }\n\n\n  bool DxvkContext::prepareOutOfOrderTransfer(\n          DxvkImage&                image,\n    const VkImageSubresourceRange&  subresources,\n          bool                      discard,\n          DxvkAccess                access) {\n    // Sparse resources can alias, need to ignore.\n    if (unlikely(image.info().flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT))\n      return false;\n\n    // Reject any images that use non-default image layouts since\n    // per-subresource layout tracking relies on proper ordering\n    if (unlikely(!image.hasUnifiedLayout()))\n      return false;\n\n    // Ensure correct order of operations in case the image is a render\n    // target and is either currently bound for rendering or has any\n    // pending clears or resolves.\n    if (image.info().usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {\n      if (findOverlappingDeferredClear(image, subresources)\n       || findOverlappingDeferredResolve(image, subresources))\n        return false;\n\n      if (m_flags.test(DxvkContextFlag::GpRenderPassActive)) {\n        if (isBoundAsRenderTarget(image, subresources))\n          return false;\n      }\n    }\n\n    // If the image hasn't been used yet or all uses are reads,\n    // we can use it in the init command buffer. Treat discards\n    // as a write since we will reinitialize the image.\n    if (discard)\n      access = DxvkAccess::Write;\n\n    return !image.isTracked(m_trackingId, access);\n  }\n\n\n  bool DxvkContext::prepareOutOfOrderTransition(\n          DxvkImage&                image) {\n    // Sparse resources can alias, need to ignore.\n    return !(image.isTracked(m_trackingId, DxvkAccess::Write))\n        && !(image.info().flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT);\n  }\n\n\n  VkStencilOpState DxvkContext::convertStencilOp(\n    const DxvkStencilOp&            op,\n          bool                      writable) {\n    VkStencilOpState result = { };\n    result.compareOp = op.compareOp();\n    result.compareMask = op.compareMask();\n\n    if (writable) {\n      result.failOp = op.failOp();\n      result.passOp = op.passOp();\n      result.depthFailOp = op.depthFailOp();\n      result.writeMask = op.writeMask();\n    }\n\n    return result;\n  }\n\n\n  bool DxvkContext::formatsAreBufferCopyCompatible(\n          VkFormat                  imageFormat,\n          VkFormat                  bufferFormat) {\n    if (!bufferFormat)\n      bufferFormat = imageFormat;\n\n    // Depth-stencil data is packed differently in client APIs than what\n    // we can do in Vulkan, and these formats cannot be reinterpreted.\n    auto imageFormatInfo = lookupFormatInfo(imageFormat);\n    auto bufferFormatInfo = lookupFormatInfo(bufferFormat);\n\n    return !((imageFormatInfo->aspectMask | bufferFormatInfo->aspectMask) & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));\n  }\n\n\n  bool DxvkContext::formatsAreImageCopyCompatible(\n          VkFormat                  dstFormat,\n          VkFormat                  srcFormat) {\n    // Assume compatibility as long as no depth/stencil formats\n    // are involved, or if the aspect masks are equal\n    constexpr VkImageAspectFlags DsAspects = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;\n\n    auto dstAspects = lookupFormatInfo(dstFormat)->aspectMask;\n    auto srcAspects = lookupFormatInfo(srcFormat)->aspectMask;\n\n    if (srcAspects == dstAspects || !((srcAspects | dstAspects) & DsAspects))\n      return true;\n\n    // If maintenance8 is enabled on the device, we can use copy functions\n    // directly when copying between certain formats. Don't bother with\n    // stencil since depth-stencil aspects are interleaved in our model.\n    if (m_device->features().khrMaintenance8.maintenance8) {\n      auto depthFormat = (dstAspects & VK_IMAGE_ASPECT_DEPTH_BIT) ? dstFormat : srcFormat;\n      auto colorFormat = (dstAspects & VK_IMAGE_ASPECT_COLOR_BIT) ? dstFormat : srcFormat;\n\n      if (depthFormat == VK_FORMAT_D16_UNORM) {\n        return colorFormat == VK_FORMAT_R16_SFLOAT\n            || colorFormat == VK_FORMAT_R16_UNORM\n            || colorFormat == VK_FORMAT_R16_UINT;\n      }\n\n      if (depthFormat == VK_FORMAT_D32_SFLOAT) {\n        return colorFormat == VK_FORMAT_R32_SFLOAT\n            || colorFormat == VK_FORMAT_R32_UINT;\n      }\n    }\n\n    return false;\n  }\n\n\n  bool DxvkContext::formatsAreResolveCompatible(\n          VkFormat                  resolveFormat,\n          VkFormat                  viewFormat) {\n    if (resolveFormat == viewFormat)\n      return true;\n\n    static const std::array<std::pair<VkFormat, VkFormat>, 3> s_pairs = {{\n      { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB },\n      { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB },\n      { VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_FORMAT_A8B8G8R8_SRGB_PACK32 },\n    }};\n\n    for (const auto& p : s_pairs) {\n      if ((p.first == resolveFormat && p.second == viewFormat)\n       || (p.first == viewFormat && p.second == resolveFormat))\n        return true;\n    }\n\n    return false;\n  }\n\n\n  VkFormat DxvkContext::sanitizeTexelBufferFormat(\n          VkFormat                  srcFormat) {\n    switch (srcFormat) {\n      case VK_FORMAT_S8_UINT:\n        return VK_FORMAT_R8_UINT;\n\n      case VK_FORMAT_D16_UNORM:\n        return VK_FORMAT_R16_UINT;\n\n      case VK_FORMAT_D16_UNORM_S8_UINT:\n        return VK_FORMAT_R16G16_UINT;\n\n      case VK_FORMAT_D24_UNORM_S8_UINT:\n      case VK_FORMAT_X8_D24_UNORM_PACK32:\n      case VK_FORMAT_D32_SFLOAT:\n        return VK_FORMAT_R32_UINT;\n\n      case VK_FORMAT_D32_SFLOAT_S8_UINT:\n        return VK_FORMAT_R32G32_UINT;\n\n      default:\n        return srcFormat;\n    }\n  }\n\n\n  void DxvkContext::pushDebugRegion(const VkDebugUtilsLabelEXT& label, util::DxvkDebugLabelType type) {\n    m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer, label);\n    m_debugLabelStack.emplace_back(label, type);\n  }\n\n\n  void DxvkContext::popDebugRegion(util::DxvkDebugLabelType type) {\n    // Find last active region of the given type\n    size_t index = m_debugLabelStack.size();\n\n    while (index && m_debugLabelStack[index - 1u].type() != type)\n      index -= 1u;\n\n    if (!index)\n      return;\n\n    // End all debug regions inside the scope we want to end, as\n    // well as the debug region of the requested type itself\n    for (size_t i = index; i <= m_debugLabelStack.size(); i++)\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n\n    // Re-emit nested debug regions and erase the region we ended\n    for (size_t i = index; i < m_debugLabelStack.size(); i++) {\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer, m_debugLabelStack[i].get());\n      m_debugLabelStack[i - 1u] = m_debugLabelStack[i];\n    }\n\n    m_debugLabelStack.pop_back();\n  }\n\n\n  bool DxvkContext::hasDebugRegion(\n          util::DxvkDebugLabelType    type) {\n    auto e = std::find_if(m_debugLabelStack.crbegin(), m_debugLabelStack.crend(),\n      [type] (const util::DxvkDebugLabel& label) { return label.type() == type; });\n    return e != m_debugLabelStack.crend();\n  }\n\n\n  void DxvkContext::beginActiveDebugRegions() {\n    for (const auto& region : m_debugLabelStack)\n      m_cmd->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer, region.get());\n  }\n\n\n  void DxvkContext::endActiveDebugRegions() {\n    for (size_t i = 0; i < m_debugLabelStack.size(); i++)\n      m_cmd->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n  }\n\n\n  DxvkResourceBufferInfo DxvkContext::allocateScratchMemory(\n          VkDeviceSize                alignment,\n          VkDeviceSize                size) {\n    if (unlikely(!m_scratchBuffer || m_scratchBuffer->info().size < size)) {\n      // We probably won't need a lot of scratch memory, so keep it small\n      DxvkBufferCreateInfo info = { };\n      info.size = DxvkPageAllocator::PageSize;\n      info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                 | VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                 | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n      info.stages = VK_PIPELINE_STAGE_2_TRANSFER_BIT\n                  | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;\n      info.access = VK_ACCESS_2_TRANSFER_READ_BIT\n                  | VK_ACCESS_2_TRANSFER_WRITE_BIT\n                  | VK_ACCESS_2_SHADER_READ_BIT\n                  | VK_ACCESS_2_SHADER_WRITE_BIT;\n      info.debugName = \"Scratch buffer\";\n\n      m_scratchBuffer = m_device->createBuffer(info,\n        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n    }\n\n    // Suballocate slice from buffer\n    VkDeviceSize offset = align(m_scratchOffset, alignment);\n\n    if (offset + size < m_scratchBuffer->info().size) {\n      invalidateBuffer(m_scratchBuffer, m_scratchBuffer->allocateStorage());\n      m_scratchOffset = 0u;\n    }\n\n    DxvkResourceBufferInfo slice = m_scratchBuffer->getSliceInfo(m_scratchOffset, size);\n    m_scratchOffset += align(size, alignment);\n\n    // Unconditionally track for writing to not bother the caller with it.\n    m_cmd->track(m_scratchBuffer, DxvkAccess::Write);\n    return slice;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_context.h",
    "content": "#pragma once\n\n#include \"dxvk_barrier.h\"\n#include \"dxvk_bind_mask.h\"\n#include \"dxvk_cmdlist.h\"\n#include \"dxvk_context_state.h\"\n#include \"dxvk_descriptor_heap.h\"\n#include \"dxvk_descriptor_worker.h\"\n#include \"dxvk_implicit_resolve.h\"\n#include \"dxvk_latency.h\"\n#include \"dxvk_objects.h\"\n#include \"dxvk_queue.h\"\n#include \"dxvk_util.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief DXVK context\n   * \n   * Tracks pipeline state and records command lists.\n   * This is where the actual rendering commands are\n   * recorded.\n   */\n  class DxvkContext : public RcObject {\n    constexpr static VkDeviceSize MaxDiscardSizeInRp = 256u << 10u;\n    constexpr static VkDeviceSize MaxDiscardSize     =  16u << 10u;\n\n    constexpr static uint32_t DirectMultiDrawBatchSize = 256u;\n\n    constexpr static uint32_t MaxUnsynchronizedDraws = 64u;\n  public:\n    \n    DxvkContext(const Rc<DxvkDevice>& device);\n    ~DxvkContext();\n\n    /**\n     * \\brief Begins command buffer recording\n     * \n     * Begins recording a command list. This does\n     * not alter any context state other than the\n     * active command list.\n     * \\param [in] cmdList Target command list\n     */\n    void beginRecording(\n      const Rc<DxvkCommandList>& cmdList);\n    \n    /**\n     * \\brief Ends command buffer recording\n     * \n     * Finishes recording the active command list.\n     * The command list can then be submitted to\n     * the device.\n     * \n     * This will not change any context state\n     * other than the active command list.\n     * \\param [in] reason Optional debug label describing the reason\n     * \\returns Active command list\n     */\n    Rc<DxvkCommandList> endRecording(\n      const VkDebugUtilsLabelEXT*       reason);\n\n    /**\n     * \\brief Ends frame\n     *\n     * Must be called once per frame before the\n     * final call to \\ref endRecording.\n     */\n    void endFrame();\n\n    /**\n     * \\brief Begins latency tracking\n     *\n     * Notifies the beginning of a frame on the CS timeline\n     * an ensures that subsequent submissions are associated\n     * with the correct frame ID. Only one tracker can be\n     * active at any given time.\n     * \\param [in] tracker Latency tracker object\n     * \\param [in] frameId Current frame ID\n     */\n    void beginLatencyTracking(\n      const Rc<DxvkLatencyTracker>&     tracker,\n            uint64_t                    frameId);\n\n    /**\n     * \\brief Ends latency tracking\n     *\n     * Notifies the end of the frame. Ignored if the\n     * tracker is not currently active.\n     * \\param [in] tracker Latency tracker object\n     */\n    void endLatencyTracking(\n      const Rc<DxvkLatencyTracker>&     tracker);\n\n    /**\n     * \\brief Flushes command buffer\n     * \n     * Transparently submits the current command\n     * buffer and allocates a new one.\n     * \\param [in] reason Optional debug label describing the reason\n     * \\param [out] status Submission feedback\n     */\n    void flushCommandList(\n      const VkDebugUtilsLabelEXT*       reason,\n            DxvkSubmitStatus*           status);\n\n    /**\n     * \\brief Synchronizes command list with WSI\n     *\n     * The next submission can be used to render\n     * to the swap chain image and present after.\n     */\n    void synchronizeWsi(PresenterSync sync) {\n      m_cmd->setWsiSemaphores(sync);\n    }\n\n    /**\n     * \\brief Begins external rendering\n     *\n     * Invalidates all state and provides the caller\n     * with the objects necessary to start drawing.\n     * \\returns Current command list object\n     */\n    Rc<DxvkCommandList> beginExternalRendering();\n\n    /**\n     * \\brief Begins generating query data\n     * \\param [in] query The query to end\n     */\n    void beginQuery(\n      const Rc<DxvkQuery>&      query);\n    \n    /**\n     * \\brief Ends generating query data\n     * \\param [in] query The query to end\n     */\n    void endQuery(\n      const Rc<DxvkQuery>&      query);\n    \n    /**\n     * \\brief Sets render targets\n     * \n     * Creates a framebuffer on the fly if necessary\n     * and binds it using \\c bindFramebuffer.\n     * \\param [in] targets Render targets to bind\n     */\n    void bindRenderTargets(\n            DxvkRenderTargets&&   targets,\n            VkImageAspectFlags    feedbackLoop) {\n      if (likely(m_state.om.renderTargets != targets)) {\n        m_state.om.renderTargets = std::move(targets);\n        m_flags.set(DxvkContextFlag::GpDirtyRenderTargets);\n      }\n\n      if (unlikely(m_state.gp.state.om.feedbackLoop() != feedbackLoop)) {\n        m_state.gp.state.om.setFeedbackLoop(feedbackLoop);\n\n        m_flags.set(DxvkContextFlag::GpDirtyRenderTargets,\n                    DxvkContextFlag::GpDirtyPipelineState);\n      }\n    }\n\n    /**\n     * \\brief Binds indirect argument buffer\n     * \n     * Sets the buffers that are going to be used\n     * for indirect draw and dispatch operations.\n     * \\param [in] argBuffer New argument buffer\n     * \\param [in] cntBuffer New count buffer\n     */\n    void bindDrawBuffers(\n            DxvkBufferSlice&&     argBuffer,\n            DxvkBufferSlice&&     cntBuffer) {\n      m_state.id.argBuffer = std::move(argBuffer);\n      m_state.id.cntBuffer = std::move(cntBuffer);\n\n      m_flags.set(DxvkContextFlag::DirtyDrawBuffer);\n    }\n\n    /**\n     * \\brief Binds index buffer\n     * \n     * The index buffer will be used when\n     * issuing \\c drawIndexed commands.\n     * \\param [in] buffer New index buffer\n     * \\param [in] indexType Index type\n     */\n    void bindIndexBuffer(\n            DxvkBufferSlice&&     buffer,\n            VkIndexType           indexType) {\n      m_state.vi.indexBuffer = std::move(buffer);\n      m_state.vi.indexType   = indexType;\n\n      m_flags.set(DxvkContextFlag::GpDirtyIndexBuffer);\n    }\n\n    /**\n     * \\brief Binds index buffer range\n     * \n     * Canges the offset and size of the bound index buffer.\n     * \\param [in] offset Index buffer offset\n     * \\param [in] length Index buffer size\n     * \\param [in] indexType Index type\n     */\n    void bindIndexBufferRange(\n            VkDeviceSize          offset,\n            VkDeviceSize          length,\n            VkIndexType           indexType) {\n      m_state.vi.indexBuffer.setRange(offset, length);\n      m_state.vi.indexType = indexType;\n\n      m_flags.set(DxvkContextFlag::GpDirtyIndexBuffer);\n    }\n\n    /**\n     * \\brief Binds buffer to the UBO set\n     * \n     * Can be used for uniform and storage buffers bound that\n     * are used within the UBO descriptor set. Storage buffers\n     * within the view set must be bound via a view.\n     * \\param [in] stages Shader stages that access the binding\n     * \\param [in] slot Resource binding slot\n     * \\param [in] buffer Buffer to bind\n     */\n    void bindUniformBuffer(\n            VkShaderStageFlags    stages,\n            uint32_t              slot,\n            DxvkBufferSlice&&     buffer) {\n      m_uniformBuffers[slot] = std::move(buffer);\n\n      m_descriptorState.dirtyBuffers(stages);\n    }\n\n    /**\n     * \\brief Changes bound range of a uniform buffer\n     * \n     * Can be used to quickly bind a new sub-range of\n     * a buffer rather than re-binding the entire buffer.\n     */\n    void bindUniformBufferRange(\n            VkShaderStageFlags    stages,\n            uint32_t              slot,\n            VkDeviceSize          offset,\n            VkDeviceSize          length) {\n      m_uniformBuffers[slot].setRange(offset, length);\n\n      m_descriptorState.dirtyBuffers(stages);\n    }\n    \n    /**\n     * \\brief Binds image view\n     *\n     * \\param [in] stages Shader stages that access the binding\n     * \\param [in] slot Resource binding slot\n     * \\param [in] view Image view to bind\n     */\n    void bindResourceImageView(\n            VkShaderStageFlags    stages,\n            uint32_t              slot,\n            Rc<DxvkImageView>&&   view) {\n      if (likely(m_resources[slot].imageView != view || m_resources[slot].bufferView)) {\n        m_resources[slot].bufferView = nullptr;\n        m_resources[slot].imageView = std::move(view);\n\n        m_descriptorState.dirtyViews(stages);\n      }\n    }\n\n    /**\n     * \\brief Binds buffer view\n     *\n     * \\param [in] stages Shader stages that access the binding\n     * \\param [in] slot Resource binding slot\n     * \\param [in] view Buffer view to bind\n     */\n    void bindResourceBufferView(\n            VkShaderStageFlags    stages,\n            uint32_t              slot,\n            Rc<DxvkBufferView>&&  view) {\n      if (likely(m_resources[slot].bufferView != view || m_resources[slot].imageView)) {\n        m_resources[slot].imageView = nullptr;\n        m_resources[slot].bufferView = std::move(view);\n\n        m_descriptorState.dirtyViews(stages);\n      }\n    }\n\n    /**\n     * \\brief Binds image sampler\n     * \n     * Binds a sampler that can be used together with\n     * an image in order to read from a texture.\n     * \\param [in] stages Shader stages that access the binding\n     * \\param [in] slot Resource binding slot\n     * \\param [in] sampler Sampler view to bind\n     */\n    void bindResourceSampler(\n            VkShaderStageFlags    stages,\n            uint32_t              slot,\n            Rc<DxvkSampler>&&     sampler) {\n      if (likely(m_samplers[slot] != sampler)) {\n        m_samplers[slot] = std::move(sampler);\n\n        m_descriptorState.dirtySamplers(stages);\n      }\n    }\n\n    /**\n     * \\brief Binds a shader to a given state\n     * \n     * \\param [in] stage Target shader stage\n     * \\param [in] shader The shader to bind\n     */\n    template<VkShaderStageFlagBits Stage>\n    void bindShader(\n            Rc<DxvkShader>&&      shader) {\n      switch (Stage) {\n        case VK_SHADER_STAGE_VERTEX_BIT:\n          m_state.gp.shaders.vs = std::move(shader);\n          break;\n\n        case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:\n          m_state.gp.shaders.tcs = std::move(shader);\n          break;\n\n        case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:\n          m_state.gp.shaders.tes = std::move(shader);\n          break;\n\n        case VK_SHADER_STAGE_GEOMETRY_BIT:\n          m_state.gp.shaders.gs = std::move(shader);\n          break;\n\n        case VK_SHADER_STAGE_FRAGMENT_BIT:\n          m_state.gp.shaders.fs = std::move(shader);\n          break;\n\n        case VK_SHADER_STAGE_COMPUTE_BIT:\n          m_state.cp.shaders.cs = std::move(shader);\n          break;\n\n        default:\n          return;\n      }\n\n      if (Stage == VK_SHADER_STAGE_COMPUTE_BIT) {\n        m_flags.set(\n          DxvkContextFlag::CpDirtyPipelineState);\n      } else {\n        m_flags.set(\n          DxvkContextFlag::GpDirtyPipeline,\n          DxvkContextFlag::GpDirtyPipelineState);\n      }\n    }\n    \n    /**\n     * \\brief Binds vertex buffer\n     * \n     * \\param [in] binding Vertex buffer binding\n     * \\param [in] buffer New vertex buffer\n     * \\param [in] stride Stride between vertices\n     */\n    void bindVertexBuffer(\n            uint32_t              binding,\n            DxvkBufferSlice&&     buffer,\n            uint32_t              stride) {\n      m_state.vi.vertexBuffers[binding] = std::move(buffer);\n      m_state.vi.vertexStrides[binding] = stride;\n      m_flags.set(DxvkContextFlag::GpDirtyVertexBuffers);\n    }\n\n    /**\n     * \\brief Binds vertex buffer range\n     * \n     * Only changes offsets of a bound vertex buffer.\n     * \\param [in] binding Vertex buffer binding\n     * \\param [in] offset Vertex buffer offset\n     * \\param [in] length Vertex buffer size\n     * \\param [in] stride Stride between vertices\n     */\n    void bindVertexBufferRange(\n            uint32_t              binding,\n            VkDeviceSize          offset,\n            VkDeviceSize          length,\n            uint32_t              stride) {\n      m_state.vi.vertexBuffers[binding].setRange(offset, length);\n      m_state.vi.vertexStrides[binding] = stride;\n      m_flags.set(DxvkContextFlag::GpDirtyVertexBuffers);\n    }\n\n    /**\n     * \\brief Binds transform feedback buffer\n     * \n     * \\param [in] binding Xfb buffer binding\n     * \\param [in] buffer The buffer to bind\n     * \\param [in] counter Xfb counter buffer\n     */\n    void bindXfbBuffer(\n            uint32_t              binding,\n            DxvkBufferSlice&&     buffer,\n            DxvkBufferSlice&&     counter) {\n      m_state.xfb.buffers [binding] = std::move(buffer);\n      m_state.xfb.counters[binding] = std::move(counter);\n\n      m_flags.set(DxvkContextFlag::GpDirtyXfbBuffers);\n    }\n\n    /**\n     * \\brief Blits an image\n     * \n     * \\param [in] dstView Destination image view\n     * \\param [in] srcView Source image view\n     * \\param [in] dstOffsets Two pixel coordinates in the destination image\n     * \\param [in] srcOffsets Two pixel coordinates in the source image\n     * \\param [in] filter Texture filter\n     */\n    void blitImageView(\n      const Rc<DxvkImageView>&    dstView,\n      const VkOffset3D*           dstOffsets,\n      const Rc<DxvkImageView>&    srcView,\n      const VkOffset3D*           srcOffsets,\n            VkFilter              filter);\n    \n    /**\n     * \\brief Clears a buffer with a fixed value\n     * \n     * Note that both \\c offset and \\c length must\n     * be multiples of four, and that \\c value is\n     * consumed as a four-byte word.\n     * \\param [in] buffer The buffer to clear\n     * \\param [in] offset Offset of the range to clear\n     * \\param [in] length Bumber of bytes to clear\n     * \\param [in] value Clear value\n     */\n    void clearBuffer(\n      const Rc<DxvkBuffer>&       buffer,\n            VkDeviceSize          offset,\n            VkDeviceSize          length,\n            uint32_t              value);\n    \n    /**\n     * \\brief Clears a buffer view\n     * \n     * Unlike \\c clearBuffer, this method can be used\n     * to clear a buffer view with format conversion. \n     * \\param [in] bufferView The buffer view\n     * \\param [in] offset Offset of the region to clear\n     * \\param [in] length Extent of the region to clear\n     * \\param [in] value The clear value\n     */\n    void clearBufferView(\n      const Rc<DxvkBufferView>&   bufferView,\n            VkDeviceSize          offset,\n            VkDeviceSize          length,\n            VkClearColorValue     value);\n    \n    /**\n     * \\brief Clears an active render target\n     * \n     * \\param [in] imageView Render target view to clear\n     * \\param [in] clearAspects Image aspects to clear\n     * \\param [in] clearValue The clear value\n     * \\param [in] discardAspects Image aspects to discard\n     */\n    void clearRenderTarget(\n      const Rc<DxvkImageView>&    imageView,\n            VkImageAspectFlags    clearAspects,\n            VkClearValue          clearValue,\n            VkImageAspectFlags    discardAspects);\n\n    /**\n     * \\brief Clears an image view\n     * \n     * Can be used to clear sub-regions of storage images\n     * that are not going to be used as render targets.\n     * Implicit format conversion will be applied.\n     * \\param [in] imageView The image view\n     * \\param [in] offset Offset of the rect to clear\n     * \\param [in] extent Extent of the rect to clear\n     * \\param [in] aspect Aspect mask to clear\n     * \\param [in] value The clear value\n     */\n    void clearImageView(\n      const Rc<DxvkImageView>&    imageView,\n            VkOffset3D            offset,\n            VkExtent3D            extent,\n            VkImageAspectFlags    aspect,\n            VkClearValue          value);\n    \n    /**\n     * \\brief Copies data from one buffer to another\n     * \n     * \\param [in] dstBuffer Destination buffer\n     * \\param [in] dstOffset Destination data offset\n     * \\param [in] srcBuffer Source buffer\n     * \\param [in] srcOffset Source data offset\n     * \\param [in] numBytes Number of bytes to copy\n     */\n    void copyBuffer(\n      const Rc<DxvkBuffer>&       dstBuffer,\n            VkDeviceSize          dstOffset,\n      const Rc<DxvkBuffer>&       srcBuffer,\n            VkDeviceSize          srcOffset,\n            VkDeviceSize          numBytes);\n    \n    /**\n     * \\brief Copies overlapping buffer region\n     * \n     * Can be used to copy potentially overlapping\n     * buffer regions within the same buffer. If\n     * the source and destination regions do not\n     * overlap, it will behave as \\ref copyBuffer.\n     * \\param [in] dstBuffer The buffer\n     * \\param [in] dstOffset Offset of target region\n     * \\param [in] srcOffset Offset of source region\n     * \\param [in] numBytes Number of bytes to copy\n     */\n    void copyBufferRegion(\n      const Rc<DxvkBuffer>&       dstBuffer,\n            VkDeviceSize          dstOffset,\n            VkDeviceSize          srcOffset,\n            VkDeviceSize          numBytes);\n    \n    /**\n     * \\brief Copies data from a buffer to an image\n     * \n     * Source data must be packed, except for the row alignment.\n     * \\param [in] dstImage Destination image\n     * \\param [in] dstSubresource Destination subresource\n     * \\param [in] dstOffset Destination area offset\n     * \\param [in] dstExtent Destination area size\n     * \\param [in] srcBuffer Source buffer\n     * \\param [in] srcOffset Source offset, in bytes\n     * \\param [in] rowAlignment Row alignment, in bytes\n     * \\param [in] sliceAlignment Slice alignment, in bytes\n     * \\param [in] srcFormat Buffer data format. May be\n     *    \\c VK_FORMAT_UNKNOWN to use the image format.\n     */\n    void copyBufferToImage(\n      const Rc<DxvkImage>&        dstImage,\n            VkImageSubresourceLayers dstSubresource,\n            VkOffset3D            dstOffset,\n            VkExtent3D            dstExtent,\n      const Rc<DxvkBuffer>&       srcBuffer,\n            VkDeviceSize          srcOffset,\n            VkDeviceSize          rowAlignment,\n            VkDeviceSize          sliceAlignment,\n            VkFormat              srcFormat);\n    \n    /**\n     * \\brief Copies data from one image to another\n     * \n     * \\param [in] dstImage Destination image\n     * \\param [in] dstSubresource Destination subresource\n     * \\param [in] dstOffset Destination area offset\n     * \\param [in] srcImage Source image\n     * \\param [in] srcSubresource Source subresource\n     * \\param [in] srcOffset Source area offset\n     * \\param [in] extent Size of the area to copy\n     */\n    void copyImage(\n      const Rc<DxvkImage>&        dstImage,\n            VkImageSubresourceLayers dstSubresource,\n            VkOffset3D            dstOffset,\n      const Rc<DxvkImage>&        srcImage,\n            VkImageSubresourceLayers srcSubresource,\n            VkOffset3D            srcOffset,\n            VkExtent3D            extent);\n    \n    /**\n     * \\brief Copies overlapping image region\n     *\n     * \\param [in] dstImage The image\n     * \\param [in] dstSubresource The image subresource\n     * \\param [in] dstOffset Destination region offset\n     * \\param [in] srcOffset Source region offset\n     * \\param [in] extent Size of the copy region\n     */\n    void copyImageRegion(\n      const Rc<DxvkImage>&        dstImage,\n            VkImageSubresourceLayers dstSubresource,\n            VkOffset3D            dstOffset,\n            VkOffset3D            srcOffset,\n            VkExtent3D            extent);\n    \n    /**\n     * \\brief Copies data from an image into a buffer\n     * \n     * \\param [in] dstBuffer Destination buffer\n     * \\param [in] dstOffset Destination offset, in bytes\n     * \\param [in] dstExtent Destination data extent\n     * \\param [in] rowAlignment Row alignment, in bytes\n     * \\param [in] sliceAlignment Slice alignment, in bytes\n     * \\param [in] dstFormat Buffer format\n     * \\param [in] srcImage Source image\n     * \\param [in] srcSubresource Source subresource\n     * \\param [in] srcOffset Source area offset\n     * \\param [in] srcExtent Source area size\n     */\n    void copyImageToBuffer(\n      const Rc<DxvkBuffer>&       dstBuffer,\n            VkDeviceSize          dstOffset,\n            VkDeviceSize          rowAlignment,\n            VkDeviceSize          sliceAlignment,\n            VkFormat              dstFormat,\n      const Rc<DxvkImage>&        srcImage,\n            VkImageSubresourceLayers srcSubresource,\n            VkOffset3D            srcOffset,\n            VkExtent3D            srcExtent);\n    \n    /**\n     * \\brief Copies image data stored in a linear buffer to another\n     *\n     * The source and destination regions may overlap, in which case\n     * a temporary copy of the source buffer will be created.\n     * \\param [in] dstBuffer Destination buffer\n     * \\param [in] dstBufferOffset Destination subresource offset\n     * \\param [in] dstOffset Destination image offset\n     * \\param [in] dstSize Total size of the destination image\n     * \\param [in] srcBuffer Source buffer\n     * \\param [in] srcBufferOffset Source subresource offset\n     * \\param [in] srcOffset Source image offset\n     * \\param [in] srcSize Total size of the source image\n     * \\param [in] extent Number of pixels to copy\n     * \\param [in] elementSize Pixel size, in bytes\n     */\n    void copyPackedBufferImage(\n      const Rc<DxvkBuffer>&       dstBuffer,\n            VkDeviceSize          dstBufferOffset,\n            VkOffset3D            dstOffset,\n            VkExtent3D            dstSize,\n      const Rc<DxvkBuffer>&       srcBuffer,\n            VkDeviceSize          srcBufferOffset,\n            VkOffset3D            srcOffset,\n            VkExtent3D            srcSize,\n            VkExtent3D            extent,\n            VkDeviceSize          elementSize);\n\n    /**\n     * \\brief Copies pages from a sparse resource to a buffer\n     *\n     * \\param [in] dstBuffer Buffer to write to\n     * \\param [in] dstOffset Buffer offset\n     * \\param [in] srcResource Source resource\n     * \\param [in] pageCount Number of pages to copy\n     * \\param [in] pages Page indices to copy\n     */\n    void copySparsePagesToBuffer(\n      const Rc<DxvkBuffer>&       dstBuffer,\n            VkDeviceSize          dstOffset,\n      const Rc<DxvkPagedResource>& srcResource,\n            uint32_t              pageCount,\n      const uint32_t*             pages);\n\n    /**\n     * \\brief Copies pages from a buffer to a sparse resource\n     *\n     * \\param [in] dstResource Resource to write to\n     * \\param [in] pageCount Number of pages to copy\n     * \\param [in] pages Page indices to copy\n     * \\param [in] srcBuffer Source buffer\n     * \\param [in] srcOffset Buffer offset\n     */\n    void copySparsePagesFromBuffer(\n      const Rc<DxvkPagedResource>& dstResource,\n            uint32_t              pageCount,\n      const uint32_t*             pages,\n      const Rc<DxvkBuffer>&       srcBuffer,\n            VkDeviceSize          srcOffset);\n\n    /**\n     * \\brief Discards contents of an image\n     *\n     * \\param [in] image Image to discard\n     */\n    void discardImage(\n      const Rc<DxvkImage>&          image);\n\n    /**\n     * \\brief Starts compute jobs\n     * \n     * \\param [in] x Number of threads in X direction\n     * \\param [in] y Number of threads in Y direction\n     * \\param [in] z Number of threads in Z direction\n     */\n    void dispatch(\n            uint32_t          x,\n            uint32_t          y,\n            uint32_t          z);\n    \n    /**\n     * \\brief Indirect dispatch call\n     * \n     * Takes arguments from a buffer. The buffer must contain\n     * a structure of the type \\c VkDispatchIndirectCommand.\n     * \\param [in] offset Draw buffer offset\n     */\n    void dispatchIndirect(\n            VkDeviceSize      offset);\n    \n    /**\n     * \\brief Draws primitive without using an index buffer\n     * \n     * \\param [in] count Number of draws\n     * \\param [in] draws Draw parameters\n     */\n    void draw(\n            uint32_t          count,\n      const VkDrawIndirectCommand* draws);\n\n    /**\n     * \\brief Indirect draw call\n     * \n     * Takes arguments from a buffer. The structure stored\n     * in the buffer must be of type \\c VkDrawIndirectCommand.\n     * \\param [in] offset Draw buffer offset\n     * \\param [in] count Number of draws\n     * \\param [in] stride Stride between dispatch calls\n     * \\param [in] unroll Whether to unroll multiple draws if\n     *    there are any potential data dependencies between them.\n     */\n    void drawIndirect(\n            VkDeviceSize      offset,\n            uint32_t          count,\n            uint32_t          stride,\n            bool              unroll);\n    \n    /**\n     * \\brief Indirect draw call\n     * \n     * Takes arguments from a buffer. The structure stored\n     * in the buffer must be of type \\c VkDrawIndirectCommand.\n     * \\param [in] offset Draw buffer offset\n     * \\param [in] countOffset Draw count offset\n     * \\param [in] maxCount Maximum number of draws\n     * \\param [in] stride Stride between dispatch calls\n     */\n    void drawIndirectCount(\n            VkDeviceSize      offset,\n            VkDeviceSize      countOffset,\n            uint32_t          maxCount,\n            uint32_t          stride);\n    \n    /**\n     * \\brief Draws primitives using an index buffer\n     * \n     * \\param [in] count Number of draws\n     * \\param [in] draws Draw parameters\n     */\n    void drawIndexed(\n            uint32_t          count,\n      const VkDrawIndexedIndirectCommand* draws);\n\n    /**\n     * \\brief Indirect indexed draw call\n     * \n     * Takes arguments from a buffer. The structure type for\n     * the draw buffer is \\c VkDrawIndexedIndirectCommand.\n     * \\param [in] offset Draw buffer offset\n     * \\param [in] count Number of draws\n     * \\param [in] stride Stride between dispatch calls\n     * \\param [in] unroll Whether to unroll multiple draws if\n     *    there are any potential data dependencies between them.\n     */\n    void drawIndexedIndirect(\n            VkDeviceSize      offset,\n            uint32_t          count,\n            uint32_t          stride,\n            bool              unroll);\n\n    /**\n     * \\brief Indirect indexed draw call\n     * \n     * Takes arguments from a buffer. The structure type for\n     * the draw buffer is \\c VkDrawIndexedIndirectCommand.\n     * \\param [in] offset Draw buffer offset\n     * \\param [in] countOffset Draw count offset\n     * \\param [in] maxCount Maximum number of draws\n     * \\param [in] stride Stride between dispatch calls\n     */\n    void drawIndexedIndirectCount(\n            VkDeviceSize      offset,\n            VkDeviceSize      countOffset,\n            uint32_t          maxCount,\n            uint32_t          stride);\n    \n    /**\n     * \\brief Transform feedback draw call\n     *\n     * \\param [in] counterOffset Draw count offset\n     * \\param [in] counterDivisor Vertex stride\n     * \\param [in] counterBias Counter bias\n     */\n    void drawIndirectXfb(\n            VkDeviceSize      counterOffset,\n            uint32_t          counterDivisor,\n            uint32_t          counterBias);\n    \n    /**\n     * \\brief Emits graphics barrier\n     *\n     * Needs to be used when the fragment shader reads a bound\n     * render target, or when subsequent draw calls access any\n     * given resource for writing. It is assumed that no hazards\n     * can happen between storage descriptors and other resources.\n     * \\param [in] srcStages Source pipeline stages\n     * \\param [in] srcAccess Source access\n     * \\param [in] dstStages Destination pipeline stages\n     * \\param [in] dstAccess Destination access\n     */\n    void emitGraphicsBarrier(\n            VkPipelineStageFlags      srcStages,\n            VkAccessFlags             srcAccess,\n            VkPipelineStageFlags      dstStages,\n            VkAccessFlags             dstAccess);\n\n    /**\n     * \\brief Acquires an external used resource\n     *\n     * \\param [in] resource Resource to acquire\n     * \\param [in] layout External image layout\n     */\n    void acquireExternalResource(\n      const Rc<DxvkPagedResource>&    resource,\n            VkImageLayout             layout);\n\n    /**\n     * \\brief Releases an external used resource\n     *\n     * \\param [in] resource Resource to release\n     * \\param [in] layout External image layout\n     */\n    void releaseExternalResource(\n      const Rc<DxvkPagedResource>&    resource,\n            VkImageLayout             layout);\n\n    /**\n     * \\brief Generates mip maps\n     * \n     * Uses blitting to generate lower mip levels from\n     * the top-most mip level passed to this method.\n     * \\param [in] imageView The image to generate mips for\n     * \\param [in] filter The filter to use for generation\n     */\n    void generateMipmaps(\n      const Rc<DxvkImageView>&        imageView,\n            VkFilter                  filter);\n\n    /**\n     * \\brief Initializes a buffer\n     *\n     * Clears the given buffer to zero. Only safe to call\n     * if the buffer is not currently in use by the GPU.\n     * \\param [in] buffer Buffer to clear\n     */\n    void initBuffer(\n      const Rc<DxvkBuffer>&           buffer);\n\n    /**\n     * \\brief Initializes an image\n     * \n     * Transitions the image into its default layout, and clears\n     * it to black unless the initial layout is preinitialized.\n     * Only safe to call if the image is not in use by the GPU.\n     * \\param [in] image The image to initialize\n     * \\param [in] initialLayout Initial image layout\n     */\n    void initImage(\n      const Rc<DxvkImage>&            image,\n            VkImageLayout             initialLayout);\n\n    /**\n     * \\brief Initializes sparse image\n     *\n     * Binds any metadata aspects that the image might\n     * have, and performs the initial layout transition.\n     * \\param [in] image Image to initialize\n     */\n    void initSparseImage(\n      const Rc<DxvkImage>&            image);\n\n    /**\n     * \\brief Invalidates a buffer's contents\n     * \n     * Discards a buffer's contents by replacing the\n     * backing resource. This allows the host to access\n     * the buffer while the GPU is still accessing the\n     * original backing resource.\n     * \\param [in] buffer The buffer to invalidate\n     * \\param [in] slice New buffer slice\n     */\n    void invalidateBuffer(\n      const Rc<DxvkBuffer>&           buffer,\n            Rc<DxvkResourceAllocation>&& slice);\n\n    /**\n     * \\brief Ensures that buffer will not be relocated\n     *\n     * This guarantees that the buffer's GPU address remains the same\n     * throughout its lifetime. Only prevents implicit invalidation or\n     * relocation by the backend, client APIs must take care to respect\n     * this too.\n     * \\param [in] buffer Buffer to lock in place\n     */\n    void ensureBufferAddress(\n      const Rc<DxvkBuffer>&           buffer);\n\n    /**\n     * \\brief Invalidates image content\n     *\n     * Replaces the backing storage of an image.\n     * \\param [in] buffer The buffer to invalidate\n     * \\param [in] slice New buffer slice\n     * \\param [in] layout Initial layout of the new storage\n     */\n    void invalidateImage(\n      const Rc<DxvkImage>&            image,\n            Rc<DxvkResourceAllocation>&& slice,\n            VkImageLayout             layout);\n    \n    /**\n     * \\brief Invalidates image content and add usage flag\n     *\n     * Replaces the backing storage of an image.\n     * \\param [in] buffer The buffer to invalidate\n     * \\param [in] slice New buffer slice\n     * \\param [in] usageInfo Added usage info\n     * \\param [in] layout Initial layout of the new storage\n     */\n    void invalidateImageWithUsage(\n      const Rc<DxvkImage>&            image,\n            Rc<DxvkResourceAllocation>&& slice,\n      const DxvkImageUsageInfo&       usageInfo,\n            VkImageLayout             layout);\n\n    /**\n     * \\brief Ensures that an image supports the given usage\n     *\n     * No-op if the image already supports the requested properties.\n     * Otherwise, this will allocate a new backing resource with the\n     * requested properties and copy the current contents to it.\n     * \\param [in] image Image resource\n     * \\param [in] usageInfo Usage info to add\n     * \\returns \\c true if the image can support the given usage\n     */\n    bool ensureImageCompatibility(\n      const Rc<DxvkImage>&            image,\n      const DxvkImageUsageInfo&       usageInfo);\n\n    /**\n     * \\brief Updates push data\n     * \n     * \\param [in] stages Stage to set data for. If multiple\n     *    stages are set, this will push to the shared block.\n     * \\param [in] offset Byte offset of data to update\n     * \\param [in] size Number of bytes to update\n     * \\param [in] data Pointer to raw data\n     */\n    void pushData(\n            VkShaderStageFlags        stages,\n            uint32_t                  offset,\n            uint32_t                  size,\n      const void*                     data) {\n      uint32_t index = DxvkPushDataBlock::computeIndex(stages);\n\n      uint32_t baseOffset = computePushDataBlockOffset(index);\n      std::memcpy(&m_state.pc.constantData[baseOffset + offset], data, size);\n\n      m_flags.set(DxvkContextFlag::DirtyPushData);\n    }\n\n    /**\n     * \\brief Resolves a multisampled image resource\n     * \n     * Resolves a multisampled image into a non-multisampled\n     * image. The subresources of both images must have the\n     * same size and compatible formats.\n     * A format can be specified for the resolve operation.\n     * If it is \\c VK_FORMAT_UNDEFINED, the resolve operation\n     * will use the source image format.\n     * \\param [in] dstImage Destination image\n     * \\param [in] srcImage Source image\n     * \\param [in] region Region to resolve\n     * \\param [in] format Format for the resolve operation\n     * \\param [in] mode Image resolve mode\n     * \\param [in] stencilMode Stencil resolve mode\n     */\n    void resolveImage(\n      const Rc<DxvkImage>&            dstImage,\n      const Rc<DxvkImage>&            srcImage,\n      const VkImageResolve&           region,\n            VkFormat                  format,\n            VkResolveModeFlagBits     mode,\n            VkResolveModeFlagBits     stencilMode);\n\n    /**\n     * \\brief Transforms image subresource layouts\n     * \n     * \\param [in] dstImage Image to transform\n     * \\param [in] dstSubresources Subresources\n     * \\param [in] srcLayout Current layout\n     * \\param [in] dstLayout Desired layout\n     */\n    void transformImage(\n      const Rc<DxvkImage>&            dstImage,\n      const VkImageSubresourceRange&  dstSubresources,\n            VkImageLayout             srcLayout,\n            VkImageLayout             dstLayout);\n    \n    /**\n     * \\brief Updates a buffer\n     * \n     * Copies data from the host into a buffer.\n     * \\param [in] buffer Destination buffer\n     * \\param [in] offset Offset of sub range to update\n     * \\param [in] size Length of sub range to update\n     * \\param [in] data Data to upload\n     */\n    void updateBuffer(\n      const Rc<DxvkBuffer>&           buffer,\n            VkDeviceSize              offset,\n            VkDeviceSize              size,\n      const void*                     data);\n    \n    /**\n     * \\brief Uses transfer queue to initialize buffer\n     *\n     * Always replaces the entire buffer. Only safe to use\n     * if the buffer is currently not in use by the GPU.\n     * \\param [in] buffer The buffer to initialize\n     * \\param [in] source Staging buffer containing data\n     * \\param [in] sourceOffset Offset into staging buffer\n     */\n    void uploadBuffer(\n      const Rc<DxvkBuffer>&           buffer,\n      const Rc<DxvkBuffer>&           source,\n            VkDeviceSize              sourceOffset);\n    \n    /**\n     * \\brief Uses transfer queue to initialize image\n     * \n     * Only safe to use if the image is not in use by the GPU.\n     * Data for each subresource is tightly packed, but individual\n     * subresources must be aligned to \\c subresourceAlignment in\n     * order to meet Vulkan requirements when using transfer queues.\n     * \\param [in] image The image to initialize\n     * \\param [in] source Staging buffer containing data\n     * \\param [in] sourceOffset Offset into staging buffer\n     * \\param [in] subresourceAlignment Subresource alignment\n     * \\param [in] format Actual data format\n     */\n    void uploadImage(\n      const Rc<DxvkImage>&            image,\n      const Rc<DxvkBuffer>&           source,\n            VkDeviceSize              sourceOffset,\n            VkDeviceSize              subresourceAlignment,\n            VkFormat                  format);\n\n    /**\n     * \\brief Sets viewports\n     * \n     * \\param [in] viewportCount Number of viewports\n     * \\param [in] viewports The viewports and scissors\n     */\n    void setViewports(\n            uint32_t            viewportCount,\n      const DxvkViewport*       viewports);\n\n    /**\n     * \\brief Sets blend constants\n     * \n     * Blend constants are a set of four floating\n     * point numbers that may be used as an input\n     * for blending operations.\n     * \\param [in] blendConstants Blend constants\n     */\n    void setBlendConstants(\n            DxvkBlendConstants  blendConstants);\n    \n    /**\n     * \\brief Sets depth bias\n     * \n     * Depth bias has to be enabled explicitly in\n     * the rasterizer state to have any effect.\n     * \\param [in] depthBias Depth bias values\n     */\n    void setDepthBias(\n            DxvkDepthBias       depthBias);\n\n    /**\n     * \\brief Sets depth bias representation\n     *\n     * \\param [in] depthBiasRepresentation Depth bias representation\n     */\n    void setDepthBiasRepresentation(\n            DxvkDepthBiasRepresentation  depthBiasRepresentation);\n    \n    /**\n     * \\brief Sets depth bounds\n     *\n     * Enables or disables the depth bounds test,\n     * and updates the values if necessary.\n     * \\param [in] depthBounds Depth bounds\n     */\n    void setDepthBounds(\n            DxvkDepthBounds     depthBounds);\n    \n    /**\n     * \\brief Sets stencil reference\n     * \n     * Sets the reference value for stencil compare operations.\n     * \\param [in] reference Reference value\n     */\n    void setStencilReference(\n            uint32_t            reference);\n    \n    /**\n     * \\brief Sets input assembly state\n     * \\param [in] ia New state object\n     */\n    void setInputAssemblyState(\n      const DxvkInputAssemblyState& ia);\n    \n    /**\n     * \\brief Sets input layout\n     * \n     * \\param [in] attributeCount Number of vertex attributes\n     * \\param [in] attributes Array of attribute infos\n     * \\param [in] bindingCount Number of buffer bindings\n     * \\param [in] bindings Array of binding infos\n     */\n    void setInputLayout(\n            uint32_t             attributeCount,\n      const DxvkVertexInput*     attributes,\n            uint32_t             bindingCount,\n      const DxvkVertexInput*     bindings);\n\n    /**\n     * \\brief Sets rasterizer state\n     * \\param [in] rs New state object\n     */\n    void setRasterizerState(\n      const DxvkRasterizerState& rs);\n    \n    /**\n     * \\brief Sets multisample state\n     * \\param [in] ms New state object\n     */\n    void setMultisampleState(\n      const DxvkMultisampleState& ms);\n    \n    /**\n     * \\brief Sets depth stencil state\n     * \\param [in] ds New state object\n     */\n    void setDepthStencilState(\n      const DxvkDepthStencilState& ds);\n    \n    /**\n     * \\brief Sets logic op state\n     * \\param [in] lo New state object\n     */\n    void setLogicOpState(\n      const DxvkLogicOpState&   lo);\n    \n    /**\n     * \\brief Sets blend mode for an attachment\n     * \n     * \\param [in] attachment The attachment index\n     * \\param [in] blendMode The blend mode\n     */\n    void setBlendMode(\n            uint32_t            attachment,\n      const DxvkBlendMode&      blendMode);\n    \n    /**\n     * \\brief Sets specialization constants\n     * \n     * Replaces current specialization constants\n     * with the given list of constant entries.\n     * \\param [in] pipeline Graphics or Compute pipeline\n     * \\param [in] index Constant index\n     * \\param [in] value Constant value\n     */\n    void setSpecConstant(\n            VkPipelineBindPoint pipeline,\n            uint32_t            index,\n            uint32_t            value) {\n      auto& scState = pipeline == VK_PIPELINE_BIND_POINT_GRAPHICS\n        ? m_state.gp.constants : m_state.cp.constants;\n      \n      if (scState.data[index] != value) {\n        scState.data[index] = value;\n\n        if (scState.mask & (1u << index)) {\n          m_flags.set(pipeline == VK_PIPELINE_BIND_POINT_GRAPHICS\n            ? DxvkContextFlag::GpDirtySpecConstants\n            : DxvkContextFlag::CpDirtySpecConstants);\n        }\n      }\n    }\n    \n    /**\n     * \\brief Sets barrier control flags\n     *\n     * Barrier control flags can be used to control\n     * implicit synchronization of compute shaders.\n     * \\param [in] control New barrier control flags\n     */\n    void setBarrierControl(\n            DxvkBarrierControlFlags control);\n\n    /**\n     * \\brief Updates page table for a given sparse resource\n     *\n     * Note that this is a very high overhead operation.\n     * \\param [in] bindInfo Sparse bind info\n     * \\param [in] flags Sparse bind flags\n     */\n    void updatePageTable(\n      const DxvkSparseBindInfo&   bindInfo,\n            DxvkSparseBindFlags   flags);\n\n    /**\n     * \\brief Launches a Cuda kernel\n     *\n     * Since the kernel is launched with an opaque set of\n     * kernel-specific parameters which may reference\n     * resources bindlessly, such resources must be listed by\n     * the caller in the 'buffers' and 'images' parameters so\n     * that their access may be tracked appropriately.\n     * \\param [in] nvxLaunchInfo Kernel launch parameter struct\n     * \\param [in] buffers List of {buffer,read,write} used by kernel\n     * \\param [in] images List of {image,read,write} used by kernel\n     */\n    void launchCuKernelNVX(\n      const VkCuLaunchInfoNVX& nvxLaunchInfo,\n      const std::vector<std::pair<Rc<DxvkBuffer>, DxvkAccessFlags>>& buffers,\n      const std::vector<std::pair<Rc<DxvkImage>, DxvkAccessFlags>>& images);\n    \n    /**\n     * \\brief Signals a GPU event\n     * \\param [in] event The event\n     */\n    void signalGpuEvent(\n      const Rc<DxvkEvent>&      event);\n    \n    /**\n     * \\brief Writes to a timestamp query\n     * \\param [in] query The timestamp query\n     */\n    void writeTimestamp(\n      const Rc<DxvkQuery>&      query);\n    \n    /**\n     * \\brief Queues a signal\n     * \n     * The signal will be notified after all\n     * previously submitted commands have\n     * finished execution on the GPU.\n     * \\param [in] signal The signal\n     * \\param [in] value Signal value\n     */\n    void signal(\n      const Rc<sync::Signal>&   signal,\n            uint64_t            value);\n\n    /**\n     * \\brief Waits for fence\n     *\n     * Stalls current command list execution until\n     * the fence reaches the given value or higher.\n     * \\param [in] fence Fence to wait on\n     * \\param [in] value Value to wait on\n     */\n    void waitFence(const Rc<DxvkFence>& fence, uint64_t value);\n\n    /**\n     * \\brief Signals fence\n     *\n     * Signals fence to the given value once the current\n     * command list execution completes on the GPU.\n     * \\param [in] fence Fence to signal\n     * \\param [in] value Value to signal\n     */\n    void signalFence(const Rc<DxvkFence>& fence, uint64_t value);\n\n    /**\n     * \\brief Begins a debug label region\n     *\n     * Marks the start of a debug label region. Used by debugging/profiling\n     * tools to mark different workloads within a frame.\n     * \\param [in] label The debug label\n     */\n    void beginDebugLabel(const VkDebugUtilsLabelEXT& label);\n\n    /**\n     * \\brief Ends a debug label region\n     *\n     * Marks the close of a debug label region. Used by debugging/profiling\n     * tools to mark different workloads within a frame.\n     */\n    void endDebugLabel();\n\n    /**\n     * \\brief Inserts a debug label\n     *\n     * Inserts an instantaneous debug label. Used by debugging/profiling\n     * tools to mark different workloads within a frame.\n     * \\param [in] label The debug label\n     */\n    void insertDebugLabel(const VkDebugUtilsLabelEXT& label);\n\n    /**\n     * \\brief Increments a given stat counter\n     *\n     * The stat counters will be merged into the global\n     * stat counters upon execution of the command list.\n     * \\param [in] counter Stat counter to increment\n     * \\param [in] value Increment value\n     */\n    void addStatCtr(DxvkStatCounter counter, uint64_t value) {\n      if (m_cmd != nullptr)\n        m_cmd->addStatCtr(counter, value);\n    }\n\n    /**\n     * \\brief Sets new debug name for a resource\n     *\n     * \\param [in] buffer Buffer object\n     * \\param [in] name New debug name, or \\c nullptr\n     */\n    void setDebugName(const Rc<DxvkPagedResource>& resource, const char* name);\n\n  private:\n    \n    Rc<DxvkDevice>          m_device;\n    DxvkObjects*            m_common;\n\n    uint64_t                m_trackingId = 0u;\n    uint32_t                m_renderPassIndex = 0u;\n    uint32_t                m_unsynchronizedDrawCount = 0u;\n\n    Rc<DxvkCommandList>     m_cmd;\n    Rc<DxvkBuffer>          m_zeroBuffer;\n\n    Rc<DxvkBuffer>          m_scratchBuffer;\n    VkDeviceSize            m_scratchOffset = 0u;\n\n    DxvkContextFlags        m_flags;\n    DxvkContextState        m_state;\n    DxvkContextFeatures     m_features;\n    DxvkDescriptorState     m_descriptorState;\n\n    Rc<DxvkDescriptorPool>  m_descriptorPool;\n\n    Rc<DxvkResourceDescriptorHeap> m_descriptorHeap;\n\n    DxvkBarrierBatch        m_sdmaAcquires;\n    DxvkBarrierBatch        m_sdmaBarriers;\n    DxvkBarrierBatch        m_initAcquires;\n    DxvkBarrierBatch        m_initBarriers;\n    DxvkBarrierBatch        m_execBarriers;\n    DxvkBarrierTracker      m_barrierTracker;\n    DxvkBarrierControlFlags m_barrierControl;\n\n    small_vector<DxvkResourceAccess, MaxNumRenderTargets + 1u> m_rtAccess;\n\n    DxvkGpuQueryManager     m_queryManager;\n\n    DxvkGlobalPipelineBarrier m_renderPassBarrierSrc = { };\n    DxvkGlobalPipelineBarrier m_renderPassBarrierDst = { };\n\n    std::vector<DxvkDeferredClear> m_deferredClears;\n    std::array<DxvkDeferredResolve, MaxNumRenderTargets + 1u> m_deferredResolves = { };\n\n    struct {\n      std::vector<VkWriteDescriptorSet> writes;\n      std::vector<DxvkLegacyDescriptor> infos;\n    } m_legacyDescriptors;\n\n    std::array<Rc<DxvkSampler>, MaxNumSamplerSlots> m_samplers;\n    std::array<DxvkBufferSlice, MaxNumUniformBufferSlots> m_uniformBuffers;\n    std::array<DxvkViewPair, MaxNumResourceSlots> m_resources;\n\n    std::array<DxvkGraphicsPipeline*, 4096> m_gpLookupCache = { };\n    std::array<DxvkComputePipeline*,   256> m_cpLookupCache = { };\n\n    std::vector<VkImageMemoryBarrier2> m_imageLayoutTransitions;\n\n    std::vector<util::DxvkDebugLabel> m_debugLabelStack;\n\n    std::vector<Rc<DxvkImage>> m_nonDefaultLayoutImages;\n\n    DxvkDescriptorCopyWorker m_descriptorWorker;\n\n    Rc<DxvkLatencyTracker>  m_latencyTracker;\n    uint64_t                m_latencyFrameId = 0u;\n    bool                    m_endLatencyTracking = false;\n\n    DxvkImplicitResolveTracker  m_implicitResolves;\n\n    void blitImageFb(\n            Rc<DxvkImageView>     dstView,\n      const VkOffset3D*           dstOffsets,\n            Rc<DxvkImageView>     srcView,\n      const VkOffset3D*           srcOffsets,\n            VkFilter              filter);\n\n    void blitImageHw(\n      const Rc<DxvkImageView>&    dstView,\n      const VkOffset3D*           dstOffsets,\n      const Rc<DxvkImageView>&    srcView,\n      const VkOffset3D*           srcOffsets,\n            VkFilter              filter);\n\n    template<bool ToImage>\n    void copyImageBufferData(\n            DxvkCmdBuffer         cmd,\n      const Rc<DxvkImage>&        image,\n      const VkImageSubresourceLayers& imageSubresource,\n            VkOffset3D            imageOffset,\n            VkExtent3D            imageExtent,\n            VkImageLayout         imageLayout,\n      const DxvkResourceBufferInfo& bufferSlice,\n            VkDeviceSize          bufferRowAlignment,\n            VkDeviceSize          bufferSliceAlignment);\n\n    void copyBufferToImageHw(\n      const Rc<DxvkImage>&        image,\n      const VkImageSubresourceLayers& imageSubresource,\n            VkOffset3D            imageOffset,\n            VkExtent3D            imageExtent,\n      const Rc<DxvkBuffer>&       buffer,\n            VkDeviceSize          bufferOffset,\n            VkDeviceSize          bufferRowAlignment,\n            VkDeviceSize          bufferSliceAlignment);\n\n    void copyBufferToImageFb(\n      const Rc<DxvkImage>&        image,\n      const VkImageSubresourceLayers& imageSubresource,\n            VkOffset3D            imageOffset,\n            VkExtent3D            imageExtent,\n      const Rc<DxvkBuffer>&       buffer,\n            VkDeviceSize          bufferOffset,\n            VkDeviceSize          bufferRowAlignment,\n            VkDeviceSize          bufferSliceAlignment,\n            VkFormat              bufferFormat);\n\n    void copyImageToBufferHw(\n      const Rc<DxvkBuffer>&       buffer,\n            VkDeviceSize          bufferOffset,\n            VkDeviceSize          bufferRowAlignment,\n            VkDeviceSize          bufferSliceAlignment,\n      const Rc<DxvkImage>&        image,\n            VkImageSubresourceLayers imageSubresource,\n            VkOffset3D            imageOffset,\n            VkExtent3D            imageExtent);\n\n    void copyImageToBufferCs(\n      const Rc<DxvkBuffer>&       buffer,\n            VkDeviceSize          bufferOffset,\n            VkDeviceSize          bufferRowAlignment,\n            VkDeviceSize          bufferSliceAlignment,\n            VkFormat              bufferFormat,\n      const Rc<DxvkImage>&        image,\n            VkImageSubresourceLayers imageSubresource,\n            VkOffset3D            imageOffset,\n            VkExtent3D            imageExtent);\n\n    void clearImageViewFb(\n      const Rc<DxvkImageView>&    imageView,\n            VkOffset3D            offset,\n            VkExtent3D            extent,\n            VkImageAspectFlags    aspect,\n            VkClearValue          value);\n    \n    void clearImageViewCs(\n      const Rc<DxvkImageView>&    imageView,\n            VkOffset3D            offset,\n            VkExtent3D            extent,\n            VkClearValue          value);\n    \n    void copyImageHw(\n      const Rc<DxvkImage>&        dstImage,\n            VkImageSubresourceLayers dstSubresource,\n            VkOffset3D            dstOffset,\n      const Rc<DxvkImage>&        srcImage,\n            VkImageSubresourceLayers srcSubresource,\n            VkOffset3D            srcOffset,\n            VkExtent3D            extent);\n    \n    void copyImageFb(\n      const Rc<DxvkImage>&        dstImage,\n            VkImageSubresourceLayers dstSubresource,\n            VkOffset3D            dstOffset,\n      const Rc<DxvkImage>&        srcImage,\n            VkImageSubresourceLayers srcSubresource,\n            VkOffset3D            srcOffset,\n            VkExtent3D            extent);\n\n    bool copyImageClear(\n      const Rc<DxvkImage>&        dstImage,\n            VkImageSubresourceLayers dstSubresource,\n            VkOffset3D            dstOffset,\n            VkExtent3D            dstExtent,\n      const Rc<DxvkImage>&        srcImage,\n            VkImageSubresourceLayers srcSubresource);\n\n    template<bool ToBuffer>\n    void copySparsePages(\n      const Rc<DxvkPagedResource>& sparse,\n            uint32_t              pageCount,\n      const uint32_t*             pages,\n      const Rc<DxvkBuffer>&       buffer,\n            VkDeviceSize          offset);\n\n    template<bool ToBuffer>\n    void copySparseBufferPages(\n      const Rc<DxvkBuffer>&       sparse,\n            uint32_t              pageCount,\n      const uint32_t*             pages,\n      const Rc<DxvkBuffer>&       buffer,\n            VkDeviceSize          offset);\n\n    template<bool ToBuffer>\n    void copySparseImagePages(\n      const Rc<DxvkImage>&        sparse,\n            uint32_t              pageCount,\n      const uint32_t*             pages,\n      const Rc<DxvkBuffer>&       buffer,\n            VkDeviceSize          offset);\n\n    template<bool Indexed, typename T>\n    void drawGeneric(\n            uint32_t              count,\n      const T*                    draws);\n\n    template<bool Indexed>\n    void drawIndirectGeneric(\n            VkDeviceSize          offset,\n            uint32_t              count,\n            uint32_t              stride,\n            bool                  unroll);\n\n    template<bool Indexed>\n    void drawIndirectCountGeneric(\n            VkDeviceSize          offset,\n            VkDeviceSize          countOffset,\n            uint32_t              maxCount,\n            uint32_t              stride);\n\n    void generateMipmapsHw(\n      const Rc<DxvkImageView>&        imageView,\n            VkFilter                  filter);\n\n    void generateMipmapsFb(\n      const Rc<DxvkImageView>&        imageView,\n            VkFilter                  filter);\n\n    void generateMipmapsCs(\n      const Rc<DxvkImageView>&        imageView);\n\n    void resolveImageHw(\n      const Rc<DxvkImage>&            dstImage,\n      const Rc<DxvkImage>&            srcImage,\n      const VkImageResolve&           region);\n    \n    void resolveImageRp(\n      const Rc<DxvkImage>&            dstImage,\n      const Rc<DxvkImage>&            srcImage,\n      const VkImageResolve&           region,\n            VkFormat                  format,\n            VkResolveModeFlagBits     mode,\n            VkResolveModeFlagBits     stencilMode,\n            bool                      flushClears);\n\n    void resolveImageFb(\n      const Rc<DxvkImage>&            dstImage,\n      const Rc<DxvkImage>&            srcImage,\n      const VkImageResolve&           region,\n            VkFormat                  format,\n            VkResolveModeFlagBits     depthMode,\n            VkResolveModeFlagBits     stencilMode);\n\n    bool resolveImageClear(\n      const Rc<DxvkImage>&            dstImage,\n      const Rc<DxvkImage>&            srcImage,\n      const VkImageResolve&           region,\n            VkFormat                  format);\n\n    bool resolveImageInline(\n      const Rc<DxvkImage>&            dstImage,\n      const Rc<DxvkImage>&            srcImage,\n      const VkImageResolve&           region,\n            VkFormat                  format,\n            VkResolveModeFlagBits     depthMode,\n            VkResolveModeFlagBits     stencilMode);\n\n    void uploadImageFb(\n      const Rc<DxvkImage>&            image,\n      const Rc<DxvkBuffer>&           source,\n            VkDeviceSize              sourceOffset,\n            VkDeviceSize              subresourceAlignment,\n            VkFormat                  format);\n\n    void uploadImageHw(\n      const Rc<DxvkImage>&            image,\n      const Rc<DxvkBuffer>&           source,\n            VkDeviceSize              subresourceAlignment,\n            VkDeviceSize              sourceOffset);\n\n    VkAttachmentStoreOp determineClearStoreOp(\n            VkAttachmentLoadOp        loadOp) const;\n\n    std::optional<DxvkClearInfo> batchClear(\n      const Rc<DxvkImageView>&        imageView,\n            int32_t                   attachmentIndex,\n            VkImageAspectFlags        discardAspects,\n            VkImageAspectFlags        clearAspects,\n            VkClearValue              clearValue);\n\n    void performClears(\n      const DxvkClearBatch&           batch);\n\n    void deferClear(\n      const Rc<DxvkImageView>&        imageView,\n            VkImageAspectFlags        clearAspects,\n            VkClearValue              clearValue);\n\n    void deferDiscard(\n      const Rc<DxvkImageView>&        imageView,\n            VkImageAspectFlags        discardAspects);\n\n    void hoistInlineClear(\n            DxvkDeferredClear&        clear,\n            VkRenderingAttachmentInfo& attachment,\n            VkImageAspectFlagBits     aspect);\n\n    void flushClearsInline();\n\n    void flushClears(\n            bool                      useRenderPass);\n\n    void flushRenderPassDiscards();\n\n    void flushRenderPassResolves();\n\n    void flushResolves();\n\n    void finalizeLoadStoreOps();\n\n    void adjustAttachmentLoadStoreOps(\n            VkRenderingAttachmentInfo&  attachment,\n            DxvkAccess                  access) const;\n\n    void beginRenderPass();\n    void endRenderPass(bool suspend);\n\n    void endCurrentPass(bool suspend);\n    \n    void acquireRenderTargets(\n      const DxvkFramebufferInfo&  framebufferInfo,\n            DxvkRenderPassOps&    ops);\n\n    void releaseRenderTargets();\n\n    bool renderPassStartUnsynchronized();\n\n    void renderPassBindFramebuffer(\n      const DxvkFramebufferInfo&  framebufferInfo,\n            DxvkRenderPassOps&    ops);\n    \n    void renderPassUnbindFramebuffer();\n    \n    void resetRenderPassOps(\n      const DxvkRenderTargets&    renderTargets,\n            DxvkRenderPassOps&    renderPassOps);\n\n    void startTransformFeedback();\n    void pauseTransformFeedback();\n    \n    void unbindComputePipeline();\n    bool updateComputePipelineState();\n    \n    void unbindGraphicsPipeline();\n    bool updateGraphicsPipeline();\n    bool updateGraphicsPipelineState();\n\n    uint32_t getGraphicsPipelineDebugColor() const;\n\n    template<VkPipelineBindPoint BindPoint>\n    void resetSpecConstants(\n            uint32_t                newMask);\n\n    template<VkPipelineBindPoint BindPoint>\n    void updateSpecConstants();\n\n    void invalidateState();\n\n    template<VkPipelineBindPoint BindPoint>\n    void updateSamplerSet(const DxvkPipelineLayout* layout);\n\n    template<VkPipelineBindPoint BindPoint, bool AlwaysTrack>\n    bool updateResourceBindings(const DxvkPipelineBindings* layout);\n\n    template<VkPipelineBindPoint BindPoint, bool AlwaysTrack>\n    void updateDescriptorSetsBindings(const DxvkPipelineBindings* layout);\n\n\n    template<VkPipelineBindPoint BindPoint, DxvkBindingModel Model, bool AlwaysTrack>\n    bool updateDescriptorHeapBindings(const DxvkPipelineBindings* layout);\n\n    template<VkPipelineBindPoint BindPoint, bool AlwaysTrack>\n    void updatePushDataBindings(const DxvkPipelineBindings* layout);\n\n    void updateComputeShaderResources();\n    bool updateGraphicsShaderResources();\n\n    DxvkFramebufferInfo makeFramebufferInfo(\n      const DxvkRenderTargets&      renderTargets);\n\n    void updateRenderTargets();\n    \n    bool flushDeferredClear(\n      const DxvkImage&              image,\n      const VkImageSubresourceRange& subresources);\n\n    DxvkDeferredClear* findDeferredClear(\n      const DxvkImage&              image,\n      const VkImageSubresourceRange& subresources);\n\n    DxvkDeferredClear* findOverlappingDeferredClear(\n      const DxvkImage&              image,\n      const VkImageSubresourceRange& subresources);\n\n    DxvkDeferredResolve* findOverlappingDeferredResolve(\n      const DxvkImage&              image,\n      const VkImageSubresourceRange& subresources);\n\n    bool isBoundAsRenderTarget(\n      const DxvkImage&              image,\n      const VkImageSubresourceRange& subresources);\n\n    void updateIndexBufferBinding();\n    void updateVertexBufferBindings();\n\n    void updateTransformFeedbackBuffers();\n    void updateTransformFeedbackState();\n\n    void updateDynamicState();\n\n    template<VkPipelineBindPoint BindPoint>\n    void updatePushData();\n\n    void beginComputePass();\n    void endComputePass();\n\n    template<bool Indirect, bool Resolve = true>\n    bool commitComputeState();\n    \n    template<bool Indexed, bool Indirect, bool Resolve = true>\n    bool commitGraphicsState();\n    \n    template<VkPipelineBindPoint BindPoint>\n    bool checkResourceHazards(\n      const DxvkPipelineBindings*     layout);\n\n    template<bool Indirect>\n    bool checkComputeHazards();\n\n    template<bool Indexed, bool Indirect>\n    bool checkGraphicsHazards();\n\n    template<VkPipelineBindPoint BindPoint>\n    bool checkBufferBarrier(\n      const DxvkBufferSlice&          bufferSlice,\n            VkAccessFlags             access,\n            DxvkAccessOp              accessOp);\n\n    template<VkPipelineBindPoint BindPoint>\n    bool checkBufferViewBarrier(\n      const Rc<DxvkBufferView>&       bufferView,\n            VkAccessFlags             access,\n            DxvkAccessOp              accessOp);\n\n    template<VkPipelineBindPoint BindPoint>\n    bool checkImageViewBarrier(\n      const Rc<DxvkImageView>&        imageView,\n            VkAccessFlags             access,\n            DxvkAccessOp              accessOp);\n\n    template<VkPipelineBindPoint BindPoint>\n    DxvkAccessFlags getAllowedStorageHazards() {\n      if (m_barrierControl.isClear() || m_flags.test(DxvkContextFlag::ForceWriteAfterWriteSync))\n        return DxvkAccessFlags();\n\n      if constexpr (BindPoint == VK_PIPELINE_BIND_POINT_COMPUTE) {\n        // If there are any pending accesses that are not directly related\n        // to shader dispatches, always insert a barrier if there is a hazard.\n        VkPipelineStageFlags2 stageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT\n                                        | VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT;\n\n        if (!m_execBarriers.hasPendingStages(~stageMask)) {\n          if (m_barrierControl.test(DxvkBarrierControl::ComputeAllowReadWriteOverlap))\n            return DxvkAccessFlags(DxvkAccess::Write, DxvkAccess::Read);\n          else if (m_barrierControl.test(DxvkBarrierControl::ComputeAllowWriteOnlyOverlap))\n            return DxvkAccessFlags(DxvkAccess::Write);\n        }\n      } else {\n        // In an unsynchronized render pass we need to ensure that we properly\n        // sync against accesses from outside the pass.\n        if (m_flags.test(DxvkContextFlag::GpRenderPassUnsynchronized)) {\n          VkPipelineStageFlags2 stageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT\n                                          | VK_PIPELINE_STAGE_2_TRANSFER_BIT\n                                          | VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;\n\n          if (m_execBarriers.hasPendingStages(stageMask))\n            return DxvkAccessFlags();\n        }\n\n        // For graphics, the only type of unrelated access we have to worry about\n        // is transform feedback writes, in which case inserting a barrier is fine.\n        if (m_barrierControl.test(DxvkBarrierControl::GraphicsAllowReadWriteOverlap))\n          return DxvkAccessFlags(DxvkAccess::Write, DxvkAccess::Read);\n      }\n\n      return DxvkAccessFlags();\n    }\n\n\n    void emitMemoryBarrier(\n            VkPipelineStageFlags      srcStages,\n            VkAccessFlags             srcAccess,\n            VkPipelineStageFlags      dstStages,\n            VkAccessFlags             dstAccess);\n\n    void trackDrawBuffer();\n\n    bool tryInvalidateDeviceLocalBuffer(\n      const Rc<DxvkBuffer>&           buffer,\n            VkDeviceSize              copySize);\n\n    Rc<DxvkImageView> ensureImageViewCompatibility(\n      const Rc<DxvkImageView>&        view,\n            VkImageUsageFlagBits      usage);\n\n    void relocateResources(\n            size_t                    bufferCount,\n      const DxvkRelocateBufferInfo*   bufferInfos,\n            size_t                    imageCount,\n      const DxvkRelocateImageInfo*    imageInfos);\n\n    void relocateQueuedResources();\n\n    Rc<DxvkSampler> createBlitSampler(\n            VkFilter                  filter);\n\n    DxvkGraphicsPipeline* lookupGraphicsPipeline(\n      const DxvkGraphicsPipelineShaders&  shaders);\n\n    DxvkComputePipeline* lookupComputePipeline(\n      const DxvkComputePipelineShaders&   shaders);\n    \n    Rc<DxvkBuffer> createZeroBuffer(\n            VkDeviceSize              size);\n\n    void freeZeroBuffer();\n\n    void resizeDescriptorArrays(\n            uint32_t                  bindingCount);\n\n    void flushImplicitResolves();\n\n    void beginCurrentCommands();\n\n    void endCurrentCommands();\n\n    void splitCommands();\n\n    void discardRenderTarget(\n      const DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources);\n\n    void flushImageLayoutTransitions(\n            DxvkCmdBuffer             cmdBuffer);\n\n    void addImageLayoutTransition(\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            VkImageLayout             srcLayout,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            VkImageLayout             dstLayout,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess);\n\n    void addImageLayoutTransition(\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            VkImageLayout             dstLayout,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess,\n            bool                      discard);\n\n    void addImageInitTransition(\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            VkImageLayout             dstLayout,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess);\n\n    void trackNonDefaultImageLayout(\n            DxvkImage&                image);\n\n    bool overlapsRenderTarget(\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources);\n\n    bool restoreImageLayout(\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            bool                      keepAttachments);\n\n    template<typename Pred>\n    void restoreImageLayouts(\n      const Pred&                     pred,\n            bool                      keepAttachments);\n\n    void prepareShaderReadableImages(\n            bool                      renderPass);\n\n    void prepareSharedImages();\n\n    bool transitionImageLayout(\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            VkImageLayout             dstLayout,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess,\n            bool                      discard);\n\n    void acquireResources(\n            DxvkCmdBuffer             cmdBuffer,\n            size_t                    count,\n      const DxvkResourceAccess*       batch,\n            bool                      flushClears = true);\n\n    void releaseResources(\n            DxvkCmdBuffer             cmdBuffer,\n            size_t                    count,\n      const DxvkResourceAccess*       batch);\n\n    void syncResources(\n            DxvkCmdBuffer             cmdBuffer,\n            size_t                    count,\n      const DxvkResourceAccess*       batch,\n            bool                      flushClears = true);\n\n    void accessMemory(\n            DxvkCmdBuffer             cmdBuffer,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess);\n\n    void accessImage(\n            DxvkCmdBuffer             cmdBuffer,\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            VkImageLayout             srcLayout,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessImage(\n            DxvkCmdBuffer             cmdBuffer,\n      const DxvkImageView&            imageView,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessImage(\n            DxvkCmdBuffer             cmdBuffer,\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            VkImageLayout             srcLayout,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            VkImageLayout             dstLayout,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessImageRegion(\n            DxvkCmdBuffer             cmdBuffer,\n            DxvkImage&                image,\n      const VkImageSubresourceLayers& subresources,\n            VkOffset3D                offset,\n            VkExtent3D                extent,\n            VkImageLayout             srcLayout,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessImageRegion(\n            DxvkCmdBuffer             cmdBuffer,\n            DxvkImage&                image,\n      const VkImageSubresourceLayers& subresources,\n            VkOffset3D                offset,\n            VkExtent3D                extent,\n            VkImageLayout             srcLayout,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            VkImageLayout             dstLayout,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessImageTransfer(\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            VkImageLayout             srcLayout,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess);\n\n    void accessBuffer(\n            DxvkCmdBuffer             cmdBuffer,\n            DxvkBuffer&               buffer,\n            VkDeviceSize              offset,\n            VkDeviceSize              size,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessBuffer(\n            DxvkCmdBuffer             cmdBuffer,\n            DxvkBuffer&               buffer,\n            VkDeviceSize              offset,\n            VkDeviceSize              size,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessBuffer(\n            DxvkCmdBuffer             cmdBuffer,\n      const DxvkBufferSlice&          bufferSlice,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessBuffer(\n            DxvkCmdBuffer             cmdBuffer,\n      const DxvkBufferSlice&          bufferSlice,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessBuffer(\n            DxvkCmdBuffer             cmdBuffer,\n            DxvkBufferView&           bufferView,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessBuffer(\n            DxvkCmdBuffer             cmdBuffer,\n            DxvkBufferView&           bufferView,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess,\n            VkPipelineStageFlags2     dstStages,\n            VkAccessFlags2            dstAccess,\n            DxvkAccessOp              accessOp);\n\n    void accessBufferTransfer(\n            DxvkBuffer&               buffer,\n            VkPipelineStageFlags2     srcStages,\n            VkAccessFlags2            srcAccess);\n\n    void accessDrawBuffer(\n            VkDeviceSize              offset,\n            uint32_t                  count,\n            uint32_t                  stride,\n            uint32_t                  size);\n\n    void accessDrawCountBuffer(\n            VkDeviceSize              offset);\n\n    void flushBarriers();\n\n    bool resourceHasAccess(\n            DxvkBuffer&               buffer,\n            VkDeviceSize              offset,\n            VkDeviceSize              size,\n            DxvkAccess                access,\n            DxvkAccessOp              accessOp);\n\n    bool resourceHasAccess(\n            DxvkBufferView&           bufferView,\n            DxvkAccess                access,\n            DxvkAccessOp              accessOp);\n\n    bool resourceHasAccess(\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            DxvkAccess                access,\n            DxvkAccessOp              accessOp);\n\n    bool resourceHasAccess(\n            DxvkImage&                image,\n      const VkImageSubresourceLayers& subresources,\n            VkOffset3D                offset,\n            VkExtent3D                extent,\n            DxvkAccess                access,\n            DxvkAccessOp              accessOp);\n\n    bool resourceHasAccess(\n            DxvkImageView&            imageView,\n            DxvkAccess                access,\n            DxvkAccessOp              accessOp);\n\n    DxvkBarrierBatch& getBarrierBatch(\n            DxvkCmdBuffer             cmdBuffer);\n\n    DxvkCmdBuffer prepareOutOfOrderTransfer(\n            DxvkCmdBuffer             cmdBuffer,\n            size_t                    accessCount,\n      const DxvkResourceAccess*       accessBatch);\n\n    bool prepareOutOfOrderTransfer(\n            DxvkBuffer&               buffer,\n            VkDeviceSize              offset,\n            VkDeviceSize              size,\n            DxvkAccess                access);\n\n    bool prepareOutOfOrderTransfer(\n            DxvkImage&                image,\n      const VkImageSubresourceRange&  subresources,\n            bool                      discard,\n            DxvkAccess                access);\n\n    bool prepareOutOfOrderTransition(\n            DxvkImage&                image);\n\n    template<VkPipelineBindPoint BindPoint, typename Pred>\n    bool checkResourceBarrier(\n      const Pred&                     pred,\n            VkAccessFlags             access) {\n      // If we're only reading the resource, only pending\n      // writes matter for synchronization purposes.\n      bool hasPendingWrite = pred(DxvkAccess::Write);\n\n      if (!(access & vk::AccessWriteMask))\n        return hasPendingWrite;\n\n      if (hasPendingWrite) {\n        // If there is a write-after-write hazard and synchronization\n        // for those is not explicitly disabled, insert a barrier.\n        DxvkAccessFlags allowedHazards = getAllowedStorageHazards<BindPoint>();\n\n        if (!allowedHazards.test(DxvkAccess::Write))\n          return true;\n\n        // Skip barrier if overlapping read-modify-write ops are allowed.\n        // This includes shader atomics, but also non-atomic load-stores.\n        if (allowedHazards.test(DxvkAccess::Read))\n          return false;\n\n        // Otherwise, check if there is a read-after-write hazard.\n        if (access & vk::AccessReadMask)\n          return true;\n      }\n\n      // Check if there are any pending reads to avoid write-after-read issues.\n      return pred(DxvkAccess::Read);\n    }\n\n    DxvkPipelineLayoutType getActivePipelineLayoutType(VkPipelineBindPoint bindPoint) const {\n      return (bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS && m_flags.test(DxvkContextFlag::GpIndependentSets))\n        ? DxvkPipelineLayoutType::Independent\n        : DxvkPipelineLayoutType::Merged;\n    }\n\n    bool needsDrawBarriers();\n\n    void beginRenderPassDebugRegion();\n\n    template<VkPipelineBindPoint BindPoint>\n    void beginBarrierControlDebugRegion();\n\n    void pushDebugRegion(\n      const VkDebugUtilsLabelEXT&       label,\n            util::DxvkDebugLabelType    type);\n\n    void popDebugRegion(\n            util::DxvkDebugLabelType    type);\n\n    bool hasDebugRegion(\n            util::DxvkDebugLabelType    type);\n\n    void beginActiveDebugRegions();\n\n    void endActiveDebugRegions();\n\n    DxvkResourceBufferInfo allocateScratchMemory(\n            VkDeviceSize                alignment,\n            VkDeviceSize                size);\n\n    template<bool AlwaysTrack>\n    force_inline void trackUniformBufferBinding(const DxvkShaderDescriptor& binding, const DxvkBufferSlice& slice) {\n      if (AlwaysTrack || unlikely(slice.buffer()->hasGfxStores())) {\n        accessBuffer(DxvkCmdBuffer::ExecBuffer, slice,\n          util::pipelineStages(binding.getStageMask()), binding.getAccess(), DxvkAccessOp::None);\n      }\n\n      m_cmd->track(slice.buffer(), DxvkAccess::Read);\n    }\n\n    template<bool AlwaysTrack, bool IsWritable>\n    force_inline void trackBufferViewBinding(const DxvkShaderDescriptor& binding, DxvkBufferView& view) {\n      DxvkAccessOp accessOp = IsWritable ? binding.getAccessOp() : DxvkAccessOp::None;\n\n      if (AlwaysTrack || unlikely(view.buffer()->hasGfxStores())) {\n        accessBuffer(DxvkCmdBuffer::ExecBuffer, view,\n          util::pipelineStages(binding.getStageMask()), binding.getAccess(), accessOp);\n      }\n\n      DxvkAccess access = IsWritable && (binding.getAccess() & vk::AccessWriteMask)\n        ? DxvkAccess::Write : DxvkAccess::Read;\n      m_cmd->track(view.buffer(), access);\n    }\n\n    template<bool AlwaysTrack, bool IsWritable>\n    force_inline void trackImageViewBinding(const DxvkShaderDescriptor& binding, DxvkImageView& view) {\n      DxvkAccessOp accessOp = IsWritable ? binding.getAccessOp() : DxvkAccessOp::None;\n\n      if (AlwaysTrack || unlikely(view.hasGfxStores())) {\n        accessImage(DxvkCmdBuffer::ExecBuffer, view,\n          util::pipelineStages(binding.getStageMask()), binding.getAccess(), accessOp);\n      }\n\n      DxvkAccess access = IsWritable && (binding.getAccess() & vk::AccessWriteMask)\n        ? DxvkAccess::Write : DxvkAccess::Read;\n      m_cmd->track(view.image(), access);\n    }\n\n    bool formatsAreImageCopyCompatible(\n            VkFormat                  dstFormat,\n            VkFormat                  srcFormat);\n\n    static uint32_t computePushDataBlockOffset(uint32_t index) {\n      return index ? MaxSharedPushDataSize + MaxPerStagePushDataSize * (index - 1u) : 0u;\n    }\n\n    static VkStencilOpState convertStencilOp(\n      const DxvkStencilOp&            op,\n            bool                      writable);\n\n    static bool formatsAreBufferCopyCompatible(\n            VkFormat                  imageFormat,\n            VkFormat                  bufferFormat);\n\n    static bool formatsAreResolveCompatible(\n            VkFormat                  resolveFormat,\n            VkFormat                  viewFormat);\n\n    static VkFormat sanitizeTexelBufferFormat(\n            VkFormat                  srcFormat);\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_context_state.h",
    "content": "#pragma once\n\n#include <optional>\n\n#include \"dxvk_barrier.h\"\n#include \"dxvk_buffer.h\"\n#include \"dxvk_compute.h\"\n#include \"dxvk_constant_state.h\"\n#include \"dxvk_framebuffer.h\"\n#include \"dxvk_graphics.h\"\n#include \"dxvk_image.h\"\n#include \"dxvk_limits.h\"\n#include \"dxvk_pipelayout.h\"\n#include \"dxvk_sampler.h\"\n#include \"dxvk_shader.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Graphics pipeline state flags\n   * \n   * Stores some information on which state\n   * of the graphics and compute pipelines\n   * has changed and/or needs to be updated.\n   */\n  enum class DxvkContextFlag : uint64_t  {\n    GpRenderPassActive,         ///< Render pass is currently bound\n    GpRenderPassSuspended,      ///< Render pass is currently suspended\n    GpRenderPassSecondaryCmd,   ///< Render pass uses secondary command buffer\n    GpRenderPassSideEffects,    ///< Render pass has side effects\n    GpRenderPassNeedsFlush,     ///< Render pass has pending resolves or discards\n    GpRenderPassUnsynchronized, ///< Render pass is not fully serialized.\n    GpXfbActive,                ///< Transform feedback is enabled\n    GpDirtyRenderTargets,       ///< Bound render targets are out of date\n    GpDirtyPipeline,            ///< Graphics pipeline binding is out of date\n    GpDirtyPipelineState,       ///< Graphics pipeline needs to be recompiled\n    GpDirtyVertexBuffers,       ///< Vertex buffer bindings are out of date\n    GpDirtyIndexBuffer,         ///< Index buffer binding are out of date\n    GpDirtyXfbBuffers,          ///< Transform feedback buffer bindings are out of date\n    GpDirtyBlendConstants,      ///< Blend constants have changed\n    GpDirtyDepthBias,           ///< Depth bias has changed\n    GpDirtyDepthBounds,         ///< Depth bounds have changed\n    GpDirtyDepthClip,           ///< Depth clip state has changed\n    GpDirtyDepthTest,           ///< Depth test state has changed\n    GpDirtyStencilTest,         ///< Stencil test state other than reference has changed\n    GpDirtyStencilRef,          ///< Stencil reference has changed\n    GpDirtyMultisampleState,    ///< Multisample state has changed\n    GpDirtyRasterizerState,     ///< Cull mode and front face have changed\n    GpDirtySampleLocations,     ///< Sample locations have changed\n    GpDirtyViewport,            ///< Viewport state has changed\n    GpDirtySpecConstants,       ///< Graphics spec constants are out of date\n    GpDynamicBlendConstants,    ///< Blend constants are dynamic\n    GpDynamicDepthBias,         ///< Depth bias is dynamic\n    GpDynamicDepthBounds,       ///< Depth bounds are dynamic\n    GpDynamicDepthClip,         ///< Depth clip state is dynamic\n    GpDynamicDepthTest,         ///< Depth test is dynamic\n    GpDynamicStencilTest,       ///< Stencil test state is dynamic\n    GpDynamicMultisampleState,  ///< Multisample state is dynamic\n    GpDynamicRasterizerState,   ///< Cull mode and front face are dynamic\n    GpDynamicSampleLocations,   ///< Sample locations are dynamic\n    GpDynamicVertexStrides,     ///< Vertex buffer strides are dynamic\n    GpHasPushData,              ///< Graphics pipeline uses push data\n    GpIndependentSets,          ///< Graphics pipeline layout was created with independent sets\n\n    CpComputePassActive,        ///< Whether we are inside a compute pass\n    CpDirtyPipelineState,       ///< Compute pipeline is out of date\n    CpDirtySpecConstants,       ///< Compute spec constants are out of date\n    CpHasPushData,              ///< Compute pipeline uses push data\n\n    DirtyDrawBuffer,            ///< Indirect argument buffer is dirty\n    DirtyPushData,              ///< Push data needs to be updated\n\n    ForceWriteAfterWriteSync,   ///< Ignores barrier control flags for write-after-write hazards\n\n    Count\n  };\n\n  static_assert(uint32_t(DxvkContextFlag::Count) <= 64u);\n\n  using DxvkContextFlags = Flags<DxvkContextFlag>;\n\n\n  /**\n   * \\brief Binding model implementation\n   */\n  enum class DxvkBindingModel : uint32_t {\n    Legacy,\n    DescriptorBuffer,\n    DescriptorHeap,\n  };\n\n\n  /**\n   * \\brief Context feature bits\n   */\n  enum class DxvkContextFeature : uint32_t {\n    TrackGraphicsPipeline,\n    VariableMultisampleRate,\n    DebugUtils,\n    DirectMultiDraw,\n    DescriptorBuffer,\n    DescriptorHeap,\n    FeatureCount\n  };\n\n  using DxvkContextFeatures = Flags<DxvkContextFeature>;\n  \n\n  /**\n   * \\brief Barrier control flags\n   * \n   * These flags specify what (not) to\n   * synchronize implicitly.\n   */\n  enum class DxvkBarrierControl : uint32_t {\n    // Ignores write-after-write hazard\n    ComputeAllowWriteOnlyOverlap  = 0,\n    ComputeAllowReadWriteOverlap  = 1,\n\n    GraphicsAllowReadWriteOverlap = 2,\n  };\n\n  using DxvkBarrierControlFlags  = Flags<DxvkBarrierControl>;\n\n\n  struct DxvkIndirectDrawState {\n    DxvkBufferSlice argBuffer;\n    DxvkBufferSlice cntBuffer;\n  };\n  \n  \n  struct DxvkVertexInputState {\n    DxvkBufferSlice indexBuffer;\n    VkIndexType     indexType   = VK_INDEX_TYPE_UINT32;\n    \n    std::array<DxvkBufferSlice, DxvkLimits::MaxNumVertexBindings> vertexBuffers = { };\n    std::array<uint32_t,        DxvkLimits::MaxNumVertexBindings> vertexStrides = { };\n    std::array<uint32_t,        DxvkLimits::MaxNumVertexBindings> vertexExtents = { };\n  };\n\n\n  struct DxvkViewport {\n    VkViewport viewport = { };\n    VkRect2D   scissor  = { };\n  };\n\n\n  struct DxvkViewportState {\n    uint32_t viewportCount = 0;\n    std::array<VkViewport, DxvkLimits::MaxNumViewports> viewports    = { };\n    std::array<VkRect2D,   DxvkLimits::MaxNumViewports> scissorRects = { };\n  };\n\n\n  struct DxvkOutputMergerState {\n    DxvkRenderingInfo   renderingInfo;\n    DxvkRenderTargets   renderTargets;\n    DxvkRenderPassOps   renderPassOps;\n    DxvkFramebufferInfo framebufferInfo;\n    DxvkAttachmentMask  attachmentMask;\n    VkOffset2D          renderAreaLo = { };\n    VkOffset2D          renderAreaHi = { };\n  };\n\n\n  struct DxvkPushDataState {\n    std::array<char, MaxTotalPushDataSize> constantData = { };\n    std::array<char, MaxTotalPushDataSize> resourceData = { };\n  };\n\n\n  struct DxvkXfbState {\n    std::array<DxvkBufferSlice, MaxNumXfbBuffers> buffers;\n    std::array<DxvkBufferSlice, MaxNumXfbBuffers> counters;\n    std::array<DxvkBufferSlice, MaxNumXfbBuffers> activeCounters;\n  };\n  \n  \n  struct DxvkSpecConstantState {\n    uint32_t                                  mask = 0;\n    std::array<uint32_t, MaxNumSpecConstants> data = { };\n  };\n  \n  \n  struct DxvkGraphicsPipelineState {\n    DxvkGraphicsPipelineShaders   shaders;\n    DxvkGraphicsPipelineStateInfo state;\n    DxvkGraphicsPipelineFlags     flags;\n    DxvkGraphicsPipeline*         pipeline = nullptr;\n    DxvkSpecConstantState         constants;\n  };\n  \n  \n  struct DxvkComputePipelineState {\n    DxvkComputePipelineShaders    shaders;\n    DxvkComputePipelineStateInfo  state;\n    DxvkComputePipeline*          pipeline = nullptr;\n    DxvkSpecConstantState         constants;\n  };\n\n\n  struct DxvkDynamicState {\n    DxvkBlendConstants          blendConstants          = { 0.0f, 0.0f, 0.0f, 0.0f };\n    DxvkDepthBias               depthBias               = { 0.0f, 0.0f, 0.0f };\n    DxvkDepthBiasRepresentation depthBiasRepresentation = { VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false };\n    DxvkDepthBounds             depthBounds             = { 0.0f, 1.0f };\n    DxvkDepthStencilState       depthStencilState       = { };\n    uint32_t                    stencilReference        = 0;\n    VkCullModeFlags             cullMode                = VK_CULL_MODE_BACK_BIT;\n    VkFrontFace                 frontFace               = VK_FRONT_FACE_CLOCKWISE;\n  };\n\n\n  struct DxvkDeferredClear {\n    Rc<DxvkImageView> imageView;\n    VkImageAspectFlags discardAspects;\n    VkImageAspectFlags clearAspects;\n    VkClearValue clearValue;\n  };\n  \n  \n  struct DxvkDeferredResolve {\n    Rc<DxvkImageView> imageView;\n    uint32_t layerMask = 0u;\n    VkResolveModeFlagBits depthMode   = { };\n    VkResolveModeFlagBits stencilMode = { };\n    VkRenderingAttachmentFlagsKHR flags = 0u;\n  };\n\n\n  /**\n   * \\brief Pipeline state\n   * \n   * Stores all bound shaders, resources,\n   * and constant pipeline state objects.\n   */\n  struct DxvkContextState {\n    DxvkIndirectDrawState     id;\n    DxvkVertexInputState      vi;\n    DxvkViewportState         vp;\n    DxvkOutputMergerState     om;\n    DxvkPushDataState         pc;\n    DxvkXfbState              xfb;\n    DxvkDynamicState          dyn;\n    \n    DxvkGraphicsPipelineState gp;\n    DxvkComputePipelineState  cp;\n  };\n\n\n  /**\n   * \\brief View pair\n   *\n   * Stores a buffer view and an image view.\n   */\n  struct DxvkViewPair {\n    Rc<DxvkBufferView> bufferView;\n    Rc<DxvkImageView> imageView;\n  };\n  \n\n  /**\n   * \\brief Deferred clear info\n   */\n  struct DxvkClearInfo {\n    Rc<DxvkImageView> view = nullptr;\n    VkAttachmentLoadOp loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    VkAttachmentLoadOp loadOpS = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    VkClearValue clearValue = { };\n    VkImageAspectFlags clearAspects = 0;\n    VkImageAspectFlags discardAspects = 0;\n  };\n\n\n  /**\n   * \\brief Deferred clear batch\n   */\n  class DxvkClearBatch {\n\n  public:\n\n    void add(std::optional<DxvkClearInfo>&& info) {\n      if (info)\n        m_batch.push_back(std::move(*info));\n    }\n\n    std::pair<const DxvkClearInfo*, size_t> getRange() const {\n      return std::make_pair(m_batch.begin(), m_batch.size());\n    }\n\n    bool empty() const {\n      return m_batch.empty();\n    }\n\n  private:\n\n    small_vector<DxvkClearInfo, 16u> m_batch;\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_cs.cpp",
    "content": "#include \"dxvk_cs.h\"\n\nnamespace dxvk {\n  \n  DxvkCsChunk::DxvkCsChunk() {\n    \n  }\n  \n  \n  DxvkCsChunk::~DxvkCsChunk() {\n    this->reset();\n  }\n  \n  \n  void DxvkCsChunk::init(DxvkCsChunkFlags flags) {\n    m_flags = flags;\n  }\n\n\n  void DxvkCsChunk::executeAll(DxvkContext* ctx) {\n    auto cmd = m_head;\n    \n    if (m_flags.test(DxvkCsChunkFlag::SingleUse)) {\n      m_commandOffset = 0;\n      \n      while (cmd != nullptr) {\n        auto next = cmd->next();\n        cmd->exec(ctx);\n        cmd->~DxvkCsCmd();\n        cmd = next;\n      }\n\n      m_head = nullptr;\n      m_next = &m_head;\n    } else {\n      while (cmd != nullptr) {\n        cmd->exec(ctx);\n        cmd = cmd->next();\n      }\n    }\n  }\n  \n  \n  void DxvkCsChunk::reset() {\n    auto cmd = m_head;\n\n    while (cmd != nullptr) {\n      auto next = cmd->next();\n      cmd->~DxvkCsCmd();\n      cmd = next;\n    }\n    \n    m_head = nullptr;\n    m_next = &m_head;\n\n    m_commandOffset = 0;\n  }\n  \n  \n  DxvkCsChunkPool::DxvkCsChunkPool() {\n    \n  }\n  \n  \n  DxvkCsChunkPool::~DxvkCsChunkPool() {\n    for (DxvkCsChunk* chunk : m_chunks)\n      delete chunk;\n  }\n  \n  \n  DxvkCsChunk* DxvkCsChunkPool::allocChunk(DxvkCsChunkFlags flags) {\n    DxvkCsChunk* chunk = nullptr;\n\n    { std::lock_guard<dxvk::mutex> lock(m_mutex);\n      \n      if (m_chunks.size() != 0) {\n        chunk = m_chunks.back();\n        m_chunks.pop_back();\n      }\n    }\n    \n    if (!chunk)\n      chunk = new DxvkCsChunk();\n    \n    chunk->init(flags);\n    return chunk;\n  }\n  \n  \n  void DxvkCsChunkPool::freeChunk(DxvkCsChunk* chunk) {\n    chunk->reset();\n    \n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    m_chunks.push_back(chunk);\n  }\n  \n  \n  DxvkCsThread::DxvkCsThread(\n    const Rc<DxvkDevice>&   device,\n    const Rc<DxvkContext>&  context)\n  : m_device(device), m_context(context),\n    m_thread([this] { threadFunc(); }) {\n    \n  }\n  \n  \n  DxvkCsThread::~DxvkCsThread() {\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      m_stopped.store(true);\n    }\n    \n    m_condOnAdd.notify_one();\n    m_thread.join();\n  }\n  \n  \n  uint64_t DxvkCsThread::dispatchChunk(DxvkCsChunkRef&& chunk) {\n    uint64_t seq;\n\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      seq = ++m_queueOrdered.seqDispatch;\n\n      auto& entry = m_queueOrdered.queue.emplace_back();\n      entry.chunk = std::move(chunk);\n      entry.seq = seq;\n\n      m_condOnAdd.notify_one();\n    }\n    \n    return seq;\n  }\n\n\n  void DxvkCsThread::injectChunk(DxvkCsQueue queue, DxvkCsChunkRef&& chunk, bool synchronize) {\n    uint64_t timeline = 0u;\n\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      auto& q = getQueue(queue);\n\n      if (synchronize)\n        timeline = ++q.seqDispatch;\n\n      auto& entry = q.queue.emplace_back();\n      entry.chunk = std::move(chunk);\n      entry.seq = timeline;\n\n      m_condOnAdd.notify_one();\n\n      if (queue == DxvkCsQueue::HighPriority) {\n        // Worker will check this flag after executing any\n        // chunk without causing additional lock contention\n        m_hasHighPrio.store(true, std::memory_order_release);\n      }\n    }\n\n    if (synchronize) {\n      std::unique_lock<dxvk::mutex> lock(m_counterMutex);\n\n      m_condOnSync.wait(lock, [this, queue, timeline] {\n        return getCounter(queue).load(std::memory_order_acquire) >= timeline;\n      });\n    }\n  }\n\n\n  void DxvkCsThread::synchronize(uint64_t seq) {\n    // Avoid locking if we know the sync is a no-op, may\n    // reduce overhead if this is being called frequently\n    if (seq > m_seqOrdered.load(std::memory_order_acquire)) {\n      // We don't need to lock the queue here, if synchronization\n      // happens while another thread is submitting then there is\n      // an inherent race anyway\n      if (seq == SynchronizeAll)\n        seq = m_queueOrdered.seqDispatch;\n\n      auto t0 = dxvk::high_resolution_clock::now();\n\n      { std::unique_lock<dxvk::mutex> lock(m_counterMutex);\n        m_condOnSync.wait(lock, [this, seq] {\n          return m_seqOrdered.load(std::memory_order_acquire) >= seq;\n        });\n      }\n\n      auto t1 = dxvk::high_resolution_clock::now();\n      auto ticks = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0);\n\n      m_device->addStatCtr(DxvkStatCounter::CsSyncCount, 1);\n      m_device->addStatCtr(DxvkStatCounter::CsSyncTicks, ticks.count());\n    }\n  }\n  \n  \n  void DxvkCsThread::threadFunc() {\n    env::setThreadName(\"dxvk-cs\");\n\n    // Local chunk queues, we use two queues and swap between\n    // them in order to potentially reduce lock contention.\n    std::vector<DxvkCsQueuedChunk> ordered;\n    std::vector<DxvkCsQueuedChunk> highPrio;\n\n    try {\n      while (!m_stopped.load()) {\n        { std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n          auto pred = [this] { return\n              !m_queueOrdered.queue.empty()\n              || !m_queueHighPrio.queue.empty()\n              || m_stopped.load();\n          };\n\n          if (unlikely(!pred())) {\n            auto t0 = dxvk::high_resolution_clock::now();\n\n            m_condOnAdd.wait(lock, [&] {\n              return pred();\n            });\n\n            auto t1 = dxvk::high_resolution_clock::now();\n            m_device->addStatCtr(DxvkStatCounter::CsIdleTicks, std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count());\n          }\n\n          std::swap(ordered, m_queueOrdered.queue);\n          std::swap(highPrio, m_queueHighPrio.queue);\n\n          m_hasHighPrio.store(false, std::memory_order_release);\n        }\n\n        size_t orderedIndex = 0u;\n        size_t highPrioIndex = 0u;\n\n        while (highPrioIndex < highPrio.size() || orderedIndex < ordered.size()) {\n          // Re-fill local high-priority queue if the app has queued anything up\n          // in the meantime, we want to reduce possible synchronization delays.\n          if (highPrioIndex >= highPrio.size() && m_hasHighPrio.load(std::memory_order_acquire)) {\n            highPrio.clear();\n            highPrioIndex = 0u;\n\n            std::unique_lock<dxvk::mutex> lock(m_mutex);\n            std::swap(highPrio, m_queueHighPrio.queue);\n\n            m_hasHighPrio.store(false, std::memory_order_release);\n          }\n\n          // Drain high-priority queue first\n          bool isHighPrio = highPrioIndex < highPrio.size();\n          auto& entry = isHighPrio ? highPrio[highPrioIndex++] : ordered[orderedIndex++];\n\n          m_context->addStatCtr(DxvkStatCounter::CsChunkCount, 1);\n\n          entry.chunk->executeAll(m_context.ptr());\n\n          if (entry.seq) {\n            // Use a separate mutex for the chunk counter, this will only\n            // ever be contested if synchronization is actually necessary.\n            std::lock_guard lock(m_counterMutex);\n\n            auto& counter = isHighPrio ? m_seqHighPrio : m_seqOrdered;\n            counter.store(entry.seq, std::memory_order_release);\n\n            m_condOnSync.notify_one();\n          }\n\n          // Immediately free the chunk to release\n          // references to any resources held by it\n          entry.chunk = DxvkCsChunkRef();\n        }\n\n        ordered.clear();\n        highPrio.clear();\n      }\n    } catch (const DxvkError& e) {\n      Logger::err(\"Exception on CS thread!\");\n      Logger::err(e.message());\n    }\n  }\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_cs.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <condition_variable>\n#include <mutex>\n#include <queue>\n\n#include \"../util/thread.h\"\n\n#include \"dxvk_device.h\"\n#include \"dxvk_context.h\"\n\nnamespace dxvk {\n\n  constexpr static size_t DxvkCsChunkSize = 16384;\n\n  /**\n   * \\brief Command stream operation\n   * \n   * An abstract representation of an operation\n   * that can be recorded into a command list.\n   */\n  class DxvkCsCmd {\n\n  public:\n\n    virtual ~DxvkCsCmd() { }\n\n    /**\n     * \\brief Retrieves next command in a command chain\n     *\n     * This can be used to quickly iterate\n     * over commands within a chunk.\n     * \\returns Pointer the next command\n     */\n    DxvkCsCmd* next() const {\n      return m_next;\n    }\n\n    /**\n     * \\brief Retrieves pointer to next chain\n     *\n     * Used to chain commands.\n     * \\returns Pointer the next command\n     */\n    DxvkCsCmd** chain() {\n      return &m_next;\n    }\n\n    /**\n     * \\brief Executes embedded commands\n     * \\param [in] ctx The target context\n     */\n    virtual void exec(DxvkContext* ctx) = 0;\n\n  private:\n\n    DxvkCsCmd* m_next = nullptr;\n\n  };\n  \n  \n  /**\n   * \\brief Typed command\n   * \n   * Stores a function object which is\n   * used to execute an embedded command.\n   */\n  template<typename T>\n  class DxvkCsTypedCmd : public DxvkCsCmd {\n    \n  public:\n    \n    DxvkCsTypedCmd(T&& cmd)\n    : m_command(std::move(cmd)) { }\n    \n    DxvkCsTypedCmd             (DxvkCsTypedCmd&&) = delete;\n    DxvkCsTypedCmd& operator = (DxvkCsTypedCmd&&) = delete;\n    \n    void exec(DxvkContext* ctx) {\n      m_command(ctx);\n    }\n    \n  private:\n    \n    T m_command;\n    \n  };\n\n\n  /**\n   * \\brief Command data block\n   *\n   * Provides functionality to allocate a potentially growing\n   * array of structures for a command to traverse.\n   */\n  class DxvkCsDataBlock {\n    friend class DxvkCsChunk;\n  public:\n\n    /**\n     * \\brief Number of structures allocated\n     * \\returns Number of structures allocated\n     */\n    size_t count() const {\n      return m_structCount;\n    }\n\n    /**\n     * \\brief Retrieves pointer to first structure\n     * \\returns Untyped pointer to first structure\n     */\n    void* first() {\n      return reinterpret_cast<char*>(this) + m_dataOffset;\n    }\n\n    /**\n     * \\brief Retrieves pointer to given structure\n     *\n     * \\param [in] idx Structure index\n     * \\returns Untyped pointer to given structure\n     */\n    void* at(uint32_t idx) {\n      return reinterpret_cast<char*>(this) + m_dataOffset + idx * uint32_t(m_structSize);\n    }\n\n  private:\n\n    uint32_t m_dataOffset  = 0u;\n    uint16_t m_structSize  = 0u;\n    uint16_t m_structCount = 0u;\n\n  };\n\n\n  /**\n   * \\brief Typed command with metadata\n   * \n   * Stores a function object and an arbitrary\n   * data structure which can be modified after\n   * submitting the command to a cs chunk.\n   */\n  template<typename T, typename M>\n  class DxvkCsDataCmd : public DxvkCsCmd {\n\n  public:\n\n    DxvkCsDataCmd(T&& cmd)\n    : m_command(std::move(cmd)) { }\n\n    ~DxvkCsDataCmd() {\n      auto data = reinterpret_cast<M*>(m_data.first());\n\n      for (size_t i = 0; i < m_data.count(); i++)\n        data[i].~M();\n    }\n\n    DxvkCsDataCmd             (DxvkCsDataCmd&&) = delete;\n    DxvkCsDataCmd& operator = (DxvkCsDataCmd&&) = delete;\n\n    void exec(DxvkContext* ctx) {\n      // No const here so that the function can move objects efficiently\n      m_command(ctx, reinterpret_cast<M*>(m_data.first()), m_data.count());\n    }\n\n    DxvkCsDataBlock* data() {\n      return &m_data;\n    }\n\n  private:\n\n    alignas(std::max(alignof(T), alignof(M)))\n    T               m_command;\n    DxvkCsDataBlock m_data;\n\n  };\n  \n  \n  /**\n   * \\brief Submission flags\n   */\n  enum class DxvkCsChunkFlag : uint32_t {\n    /// Indicates that the submitted chunk will\n    /// no longer be needed after one submission.\n    SingleUse,\n  };\n\n  using DxvkCsChunkFlags = Flags<DxvkCsChunkFlag>;\n  \n  \n  /**\n   * \\brief Command chunk\n   * \n   * Stores a list of commands.\n   */\n  class DxvkCsChunk : public RcObject {\n\n  public:\n\n    DxvkCsChunk();\n    ~DxvkCsChunk();\n\n    /**\n     * \\brief Checks whether the chunk is empty\n     * \\returns \\c true if the chunk is empty\n     */\n    bool empty() const {\n      return m_commandOffset == 0;\n    }\n\n    /**\n     * \\brief Tries to add a command to the chunk\n     * \n     * If the given command can be added to the chunk, it\n     * will be consumed. Otherwise, a new chunk must be\n     * created which is large enough to hold the command.\n     * \\param [in] command The command to add\n     * \\returns \\c true on success, \\c false if\n     *          a new chunk needs to be allocated\n     */\n    template<typename T>\n    bool push(T& command) {\n      using FuncType = DxvkCsTypedCmd<T>;\n      void* ptr = alloc<FuncType>(0u);\n\n      if (unlikely(!ptr))\n        return false;\n\n      auto next = new (ptr) FuncType(std::move(command));\n      append(next);\n      return true;\n    }\n\n    template<typename T>\n    bool push(T&& command) {\n      return push(command);\n    }\n\n    /**\n     * \\brief Adds a command with data to the chunk \n     * \n     * \\param [in] command The command to add\n     * \\param [in] count Number of items to allocate. Should be at least\n     *    1 in order to avoid the possibility of an empty command. Note\n     *    that all allocated structures \\e must be initialized before\n     *    handing off the command to the worker thread.\n     * \\returns Pointer to the data object, or \\c nullptr\n     */\n    template<typename M, typename T>\n    DxvkCsDataBlock* pushCmd(T& command, size_t count) {\n      size_t dataSize = count * sizeof(M);\n\n      // DxvkCsDataCmd is aligned to M\n      using FuncType = DxvkCsDataCmd<T, M>;\n      void* ptr = alloc<FuncType>(dataSize);\n\n      if (unlikely(!ptr))\n        return nullptr;\n\n      // Command data is always packed tightly after the function object\n      auto next = new (ptr) FuncType(std::move(command));\n      append(next);\n\n      // Do some cursed pointer math here so that the block can figure out\n      // where its data is stored based on its own address. This saves a\n      // decent amount of CS chunk memory compared to storing a pointer.\n      auto block = next->data();\n      block->m_dataOffset = reinterpret_cast<uintptr_t>(&m_data[m_commandOffset - dataSize])\n                          - reinterpret_cast<uintptr_t>(block);\n      block->m_structSize = sizeof(M);\n      block->m_structCount = count;\n      return block;\n    }\n\n    /**\n     * \\brief Allocates more storage for a data block\n     *\n     * The data bock \\e must be owned by the last command added to\n     * the CS chunk, or this may override subsequent command data.\n     * \\param [in] block Data block\n     * \\param [in] count Number of structures to allocate\n     * \\returns Pointer to first allocated structure, or \\c nullptr\n     */\n    void* pushData(DxvkCsDataBlock* block, uint32_t count) {\n      uint32_t dataSize = block->m_structSize * count;\n\n      if (unlikely(m_commandOffset + dataSize > DxvkCsChunkSize))\n        return nullptr;\n\n      void* ptr = &m_data[m_commandOffset];\n      m_commandOffset += dataSize;\n\n      block->m_structCount += count;\n      return ptr;\n    }\n\n    /**\n     * \\brief Initializes chunk for recording\n     * \\param [in] flags Chunk flags\n     */\n    void init(DxvkCsChunkFlags flags);\n    \n    /**\n     * \\brief Executes all commands\n     * \n     * This will also reset the chunk\n     * so that it can be reused.\n     * \\param [in] ctx The context\n     */\n    void executeAll(DxvkContext* ctx);\n    \n    /**\n     * \\brief Resets chunk\n     * \n     * Destroys all recorded commands and\n     * marks the chunk itself as empty, so\n     * that it can be reused later.\n     */\n    void reset();\n    \n  private:\n    \n    size_t m_commandOffset = 0;\n    \n    DxvkCsCmd*  m_head = nullptr;\n    DxvkCsCmd** m_next = &m_head;\n\n    DxvkCsChunkFlags m_flags;\n    \n    alignas(64)\n    char m_data[DxvkCsChunkSize];\n\n    template<typename T>\n    void* alloc(size_t extra) {\n      if (alignof(T) > alignof(DxvkCsCmd))\n        m_commandOffset = dxvk::align(m_commandOffset, alignof(T));\n\n      if (unlikely(m_commandOffset + sizeof(T) + extra > DxvkCsChunkSize))\n        return nullptr;\n\n      void* result = &m_data[m_commandOffset];\n      m_commandOffset += sizeof(T) + extra;\n      return result;\n    }\n\n    void append(DxvkCsCmd* cmd) {\n      *m_next = cmd;\n      m_next = cmd->chain();\n    }\n    \n  };\n  \n  \n  /**\n   * \\brief Chunk pool\n   * \n   * Implements a pool of CS chunks which can be\n   * recycled. The goal is to reduce the number\n   * of dynamic memory allocations.\n   */\n  class DxvkCsChunkPool {\n    \n  public:\n    \n    DxvkCsChunkPool();\n    ~DxvkCsChunkPool();\n    \n    DxvkCsChunkPool             (const DxvkCsChunkPool&) = delete;\n    DxvkCsChunkPool& operator = (const DxvkCsChunkPool&) = delete;\n    \n    /**\n     * \\brief Allocates a chunk\n     * \n     * Takes an existing chunk from the pool,\n     * or creates a new one if necessary.\n     * \\param [in] flags Chunk flags\n     * \\returns Allocated chunk object\n     */\n    DxvkCsChunk* allocChunk(DxvkCsChunkFlags flags);\n    \n    /**\n     * \\brief Releases a chunk\n     * \n     * Resets the chunk and adds it to the pool.\n     * \\param [in] chunk Chunk to release\n     */\n    void freeChunk(DxvkCsChunk* chunk);\n    \n  private:\n    \n    dxvk::mutex               m_mutex;\n    std::vector<DxvkCsChunk*> m_chunks;\n    \n  };\n  \n  \n  /**\n   * \\brief Chunk reference\n   * \n   * Implements basic reference counting for\n   * CS chunks and returns them to the pool\n   * as soon as they are no longer needed.\n   */\n  class DxvkCsChunkRef {\n    \n  public:\n    \n    DxvkCsChunkRef() { }\n    DxvkCsChunkRef(\n      DxvkCsChunk*      chunk,\n      DxvkCsChunkPool*  pool)\n    : m_chunk (chunk),\n      m_pool  (pool) {\n      this->incRef();\n    }\n    \n    DxvkCsChunkRef(const DxvkCsChunkRef& other)\n    : m_chunk (other.m_chunk),\n      m_pool  (other.m_pool) {\n      this->incRef();\n    }\n    \n    DxvkCsChunkRef(DxvkCsChunkRef&& other)\n    : m_chunk (other.m_chunk),\n      m_pool  (other.m_pool) {\n      other.m_chunk = nullptr;\n      other.m_pool  = nullptr;\n    }\n    \n    DxvkCsChunkRef& operator = (const DxvkCsChunkRef& other) {\n      other.incRef();\n      this->decRef();\n      this->m_chunk = other.m_chunk;\n      this->m_pool  = other.m_pool;\n      return *this;\n    }\n    \n    DxvkCsChunkRef& operator = (DxvkCsChunkRef&& other) {\n      this->decRef();\n      this->m_chunk = other.m_chunk;\n      this->m_pool  = other.m_pool;\n      other.m_chunk = nullptr;\n      other.m_pool  = nullptr;\n      return *this;\n    }\n    \n    ~DxvkCsChunkRef() {\n      this->decRef();\n    }\n    \n    DxvkCsChunk* operator -> () const {\n      return m_chunk;\n    }\n    \n    explicit operator bool () const {\n      return m_chunk != nullptr;\n    }\n    \n  private:\n    \n    DxvkCsChunk*      m_chunk = nullptr;\n    DxvkCsChunkPool*  m_pool  = nullptr;\n    \n    void incRef() const {\n      if (m_chunk != nullptr)\n        m_chunk->incRef();\n    }\n    \n    void decRef() const {\n      if (m_chunk != nullptr && m_chunk->decRef() == 0)\n        m_pool->freeChunk(m_chunk);\n    }\n    \n  };\n\n\n  /**\n   * \\brief Queue type\n   */\n  enum class DxvkCsQueue : uint32_t {\n    Ordered       = 0,  /// Normal queue with ordering guarantees\n    HighPriority  = 1,  /// High-priority queue\n  };\n\n\n  /**\n   * \\brief Queued chunk entry\n   */\n  struct DxvkCsQueuedChunk {\n    DxvkCsChunkRef  chunk;\n    uint64_t        seq;\n  };\n\n\n  /**\n   * \\brief Chunk queue\n   *\n   * Stores queued chunks as well as the sequence\n   * counters for synchronization.\n   */\n  struct DxvkCsChunkQueue {\n    std::vector<DxvkCsQueuedChunk> queue;\n    uint64_t                       seqDispatch = 0u;\n  };\n\n\n  /**\n   * \\brief Command stream thread\n   * \n   * Spawns a thread that will execute\n   * commands on a DXVK context. \n   */\n  class DxvkCsThread {\n\n  public:\n\n    constexpr static uint64_t SynchronizeAll = ~0ull;\n\n    DxvkCsThread(\n      const Rc<DxvkDevice>&   device,\n      const Rc<DxvkContext>&  context);\n    ~DxvkCsThread();\n    \n    /**\n     * \\brief Dispatches an entire chunk\n     * \n     * Can be used to efficiently play back large\n     * command lists recorded on another thread.\n     * \\param [in] chunk The chunk to dispatch\n     * \\returns Sequence number of the submission\n     */\n    uint64_t dispatchChunk(DxvkCsChunkRef&& chunk);\n\n    /**\n     * \\brief Injects chunk into the command stream\n     *\n     * This is meant to be used when serialized execution is required\n     * from a thread other than the main thread recording rendering\n     * commands. The context can still be safely accessed, but chunks\n     * will not be executed in any particular oder. These chunks also\n     * do not contribute to the main timeline.\n     * \\param [in] queue Which queue to add the chunk to\n     * \\param [in] chunk The chunk to dispatch\n     * \\param [in] synchronize Whether to wait for execution to complete\n     */\n    void injectChunk(\n            DxvkCsQueue       queue,\n            DxvkCsChunkRef&&  chunk,\n            bool              synchronize);\n\n    /**\n     * \\brief Synchronizes with the thread\n     * \n     * This waits for all chunks in the dispatch queue to\n     * be processed by the thread, up to the given sequence\n     * number. If the sequence number is 0, this will wait\n     * for all pending chunks to complete execution.\n     * \\param [in] seq Sequence number to wait for.\n     */\n    void synchronize(uint64_t seq);\n    \n    /**\n     * \\brief Retrieves last executed sequence number\n     *\n     * Can be used to avoid synchronization in some cases.\n     * \\returns Sequence number of last executed chunk\n     */\n    uint64_t lastSequenceNumber() const {\n      return m_seqOrdered.load(std::memory_order_acquire);\n    }\n\n  private:\n\n    Rc<DxvkDevice>              m_device;\n    Rc<DxvkContext>             m_context;\n\n    alignas(CACHE_LINE_SIZE)\n    dxvk::mutex                 m_counterMutex;\n\n    std::atomic<uint64_t>       m_seqHighPrio = { 0u };\n    std::atomic<uint64_t>       m_seqOrdered  = { 0u };\n\n    std::atomic<bool>           m_stopped     = { false };\n    std::atomic<bool>           m_hasHighPrio = { false };\n\n    alignas(CACHE_LINE_SIZE)\n    dxvk::mutex                 m_mutex;\n    dxvk::condition_variable    m_condOnAdd;\n    dxvk::condition_variable    m_condOnSync;\n\n    DxvkCsChunkQueue            m_queueOrdered;\n    DxvkCsChunkQueue            m_queueHighPrio;\n\n    dxvk::thread                m_thread;\n\n    auto& getQueue(DxvkCsQueue which) {\n      return which == DxvkCsQueue::Ordered\n        ? m_queueOrdered : m_queueHighPrio;\n    }\n\n    auto& getCounter(DxvkCsQueue which) {\n      return which == DxvkCsQueue::Ordered\n        ? m_seqOrdered : m_seqHighPrio;\n    }\n\n    void threadFunc();\n    \n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_descriptor.h",
    "content": "#pragma once\n\n#include \"dxvk_include.h\"\n#include \"dxvk_limits.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Legacy Vulkan descriptor info\n   *\n   * This structure can be used directly with\n   * descriptor update templates.\n   */\n  union DxvkLegacyDescriptor {\n    VkDescriptorBufferInfo buffer;\n    VkDescriptorImageInfo image;\n    VkBufferView bufferView;\n  };\n\n\n  /**\n   * \\brief Descriptor info\n   *\n   * Stores a resource or view descriptor.\n   */\n  struct DxvkDescriptor {\n    /** Legacy view handle or buffer info, can be passed\n     *  directly to WriteDescriptorSet and friends */\n    DxvkLegacyDescriptor legacy;\n    /** Explicit padding to make msvc happy */\n    uint64_t reserved;\n    /** Actual descriptor data */\n    std::array<char, 256u> descriptor;\n\n    /**\n     * \\brief Computes host address range for descriptor data\n     *\n     * For use with descriptor heaps.\n     * \\returns Host address range info\n     */\n    VkHostAddressRangeEXT getHostAddressRange() {\n      VkHostAddressRangeEXT result = { };\n      result.address = descriptor.data();\n      result.size = descriptor.size();\n      return result;\n    }\n  };\n\n\n  /**\n   * \\brief Sampler descriptor info\n   *\n   * Stores info on a sampler descriptor.\n   */\n  struct DxvkSamplerDescriptor {\n    VkSampler samplerObject = VK_NULL_HANDLE;\n    uint16_t samplerIndex = 0u;\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_descriptor_heap.cpp",
    "content": "#include \"dxvk_descriptor_heap.h\"\n#include \"dxvk_device.h\"\n\nnamespace dxvk {\n\n  DxvkResourceDescriptorRange::DxvkResourceDescriptorRange(\n          DxvkResourceDescriptorHeap*         heap,\n          Rc<DxvkBuffer>                      gpuBuffer,\n          VkDeviceSize                        rangeSize,\n          VkDeviceSize                        rangeIndex,\n          VkDeviceSize                        reservedSize)\n  : m_heap        (heap),\n    m_gpuBuffer   (std::move(gpuBuffer)),\n    m_rangeOffset (rangeSize * rangeIndex + reservedSize),\n    m_rangeSize   (rangeSize),\n    m_reservedSize(reservedSize),\n    m_bufferSize  (m_gpuBuffer->info().size),\n    m_rangeInfo   (m_gpuBuffer->getSliceInfo(m_rangeOffset, m_rangeSize)) {\n\n  }\n\n\n  DxvkResourceDescriptorRange::~DxvkResourceDescriptorRange() {\n\n  }\n\n\n\n\n  DxvkResourceDescriptorHeap::DxvkResourceDescriptorHeap(DxvkDevice* device)\n  : m_device(device) {\n    if (m_device->canUseDescriptorHeap())\n      m_reservedSize = device->properties().extDescriptorHeap.minResourceHeapReservedRange;\n  }\n\n\n  DxvkResourceDescriptorHeap::~DxvkResourceDescriptorHeap() {\n\n  }\n\n\n  Rc<DxvkResourceDescriptorRange> DxvkResourceDescriptorHeap::allocRange() {\n    VkDeviceAddress baseAddress = 0u;\n\n    if (likely(m_currentRange))\n      baseAddress = m_currentRange->getHeapInfo().gpuAddress;\n\n    // Check if there are any existing ranges not in use, and prioritize\n    // a range with the same base address as the current one.\n    DxvkResourceDescriptorRange* newRange = nullptr;\n\n    for (auto& r : m_ranges) {\n      if (!r.isInUse()) {\n        newRange = &r;\n\n        if (r.getHeapInfo().gpuAddress == baseAddress)\n          break;\n      }\n    }\n\n    // If there is no unused range, allocate a new one.\n    if (!newRange)\n      newRange = addRanges();\n\n    newRange->reset();\n\n    return (m_currentRange = newRange);\n  }\n\n\n  DxvkResourceDescriptorRange* DxvkResourceDescriptorHeap::addRanges() {\n    // Use a fixed heap size regardless of descriptor size. This avoids\n    // creating unnecessarily large buffers in simple apps on devices\n    // that have pathologically large descriptors.\n    constexpr VkDeviceSize MaxHeapSize = env::is32BitHostPlatform() ? (4ull << 20) : (8ull << 20);\n    constexpr VkDeviceSize SliceCount = 8u;\n\n    // Check selected heap size against device capabilities. If the device\n    // gives us indices in place of real descriptors, we might only get a\n    // smaller maximum supported size as well.\n    VkDeviceSize deviceHeapSize = MaxHeapSize;\n    VkDeviceSize deviceDescriptorAlignment = m_device->getDescriptorProperties().getDescriptorSetAlignment();\n\n    if (m_device->canUseDescriptorHeap()) {\n      const auto& properties = m_device->properties().extDescriptorHeap;\n      deviceHeapSize = std::min(deviceHeapSize, properties.maxResourceHeapSize - properties.minResourceHeapReservedRange);\n    }\n\n    if (m_device->canUseDescriptorBuffer())\n      deviceHeapSize = std::min(deviceHeapSize, m_device->properties().extDescriptorBuffer.maxResourceDescriptorBufferRange);\n\n    // Ensure that the selected slice size meets all alignment requirements.\n    VkDeviceSize sliceSize = std::min(MaxHeapSize, deviceHeapSize) / SliceCount;\n    sliceSize &= ~(deviceDescriptorAlignment - 1u);\n\n    // Create buffer and add ranges all using one slice of that new buffer\n    Rc<DxvkBuffer> buffer = createBuffer(sliceSize * SliceCount);\n\n    DxvkResourceDescriptorRange* first = nullptr;\n\n    for (uint32_t i = 0u; i < SliceCount; i++) {\n      auto& range = m_ranges.emplace_back(this, buffer, sliceSize, i, m_reservedSize);\n\n      if (!first)\n        first = &range;\n    }\n\n    return first;\n  }\n\n\n  Rc<DxvkBuffer> DxvkResourceDescriptorHeap::createBuffer(VkDeviceSize baseSize) {\n    DxvkBufferCreateInfo info = { };\n    info.size = baseSize + m_reservedSize;\n    info.usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;\n    info.debugName = \"Resource heap\";\n\n    if (m_device->canUseDescriptorHeap())\n      info.usage |= VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (m_device->canUseDescriptorBuffer())\n      info.usage |= VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkMemoryPropertyFlags memoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT\n                                      | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n                                      | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;\n\n    Logger::info(str::format(\"Creating resource descriptor heap (\", info.size >> 10u, \" kB)\"));\n\n    m_device->addStatCtr(DxvkStatCounter::DescriptorHeapSize, info.size);\n    m_device->addStatCtr(DxvkStatCounter::DescriptorHeapCount, 1u);\n    return m_device->createBuffer(info, memoryFlags);\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_descriptor_heap.h",
    "content": "#pragma once\n\n#include <list>\n\n#include \"dxvk_buffer.h\"\n#include \"dxvk_descriptor_info.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n  class DxvkResourceDescriptorHeap;\n\n  /**\n   * \\brief Descriptor heap binding info\n   *\n   * Stores buffer properties for the purpose of\n   * binding the descriptor heap.\n   */\n  struct DxvkDescriptorHeapBindingInfo {\n    VkBuffer        buffer        = VK_NULL_HANDLE;\n    VkDeviceAddress gpuAddress    = 0u;\n    VkDeviceSize    reservedSize  = 0u;\n    VkDeviceSize    bufferSize    = 0u;\n  };\n\n\n  /**\n   * \\brief Resource descriptor range\n   *\n   * Provides a reference-counted descriptor range that is suballocated from\n   * a larger buffer. The intention is that each range provides a linear\n   * allocator to allocate descriptors from, and each buffer serves as a ring\n   * buffer that can be bound with the same base address.\n   */\n  class DxvkResourceDescriptorRange {\n    friend class DxvkResourceDescriptorHeap;\n  public:\n\n    DxvkResourceDescriptorRange(\n            DxvkResourceDescriptorHeap*         heap,\n            Rc<DxvkBuffer>                      gpuBuffer,\n            VkDeviceSize                        rangeSize,\n            VkDeviceSize                        rangeIndex,\n            VkDeviceSize                        reservedSize);\n\n    ~DxvkResourceDescriptorRange();\n\n    void incRef();\n    void decRef();\n\n    /**\n     * \\brief Checks whether any live references to this range exist\n     *\n     * Live references consider both CPU-side usage as well as GPU\n     * usage tracking. If this returns \\c false for any given range,\n     * that range is guaranteed to be safe to use for allocations.\n     * \\returns \\c true if the descriptor range is in use\n     */\n    bool isInUse() const {\n      return m_useCount.load(std::memory_order_relaxed) != 0u;\n    }\n\n    /**\n     * \\brief Queries current allocation offset\n     *\n     * Primarily useful for statistics.\n     * \\returns Current allocation offset\n     */\n    VkDeviceSize getAllocationOffset() const {\n      return m_allocationOffset;\n    }\n\n    /**\n     * \\brief Queries descriptor heap info\n     *\n     * Returns the base address of the descriptor heap rather than the\n     * address of the specific slice. This is done to only bind each\n     * buffer once.\n     * \\returns Buffer slice for descriptor heap binding\n     */\n    DxvkDescriptorHeapBindingInfo getHeapInfo() const {\n      DxvkDescriptorHeapBindingInfo result = { };\n      result.buffer = m_rangeInfo.buffer;\n      result.gpuAddress = m_rangeInfo.gpuAddress - m_rangeInfo.offset;\n      result.reservedSize = m_reservedSize;\n      result.bufferSize = m_bufferSize;\n      return result;\n    }\n\n    /**\n     * \\brief Queries underlying buffer ranges\n     * \\returns Buffer slice covered by the range\n     */\n    DxvkResourceBufferInfo getRangeInfo() const {\n      return m_gpuBuffer->getSliceInfo(m_rangeOffset, m_rangeSize);\n    }\n\n    /**\n     * \\brief Checks whether the range can service an allocation\n     *\n     * Use this to test in advance whether this range has enough space for\n     * descriptor sets with a combined size less than or equal to \\c size.\n     * If this returns \\c true, such allocations are guaranteed to succeed,\n     * otherwise a new range must be allocated from the heap.\n     * \\param [in] size Maximum size of sets to allocate\n     * \\returns \\c true if there is enough space, \\c false otherwise.\n     */\n    bool testAllocation(VkDeviceSize size) const {\n      return m_allocationOffset + size <= m_rangeSize;\n    }\n\n    /**\n     * \\brief Allocates descriptor memory from the range\n     *\n     * Must only be used after verifying that the range has enough memory\n     * left to service the allocation. \\c size must be a multiple of the\n     * maximum required descriptor set alignment.\n     * \\param [in] size Size of the set to allocate\n     * \\returns Properties of the allocated GPU buffer slice, but\n     *    with the map pointer from the host buffer slice.\n     */\n    DxvkResourceBufferInfo alloc(VkDeviceSize size) {\n      DxvkResourceBufferInfo result = { };\n      result.buffer = m_rangeInfo.buffer;\n      result.offset = m_rangeInfo.offset + m_allocationOffset;\n      result.size = size;\n      result.mapPtr = reinterpret_cast<char*>(m_rangeInfo.mapPtr) + m_allocationOffset;\n      result.gpuAddress = m_rangeInfo.gpuAddress + m_allocationOffset;\n\n      m_allocationOffset += size;\n      return result;\n    }\n\n  private:\n\n    DxvkResourceDescriptorHeap* m_heap = nullptr;\n\n    std::atomic<uint32_t>   m_useCount = { 0u };\n\n    Rc<DxvkBuffer>          m_gpuBuffer = nullptr;\n\n    VkDeviceSize            m_rangeOffset = 0u;\n    VkDeviceSize            m_rangeSize   = 0u;\n\n    VkDeviceSize            m_allocationOffset = 0u;\n\n    VkDeviceSize            m_reservedSize = 0u;\n    VkDeviceSize            m_bufferSize = 0u;\n\n    DxvkResourceBufferInfo  m_rangeInfo = { };\n\n    void reset() {\n      m_allocationOffset = 0u;\n    }\n\n  };\n\n\n  /**\n   * \\brief Resource descriptor heap\n   *\n   * Manages descriptor memory for view and buffer descriptors.\n   */\n  class DxvkResourceDescriptorHeap {\n\n  public:\n\n    DxvkResourceDescriptorHeap(DxvkDevice* device);\n\n    ~DxvkResourceDescriptorHeap();\n\n    /**\n     * \\brief Increments ref count\n     */\n    void incRef() {\n      m_useCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    /**\n     * \\brief Decrements ref count\n     * Frees object when the last reference is removed.\n     */\n    void decRef() {\n      if (m_useCount.fetch_sub(1u, std::memory_order_release) == 1u)\n        delete this;\n    }\n\n    /**\n     * \\brief Retrieves current descriptor range\n     *\n     * This will always be the most recently allocated range.\n     * It is not guaranteed to be empty or be able to service\n     * any allocations.\n     * \\returns Current descriptor range\n     */\n    Rc<DxvkResourceDescriptorRange> getRange() {\n      if (unlikely(!m_currentRange))\n        m_currentRange = addRanges();\n\n      return m_currentRange;\n    }\n\n    /**\n     * \\brief Allocates a new descriptor range\n     *\n     * Returns an empty and unused descriptor range. Subsequent\n     * calls to \\c getRange will return the same range.\n     * If the base address of the underlying descriptor heap\n     * changes, it must be bound to the command list.\n     * \\returns Newly allocated descriptor range\n     */\n    Rc<DxvkResourceDescriptorRange> allocRange();\n\n  private:\n\n    DxvkDevice*           m_device    = nullptr;\n    std::atomic<uint32_t> m_useCount  = { 0u };\n\n    VkDeviceSize          m_reservedSize = 0u;\n\n    std::list<DxvkResourceDescriptorRange> m_ranges;\n\n    DxvkResourceDescriptorRange* m_currentRange = nullptr;\n\n    DxvkResourceDescriptorRange* addRanges();\n\n    Rc<DxvkBuffer> createBuffer(VkDeviceSize baseSize);\n\n  };\n\n\n\n  inline void DxvkResourceDescriptorRange::incRef() {\n    if (m_useCount.fetch_add(1u, std::memory_order_acquire) == 0u)\n      m_heap->incRef();\n  }\n\n\n  inline void DxvkResourceDescriptorRange::decRef() {\n    if (m_useCount.fetch_sub(1u, std::memory_order_release) == 1u)\n      m_heap->decRef();\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_descriptor_info.cpp",
    "content": "#include <algorithm>\n\n#include \"dxvk_descriptor_info.h\"\n#include \"dxvk_device.h\"\n\n#include \"../util/util_bit.h\"\n\nnamespace dxvk {\n\n  template<size_t Size>\n  static force_inline void copy_nontemporal(void* dst, const void* src) {\n    static_assert(Size == 4u || Size == 8u || Size == 16u);\n\n    #if defined(DXVK_ARCH_X86) && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER))\n    switch (Size) {\n      case 4u: {\n        auto dstPtr = reinterpret_cast<      int*>(dst);\n        auto srcPtr = reinterpret_cast<const int*>(src);\n        _mm_stream_si32(dstPtr, srcPtr[0u]);\n      } break;\n\n      case 8u: {\n        #if defined(DXVK_ARCH_X86_64)\n        auto dstPtr = reinterpret_cast<      long long int*>(dst);\n        auto srcPtr = reinterpret_cast<const long long int*>(src);\n        _mm_stream_si64(dstPtr, srcPtr[0u]);\n        #else\n        auto dstPtr = reinterpret_cast<      int32_t*>(dst);\n        auto srcPtr = reinterpret_cast<const int32_t*>(src);\n        _mm_stream_si32(dstPtr + 0u, srcPtr[0u]);\n        _mm_stream_si32(dstPtr + 1u, srcPtr[1u]);\n        #endif\n      } break;\n\n      case 16u: {\n        auto dstPtr = reinterpret_cast<      __m128i*>(dst);\n        auto srcPtr = reinterpret_cast<const __m128i*>(src);\n        _mm_stream_si128(dstPtr, _mm_loadu_si128(srcPtr));\n      } break;\n    }\n    #else\n    std::memcpy(dst, src, Size);\n    #endif\n  }\n\n\n  template<size_t Size>\n  static force_inline void clear_nontemporal(void* dst) {\n    static_assert(Size == 4u || Size == 8u || Size == 16u);\n\n    #if defined(DXVK_ARCH_X86) && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER))\n    switch (Size) {\n      case 4u: {\n        auto dstPtr = reinterpret_cast<int*>(dst);\n        _mm_stream_si32(dstPtr, 0);\n      } break;\n\n      case 8u: {\n        #if defined(DXVK_ARCH_X86_64)\n        auto dstPtr = reinterpret_cast<long long int*>(dst);\n        _mm_stream_si64(dstPtr, 0l);\n        #else\n        auto dstPtr = reinterpret_cast<int*>(dst);\n        _mm_stream_si32(dstPtr + 0u, 0);\n        _mm_stream_si32(dstPtr + 1u, 0);\n        #endif\n      } break;\n\n      case 16u: {\n        auto dstPtr = reinterpret_cast<__m128i*>(dst);\n        _mm_stream_si128(dstPtr, _mm_setzero_si128());\n      } break;\n    }\n    #else\n    std::memset(dst, 0, Size);\n    #endif\n  }\n\n\n  DxvkDescriptorUpdateList::DxvkDescriptorUpdateList(\n          DxvkDevice*               device,\n          uint32_t                  setSize,\n          uint32_t                  descriptorCount,\n    const DxvkDescriptorUpdateInfo* descriptorInfos)\n  : m_device(device) {\n    // Concatenate update infos with view indices (if any) and sort\n    // by offset, so that we can more easily process the list.\n    using Entry = std::pair<int32_t, DxvkDescriptorUpdateInfo>;\n\n    std::vector<Entry> list;\n\n    for (uint32_t i = 0u; i < descriptorCount; i++)\n      list.push_back({ int32_t(i), descriptorInfos[i] });\n\n    std::sort(list.begin(), list.end(), [] (const Entry& a, const Entry& b) {\n      return a.second.offset < b.second.offset;\n    });\n\n    // Iterate over ranges and insert padding and copies as necessary,\n    // while merging ranges as best we can. Skip buffers here as they\n    // will be written separately with an API call.\n    DxvkDescriptorUpdateRange range = { };\n    VkDescriptorType rangeType = VK_DESCRIPTOR_TYPE_MAX_ENUM;\n\n    for (const Entry& e : list) {\n      const auto& index = e.first;\n      const auto& info = e.second;\n\n      // Merge consecutive ranges of the same descriptor type\n      bool canMerge = info.descriptorType == rangeType &&\n        uint32_t(index) == range.srcIndex + range.descriptorCount;\n\n      if (canMerge && range.descriptorCount > 1u)\n        canMerge = info.offset == uint32_t(range.dstOffset + range.descriptorCount * range.descriptorSize);\n\n      // If there is padding between descriptors of the same type for\n      // whatever reason, just increase the amount of data we copy.\n      if (canMerge && range.descriptorCount == 1u)\n        range.descriptorSize = info.offset - range.dstOffset;\n\n      if (canMerge) {\n        range.descriptorCount += 1u;\n      } else {\n        addCopy(range);\n        addPadding(range.dstOffset + range.descriptorSize * range.descriptorCount, info.offset);\n\n        range = { };\n        range.dstOffset = info.offset;\n        range.srcIndex = uint32_t(index);\n        range.descriptorCount = 1u;\n        range.descriptorSize = getDescriptorSize(info.descriptorType);\n\n        rangeType = info.descriptorType;\n      }\n    }\n\n    // Add final copy range and padding to ensure we fill entire cache\n    // lines and do not accidentally read back memory during updates.\n    addCopy(range);\n    addPadding(range.dstOffset + range.descriptorSize * range.descriptorCount, setSize);\n  }\n\n\n  DxvkDescriptorUpdateList::~DxvkDescriptorUpdateList() {\n\n  }\n\n\n  void DxvkDescriptorUpdateList::addCopy(const DxvkDescriptorUpdateRange& range) {\n    if (!range.descriptorCount)\n      return;\n\n    uint32_t offsetAlignment = range.dstOffset & -range.dstOffset;\n\n    auto& entry = m_entries.emplace_back();\n    entry.range = range;\n    entry.fn = getCopyFn(offsetAlignment, range.descriptorSize);\n  }\n\n\n  void DxvkDescriptorUpdateList::addPadding(uint32_t loOffset, uint32_t hiOffset) {\n    if (loOffset >= hiOffset)\n      return;\n\n    uint32_t offsetAlignment = hiOffset & -hiOffset;\n\n    auto& entry = m_entries.emplace_back();\n    entry.range.dstOffset = loOffset;\n    entry.range.descriptorCount = 1u;\n    entry.range.descriptorSize = hiOffset - loOffset;\n    entry.fn = getPaddingFn(offsetAlignment, hiOffset - loOffset);\n  }\n\n\n  uint32_t DxvkDescriptorUpdateList::getDescriptorSize(VkDescriptorType type) const {\n    return m_device->getDescriptorProperties().getDescriptorTypeInfo(type).size;\n  }\n\n\n  DxvkDescriptorUpdateFn* DxvkDescriptorUpdateList::getCopyFn(uint32_t alignment, uint32_t size) {\n    if (alignment >= 16u || alignment >= size || !alignment) {\n      switch (size) {\n        case   4u: return &copyAligned< 4u>;\n        case   8u: return &copyAligned< 8u>;\n        case  16u: return &copyAligned<16u>;\n        case  24u: return &copyAligned<24u>;\n        case  32u: return &copyAligned<32u>;\n        case  48u: return &copyAligned<48u>;\n        case  64u: return &copyAligned<64u>;\n        case  96u: return &copyAligned<96u>;\n        case 128u: return &copyAligned<128u>;\n        case 160u: return &copyAligned<160u>;\n        case 192u: return &copyAligned<192u>;\n        case 224u: return &copyAligned<224u>;\n        case 256u: return &copyAligned<256u>;\n      }\n    }\n\n    return &copyGeneric;\n  }\n\n\n  DxvkDescriptorUpdateFn* DxvkDescriptorUpdateList::getPaddingFn(uint32_t alignment, uint32_t size) {\n    if (alignment >= 16u || alignment >= size) {\n      switch (size) {\n        case  4u: return &padAligned< 4u>;\n        case  8u: return &padAligned< 8u>;\n        case 12u: return &padAligned<12u>;\n        case 16u: return &padAligned<16u>;\n        case 24u: return &padAligned<24u>;\n        case 32u: return &padAligned<32u>;\n        case 40u: return &padAligned<40u>;\n        case 48u: return &padAligned<48u>;\n        case 56u: return &padAligned<56u>;\n        case 64u: return &padAligned<64u>;\n        default: return &padAlignedAnySize;\n      }\n    }\n\n    return &padGeneric;\n  }\n\n\n  void DxvkDescriptorUpdateList::copyGeneric(\n          void*                       dst,\n    const DxvkDescriptor**            descriptor,\n    const DxvkDescriptorUpdateRange&  range) {\n    auto dstPtr = reinterpret_cast<char*>(dst) + range.dstOffset;\n    auto srcPtr = descriptor + range.srcIndex;\n\n    for (uint32_t i = 0u; i < range.descriptorCount; i++) {\n      std::memcpy(dstPtr, srcPtr[i]->descriptor.data(), range.descriptorSize);\n      dstPtr += range.descriptorSize;\n    }\n  }\n\n\n  void DxvkDescriptorUpdateList::padGeneric(\n          void*                       dst,\n    const DxvkDescriptor**            descriptor,\n    const DxvkDescriptorUpdateRange&  range) {\n    auto dstPtr = reinterpret_cast<char*>(dst) + range.dstOffset;\n\n    std::memset(dstPtr, 0, range.descriptorSize);\n  }\n\n\n  template<size_t Size>\n  void DxvkDescriptorUpdateList::copyAligned(\n          void*                       dst,\n    const DxvkDescriptor**            descriptor,\n    const DxvkDescriptorUpdateRange&  range) {\n    auto dstPtr = reinterpret_cast<char*>(dst) + range.dstOffset;\n    auto srcBase = descriptor + range.srcIndex;\n\n    for (uint32_t i = 0u; i < range.descriptorCount; i++) {\n      auto srcPtr = reinterpret_cast<const char*>(srcBase[i]->descriptor.data());\n\n      for (size_t i = 0u; i < Size / 16u; i++)\n        copy_nontemporal<16u>(dstPtr + 16u * i, srcPtr + 16u * i);\n\n      dstPtr += 16u * (Size / 16u);\n      srcPtr += 16u * (Size / 16u);\n\n      if (Size & 8u) {\n        copy_nontemporal<8u>(dstPtr, srcPtr);\n\n        dstPtr += 8u;\n        srcPtr += 8u;\n      }\n\n      if (Size & 4u) {\n        copy_nontemporal<4u>(dstPtr, srcPtr);\n\n        dstPtr += 4u;\n      }\n    }\n  }\n\n\n  template<size_t Size>\n  void DxvkDescriptorUpdateList::padAligned(\n          void*                       dst,\n    const DxvkDescriptor**            descriptor,\n    const DxvkDescriptorUpdateRange&  range) {\n    auto dstPtr = reinterpret_cast<char*>(dst) + range.dstOffset;\n\n    if (Size & 4u) {\n      clear_nontemporal<4u>(dstPtr);\n      dstPtr += 4u;\n    }\n\n    if (Size & 8u) {\n      clear_nontemporal<8u>(dstPtr);\n      dstPtr += 8u;\n    }\n\n    for (size_t i = 0u; i < Size / 16u; i++)\n      clear_nontemporal<16u>(dstPtr + 16u * i);\n  }\n\n\n  void DxvkDescriptorUpdateList::padAlignedAnySize(\n          void*                       dst,\n    const DxvkDescriptor**            descriptor,\n    const DxvkDescriptorUpdateRange&  range) {\n    auto dstPtr = reinterpret_cast<char*>(dst) + range.dstOffset;\n\n    if (range.descriptorSize & 4u) {\n      clear_nontemporal<4u>(dstPtr);\n      dstPtr += 4u;\n    }\n\n    if (range.descriptorSize & 8u) {\n      clear_nontemporal<8u>(dstPtr);\n      dstPtr += 8u;\n    }\n\n    for (size_t i = 0u; i < range.descriptorSize / 16u; i++)\n      clear_nontemporal<16u>(dstPtr + 16u * i);\n  }\n\n\n\n\n  DxvkDescriptorProperties::DxvkDescriptorProperties(DxvkDevice* device) {\n    if (device->canUseDescriptorHeap())\n      initDescriptorHeapProperties(device);\n    else if (device->canUseDescriptorBuffer())\n      initDescriptorBufferProperties(device);\n  }\n\n\n  DxvkDescriptorProperties::~DxvkDescriptorProperties() {\n\n  }\n\n\n  void DxvkDescriptorProperties::initDescriptorHeapProperties(const DxvkDevice* device) {\n    auto vkd = device->vkd();\n    auto vki = device->adapter()->vki();\n\n    // Query tight descriptor sizes for each type, but pad them out to the required\n    // alignment since we have no use for the memory in between descriptors. This\n    // may still be useful on devicesw here raw buffer descriptors are smaller than\n    // texel buffer descriptors.\n    auto properties = device->properties().extDescriptorHeap;\n\n    std::array<std::pair<VkDescriptorType, VkDeviceSize>, 7> types = {{\n      { VK_DESCRIPTOR_TYPE_SAMPLER,              properties.samplerDescriptorAlignment },\n      { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,       properties.bufferDescriptorAlignment  },\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,       properties.bufferDescriptorAlignment  },\n      { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, properties.imageDescriptorAlignment   },\n      { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, properties.imageDescriptorAlignment   },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,        properties.imageDescriptorAlignment   },\n      { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,        properties.imageDescriptorAlignment   },\n    }};\n\n    for (const auto& s : types) {\n      auto type = uint32_t(s.first);\n\n      VkDeviceSize size = vki->vkGetPhysicalDeviceDescriptorSizeEXT(device->adapter()->handle(), s.first);\n      VkDeviceSize alignment = s.second;\n\n      auto& info = m_descriptorTypes[type];\n      info.size       = align(size, alignment);\n      info.alignment  = alignment;\n\n      m_setAlignment = std::max(m_setAlignment, alignment);\n\n      if (s.first != VK_DESCRIPTOR_TYPE_SAMPLER) {\n        VkResourceDescriptorInfoEXT nullInfo = { VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT };\n        nullInfo.type = s.first;\n\n        VkHostAddressRangeEXT nullData = m_nullDescriptors[type].getHostAddressRange();\n        vkd->vkWriteResourceDescriptorsEXT(vkd->device(), 1u, &nullInfo, &nullData);\n      }\n    }\n\n    // Pad to full cache lines for better write patterns\n    m_setAlignment = std::max<VkDeviceSize>(m_setAlignment, CACHE_LINE_SIZE);\n\n    logDescriptorProperties();\n  }\n\n\n  void DxvkDescriptorProperties::initDescriptorBufferProperties(const DxvkDevice* device) {\n    auto vk = device->vkd();\n    auto properties = device->properties().extDescriptorBuffer;\n\n    std::array<std::pair<VkDescriptorType, size_t>, 7u> sizes = {{\n      { VK_DESCRIPTOR_TYPE_SAMPLER,               properties.samplerDescriptorSize                  },\n      { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,        properties.robustUniformBufferDescriptorSize      },\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,        properties.robustStorageBufferDescriptorSize      },\n      { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,  properties.robustUniformTexelBufferDescriptorSize },\n      { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,  properties.robustStorageTexelBufferDescriptorSize },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,         properties.sampledImageDescriptorSize             },\n      { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,         properties.storageImageDescriptorSize             },\n    }};\n\n    for (const auto& s : sizes) {\n      auto type = uint32_t(s.first);\n\n      // We don't get alignments from this extension\n      auto& info = m_descriptorTypes[type];\n      info.size       = s.second;\n      info.alignment  = 1u;\n\n      if (s.first != VK_DESCRIPTOR_TYPE_SAMPLER) {\n        VkDescriptorGetInfoEXT nullInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };\n        nullInfo.type = s.first;\n\n        vk->vkGetDescriptorEXT(vk->device(),\n          &nullInfo, s.second, m_nullDescriptors[type].descriptor.data());\n      }\n    }\n\n    m_setAlignment = std::max<uint32_t>(CACHE_LINE_SIZE, properties.descriptorBufferOffsetAlignment);\n\n    logDescriptorProperties();\n  }\n\n\n  void DxvkDescriptorProperties::logDescriptorProperties() {\n    Logger::info(str::format(\n      \"Descriptor sizes (set alignment: \", m_setAlignment, \")\",\n      \"\\n  Sampler              : \", getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_SAMPLER).size,\n      \"\\n  Uniform buffer       : \", getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER).size,\n      \"\\n  Storage buffer       : \", getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER).size,\n      \"\\n  Uniform texel buffer : \", getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER).size,\n      \"\\n  Storage texel buffer : \", getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER).size,\n      \"\\n  Sampled image        : \", getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE).size,\n      \"\\n  Storage image        : \", getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE).size));\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_descriptor_info.h",
    "content": "#pragma once\n\n#include <array>\n#include <utility>\n#include <vector>\n\n#include \"../util/util_small_vector.h\"\n\n#include \"dxvk_descriptor.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n\n  /**\n   * \\brief Size and required alignment of a specific descriptor type\n   *\n   * The reported size is always going to be a multiple of the alignment.\n   * Relevant for computing descriptor layouts and retrieving descriptors.\n   */\n  struct DxvkDescriptorTypeInfo {\n    uint16_t size       = 0u;\n    uint16_t alignment  = 0u;\n  };\n\n\n  /**\n   * \\brief Descriptor range properties\n   */\n  struct DxvkDescriptorUpdateRange {\n    /** Descriptor offset, in bytes */\n    uint16_t dstOffset        = 0u;\n    /** First source descriptor to read */\n    uint16_t srcIndex         = 0u;\n    /** Number of descriptors to copy */\n    uint16_t descriptorCount  = 0u;\n    /** Descriptor size, in bytes. Relevant if no optimized\n     *  function for the given descriptor size exists. */\n    uint16_t descriptorSize   = 0u;\n  };\n\n\n  /**\n   * \\brief Descriptor update function\n   *\n   * Copies or pads descriptor memory. May be\n   * optimized for a specific descriptor size.\n   *\n   * The parameters, in order, are:\n   * - Base pointer to descriptor memory to write\n   * - Base poitner to descriptor list to read\n   * - Reference to copy metadata, used to determine\n   *   offsets and how many descriptors to write.\n   */\n  using DxvkDescriptorUpdateFn = void (void*, const DxvkDescriptor**, const DxvkDescriptorUpdateRange&);\n\n\n  /**\n   * \\brief Descriptor update entry\n   *\n   * Convenience struct that bundles update\n   * info with an update function.\n   */\n  struct DxvkDescriptorUpdateEntry {\n    DxvkDescriptorUpdateRange range = { };\n    DxvkDescriptorUpdateFn*   fn    = nullptr;\n  };\n\n\n  /**\n   * \\brief Descriptor properties\n   *\n   * Stores the descriptor type, offset in the descriptor\n   * set, and whether or not this is sourced from a raw\n   * buffer address range or an actual view descriptor.\n   */\n  struct DxvkDescriptorUpdateInfo {\n    VkDescriptorType  descriptorType  = VK_DESCRIPTOR_TYPE_MAX_ENUM;\n    uint32_t          offset          = 0u;\n  };\n\n\n  /**\n   * \\brief Descriptor update class\n   *\n   * List of descriptor update entries that\n   */\n  class DxvkDescriptorUpdateList {\n\n  public:\n\n    DxvkDescriptorUpdateList() = default;\n\n    /**\n     * \\brief Builds descriptor update list\n     *\n     * Generates an optimized descriptor update list\n     * specifically for the given set layout.\n     * \\param [in] setSize Total descriptor set size, in bytes\n     * \\param [in] descriptorCount Number of descriptors\n     * \\param [in] descriptorInfos Descriptor infos\n     */\n    DxvkDescriptorUpdateList(\n            DxvkDevice*               device,\n            uint32_t                  setSize,\n            uint32_t                  descriptorCount,\n      const DxvkDescriptorUpdateInfo* descriptorInfos);\n\n    ~DxvkDescriptorUpdateList();\n\n    /**\n     * \\brief Updates descriptor memory\n     *\n     * Note that descriptor and buffer lists must list descriptors\n     * in the exact same order as they were passed into the constructor.\n     * \\param [in] dst Pointer to descriptor memory\n     * \\param [in] descriptors Pointer to source descriptor list\n     * \\param [in] buffers Pointer to raw buffer ranges\n     */\n    void update(\n            void*                   dst,\n      const DxvkDescriptor**        descriptors) const {\n      for (size_t i = 0u; i < m_entries.size(); i++) {\n        const auto& e = m_entries[i];\n        e.fn(dst, descriptors, e.range);\n      }\n    }\n\n  private:\n\n    DxvkDevice* m_device = nullptr;\n\n    small_vector<DxvkDescriptorUpdateEntry, 16u> m_entries;\n\n    void addCopy(const DxvkDescriptorUpdateRange& range);\n\n    void addPadding(uint32_t loOffset, uint32_t hiOffset);\n\n    uint32_t getDescriptorSize(VkDescriptorType type) const;\n\n    DxvkDescriptorUpdateFn* getCopyFn(uint32_t alignment, uint32_t size);\n\n    DxvkDescriptorUpdateFn* getPaddingFn(uint32_t alignment, uint32_t size);\n\n    static void copyGeneric(\n            void*                       dst,\n      const DxvkDescriptor**            descriptor,\n      const DxvkDescriptorUpdateRange&  range);\n\n    static void padGeneric(\n            void*                       dst,\n      const DxvkDescriptor**            descriptor,\n      const DxvkDescriptorUpdateRange&  range);\n\n    template<size_t Size>\n    static void copyAligned(\n            void*                       dst,\n      const DxvkDescriptor**            descriptor,\n      const DxvkDescriptorUpdateRange&  range);\n\n    template<size_t Size>\n    static void padAligned(\n            void*                       dst,\n      const DxvkDescriptor**            descriptor,\n      const DxvkDescriptorUpdateRange&  range);\n\n    static void padAlignedAnySize(\n            void*                       dst,\n      const DxvkDescriptor**            descriptor,\n      const DxvkDescriptorUpdateRange&  range);\n\n  };\n\n\n  /**\n   * \\brief Descriptor properties\n   *\n   * Caches descriptor properties and null descriptors.\n   * Not meaningful if the legacy descriptor model is used.\n   */\n  class DxvkDescriptorProperties {\n    constexpr static uint32_t TypeCount = uint32_t(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) + 1u;\n  public:\n\n    DxvkDescriptorProperties(DxvkDevice* device);\n    ~DxvkDescriptorProperties();\n\n    /**\n     * \\brief Queries descriptor type properties\n     *\n     * \\param [in] type Descriptor type to query\n     * \\returns Descriptor size and alignment\n     */\n    DxvkDescriptorTypeInfo getDescriptorTypeInfo(VkDescriptorType type) const {\n      return m_descriptorTypes[uint32_t(type)];\n    }\n\n    /**\n     * \\brief Queries null descriptor\n     *\n     * Not valid for sampler descriptors.\n     * \\param [in] type Descriptor type\n     * \\returns Pointer to null descriptor\n     */\n    const DxvkDescriptor* getNullDescriptor(VkDescriptorType type) const {\n      return &m_nullDescriptors[uint32_t(type)];\n    }\n\n    /**\n     * \\brief Queries descriptor set alignment\n     *\n     * All sets must be padded to this size.\n     * \\returns Descriptor set alignment\n     */\n    VkDeviceSize getDescriptorSetAlignment() const {\n      return m_setAlignment;\n    }\n\n    /**\n     * \\brief Queries maximum descriptor size\n     * \\returns Size of the largest descriptor type\n     */\n    VkDeviceSize getMaxDescriptorSize() const {\n      VkDeviceSize size = 0u;\n\n      for (const auto& e : m_descriptorTypes)\n        size = std::max<VkDeviceSize>(size, e.size);\n\n      return size;\n    }\n\n  private:\n\n    VkDeviceSize m_setAlignment = 0u;\n\n    std::array<DxvkDescriptorTypeInfo, TypeCount> m_descriptorTypes = { };\n    std::array<DxvkDescriptor,         TypeCount> m_nullDescriptors = { };\n\n    void initDescriptorHeapProperties(const DxvkDevice* device);\n\n    void initDescriptorBufferProperties(const DxvkDevice* device);\n\n    void logDescriptorProperties();\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_descriptor_pool.cpp",
    "content": "#include \"dxvk_descriptor_pool.h\"\n#include \"dxvk_device.h\"\n\nnamespace dxvk {\n\n  DxvkDescriptorPool::DxvkDescriptorPool(\n          DxvkDevice*               device)\n  : m_device(device) {\n\n  }\n\n\n  DxvkDescriptorPool::~DxvkDescriptorPool() {\n    auto vk = m_device->vkd();\n\n    for (auto pool : m_pools)\n      vk->vkDestroyDescriptorPool(vk->device(), pool.pool, nullptr);\n\n    m_device->addStatCtr(DxvkStatCounter::DescriptorPoolCount,\n      uint64_t(-int64_t(m_pools.size())));\n  }\n\n\n  void DxvkDescriptorPool::alloc(\n          uint64_t                  trackingId,\n    const DxvkPipelineLayout*       layout,\n          uint32_t                  setMask,\n          VkDescriptorSet*          sets) {\n    for (auto setIndex : bit::BitMask(setMask))\n      sets[setIndex] = alloc(trackingId, layout->getDescriptorSetLayout(setIndex));\n  }\n\n\n  VkDescriptorSet DxvkDescriptorPool::alloc(\n          uint64_t                  trackingId,\n    const DxvkDescriptorSetLayout*  layout) {\n    auto vk = m_device->vkd();\n\n    VkDescriptorSetLayout setLayout = layout->getSetLayout();\n\n    VkResult vr = VK_ERROR_OUT_OF_POOL_MEMORY;\n    VkDescriptorSet set = VK_NULL_HANDLE;\n\n    VkDescriptorSetAllocateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };\n    info.descriptorPool = m_pool.second.pool;\n    info.descriptorSetCount = 1u;\n    info.pSetLayouts = &setLayout;\n\n    if (likely(info.descriptorPool))\n      vr = vk->vkAllocateDescriptorSets(vk->device(), &info, &set);\n\n    if (unlikely(vr != VK_SUCCESS)) {\n      m_pool = getNextPool();\n\n      info.descriptorPool = m_pool.second.pool;\n      vr = vk->vkAllocateDescriptorSets(vk->device(), &info, &set);\n\n      if (vr != VK_SUCCESS)\n        throw DxvkError(str::format(\"Failed to allocate descriptor set: \", vr));\n    }\n\n    m_pool.second.trackingId = trackingId;\n\n    m_setsAllocated++;\n    return set;\n  }\n\n\n  void DxvkDescriptorPool::notifyCompletion(\n          uint64_t                    trackingId) {\n    small_vector<std::pair<size_t, VkDescriptorPool>, 16u> pools;\n\n    { std::lock_guard lock(m_mutex);\n\n      for (size_t i = 0u; i < m_pools.size(); i++) {\n        auto& pool = m_pools[i];\n\n        if (trackingId >= pool.trackingId && pool.status == Status::InFlight)\n          pools.push_back(std::make_pair(i, pool.pool));\n      }\n    }\n\n    if (!pools.empty()) {\n      auto vk = m_device->vkd();\n\n      for (const auto& pool : pools)\n        vk->vkResetDescriptorPool(vk->device(), pool.second, 0u);\n\n      std::lock_guard lock(m_mutex);\n\n      for (const auto& pool : pools)\n        m_pools[pool.first].status = Status::Reset;\n    }\n  }\n\n\n  void DxvkDescriptorPool::updateStats(DxvkStatCounters& counters) {\n    counters.addCtr(DxvkStatCounter::DescriptorSetCount, m_setsAllocated);\n    m_setsAllocated = 0u;\n  }\n\n\n  std::pair<size_t, DxvkDescriptorPool::DescriptorPool> DxvkDescriptorPool::getNextPool() {\n    std::lock_guard lock(m_mutex);\n\n    // Commit current pool and mark as in flight\n    if (m_pool.second.pool) {\n      m_pools[m_pool.first] = m_pool.second;\n      m_pools[m_pool.first].status = Status::InFlight;\n    }\n\n    // Find available pool that's not in use\n    for (size_t i = 0u; i < m_pools.size(); i++) {\n      if (m_pools[i].status == Status::Reset) {\n        m_pools[i].status = Status::InUse;\n        return std::make_pair(i, m_pools[i]);\n      }\n    }\n\n    // Create new pool as necessary\n    auto poolIndex = m_pools.size();\n\n    auto& pool = m_pools.emplace_back();\n    pool.pool = createDescriptorPool();\n    pool.status = Status::InUse;\n\n    return std::make_pair(poolIndex, pool);\n  }\n\n\n  VkDescriptorPool DxvkDescriptorPool::createDescriptorPool() const {\n    auto vk = m_device->vkd();\n\n    // Samplers and uniform buffers may be special on some implementations\n    // so we should allocate space for a reasonable number of both, but\n    // assume that all other descriptor types share pool memory.\n    constexpr static uint32_t MaxSets = 1024u;\n\n    static const std::array<VkDescriptorPoolSize, 6> pools = {{\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,          MaxSets / 2  },\n      { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,          MaxSets / 64 },\n      { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,   MaxSets / 2  },\n      { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,   MaxSets / 64 },\n      { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,         MaxSets * 2  },\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,         MaxSets / 2  },\n    }};\n\n    VkDescriptorPoolCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };\n    info.maxSets       = MaxSets;\n    info.poolSizeCount = pools.size();\n    info.pPoolSizes    = pools.data();\n\n    VkDescriptorPool pool = VK_NULL_HANDLE;\n\n    VkResult vr = vk->vkCreateDescriptorPool(vk->device(), &info, nullptr, &pool);\n\n    if (vr)\n      throw DxvkError(str::format(\"Failed create descriptor pool: \", vr));\n\n    m_device->addStatCtr(DxvkStatCounter::DescriptorPoolCount, 1);\n    return pool;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_descriptor_pool.h",
    "content": "#pragma once\n\n#include <vector>\n\n#include \"dxvk_descriptor.h\"\n#include \"dxvk_pipelayout.h\"\n#include \"dxvk_recycler.h\"\n#include \"dxvk_stats.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n  \n  /**\n   * \\brief Descriptor pool\n   *\n   * Legacy descriptor pool allocator with submission-based lifetime\n   * tracking.\n   */\n  class DxvkDescriptorPool : public RcObject {\n    constexpr static uint32_t MaxDesiredPoolCount = 2;\n  public:\n\n    DxvkDescriptorPool(DxvkDevice* device);\n\n    ~DxvkDescriptorPool();\n\n    /**\n     * \\brief Allocates one or multiple descriptor sets\n     *\n     * \\param [in] trackingId Submission tracking ID\n     * \\param [in] layout Pipeline layout\n     * \\param [in] setMask Descriptor set mask\n     * \\param [out] sets Descriptor sets\n     */\n    void alloc(\n            uint64_t                  trackingId,\n      const DxvkPipelineLayout*       layout,\n            uint32_t                  setMask,\n            VkDescriptorSet*          sets);\n\n    /**\n     * \\brief Allocates a single descriptor set\n     *\n     * \\param [in] trackingId Submission tracking ID\n     * \\param [in] layout Descriptor set layout\n     * \\returns The descriptor set\n     */\n    VkDescriptorSet alloc(\n            uint64_t                  trackingId,\n      const DxvkDescriptorSetLayout*  layout);\n\n    /**\n     * \\brief Declares given submission ID as complete\n     *\n     * Used for tracking descriptor pool lifetimes.\n     * \\param [in] trackingId last completed tracking ID\n     */\n    void notifyCompletion(\n            uint64_t                    trackingId);\n\n    /**\n     * \\brief Updates stat counters with set count\n     * \\param [out] counters Stat counters\n     */\n    void updateStats(DxvkStatCounters& counters);\n\n  private:\n\n    DxvkDevice* m_device = nullptr;\n\n    uint64_t m_setsAllocated = 0u;\n\n    enum class Status : uint32_t {\n      Reset     = 0,\n      InUse     = 1,\n      InFlight  = 2,\n    };\n\n    struct DescriptorPool {\n      VkDescriptorPool pool = VK_NULL_HANDLE;\n      uint64_t trackingId = 0u;\n      Status status = Status::Reset;\n    };\n\n    dxvk::mutex m_mutex;\n\n    small_vector<DescriptorPool, 64u> m_pools;\n    std::pair<size_t, DescriptorPool> m_pool = { };\n\n    std::pair<size_t, DescriptorPool> getNextPool();\n\n    VkDescriptorPool createDescriptorPool() const;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_descriptor_worker.cpp",
    "content": "#include \"dxvk_descriptor_worker.h\"\n#include \"dxvk_device.h\"\n\nnamespace dxvk {\n\n  DxvkDescriptorCopyWorker::DxvkDescriptorCopyWorker(const Rc<DxvkDevice>& device)\n  : m_device        (device),\n    m_vkd           (device->vkd()),\n    m_appendFence   (new sync::Fence()),\n    m_consumeFence  (new sync::Fence()),\n    m_writeBufferDescriptorsFn(getWriteBufferDescriptorFn()) {\n    if (m_device->canUseDescriptorHeap() || m_device->canUseDescriptorBuffer())\n      m_thread = std::thread([this] { runWorker(); });\n  }\n\n\n  DxvkDescriptorCopyWorker::~DxvkDescriptorCopyWorker() {\n    if (m_thread.joinable()) {\n      m_consumeFence->wait(m_appendFence->value());\n      m_appendFence->signal(-1);\n      m_thread.join();\n    }\n  }\n\n\n  DxvkDescriptorCopyWorker::Block* DxvkDescriptorCopyWorker::flushBlock() {\n    // No need to do anything if the block is empty\n    if (!m_blocks[m_blockIndex].rangeCount)\n      return &m_blocks[m_blockIndex];\n\n    // Ensure the next block is actually usable\n    uint64_t append = m_appendFence->value() + 1u;\n    m_appendFence->signal(append);\n\n    if (append >= BlockCount)\n      m_consumeFence->wait(append - BlockCount + 1u);\n\n    m_blockIndex = append % BlockCount;\n    return &m_blocks[m_blockIndex];\n  }\n\n\n  WriteBufferDescriptorsFn* DxvkDescriptorCopyWorker::getWriteBufferDescriptorFn() const {\n    // HACK: Hard-code the RDNA2 raw buffer descriptor format for Steam Deck\n    // in order to dodge significant API call overhead on 32-bit winevulkan.\n    if (env::is32BitHostPlatform() && m_device->debugFlags().isClear()) {\n      const auto& props = m_device->properties();\n\n      bool isDeck = props.vk12.driverID == VK_DRIVER_ID_MESA_RADV\n                 && props.core.properties.vendorID == uint16_t(DxvkGpuVendor::Amd)\n                 && (props.core.properties.deviceID == 0x163fu\n                  || props.core.properties.deviceID == 0x1435u);\n\n      // Validate descriptor sizes to make sure we're *actually* a Deck and\n      // not running with any layers that screw around with descriptor data.\n      // This still isn't guaranteed to be reliable.\n      auto uboSize = m_device->getDescriptorProperties().getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER).size;\n      auto ssboSize = m_device->getDescriptorProperties().getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER).size;\n\n      if (isDeck && uboSize == 16u && ssboSize == 16u) {\n        Logger::info(\"Steam Deck detected, using custom buffer descriptors!\");\n        return &writeBufferDescriptorsSteamDeck;\n      }\n    }\n\n    if (m_device->canUseDescriptorHeap())\n      return &writeBufferDescriptorsGeneric;\n\n    if (m_device->canUseDescriptorBuffer())\n      return &writeBufferDescriptorsGetDescriptorExt;\n\n    return nullptr;\n  }\n\n\n  void DxvkDescriptorCopyWorker::processBlock(Block& block) {\n    // Local memory for uniform buffers descriptors in each set\n    std::array<DxvkDescriptor, MaxNumUniformBufferSlots> scratchDescriptors;\n\n    DxvkDescriptorCopy e = { };\n    e.descriptors = block.descriptors.data();\n    e.buffers = block.buffers.data();\n\n    for (uint32_t i = 0u; i < block.rangeCount; i++) {\n      const auto& range = block.ranges[i];\n\n      m_writeBufferDescriptorsFn(this,\n        scratchDescriptors.data(), range.bufferCount, e.buffers);\n\n      for (uint32_t j = 0u; j < range.bufferCount; j++)\n        e.descriptors[e.buffers[j].indexInSet] = &scratchDescriptors[j];\n\n      range.layout->update(range.descriptorMemory, e.descriptors);\n\n      e.descriptors += range.descriptorCount;\n      e.buffers += range.bufferCount;\n    }\n\n    // Reset entire block to avoid stale descriptors if\n    // anything goes wrong; may improve debuggability.\n    block = Block();\n  }\n\n\n  void DxvkDescriptorCopyWorker::runWorker() {\n    env::setThreadName(\"dxvk-descriptor\");\n\n    uint64_t consume = 0u;\n\n    while (true) {\n      m_appendFence->wait(consume + 1u);\n\n      // Explicitly check current append counter value\n      // since that's how we stop the worker thread\n      uint64_t append = m_appendFence->value();\n\n      if (append == uint64_t(-1))\n        return;\n\n      // Process all blocks that have been queued up\n      auto t0 = dxvk::high_resolution_clock::now();\n\n      while (consume < append) {\n        processBlock(m_blocks[consume % BlockCount]);\n        m_consumeFence->signal(++consume);\n      }\n\n      // Update stat counters\n      auto t1 = dxvk::high_resolution_clock::now();\n      auto td = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0);\n\n      m_device->addStatCtr(DxvkStatCounter::DescriptorCopyBusyTicks, td.count());\n    }\n  }\n\n\n  void DxvkDescriptorCopyWorker::writeBufferDescriptorsGeneric(\n    const DxvkDescriptorCopyWorker* worker,\n          DxvkDescriptor*           descriptors,\n          uint32_t                  bufferCount,\n    const DxvkDescriptorCopyBuffer* bufferInfos) {\n    // Batch API calls to avoid overhead, especially on 32-bit\n    constexpr size_t MaxWrites = 32u;\n\n    small_vector<VkHostAddressRangeEXT, MaxWrites> hostRanges;\n    small_vector<VkDeviceAddressRangeEXT, MaxWrites> bufferRanges;\n    small_vector<VkResourceDescriptorInfoEXT, MaxWrites> writes;\n\n    for (uint32_t i = 0u; i < bufferCount; i++) {\n      auto& buffer = bufferInfos[i];\n\n      hostRanges.push_back(descriptors[i].getHostAddressRange());\n\n      auto& bufferRange = bufferRanges.emplace_back();\n      bufferRange.address = buffer.gpuAddress;\n      bufferRange.size = buffer.size;\n\n      auto& write = writes.emplace_back();\n      write.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT;\n      write.type = VkDescriptorType(buffer.descriptorType);\n\n      if (buffer.size)\n        write.data.pAddressRange = &bufferRange;\n\n      if (writes.size() == MaxWrites || i + 1u == bufferCount) {\n        worker->m_vkd->vkWriteResourceDescriptorsEXT(worker->m_vkd->device(),\n          writes.size(), writes.data(), hostRanges.data());\n\n        hostRanges.clear();\n        bufferRanges.clear();\n        writes.clear();\n      }\n    }\n  }\n\n\n  void DxvkDescriptorCopyWorker::writeBufferDescriptorsGetDescriptorExt(\n    const DxvkDescriptorCopyWorker* worker,\n          DxvkDescriptor*           descriptors,\n          uint32_t                  bufferCount,\n    const DxvkDescriptorCopyBuffer* bufferInfos) {\n    for (uint32_t i = 0u; i < bufferCount; i++) {\n      auto& descriptor = descriptors[i];\n      auto& buffer = bufferInfos[i];\n\n      VkDescriptorAddressInfoEXT bufferInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT };\n      bufferInfo.address = buffer.gpuAddress;\n      bufferInfo.range = buffer.size;\n\n      VkDescriptorGetInfoEXT descriptorInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };\n      descriptorInfo.type = VkDescriptorType(buffer.descriptorType);\n\n      if (bufferInfo.range)\n        descriptorInfo.data.pUniformBuffer = &bufferInfo;\n\n      VkDeviceSize descriptorSize = worker->m_device->getDescriptorProperties().getDescriptorTypeInfo(descriptorInfo.type).size;\n\n      worker->m_vkd->vkGetDescriptorEXT(worker->m_vkd->device(),\n        &descriptorInfo, descriptorSize, descriptor.descriptor.data());\n    }\n  }\n\n\n  void DxvkDescriptorCopyWorker::writeBufferDescriptorsSteamDeck(\n    const DxvkDescriptorCopyWorker* worker,\n          DxvkDescriptor*           descriptors,\n          uint32_t                  bufferCount,\n    const DxvkDescriptorCopyBuffer* bufferInfos) {\n    for (uint32_t i = 0u; i < bufferCount; i++) {\n      auto& descriptor = descriptors[i];\n      auto& buffer = bufferInfos[i];\n\n      std::array<uint32_t, 4u> rawDescriptor = { };\n\n      if (buffer.size) {\n        rawDescriptor[0u] = uint32_t(buffer.gpuAddress);\n        rawDescriptor[1u] = uint32_t(buffer.gpuAddress >> 32u) & 0xffffu;\n        rawDescriptor[2u] = buffer.size;\n        rawDescriptor[3u] = 0x31016facu; /* don't ask */\n      }\n\n      std::memcpy(descriptor.descriptor.data(), rawDescriptor.data(), sizeof(rawDescriptor));\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_descriptor_worker.h",
    "content": "#pragma once\n\n#include <array>\n\n#include \"dxvk_descriptor_heap.h\"\n#include \"dxvk_pipelayout.h\"\n\n#include \"../util/thread.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n  class DxvkDescriptorCopyWorker;\n\n  /**\n   * \\brief Buffer descriptor update\n   *\n   * Stores the address range and descriptor\n   * metadata for a uniform buffer update.\n   */\n  struct DxvkDescriptorCopyBuffer {\n    VkDeviceAddress gpuAddress      = 0u;\n    uint32_t        size            = 0u;\n    uint16_t        indexInSet      = 0u;\n    uint16_t        descriptorType  = 0u;\n  };\n\n\n  /**\n   * \\brief Descriptor copy metadata\n   *\n   * Stores info for a single descriptor set update.\n   */\n  struct DxvkDescriptorCopyRange {\n    const DxvkDescriptorSetLayout* layout = nullptr;\n    void*    descriptorMemory = nullptr;\n    uint32_t descriptorCount = 0u;\n    uint32_t bufferCount = 0u;\n  };\n\n\n  /**\n   * \\brief Allocated descriptor copy\n   *\n   * Stores pointers to the allocated descriptor and\n   * buffer arrays. Both arrays are tightly packed.\n   */\n  struct DxvkDescriptorCopy {\n    const DxvkDescriptor** descriptors = nullptr;\n    DxvkDescriptorCopyBuffer* buffers = nullptr;\n  };\n\n\n  /** Function type to process buffer descriptors */\n  using WriteBufferDescriptorsFn = void (const DxvkDescriptorCopyWorker*, DxvkDescriptor*, uint32_t, const DxvkDescriptorCopyBuffer*);\n\n\n  /**\n   * \\brief Descriptor copy worker\n   *\n   * Off-loads descriptor uploads to a worker thread using a small\n   * ring buffer. This is useful for moving the API call overhead\n   * from uniform buffer updates away from the main worker thread,\n   * without adding much latency to the command submission.\n   */\n  class DxvkDescriptorCopyWorker {\n    constexpr static size_t DescriptorCount = 4096u;\n    constexpr static size_t RangeCount      = 256u;\n    constexpr static size_t BlockCount      = 4u;\n  public:\n\n    DxvkDescriptorCopyWorker(const Rc<DxvkDevice>& device);\n\n    ~DxvkDescriptorCopyWorker();\n\n    /**\n     * \\brief Allocates ring buffer entry for a descriptor update\n     *\n     * \\param [in] layout Descriptor set layout\n     * \\param [in] descriptorMemory Allocated descriptor storage\n     * \\param [in] descriptorCount Total number of descriptors\n     * \\param [in] bufferCount Number of uniform buffer descriptors\n     * \\returns Allocated descriptor update range\n     */\n    DxvkDescriptorCopy allocEntry(\n      const DxvkDescriptorSetLayout*  layout,\n            void*                     descriptorMemory,\n            uint32_t                  descriptorCount,\n            uint32_t                  bufferCount) {\n      auto* block = getBlock();\n\n      if (block->descriptorCount + descriptorCount > DescriptorCount || block->rangeCount == RangeCount)\n        block = flushBlock();\n\n      DxvkDescriptorCopy result = { };\n      result.descriptors = &block->descriptors[block->descriptorCount];\n      result.buffers = &block->buffers[block->bufferCount];\n\n      block->descriptorCount += descriptorCount;\n      block->bufferCount += bufferCount;\n\n      auto& range = block->ranges[block->rangeCount++];\n      range.layout = layout;\n      range.descriptorMemory = descriptorMemory;\n      range.descriptorCount = descriptorCount;\n      range.bufferCount = bufferCount;\n      return result;\n    }\n\n    /**\n     * \\brief Flushes pending copies and retrieves sync handle\n     *\n     * The sync point can be used to ensure that all previously recorded\n     * descriptor updates complete before submitting a command list.\n     * \\returns Sync handle\n     */\n    sync::SyncPoint getSyncHandle() {\n      flushBlock();\n\n      return sync::SyncPoint(\n        m_consumeFence,\n        m_appendFence->value());\n    }\n\n  private:\n\n    Rc<DxvkDevice>    m_device;\n    Rc<vk::DeviceFn>  m_vkd;\n\n    Rc<sync::Fence> m_appendFence;\n    Rc<sync::Fence> m_consumeFence;\n\n    WriteBufferDescriptorsFn* m_writeBufferDescriptorsFn = nullptr;\n\n    struct alignas(CACHE_LINE_SIZE) Block {\n      size_t descriptorCount  = 0u;\n      size_t bufferCount      = 0u;\n      size_t rangeCount       = 0u;\n\n      std::array<const DxvkDescriptor*,     DescriptorCount> descriptors  = { };\n      std::array<DxvkDescriptorCopyBuffer,  DescriptorCount> buffers      = { };\n      std::array<DxvkDescriptorCopyRange,   RangeCount>      ranges       = { };\n    };\n\n    std::array<Block, BlockCount> m_blocks = { };\n    size_t                        m_blockIndex = 0u;\n\n    std::thread m_thread;\n\n    Block* getBlock() {\n      return &m_blocks[m_blockIndex];\n    }\n\n    Block* flushBlock();\n\n    WriteBufferDescriptorsFn* getWriteBufferDescriptorFn() const;\n\n    void processBlock(Block& block);\n\n    void runWorker();\n\n    static void writeBufferDescriptorsGeneric(\n      const DxvkDescriptorCopyWorker* worker,\n            DxvkDescriptor*           descriptors,\n            uint32_t                  bufferCount,\n      const DxvkDescriptorCopyBuffer* bufferInfos);\n\n    static void writeBufferDescriptorsGetDescriptorExt(\n      const DxvkDescriptorCopyWorker* worker,\n            DxvkDescriptor*           descriptors,\n            uint32_t                  bufferCount,\n      const DxvkDescriptorCopyBuffer* bufferInfos);\n\n    static void writeBufferDescriptorsSteamDeck(\n      const DxvkDescriptorCopyWorker* worker,\n            DxvkDescriptor*           descriptors,\n            uint32_t                  bufferCount,\n      const DxvkDescriptorCopyBuffer* bufferInfos);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_device.cpp",
    "content": "#include \"dxvk_device.h\"\n#include \"dxvk_instance.h\"\n#include \"dxvk_latency_builtin.h\"\n#include \"dxvk_latency_reflex.h\"\n#include \"dxvk_shader_cache.h\"\n#include \"dxvk_shader_ir.h\"\n\nnamespace dxvk {\n  \n  DxvkDevice::DxvkDevice(\n    const Rc<DxvkInstance>&         instance,\n    const Rc<DxvkAdapter>&          adapter,\n    const Rc<vk::DeviceFn>&         vkd,\n    const DxvkDeviceFeatures&       features,\n    const DxvkDeviceQueueSet&       queues,\n    const DxvkQueueCallback&        queueCallback)\n  : m_options           (instance->options()),\n    m_instance          (instance),\n    m_adapter           (adapter),\n    m_vkd               (vkd),\n    m_debugFlags        (instance->debugFlags()),\n    m_queues            (queues),\n    m_features          (features),\n    m_properties        (adapter->deviceProperties()),\n    m_perfHints         (getPerfHints()),\n    m_objects           (this),\n    m_submissionQueue   (this, queueCallback) {\n\n    if (adapter->kmtLocal()) {\n      D3DKMT_CREATEDEVICE create = { };\n      create.hAdapter = adapter->kmtLocal();\n      if (D3DKMTCreateDevice(&create))\n        Logger::warn(\"Failed to create D3DKMT device\");\n      else\n        m_kmtLocal = create.hDevice;\n    }\n\n    determineShaderOptions();\n\n    if (env::getEnvVar(\"DXVK_SHADER_CACHE\") != \"0\" && DxvkShader::getShaderDumpPath().empty())\n      m_shaderCache = DxvkShaderCache::getInstance();\n\n    logBindingModel();\n  }\n  \n  \n  DxvkDevice::~DxvkDevice() {\n    if (m_kmtLocal) {\n      D3DKMT_DESTROYDEVICE destroy = { };\n      destroy.hDevice = m_kmtLocal;\n      D3DKMTDestroyDevice(&destroy);\n    }\n\n    // If we are being destroyed during/after DLL process detachment\n    // from TerminateProcess, etc, our CS threads are already destroyed\n    // and we cannot synchronize against them.\n    // The best we can do is just wait for the Vulkan device to be idle.\n    if (this_thread::isInModuleDetachment())\n      return;\n\n    // Wait for all pending Vulkan commands to be\n    // executed before we destroy any resources.\n    this->waitForIdle();\n\n    // Stop workers explicitly in order to prevent\n    // access to structures that are being destroyed.\n    m_objects.pipelineManager().stopWorkerThreads();\n  }\n\n\n  VkSubresourceLayout DxvkDevice::queryImageSubresourceLayout(\n    const DxvkImageCreateInfo&        createInfo,\n    const VkImageSubresource&         subresource) {\n    VkImageFormatListCreateInfo formatList = { VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO };\n\n    VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };\n    info.flags = createInfo.flags;\n    info.imageType = createInfo.type;\n    info.format = createInfo.format;\n    info.extent = createInfo.extent;\n    info.mipLevels = createInfo.mipLevels;\n    info.arrayLayers = createInfo.numLayers;\n    info.samples = createInfo.sampleCount;\n    info.tiling = VK_IMAGE_TILING_LINEAR;\n    info.usage = createInfo.usage;\n    info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n    info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;\n\n    if (createInfo.viewFormatCount && (createInfo.viewFormatCount > 1u || createInfo.viewFormats[0] != createInfo.format)) {\n      formatList.viewFormatCount = createInfo.viewFormatCount;\n      formatList.pViewFormats = createInfo.viewFormats;\n\n      info.pNext = &formatList;\n    }\n\n    VkImageSubresource2KHR subresourceInfo = { VK_STRUCTURE_TYPE_IMAGE_SUBRESOURCE_2_KHR };\n    subresourceInfo.imageSubresource = subresource;\n\n    VkDeviceImageSubresourceInfoKHR query = { VK_STRUCTURE_TYPE_DEVICE_IMAGE_SUBRESOURCE_INFO_KHR };\n    query.pCreateInfo = &info;\n    query.pSubresource = &subresourceInfo;\n\n    VkSubresourceLayout2KHR layout = { VK_STRUCTURE_TYPE_SUBRESOURCE_LAYOUT_2_KHR };\n    m_vkd->vkGetDeviceImageSubresourceLayoutKHR(m_vkd->device(), &query, &layout);\n    return layout.subresourceLayout;\n  }\n\n\n  bool DxvkDevice::isUnifiedMemoryArchitecture() const {\n    return m_adapter->isUnifiedMemoryArchitecture();\n  }\n\n\n  bool DxvkDevice::canUseGraphicsPipelineLibrary() const {\n    // Without graphicsPipelineLibraryIndependentInterpolationDecoration, we\n    // cannot use this effectively in many games since no client API provides\n    // interpoation qualifiers in vertex shaders.\n    return m_features.extGraphicsPipelineLibrary.graphicsPipelineLibrary\n        && m_properties.extGraphicsPipelineLibrary.graphicsPipelineLibraryIndependentInterpolationDecoration\n        && m_options.enableGraphicsPipelineLibrary != Tristate::False;\n  }\n\n\n  bool DxvkDevice::canUsePipelineCacheControl() const {\n    // Don't bother with this unless the device also supports shader module\n    // identifiers, since decoding and hashing the shaders is slow otherwise\n    // and likely provides no benefit over linking pipeline libraries.\n    return m_features.vk13.pipelineCreationCacheControl\n        && m_features.extShaderModuleIdentifier.shaderModuleIdentifier\n        && m_options.enableGraphicsPipelineLibrary != Tristate::True;\n  }\n\n\n  bool DxvkDevice::canUseSampleLocations(VkSampleCountFlags samples) const {\n    return (m_features.extSampleLocations)\n        && (m_features.extExtendedDynamicState3.extendedDynamicState3SampleLocationsEnable)\n        && (m_properties.extSampleLocations.variableSampleLocations)\n        && (m_properties.extSampleLocations.sampleLocationSampleCounts & samples) == samples;\n  }\n\n\n  bool DxvkDevice::mustTrackPipelineLifetime() const {\n    switch (m_options.trackPipelineLifetime) {\n      case Tristate::True:\n        return canUseGraphicsPipelineLibrary();\n\n      case Tristate::False:\n        return false;\n\n      default:\n      case Tristate::Auto:\n        if (!env::is32BitHostPlatform() || !canUseGraphicsPipelineLibrary())\n          return false;\n\n        // Disable lifetime tracking for drivers that do not have any\n        // significant issues with 32-bit address space to begin with\n        if (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR))\n          return false;\n\n        return true;\n    }\n  }\n\n\n  DxvkFramebufferSize DxvkDevice::getDefaultFramebufferSize() const {\n    return DxvkFramebufferSize {\n      m_properties.core.properties.limits.maxFramebufferWidth,\n      m_properties.core.properties.limits.maxFramebufferHeight,\n      m_properties.core.properties.limits.maxFramebufferLayers };\n  }\n\n\n  VkPipelineStageFlags DxvkDevice::getShaderPipelineStages() const {\n    VkPipelineStageFlags result = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT\n                                | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT\n                                | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n    \n    if (m_features.core.features.geometryShader)\n      result |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;\n    \n    if (m_features.core.features.tessellationShader) {\n      result |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT\n             |  VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;\n    }\n\n    return result;\n  }\n  \n  \n  Rc<DxvkCommandList> DxvkDevice::createCommandList() {\n    Rc<DxvkCommandList> cmdList = m_recycledCommandLists.retrieveObject();\n    \n    if (cmdList == nullptr)\n      cmdList = new DxvkCommandList(this);\n    \n    return cmdList;\n  }\n\n\n  Rc<DxvkContext> DxvkDevice::createContext() {\n    return new DxvkContext(this);\n  }\n\n\n  Rc<DxvkEvent> DxvkDevice::createGpuEvent() {\n    return new DxvkEvent(this);\n  }\n\n\n  Rc<DxvkQuery> DxvkDevice::createGpuQuery(\n          VkQueryType           type,\n          VkQueryControlFlags   flags,\n          uint32_t              index) {\n    return new DxvkQuery(this, type, flags, index);\n  }\n\n\n  Rc<DxvkGpuQuery> DxvkDevice::createRawQuery(\n          VkQueryType           type) {\n    return m_objects.queryPool().allocQuery(type);\n  }\n\n\n  Rc<DxvkFence> DxvkDevice::createFence(\n    const DxvkFenceCreateInfo& fenceInfo) {\n    return new DxvkFence(this, fenceInfo);\n  }\n\n\n  Rc<DxvkBuffer> DxvkDevice::createBuffer(\n    const DxvkBufferCreateInfo& createInfo,\n          VkMemoryPropertyFlags memoryType) {\n    return new DxvkBuffer(this, createInfo, m_objects.memoryManager(), memoryType);\n  }\n  \n  \n  Rc<DxvkImage> DxvkDevice::createImage(\n    const DxvkImageCreateInfo&  createInfo,\n          VkMemoryPropertyFlags memoryType) {\n    return new DxvkImage(this, createInfo, m_objects.memoryManager(), memoryType);\n  }\n  \n  \n  Rc<DxvkSampler> DxvkDevice::createSampler(\n    const DxvkSamplerKey&         createInfo) {\n    return m_objects.samplerPool().createSampler(createInfo);\n  }\n\n\n  DxvkLocalAllocationCache DxvkDevice::createAllocationCache(\n          VkBufferUsageFlags    bufferUsage,\n          VkMemoryPropertyFlags propertyFlags) {\n    return m_objects.memoryManager().createAllocationCache(bufferUsage, propertyFlags);\n  }\n\n\n  Rc<DxvkSparsePageAllocator> DxvkDevice::createSparsePageAllocator() {\n    return new DxvkSparsePageAllocator(m_objects.memoryManager());\n  }\n\n\n  const DxvkPipelineLayout* DxvkDevice::createBuiltInPipelineLayout(\n          DxvkPipelineLayoutFlags         flags,\n          VkShaderStageFlags              pushDataStages,\n          VkDeviceSize                    pushDataSize,\n          uint32_t                        bindingCount,\n    const DxvkDescriptorSetLayoutBinding* bindings) {\n    DxvkPipelineLayoutKey key(DxvkPipelineLayoutType::BuiltIn, flags);\n\n    if (pushDataSize) {\n      key.addStages(pushDataStages);\n\n      DxvkPushDataBlock pushData(pushDataStages,\n        0u, pushDataSize, sizeof(uint32_t), 0u);\n\n      key.addPushData(pushData);\n    }\n\n    if (bindingCount) {\n      DxvkDescriptorSetLayoutKey setLayoutKey;\n\n      for (uint32_t i = 0; i < bindingCount; i++) {\n        key.addStages(bindings[i].getStageMask());\n        setLayoutKey.add(bindings[i]);\n      }\n\n      const auto* layout = m_objects.pipelineManager().createDescriptorSetLayout(setLayoutKey);\n      key.setDescriptorSetLayouts(1, &layout);\n    }\n\n    return m_objects.pipelineManager().createPipelineLayout(key);\n  }\n\n\n  VkPipeline DxvkDevice::createBuiltInComputePipeline(\n    const DxvkPipelineLayout*             layout,\n    const util::DxvkBuiltInShaderStage&   stage) {\n    auto mappingInfo = layout->getMappingInfo();\n\n    VkShaderModuleCreateInfo moduleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };\n    moduleInfo.codeSize = stage.size;\n    moduleInfo.pCode = stage.code;\n\n    if (canUseDescriptorHeap())\n      moduleInfo.pNext = &mappingInfo;\n\n    VkPipelineCreateFlags2CreateInfo pipelineFlags = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO };\n\n    if (canUseDescriptorHeap())\n      pipelineFlags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (canUseDescriptorBuffer())\n      pipelineFlags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkComputePipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };\n    pipelineInfo.layout = layout->getPipelineLayout();\n    pipelineInfo.basePipelineIndex = -1;\n\n    if (pipelineFlags.flags)\n      pipelineFlags.pNext = std::exchange(pipelineInfo.pNext, &pipelineFlags);\n\n    VkPipelineShaderStageCreateInfo& stageInfo = pipelineInfo.stage;\n    stageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, &moduleInfo };\n    stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;\n    stageInfo.pName = \"main\";\n    stageInfo.pSpecializationInfo = stage.spec;\n\n    VkPipeline pipeline = VK_NULL_HANDLE;\n\n    VkResult vr = m_vkd->vkCreateComputePipelines(m_vkd->device(),\n      VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);\n\n    if (vr)\n      throw DxvkError(str::format(\"Failed to create built-in compute pipeline: \", vr));\n\n    return pipeline;\n  }\n\n\n  VkPipeline DxvkDevice::createBuiltInGraphicsPipeline(\n    const DxvkPipelineLayout*             layout,\n    const util::DxvkBuiltInGraphicsState& state) {\n    constexpr size_t MaxStages = 3u;\n\n    auto mappingInfo = layout->getMappingInfo();\n\n    // Build shader stage infos\n    small_vector<std::pair<VkShaderStageFlagBits, util::DxvkBuiltInShaderStage>, MaxStages> stages;\n\n    if (state.vs.code) stages.push_back({ VK_SHADER_STAGE_VERTEX_BIT,   state.vs });\n    if (state.gs.code) stages.push_back({ VK_SHADER_STAGE_GEOMETRY_BIT, state.gs });\n    if (state.fs.code) stages.push_back({ VK_SHADER_STAGE_FRAGMENT_BIT, state.fs });\n\n    small_vector<VkShaderModuleCreateInfo, MaxStages> moduleInfos;\n\n    for (size_t i = 0; i < stages.size(); i++) {\n      auto& info = moduleInfos.emplace_back();\n      info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n      info.codeSize = stages[i].second.size;\n      info.pCode = stages[i].second.code;\n\n      if (canUseDescriptorHeap())\n        info.pNext = &mappingInfo;\n    }\n\n    small_vector<VkPipelineShaderStageCreateInfo, MaxStages> stageInfos;\n\n    for (size_t i = 0; i < stages.size(); i++) {\n      auto& info = stageInfos.emplace_back();\n      info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n      info.pNext = &moduleInfos[i];\n      info.stage = stages[i].first;\n      info.pName = \"main\";\n      info.pSpecializationInfo = stages[i].second.spec;\n    }\n\n    // Attachment format infos, useful to set up state\n    auto depthFormatInfo = lookupFormatInfo(state.depthFormat);\n\n    // Default vertex input state\n    VkPipelineVertexInputStateCreateInfo viState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };\n\n    // Default input assembly state using triangle list\n    VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };\n    iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n\n    // Default viewport state, needs to be defined even if everything is dynamic\n    VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };\n\n    // Default rasterization state\n    VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };\n    rsState.cullMode          = VK_CULL_MODE_NONE;\n    rsState.frontFace         = VK_FRONT_FACE_COUNTER_CLOCKWISE;\n    rsState.polygonMode       = VK_POLYGON_MODE_FILL;\n    rsState.depthClampEnable  = state.depthFormat != VK_FORMAT_UNDEFINED;\n    rsState.lineWidth         = 1.0f;\n\n    // Multisample state. Enables rendering to all samples at once.\n    uint32_t sampleMask = (1u << uint32_t(state.sampleCount)) - 1u;\n\n    VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };\n    msState.rasterizationSamples  = state.sampleCount;\n    msState.pSampleMask           = &sampleMask;\n    msState.sampleShadingEnable   = VK_FALSE;\n    msState.minSampleShading      = 1.0f;\n\n    // Default depth-stencil state, enables depth and stencil write-through\n    VkPipelineDepthStencilStateCreateInfo dsState = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };\n\n    if (state.depthFormat && (depthFormatInfo->aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)) {\n      dsState.depthTestEnable   = VK_TRUE;\n      dsState.depthWriteEnable  = VK_TRUE;\n      dsState.depthCompareOp    = VK_COMPARE_OP_ALWAYS;\n    }\n\n    if (state.depthFormat && (depthFormatInfo->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)) {\n      VkStencilOpState stencil = { };\n      stencil.passOp      = VK_STENCIL_OP_REPLACE;\n      stencil.failOp      = VK_STENCIL_OP_REPLACE;\n      stencil.depthFailOp = VK_STENCIL_OP_REPLACE;\n      stencil.compareOp   = VK_COMPARE_OP_ALWAYS;\n      stencil.compareMask = 0xffffffffu;\n      stencil.writeMask   = 0xffffffffu;\n\n      dsState.stencilTestEnable = VK_TRUE;\n      dsState.front       = stencil;\n      dsState.back        = stencil;\n    }\n\n    // Default blend state, only used if color attachments are present\n    VkPipelineColorBlendAttachmentState cbAttachment = { };\n    cbAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT\n                                | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n\n    VkPipelineColorBlendStateCreateInfo cbState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };\n\n    if (state.colorFormat) {\n      cbState.attachmentCount = 1u;\n      cbState.pAttachments = state.cbAttachment ? state.cbAttachment : &cbAttachment;\n    }\n\n    // Prepare dynamic states\n    small_vector<VkDynamicState, 4> dynamicStates;\n    dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT);\n\n    for (uint32_t i = 0; i < state.dynamicStateCount; i++)\n      dynamicStates.push_back(state.dynamicStates[i]);\n\n    VkPipelineDynamicStateCreateInfo dyState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };\n    dyState.dynamicStateCount = dynamicStates.size();\n    dyState.pDynamicStates = dynamicStates.data();\n\n    // Build rendering attachment info\n    VkPipelineRenderingCreateInfo renderingInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };\n\n    if (state.colorFormat) {\n      renderingInfo.colorAttachmentCount = 1u;\n      renderingInfo.pColorAttachmentFormats = &state.colorFormat;\n    }\n\n    if (state.depthFormat && (depthFormatInfo->aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT))\n      renderingInfo.depthAttachmentFormat = state.depthFormat;\n\n    if (state.depthFormat && (depthFormatInfo->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT))\n      renderingInfo.stencilAttachmentFormat = state.depthFormat;\n\n    VkPipelineCreateFlags2CreateInfo pipelineFlags = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO };\n\n    if (canUseDescriptorHeap())\n      pipelineFlags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (canUseDescriptorBuffer())\n      pipelineFlags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &renderingInfo };\n    pipelineInfo.stageCount = stageInfos.size();\n    pipelineInfo.pStages = stageInfos.data();\n    pipelineInfo.pVertexInputState = state.viState ? state.viState : &viState;\n    pipelineInfo.pInputAssemblyState = state.iaState ? state.iaState : &iaState;\n    pipelineInfo.pViewportState = &vpState;\n    pipelineInfo.pRasterizationState = state.rsState ? state.rsState : &rsState;\n    pipelineInfo.pMultisampleState = &msState;\n    pipelineInfo.pDepthStencilState = state.depthFormat ? (state.dsState ? state.dsState : &dsState) : nullptr;\n    pipelineInfo.pColorBlendState = state.colorFormat ? &cbState : nullptr;\n    pipelineInfo.pDynamicState = &dyState;\n    pipelineInfo.layout = layout->getPipelineLayout();\n    pipelineInfo.basePipelineIndex = -1;\n\n    if (pipelineFlags.flags)\n      pipelineFlags.pNext = std::exchange(pipelineInfo.pNext, &pipelineFlags);\n\n    VkPipeline pipeline = VK_NULL_HANDLE;\n\n    VkResult vr = m_vkd->vkCreateGraphicsPipelines(m_vkd->device(),\n      VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);\n\n    if (vr)\n      throw DxvkError(str::format(\"Failed to create built-in graphics pipeline: \", vr));\n\n    return pipeline;\n  }\n\n\n  DxvkStatCounters DxvkDevice::getStatCounters() {\n    DxvkPipelineCount pipe = m_objects.pipelineManager().getPipelineCount();\n    DxvkPipelineWorkerStats workers = m_objects.pipelineManager().getWorkerStats();\n    \n    DxvkStatCounters result;\n    result.setCtr(DxvkStatCounter::PipeCountGraphics, pipe.numGraphicsPipelines);\n    result.setCtr(DxvkStatCounter::PipeCountLibrary,  pipe.numGraphicsLibraries);\n    result.setCtr(DxvkStatCounter::PipeCountCompute,  pipe.numComputePipelines);\n    result.setCtr(DxvkStatCounter::PipeTasksDone,     workers.tasksCompleted);\n    result.setCtr(DxvkStatCounter::PipeTasksTotal,    workers.tasksTotal);\n    result.setCtr(DxvkStatCounter::GpuIdleTicks,      m_submissionQueue.gpuIdleTicks());\n\n    std::lock_guard<sync::Spinlock> lock(m_statLock);\n    result.merge(m_statCounters);\n    return result;\n  }\n  \n  \n  Rc<DxvkShader> DxvkDevice::createCachedShader(\n    const std::string&                    name,\n    const DxvkIrShaderCreateInfo&         createInfo,\n    const Rc<DxvkIrShaderConverter>&      converter) {\n    Rc<DxvkIrShader> shader = nullptr;\n\n    if (m_shaderCache && !converter)\n      shader = m_shaderCache->lookupShader(name, createInfo);\n\n    if (!shader && converter) {\n      shader = new DxvkIrShader(createInfo, converter);\n\n      if (m_shaderCache)\n        m_shaderCache->addShader(shader);\n    }\n\n    return shader;\n  }\n\n\n  Rc<DxvkBuffer> DxvkDevice::importBuffer(\n    const DxvkBufferCreateInfo& createInfo,\n    const DxvkBufferImportInfo& importInfo,\n          VkMemoryPropertyFlags memoryType) {\n    return new DxvkBuffer(this, createInfo,\n      importInfo, m_objects.memoryManager(), memoryType);\n  }\n\n\n  Rc<DxvkImage> DxvkDevice::importImage(\n    const DxvkImageCreateInfo&  createInfo,\n          VkImage               image,\n          VkMemoryPropertyFlags memoryType) {\n    return new DxvkImage(this, createInfo, image,\n      m_objects.memoryManager(), memoryType);\n  }\n\n\n  DxvkMemoryStats DxvkDevice::getMemoryStats(uint32_t heap) {\n    return m_objects.memoryManager().getMemoryStats(heap);\n  }\n\n\n  DxvkSharedAllocationCacheStats DxvkDevice::getMemoryAllocationStats(DxvkMemoryAllocationStats& stats) {\n    m_objects.memoryManager().getAllocationStats(stats);\n    return m_objects.memoryManager().getAllocationCacheStats();\n  }\n\n\n  uint32_t DxvkDevice::getCurrentFrameId() const {\n    return m_statCounters.getCtr(DxvkStatCounter::QueuePresentCount);\n  }\n  \n  \n  void DxvkDevice::registerShader(const Rc<DxvkShader>& shader) {\n    m_objects.pipelineManager().registerShader(shader);\n  }\n  \n  \n  void DxvkDevice::requestCompileShader(\n    const Rc<DxvkShader>&           shader) {\n    m_objects.pipelineManager().requestCompileShader(shader);\n  }\n\n\n  Rc<DxvkLatencyTracker> DxvkDevice::createLatencyTracker(\n    const Rc<Presenter>&            presenter) {\n    if (m_options.latencySleep == Tristate::False)\n      return nullptr;\n\n    if (m_options.latencySleep == Tristate::Auto) {\n      if (m_features.nvLowLatency2)\n        return new DxvkReflexLatencyTrackerNv(presenter);\n      else\n        return nullptr;\n    }\n\n    return new DxvkBuiltInLatencyTracker(presenter,\n      m_options.latencyTolerance, m_features.nvLowLatency2);\n  }\n\n\n  void DxvkDevice::presentImage(\n    const Rc<Presenter>&            presenter,\n    const Rc<DxvkLatencyTracker>&   tracker,\n          uint64_t                  frameId,\n          DxvkSubmitStatus*         status) {\n    DxvkPresentInfo presentInfo = { };\n    presentInfo.presenter = presenter;\n    presentInfo.frameId = frameId;\n\n    DxvkLatencyInfo latencyInfo;\n    latencyInfo.tracker = tracker;\n    latencyInfo.frameId = frameId;\n\n    m_submissionQueue.present(presentInfo, latencyInfo, status);\n    \n    std::lock_guard<sync::Spinlock> statLock(m_statLock);\n    m_statCounters.addCtr(DxvkStatCounter::QueuePresentCount, 1);\n  }\n\n\n  void DxvkDevice::submitCommandList(\n    const Rc<DxvkCommandList>&      commandList,\n    const Rc<DxvkLatencyTracker>&   tracker,\n          uint64_t                  frameId,\n          DxvkSubmitStatus*         status) {\n    DxvkSubmitInfo submitInfo = { };\n    submitInfo.cmdList = commandList;\n\n    DxvkLatencyInfo latencyInfo;\n    latencyInfo.tracker = tracker;\n    latencyInfo.frameId = frameId;\n\n    m_submissionQueue.submit(submitInfo, latencyInfo, status);\n\n    std::lock_guard<sync::Spinlock> statLock(m_statLock);\n    m_statCounters.merge(commandList->statCounters());\n  }\n  \n  \n  VkResult DxvkDevice::waitForSubmission(DxvkSubmitStatus* status) {\n    VkResult result = status->result.load();\n\n    if (result == VK_NOT_READY) {\n      m_submissionQueue.synchronizeSubmission(status);\n      result = status->result.load();\n    }\n\n    return result;\n  }\n\n\n  void DxvkDevice::waitForFence(sync::Fence& fence, uint64_t value) {\n    if (fence.value() >= value)\n      return;\n\n    auto t0 = dxvk::high_resolution_clock::now();\n\n    fence.wait(value);\n\n    auto t1 = dxvk::high_resolution_clock::now();\n    auto us = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0);\n\n    m_statCounters.addCtr(DxvkStatCounter::GpuSyncCount, 1);\n    m_statCounters.addCtr(DxvkStatCounter::GpuSyncTicks, us.count());\n  }\n\n\n  void DxvkDevice::waitForResource(const DxvkPagedResource& resource, DxvkAccess access) {\n    if (resource.isInUse(access)) {\n      auto t0 = dxvk::high_resolution_clock::now();\n\n      m_submissionQueue.synchronizeUntil([&resource, access] {\n        return !resource.isInUse(access);\n      });\n\n      auto t1 = dxvk::high_resolution_clock::now();\n      auto us = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0);\n\n      std::lock_guard<sync::Spinlock> lock(m_statLock);\n      m_statCounters.addCtr(DxvkStatCounter::GpuSyncCount, 1);\n      m_statCounters.addCtr(DxvkStatCounter::GpuSyncTicks, us.count());\n    }\n  }\n  \n  \n  void DxvkDevice::waitForIdle() {\n    m_submissionQueue.waitForIdle();\n    m_submissionQueue.lockDeviceQueue();\n\n    if (m_vkd->vkDeviceWaitIdle(m_vkd->device()) != VK_SUCCESS)\n      Logger::err(\"DxvkDevice: waitForIdle: Operation failed\");\n\n    m_submissionQueue.unlockDeviceQueue();\n  }\n  \n  \n  DxvkDevicePerfHints DxvkDevice::getPerfHints() {\n    DxvkDevicePerfHints hints;\n\n    // RADV properly fuses depth-stencil copies now\n    hints.preferFbDepthStencilCopy = m_features.extShaderStencilExport\n      && (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV_KHR, Version(), Version(25, 3, 99))\n       || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR)\n       || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY_KHR));\n\n    // Older Nvidia drivers sometimes use the wrong format\n    // to interpret the clear color in render pass clears.\n    hints.renderPassClearFormatBug = m_adapter->matchesDriver(\n      VK_DRIVER_ID_NVIDIA_PROPRIETARY, Version(), Version(560, 28, 3));\n\n    // There's a similar bug that affects resolve attachments\n    hints.renderPassResolveFormatBug = m_adapter->matchesDriver(\n      VK_DRIVER_ID_NVIDIA_PROPRIETARY);\n\n    // On tilers we need to respect render passes some more. Most of\n    // these drivers probably can't run DXVK anyway, but might as well\n    bool tilerMode = m_adapter->matchesDriver(VK_DRIVER_ID_MESA_TURNIP)\n                  || m_adapter->matchesDriver(VK_DRIVER_ID_QUALCOMM_PROPRIETARY)\n                  || m_adapter->matchesDriver(VK_DRIVER_ID_MESA_HONEYKRISP)\n                  || m_adapter->matchesDriver(VK_DRIVER_ID_MOLTENVK)\n                  || m_adapter->matchesDriver(VK_DRIVER_ID_MESA_PANVK)\n                  || m_adapter->matchesDriver(VK_DRIVER_ID_ARM_PROPRIETARY)\n                  || m_adapter->matchesDriver(VK_DRIVER_ID_MESA_V3DV)\n                  || m_adapter->matchesDriver(VK_DRIVER_ID_BROADCOM_PROPRIETARY)\n                  || m_adapter->matchesDriver(VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA)\n                  || m_adapter->matchesDriver(VK_DRIVER_ID_IMAGINATION_PROPRIETARY);\n\n    applyTristate(tilerMode, m_options.tilerMode);\n    hints.preferRenderPassOps = tilerMode;\n\n    // Honeykrisp does not have native support for secondary command buffers\n    // and would suffer from added CPU overhead, so be less aggressive.\n    // TODO: Enable ANV once mesa issue 12791 is resolved.\n    // RADV has issues on RDNA4 up to version 25.0.1.\n    hints.preferPrimaryCmdBufs = m_adapter->matchesDriver(VK_DRIVER_ID_MESA_HONEYKRISP)\n                              || m_adapter->matchesDriver(VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA)\n                              || m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV, Version(), Version(25, 0, 2));\n\n    // Compute-based mip generation has some potential for performance\n    // regressions or driver issues. Just enable it on Nvidia and RADV\n    // (GFX10+) for now, where it is proven to work.\n    hints.preferComputeMipGen = (m_adapter->matchesDriver(VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR)\n                             || (m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV)\n                              && m_adapter->deviceProperties().vk13.minSubgroupSize == 32u));\n\n    // On AMD we can expect it to be optimal to simply pass the heap offset\n    // to descriptor memory through as-is to avoid some ALU. Some other\n    // vendors are more sensitive towards knowing descriptor alignment.\n    hints.preferDescriptorByteOffsets = m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV)\n                                     || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_OPEN_SOURCE)\n                                     || m_adapter->matchesDriver(VK_DRIVER_ID_AMD_PROPRIETARY);\n\n    return hints;\n  }\n\n\n  void DxvkDevice::recycleCommandList(const Rc<DxvkCommandList>& cmdList) {\n    m_recycledCommandLists.returnObject(cmdList);\n  }\n\n\n  void DxvkDevice::determineShaderOptions() {\n    m_shaderOptions.minStorageBufferAlignment =\n      m_properties.core.properties.limits.minStorageBufferOffsetAlignment;\n\n    if (m_features.core.features.shaderInt16 && m_features.vk12.shaderFloat16)\n      m_shaderOptions.flags.set(DxvkShaderCompileFlag::Supports16BitArithmetic);\n\n    // RADV currently does not emit great code with 16-bit sampler indices\n    if (m_features.core.features.shaderInt16 && m_features.vk11.storagePushConstant16\n     && !m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV))\n      m_shaderOptions.flags.set(DxvkShaderCompileFlag::Supports16BitPushData);\n\n    // Need to tag typed storage image loads with the format on some devices\n    auto r32Features = getFormatFeatures(VK_FORMAT_R32_SFLOAT).optimal\n                     & getFormatFeatures(VK_FORMAT_R32_UINT).optimal\n                     & getFormatFeatures(VK_FORMAT_R32_SINT).optimal;\n\n    if (!(r32Features & VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT))\n      m_shaderOptions.flags.set(DxvkShaderCompileFlag::TypedR32LoadRequiresFormat);\n\n    // Intel's hardware sin/cos is so inaccurate that it causes rendering issues in some games\n    bool lowerSinCos = m_adapter->matchesDriver(VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA)\n                    || m_adapter->matchesDriver(VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS);\n    applyTristate(lowerSinCos, m_options.lowerSinCos);\n\n\n    if (lowerSinCos)\n      m_shaderOptions.flags.set(DxvkShaderCompileFlag::LowerSinCos);\n\n    // RADV generally does the right thing for f32tof16 and int conversions by default\n    if (!m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV)) {\n      m_shaderOptions.flags.set(\n        DxvkShaderCompileFlag::LowerFtoI,\n        DxvkShaderCompileFlag::LowerF32toF16);\n    }\n\n    // Converting unsigned integers to float should return an unsigned float,\n    // but Nvidia drivers prior to 580 don't agree\n    if (m_adapter->matchesDriver(VK_DRIVER_ID_NVIDIA_PROPRIETARY, Version(), Version(580u, 0u, 0u)))\n      m_shaderOptions.flags.set(DxvkShaderCompileFlag::LowerItoF);\n\n    // Forward UBO device limit as-is\n    m_shaderOptions.maxUniformBufferSize = m_properties.core.properties.limits.maxUniformBufferRange;\n    m_shaderOptions.maxUniformBufferCount = m_properties.core.properties.limits.maxPerStageDescriptorUniformBuffers < MaxNumUniformBufferSlots\n      ? int32_t(m_properties.core.properties.limits.maxPerStageDescriptorUniformBuffers)\n      : -1;\n\n    // ANV up to mesa 25.0.2 breaks when we *don't* explicitly write point size\n    if (m_adapter->matchesDriver(VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA, Version(), Version(25, 0, 3)))\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::ExportPointSize);\n\n    if (m_features.nvRawAccessChains.shaderRawAccessChains)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsNvRawAccessChains);\n\n    // Mesa drivers generally optimize large constant arrays to a buffer, some other\n    // drivers do not and suffer a significant performance loss. Enable lowering on\n    // those drivers.\n    if (!m_adapter->matchesDriver(VK_DRIVER_ID_MESA_RADV)\n     && !m_adapter->matchesDriver(VK_DRIVER_ID_MESA_NVK)\n     && !m_adapter->matchesDriver(VK_DRIVER_ID_MESA_TURNIP)\n     && !m_adapter->matchesDriver(VK_DRIVER_ID_MESA_HONEYKRISP)\n     && !m_adapter->matchesDriver(VK_DRIVER_ID_MESA_LLVMPIPE)\n     && !m_adapter->matchesDriver(VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA))\n      m_shaderOptions.flags.set(DxvkShaderCompileFlag::LowerConstantArrays);\n\n    // Set up float control feature flags\n    if (m_properties.vk12.shaderSignedZeroInfNanPreserveFloat16)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsSzInfNanPreserve16);\n    if (m_properties.vk12.shaderSignedZeroInfNanPreserveFloat32)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsSzInfNanPreserve32);\n    if (m_properties.vk12.shaderSignedZeroInfNanPreserveFloat64)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsSzInfNanPreserve64);\n\n    if (m_properties.vk12.shaderRoundingModeRTEFloat16)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsRte16);\n    if (m_properties.vk12.shaderRoundingModeRTEFloat32)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsRte32);\n    if (m_properties.vk12.shaderRoundingModeRTEFloat64)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsRte64);\n\n    if (m_properties.vk12.shaderRoundingModeRTZFloat16)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsRtz16);\n    if (m_properties.vk12.shaderRoundingModeRTZFloat32)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsRtz32);\n    if (m_properties.vk12.shaderRoundingModeRTZFloat64)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsRtz64);\n\n    if (m_properties.vk12.shaderDenormFlushToZeroFloat16)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsDenormFlush16);\n    if (m_properties.vk12.shaderDenormFlushToZeroFloat32)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsDenormFlush32);\n    if (m_properties.vk12.shaderDenormFlushToZeroFloat64)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsDenormFlush64);\n\n    if (m_properties.vk12.shaderDenormPreserveFloat16)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsDenormPreserve16);\n    if (m_properties.vk12.shaderDenormPreserveFloat32)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsDenormPreserve32);\n    if (m_properties.vk12.shaderDenormPreserveFloat64)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsDenormPreserve64);\n\n    if (m_properties.vk12.roundingModeIndependence != VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::IndependentRoundMode);\n\n    if (m_properties.vk12.denormBehaviorIndependence != VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::IndependentDenormMode);\n\n    if (m_features.khrShaderFloatControls2.shaderFloatControls2)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsFloatControls2);\n\n    // Set up resource indexing flags\n    if (m_features.core.features.shaderUniformBufferArrayDynamicIndexing &&\n        m_features.core.features.shaderSampledImageArrayDynamicIndexing &&\n        m_features.core.features.shaderStorageBufferArrayDynamicIndexing &&\n        m_features.core.features.shaderStorageImageArrayDynamicIndexing &&\n        m_features.vk12.shaderUniformTexelBufferArrayDynamicIndexing &&\n        m_features.vk12.shaderStorageTexelBufferArrayDynamicIndexing &&\n        m_features.vk12.shaderUniformBufferArrayNonUniformIndexing &&\n        m_features.vk12.shaderSampledImageArrayNonUniformIndexing &&\n        m_features.vk12.shaderStorageBufferArrayNonUniformIndexing &&\n        m_features.vk12.shaderStorageImageArrayNonUniformIndexing &&\n        m_features.vk12.shaderUniformTexelBufferArrayNonUniformIndexing &&\n        m_features.vk12.shaderStorageTexelBufferArrayNonUniformIndexing)\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsResourceIndexing);\n\n    // Descriptor heap implicitly also enables resource indexing\n    if (canUseDescriptorHeap())\n      m_shaderOptions.spirv.set(DxvkShaderSpirvFlag::SupportsResourceIndexing);\n  }\n\n\n  void DxvkDevice::logBindingModel() {\n    const char* descriptorModel = \"Legacy\";\n\n    if (canUseDescriptorHeap())\n      descriptorModel = \"Descriptor heap\";\n    else if (canUseDescriptorBuffer())\n      descriptorModel = \"Descriptor buffer\";\n\n    Logger::info(str::format(\"Binding model: \", descriptorModel));\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_device.h",
    "content": "#pragma once\n\n#include \"dxvk_adapter.h\"\n#include \"dxvk_buffer.h\"\n#include \"dxvk_compute.h\"\n#include \"dxvk_constant_state.h\"\n#include \"dxvk_context.h\"\n#include \"dxvk_fence.h\"\n#include \"dxvk_framebuffer.h\"\n#include \"dxvk_image.h\"\n#include \"dxvk_instance.h\"\n#include \"dxvk_latency.h\"\n#include \"dxvk_memory.h\"\n#include \"dxvk_meta_clear.h\"\n#include \"dxvk_objects.h\"\n#include \"dxvk_options.h\"\n#include \"dxvk_pipemanager.h\"\n#include \"dxvk_presenter.h\"\n#include \"dxvk_queue.h\"\n#include \"dxvk_recycler.h\"\n#include \"dxvk_renderpass.h\"\n#include \"dxvk_sampler.h\"\n#include \"dxvk_shader.h\"\n#include \"dxvk_sparse.h\"\n#include \"dxvk_stats.h\"\n#include \"dxvk_unbound.h\"\n\nnamespace dxvk {\n  \n  class DxvkInstance;\n  class DxvkShaderCache;\n\n  class DxvkIrShader;\n  class DxvkIrShaderConverter;\n  class DxvkIrShaderCreateInfo;\n\n  /**\n   * \\brief Device performance hints\n   */\n  struct DxvkDevicePerfHints {\n    VkBool32 preferFbDepthStencilCopy   : 1;\n    VkBool32 renderPassClearFormatBug   : 1;\n    VkBool32 renderPassResolveFormatBug : 1;\n    VkBool32 preferRenderPassOps        : 1;\n    VkBool32 preferPrimaryCmdBufs       : 1;\n    VkBool32 preferComputeMipGen        : 1;\n    VkBool32 preferDescriptorByteOffsets: 1;\n  };\n  \n  /**\n   * \\brief Device queue\n   * \n   * Stores a Vulkan queue and the\n   * queue family that it belongs to.\n   */\n  struct DxvkDeviceQueue {\n    VkQueue       queueHandle = VK_NULL_HANDLE;\n    uint32_t      queueFamily = 0u;\n    uint32_t      queueIndex  = 0u;\n\n    DxvkDeviceQueueInfo properties = { };\n  };\n\n  /**\n   * \\brief Device queue infos\n   */\n  struct DxvkDeviceQueueSet {\n    DxvkDeviceQueue graphics;\n    DxvkDeviceQueue transfer;\n    DxvkDeviceQueue sparse;\n  };\n  \n  /**\n   * \\brief DXVK device\n   * \n   * Device object. This is responsible for resource creation,\n   * memory allocation, command submission and state tracking.\n   * Rendering commands are recorded into command lists using\n   * contexts. Multiple contexts can be created for a device.\n   */\n  class DxvkDevice : public RcObject {\n    friend class DxvkContext;\n    friend class DxvkSubmissionQueue;\n    friend class DxvkDescriptorPoolTracker;\n  public:\n    \n    DxvkDevice(\n      const Rc<DxvkInstance>&         instance,\n      const Rc<DxvkAdapter>&          adapter,\n      const Rc<vk::DeviceFn>&         vkd,\n      const DxvkDeviceFeatures&       features,\n      const DxvkDeviceQueueSet&       queues,\n      const DxvkQueueCallback&        queueCallback);\n      \n    ~DxvkDevice();\n    \n    /**\n     * \\brief Vulkan device functions\n     * \\returns Vulkan device functions\n     */\n    Rc<vk::DeviceFn> vkd() const {\n      return m_vkd;\n    }\n    \n    /**\n     * \\brief Vulkan instance functions\n     * \\returns Vulkan instance functions\n     */\n    Rc<vk::InstanceFn> vki() const {\n      return m_instance->vki();\n    }\n    \n    /**\n     * \\brief Logical device handle\n     * \\returns The device handle\n     */\n    VkDevice handle() const {\n      return m_vkd->device();\n    }\n\n    /**\n     * \\brief D3DKMT device local handle\n     * \\returns The device D3DKMT local handle\n     * \\returns \\c 0 if there's no matching D3DKMT device\n     */\n    D3DKMT_HANDLE kmtLocal() const {\n      return m_kmtLocal;\n    }\n\n    /**\n     * \\brief Checks whether debug functionality is enabled\n     * \\returns \\c true if debug utils are enabled\n     */\n    DxvkDebugFlags debugFlags() const {\n      return m_debugFlags;\n    }\n\n    /**\n     * \\brief Device options\n     * \\returns Device options\n     */\n    const DxvkOptions& config() const {\n      return m_options;\n    }\n    \n    /**\n     * \\brief Queue handles\n     * \n     * Handles and queue family indices\n     * of all known device queues.\n     * \\returns Device queue infos\n     */\n    const DxvkDeviceQueueSet& queues() const {\n      return m_queues;\n    }\n\n    /**\n     * \\brief Tests whether a dedicated transfer queue is available\n     * \\returns \\c true if an SDMA queue is supported by the device\n     */\n    bool hasDedicatedTransferQueue() const {\n      return m_queues.transfer.queueHandle\n          != m_queues.graphics.queueHandle;\n    }\n\n    /**\n     * \\brief Queries sharing mode info\n     * \\returns Sharing mode info\n     */\n    DxvkSharingModeInfo getSharingMode() const {\n      DxvkSharingModeInfo result = { };\n      result.queueFamilies[0] = m_queues.graphics.queueFamily;\n      result.queueFamilies[1] = m_queues.transfer.queueFamily;\n      return result;\n    }\n\n    /**\n     * \\brief The instance\n     * \n     * The DXVK instance that created this device.\n     * \\returns Instance\n     */\n    Rc<DxvkInstance> instance() const {\n      return m_instance;\n    }\n\n    /**\n     * \\brief The adapter\n     * \n     * The physical device that the\n     * device has been created for.\n     * \\returns Adapter\n     */\n    Rc<DxvkAdapter> adapter() const {\n      return m_adapter;\n    }\n\n    /**\n     * \\brief Enabled device features\n     * \\returns Enabled features\n     */\n    const DxvkDeviceFeatures& features() const {\n      return m_features;\n    }\n\n    /**\n     * \\brief Device properties\n     * \\returns Device properties\n     */\n    const DxvkDeviceInfo& properties() const {\n      return m_properties;\n    }\n\n    /**\n     * \\brief Queries format feature support\n     *\n     * \\param [in] format Format to query\n     * \\returns Format feature bits\n     */\n    DxvkFormatFeatures getFormatFeatures(VkFormat format) const {\n      return m_adapter->getFormatFeatures(format);\n    }\n\n    /**\n     * \\brief Queries format limits\n     *\n     * \\param [in] query Format query info\n     * \\returns Format limits if the given image is supported\n     */\n    std::optional<DxvkFormatLimits> getFormatLimits(\n      const DxvkFormatQuery&          query) const {\n      return m_adapter->getFormatLimits(query);\n    }\n\n\n    /**\n     * \\brief Queries default shader compile options\n     *\n     * Can be overridden by the client API. Only applies to\n     * shaders using internal IR rather than SPIR-V binaries.\n     * \\returns Device-global shader compile options.\n     */\n    DxvkShaderOptions getShaderCompileOptions() const {\n      return m_shaderOptions;\n    }\n\n    /**\n     * \\brief Get device status\n     * \n     * This may report device loss in\n     * case a submission failed.\n     * \\returns Device status\n     */\n    VkResult getDeviceStatus() const {\n      return m_submissionQueue.getLastError();\n    }\n\n    /**\n     * \\brief Queries mapped image subresource layout\n     *\n     * Assumes that the image tiling is linear even\n     * if not explcitly set in the create info.\n     * \\param [in] createInfo Image create info\n     * \\param [in] subresource Subresource to query\n     * \\returns Subresource layout\n     */\n    VkSubresourceLayout queryImageSubresourceLayout(\n      const DxvkImageCreateInfo&        createInfo,\n      const VkImageSubresource&         subresource);\n\n    /**\n     * \\brief Checks whether this is a UMA system\n     *\n     * Basically tests whether all heaps are device-local.\n     * Can be used for various optimizations in client APIs.\n     * \\returns \\c true if the system has unified memory.\n     */\n    bool isUnifiedMemoryArchitecture() const;\n\n    /**\n     * \\brief Checks whether graphics pipeline libraries can be used\n     * \\returns \\c true if all required features are supported.\n     */\n    bool canUseGraphicsPipelineLibrary() const;\n\n    /**\n     * \\brief Checks whether pipeline creation cache control can be used\n     * \\returns \\c true if all required features are supported.\n     */\n    bool canUsePipelineCacheControl() const;\n\n    /**\n     * \\brief Checks whether sample locations can be used\n     * \\returns \\c true if sample locations are supported for any of the given sample counts\n     */\n    bool canUseSampleLocations(VkSampleCountFlags samples) const;\n\n    /**\n     * \\brief Checks whether pipelines should be tracked\n     * \\returns \\c true if pipelines need to be tracked\n     */\n    bool mustTrackPipelineLifetime() const;\n\n    /**\n     * \\brief Checks whether descriptor heaps can be used\n     * \\returns \\c true if all required features are supported.\n     */\n    bool canUseDescriptorHeap() const {\n      return m_features.extDescriptorHeap.descriptorHeap;\n    }\n\n    /**\n     * \\brief Checks whether descriptor buffers can be used\n     * \\returns \\c true if all required features are supported.\n     */\n    bool canUseDescriptorBuffer() const {\n      return m_features.extDescriptorBuffer.descriptorBuffer && !canUseDescriptorHeap();\n    }\n\n    /**\n     * \\brief Checks whether CUDA interop is enabled\n     *\n     * Relevant for descriptor heap usage since CUDA interop still\n     * needs legacy image view and sampler handles.\n     * \\returns \\c true if all required features are supported.\n     */\n    bool hasCudaInterop() const {\n      return m_features.nvxImageViewHandle;\n    }\n\n    /**\n     * \\brief Queries default framebuffer size\n     * \\returns Default framebuffer size\n     */\n    DxvkFramebufferSize getDefaultFramebufferSize() const;\n\n    /**\n     * \\brief Queries supported shader stages\n     * \\returns Supported shader pipeline stages\n     */\n    VkPipelineStageFlags getShaderPipelineStages() const;\n\n    /**\n     * \\brief Retrieves performance hints\n     * \\returns Device-specific perf hints\n     */\n    DxvkDevicePerfHints perfHints() const {\n      return m_perfHints;\n    }\n    \n    /**\n     * \\brief Creates a command list\n     * \\returns The command list\n     */\n    Rc<DxvkCommandList> createCommandList();\n    \n    /**\n     * \\brief Creates a context\n     * \n     * Creates a context object that can\n     * be used to record command buffers.\n     * \\returns The context object\n     */\n    Rc<DxvkContext> createContext();\n\n    /**\n     * \\brief Creates a GPU event\n     * \\returns New GPU event\n     */\n    Rc<DxvkEvent> createGpuEvent();\n\n    /**\n     * \\brief Creates a query\n     * \n     * \\param [in] type Query type\n     * \\param [in] flags Query flags\n     * \\param [in] index Query index\n     * \\returns New query\n     */\n    Rc<DxvkQuery> createGpuQuery(\n            VkQueryType           type,\n            VkQueryControlFlags   flags,\n            uint32_t              index);\n\n    /**\n     * \\brief Creates a raw GPU query\n     *\n     * \\param [in] type Query type\n     * \\returns New query\n     */\n    Rc<DxvkGpuQuery> createRawQuery(\n            VkQueryType           type);\n\n    /**\n     * \\brief Creates new fence\n     *\n     * \\param [in] info Fence create info\n     * \\returns The fence\n     */\n    Rc<DxvkFence> createFence(\n      const DxvkFenceCreateInfo& fenceInfo);\n    \n    /**\n     * \\brief Creates a buffer object\n     * \n     * \\param [in] createInfo Buffer create info\n     * \\param [in] memoryType Memory type flags\n     * \\returns The buffer object\n     */\n    Rc<DxvkBuffer> createBuffer(\n      const DxvkBufferCreateInfo& createInfo,\n            VkMemoryPropertyFlags memoryType);\n\n    /**\n     * \\brief Creates an image object\n     * \n     * \\param [in] createInfo Image create info\n     * \\param [in] memoryType Memory type flags\n     * \\returns The image object\n     */\n    Rc<DxvkImage> createImage(\n      const DxvkImageCreateInfo&  createInfo,\n            VkMemoryPropertyFlags memoryType);\n    \n    /**\n     * \\brief Creates a sampler object\n     * \n     * \\param [in] createInfo Sampler parameters\n     * \\returns Newly created sampler object\n     */\n    Rc<DxvkSampler> createSampler(\n      const DxvkSamplerKey&         createInfo);\n\n    /**\n     * \\brief Creates local allocation cache\n     *\n     * \\param [in] bufferUsage Required buffer usage\n     * \\param [in] propertyFlags Memory properties\n     * \\returns Allocation cache object\n     */\n    DxvkLocalAllocationCache createAllocationCache(\n            VkBufferUsageFlags    bufferUsage,\n            VkMemoryPropertyFlags propertyFlags);\n\n    /**\n     * \\brief Creates a sparse page allocator\n     * \\returns Sparse page allocator\n     */\n    Rc<DxvkSparsePageAllocator> createSparsePageAllocator();\n\n    /**\n     * \\brief Creates built-in pipeline layout\n     *\n     * \\param [in] flags Pipeline layout flags\n     * \\param [in] pushDataStages Push data stage mask\n     * \\param [in] pushDataSize Push data size\n     * \\param [in] bindingCount Number of resource bindings\n     * \\param [in] bindings Resource bindings\n     * \\returns Unique pipeline layout\n     */\n    const DxvkPipelineLayout* createBuiltInPipelineLayout(\n            DxvkPipelineLayoutFlags         flags,\n            VkShaderStageFlags              pushDataStages,\n            VkDeviceSize                    pushDataSize,\n            uint32_t                        bindingCount,\n      const DxvkDescriptorSetLayoutBinding* bindings);\n\n    /**\n     * \\brief Creates built-in compute pipeline\n     *\n     * \\param [in] layout Pipeline layout\n     * \\param [in] stage Shader stage info\n     */\n    VkPipeline createBuiltInComputePipeline(\n      const DxvkPipelineLayout*             layout,\n      const util::DxvkBuiltInShaderStage&   stage);\n\n    /**\n     * \\brief Creates built-in graphics pipeline\n     *\n     * \\param [in] layout Pipeline layout\n     * \\param [in] state Pipeline state\n     */\n    VkPipeline createBuiltInGraphicsPipeline(\n      const DxvkPipelineLayout*             layout,\n      const util::DxvkBuiltInGraphicsState& state);\n\n    /**\n     * \\brief Creates IR shader from cache\n     *\n     * Will try to look up and retrive the given shader from\n     * the shader cache. If no shader converter is provided\n     * and the look-up fails, this returns \\c nullptr.\n     * \\param [in] name Shader name\n     * \\param [in] createInfo Shader create info\n     * \\param [in] converter Shader converter to\n     *    use when cache look-upo fails.\n     */\n    Rc<DxvkShader> createCachedShader(\n      const std::string&                    name,\n      const DxvkIrShaderCreateInfo&         createInfo,\n      const Rc<DxvkIrShaderConverter>&      converter);\n\n    /**\n     * \\brief Imports a buffer\n     *\n     * \\param [in] createInfo Buffer create info\n     * \\param [in] importInfo Buffer import info\n     * \\param [in] memoryType Memory type flags\n     * \\returns The buffer object\n     */\n    Rc<DxvkBuffer> importBuffer(\n      const DxvkBufferCreateInfo& createInfo,\n      const DxvkBufferImportInfo& importInfo,\n            VkMemoryPropertyFlags memoryType);\n\n    /**\n     * \\brief Imports an image\n     *\n     * \\param [in] createInfo Image create info\n     * \\param [in] image Vulkan image to wrap\n     * \\param [in] memoryType Memory type flags\n     * \\returns The image object\n     */\n    Rc<DxvkImage> importImage(\n      const DxvkImageCreateInfo&  createInfo,\n            VkImage               image,\n            VkMemoryPropertyFlags memoryType);\n\n    /**\n     * \\brief Retrieves stat counters\n     * \n     * Can be used by the HUD to display some\n     * internal information, such as memory\n     * usage, draw calls, etc.\n     */\n    DxvkStatCounters getStatCounters();\n\n    /**\n     * \\brief Queries memory statistics\n     *\n     * \\param [in] heap Memory heap index\n     * \\returns Memory usage for this heap\n     */\n    DxvkMemoryStats getMemoryStats(uint32_t heap);\n\n    /**\n     * \\brief Queries detailed memory allocation statistics\n     *\n     * Expensive, should be used with caution.\n     * \\param [out] stats Allocation statistics\n     * \\returns Shared allocation cache stats\n     */\n    DxvkSharedAllocationCacheStats getMemoryAllocationStats(DxvkMemoryAllocationStats& stats);\n\n    /**\n     * \\brief Queries descriptor properties\n     *\n     * And null descriptors.\n     */\n    const DxvkDescriptorProperties& getDescriptorProperties() {\n      return m_objects.descriptors();\n    }\n\n    /**\n     * \\brief Queries sampler statistics\n     * \\returns Sampler stats\n     */\n    DxvkSamplerStats getSamplerStats() {\n      return m_objects.samplerPool().getStats();\n    }\n\n    /**\n     * \\brief Queries sampler descriptor set\n     * \\returns Global sampler set and layout\n     */\n    DxvkSamplerDescriptorSet getSamplerDescriptorSet() {\n      return m_objects.samplerPool().getDescriptorSetInfo();\n    }\n\n    /**\n     * \\brief Queries sampler descriptor set\n     * \\returns Global sampler set and layout\n     */\n    DxvkDescriptorHeapBindingInfo getSamplerDescriptorHeap() {\n      return m_objects.samplerPool().getDescriptorHeapInfo();\n    }\n\n    /**\n     * \\brief Retreves current frame ID\n     * \\returns Current frame ID\n     */\n    uint32_t getCurrentFrameId() const;\n    \n    /**\n     * \\brief Notifies adapter about memory allocation changes\n     *\n     * \\param [in] heap Memory heap index\n     * \\param [in] allocated Allocated size delta\n     * \\param [in] used Used size delta\n     */\n    void notifyMemoryStats(\n            uint32_t            heap,\n            int64_t             allocated,\n            int64_t             used) {\n      m_adapter->notifyMemoryStats(heap, allocated, used);\n    }\n\n    /**\n     * \\brief Registers a shader\n     * \\param [in] shader Newly compiled shader\n     */\n    void registerShader(\n      const Rc<DxvkShader>&         shader);\n    \n    /**\n     * \\brief Prioritizes compilation of a given shader\n     * \\param [in] shader Shader to start compiling\n     */\n    void requestCompileShader(\n      const Rc<DxvkShader>&         shader);\n\n    /**\n     * \\brief Creates latency tracker for a presenter\n     *\n     * The specicfic implementation and parameters used\n     * depend on user configuration.\n     * \\param [in] presenter Presenter instance\n     */\n    Rc<DxvkLatencyTracker> createLatencyTracker(\n      const Rc<Presenter>&            presenter);\n\n    /**\n     * \\brief Presents a swap chain image\n     * \n     * Invokes the presenter's \\c presentImage method on\n     * the submission thread. The status of this operation\n     * can be retrieved with \\ref waitForSubmission.\n     * \\param [in] presenter The presenter\n     * \\param [in] tracker Latency tracker\n     * \\param [in] frameId Frame ID\n     * \\param [out] status Present status\n     */\n    void presentImage(\n      const Rc<Presenter>&            presenter,\n      const Rc<DxvkLatencyTracker>&   tracker,\n            uint64_t                  frameId,\n            DxvkSubmitStatus*         status);\n    \n    /**\n     * \\brief Submits a command list\n     * \n     * Submits the given command list to the device using\n     * the given set of optional synchronization primitives.\n     * \\param [in] commandList The command list to submit\n     * \\param [in] tracker Latency tracker\n     * \\param [in] frameId Frame ID\n     * \\param [out] status Submission feedback\n     */\n    void submitCommandList(\n      const Rc<DxvkCommandList>&      commandList,\n      const Rc<DxvkLatencyTracker>&   tracker,\n            uint64_t                  frameId,\n            DxvkSubmitStatus*         status);\n\n    /**\n     * \\brief Locks submission queue\n     * \n     * Since Vulkan queues are only meant to be accessed\n     * from one thread at a time, external libraries need\n     * to lock the queue before submitting command buffers.\n     */\n    void lockSubmission() {\n      m_submissionQueue.synchronize();\n      m_submissionQueue.lockDeviceQueue();\n    }\n    \n    /**\n     * \\brief Unlocks submission queue\n     * \n     * Releases the Vulkan queues again so that DXVK\n     * itself can use them for submissions again.\n     */\n    void unlockSubmission() {\n      m_submissionQueue.unlockDeviceQueue();\n    }\n\n    /**\n     * \\brief Increments a given stat counter\n     *\n     * \\param [in] counter Stat counter to increment\n     * \\param [in] value Increment value\n     */\n    void addStatCtr(DxvkStatCounter counter, uint64_t value) {\n      std::lock_guard<sync::Spinlock> lock(m_statLock);\n      m_statCounters.addCtr(counter, value);\n    }\n\n    /**\n     * \\brief Waits for a given submission\n     * \n     * \\param [in,out] status Submission status\n     * \\returns Result of the submission\n     */\n    VkResult waitForSubmission(DxvkSubmitStatus* status);\n\n    /**\n     * \\brief Waits for a fence to become signaled\n     *\n     * Treats the fence wait as a GPU sync point, which can\n     * be useful for device statistics. Should only be used\n     * if rendering is stalled because of this wait.\n     * \\param [in] fence Fence to wait on\n     * \\param [in] value Fence value\n     */\n    void waitForFence(sync::Fence& fence, uint64_t value);\n\n    /**\n     * \\brief Waits for resource to become idle\n     *\n     * \\param [in] resource Resource to wait for\n     * \\param [in] access Access mode to check\n     */\n    void waitForResource(const DxvkPagedResource& resource, DxvkAccess access);\n    \n    /**\n     * \\brief Waits until the device becomes idle\n     * \n     * Waits for the GPU to complete the execution of all\n     * previously submitted command buffers. This may be\n     * used to ensure that resources that were previously\n     * used by the GPU can be safely destroyed.\n     */\n    void waitForIdle();\n    \n  private:\n    \n    DxvkOptions                 m_options;\n\n    Rc<DxvkInstance>            m_instance;\n    Rc<DxvkAdapter>             m_adapter;\n    Rc<vk::DeviceFn>            m_vkd;\n    D3DKMT_HANDLE               m_kmtLocal = 0;\n\n    DxvkDebugFlags              m_debugFlags;\n    DxvkDeviceQueueSet          m_queues;\n\n    DxvkDeviceFeatures          m_features;\n    DxvkDeviceInfo              m_properties;\n\n    DxvkShaderOptions           m_shaderOptions;\n\n    DxvkDevicePerfHints         m_perfHints;\n    DxvkObjects                 m_objects;\n\n    sync::Spinlock              m_statLock;\n    DxvkStatCounters            m_statCounters;\n\n    DxvkRecycler<DxvkCommandList, 16> m_recycledCommandLists;\n\n    DxvkSubmissionQueue         m_submissionQueue;\n\n    Rc<DxvkShaderCache>         m_shaderCache;\n\n    DxvkDevicePerfHints getPerfHints();\n\n    void recycleCommandList(\n      const Rc<DxvkCommandList>& cmdList);\n\n    void determineShaderOptions();\n\n    void logBindingModel();\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_device_filter.cpp",
    "content": "#include \"dxvk_device_filter.h\"\n#include <iomanip>\n#include <sstream>\n\nnamespace dxvk {\n  \n  static std::string convertUUID(const uint8_t uuid[VK_UUID_SIZE]) {\n    std::ostringstream stream;\n    stream << std::hex << std::setfill('0');\n    for (size_t i = 0; i < VK_UUID_SIZE; ++i)\n      stream << std::setw(2) << static_cast<uint32_t>(uuid[i] & 0xff);\n    return stream.str();\n  }\n\n\n  DxvkDeviceFilter::DxvkDeviceFilter(\n          DxvkDeviceFilterFlags flags,\n    const DxvkOptions&          options)\n  : m_flags(flags) {\n    m_matchDeviceName = env::getEnvVar(\"DXVK_FILTER_DEVICE_NAME\");\n    m_matchDeviceUUID = env::getEnvVar(\"DXVK_FILTER_DEVICE_UUID\");\n\n    if (m_matchDeviceName.empty())\n      m_matchDeviceName = options.deviceFilter;\n\n    if (!m_matchDeviceName.empty())\n      m_flags.set(DxvkDeviceFilterFlag::MatchDeviceName);\n\n    if (!m_matchDeviceUUID.empty())\n      m_flags.set(DxvkDeviceFilterFlag::MatchDeviceUUID);\n\n    if (m_flags.any(DxvkDeviceFilterFlag::MatchDeviceName,\n                    DxvkDeviceFilterFlag::MatchDeviceUUID))\n      m_flags.clr(DxvkDeviceFilterFlag::SkipCpuDevices);\n  }\n\n\n  DxvkDeviceFilter::~DxvkDeviceFilter() {\n\n  }\n\n\n  bool DxvkDeviceFilter::testAdapter(DxvkAdapter& adapter) const {\n    const auto& properties = adapter.deviceProperties();\n\n    Logger::info(str::format(\"Found device: \",\n      properties.core.properties.deviceName, \" (\",\n      properties.vk12.driverName, \" \",\n      properties.driverVersion.toString(), \")\"));\n\n    std::string compatError;\n\n    if (!adapter.isCompatible(compatError)) {\n      Logger::info(str::format(\"  Skipping: \", compatError));\n      return false;\n    }\n\n    if (m_flags.test(DxvkDeviceFilterFlag::MatchDeviceName)) {\n      if (std::string(properties.core.properties.deviceName).find(m_matchDeviceName) == std::string::npos) {\n        Logger::info(\"  Skipping: Device filter\");\n        return false;\n      }\n    }\n\n    if (m_flags.test(DxvkDeviceFilterFlag::MatchDeviceUUID)) {\n      std::string uuidStr = convertUUID(properties.vk11.deviceUUID);\n\n      if (uuidStr.find(m_matchDeviceUUID) == std::string::npos) {\n        Logger::info(\"  Skipping: UUID filter\");\n        return false;\n      }\n    }\n\n    if (m_flags.test(DxvkDeviceFilterFlag::SkipCpuDevices)) {\n      if (properties.core.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {\n        Logger::info(\"  Skipping: Software driver\");\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_device_filter.h",
    "content": "#pragma once\n\n#include \"dxvk_adapter.h\"\n#include \"dxvk_options.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Device filter flags\n   * \n   * The device filter flags specify which device\n   * properties are considered when testing adapters.\n   * If no flags are set, all devices pass the test.\n   */\n  enum class DxvkDeviceFilterFlag {\n    MatchDeviceName   = 0,\n    SkipCpuDevices    = 1,\n    MatchDeviceUUID   = 3\n  };\n\n  using DxvkDeviceFilterFlags = Flags<DxvkDeviceFilterFlag>;\n\n\n  /**\n   * \\brief DXVK device filter\n   * \n   * Used to select specific Vulkan devices to use\n   * with DXVK. This may be useful for games which\n   * do not offer an option to select the correct\n   * device.\n   */\n  class DxvkDeviceFilter {\n\n  public:\n\n    DxvkDeviceFilter(\n            DxvkDeviceFilterFlags flags,\n      const DxvkOptions&          options);\n\n    ~DxvkDeviceFilter();\n\n    /**\n     * \\brief Tests an adapter\n     *\n     * \\param [in] adapter Adapter object\n     * \\returns \\c true if the device can be used\n     */\n    bool testAdapter(DxvkAdapter& adapter) const;\n\n  private:\n\n    DxvkDeviceFilterFlags m_flags;\n\n    std::string m_matchDeviceName;\n    std::string m_matchDeviceUUID;\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_device_info.cpp",
    "content": "#include <algorithm>\n#include <iomanip>\n#include <set>\n#include <sstream>\n#include <type_traits>\n\n#include \"dxvk_device_info.h\"\n#include \"dxvk_instance.h\"\n#include \"dxvk_limits.h\"\n\nnamespace dxvk {\n\n  #define CORE_VERSIONS                            \\\n    HANDLE_CORE(vk11);                             \\\n    HANDLE_CORE(vk12);                             \\\n    HANDLE_CORE(vk13);                             \\\n\n  #define EXTENSIONS_WITH_FEATURES                 \\\n    HANDLE_EXT(extAttachmentFeedbackLoopLayout);   \\\n    HANDLE_EXT(extBorderColorSwizzle);             \\\n    HANDLE_EXT(extConservativeRasterization);      \\\n    HANDLE_EXT(extCustomBorderColor);              \\\n    HANDLE_EXT(extDepthClipEnable);                \\\n    HANDLE_EXT(extDepthBiasControl);               \\\n    HANDLE_EXT(extDescriptorBuffer);               \\\n    HANDLE_EXT(extDescriptorHeap);                 \\\n    HANDLE_EXT(extExtendedDynamicState3);          \\\n    HANDLE_EXT(extFragmentShaderInterlock);        \\\n    HANDLE_EXT(extFullScreenExclusive);            \\\n    HANDLE_EXT(extGraphicsPipelineLibrary);        \\\n    HANDLE_EXT(extHdrMetadata);                    \\\n    HANDLE_EXT(extLineRasterization);              \\\n    HANDLE_EXT(extMemoryBudget);                   \\\n    HANDLE_EXT(extMemoryPriority);                 \\\n    HANDLE_EXT(extMultiDraw);                      \\\n    HANDLE_EXT(extNonSeamlessCubeMap);             \\\n    HANDLE_EXT(extPageableDeviceLocalMemory);      \\\n    HANDLE_EXT(extRobustness2);                    \\\n    HANDLE_EXT(extSampleLocations);                \\\n    HANDLE_EXT(extShaderModuleIdentifier);         \\\n    HANDLE_EXT(extShaderStencilExport);            \\\n    HANDLE_EXT(extSwapchainColorSpace);            \\\n    HANDLE_EXT(extSwapchainMaintenance1);          \\\n    HANDLE_EXT(extTransformFeedback);              \\\n    HANDLE_EXT(extVertexAttributeDivisor);         \\\n    HANDLE_EXT(khrExternalMemoryWin32);            \\\n    HANDLE_EXT(khrExternalSemaphoreWin32);         \\\n    HANDLE_EXT(khrLoadStoreOpNone);                \\\n    HANDLE_EXT(khrMaintenance5);                   \\\n    HANDLE_EXT(khrMaintenance6);                   \\\n    HANDLE_EXT(khrMaintenance7);                   \\\n    HANDLE_EXT(khrMaintenance8);                   \\\n    HANDLE_EXT(khrMaintenance9);                   \\\n    HANDLE_EXT(khrMaintenance10);                  \\\n    HANDLE_EXT(khrPipelineLibrary);                \\\n    HANDLE_EXT(khrPresentId);                      \\\n    HANDLE_EXT(khrPresentId2);                     \\\n    HANDLE_EXT(khrPresentWait);                    \\\n    HANDLE_EXT(khrPresentWait2);                   \\\n    HANDLE_EXT(khrShaderFloatControls2);           \\\n    HANDLE_EXT(khrShaderSubgroupUniformControlFlow);\\\n    HANDLE_EXT(khrShaderUntypedPointers);          \\\n    HANDLE_EXT(khrSwapchain);                      \\\n    HANDLE_EXT(khrSwapchainMaintenance1);          \\\n    HANDLE_EXT(khrSwapchainMutableFormat);         \\\n    HANDLE_EXT(khrUnifiedImageLayouts);            \\\n    HANDLE_EXT(khrWin32KeyedMutex);                \\\n    HANDLE_EXT(nvLowLatency2);                     \\\n    HANDLE_EXT(nvRawAccessChains);                 \\\n    HANDLE_EXT(nvxBinaryImport);                   \\\n    HANDLE_EXT(nvxImageViewHandle);\n\n  #define EXTENSIONS_WITH_PROPERTIES               \\\n    HANDLE_EXT(extConservativeRasterization);      \\\n    HANDLE_EXT(extCustomBorderColor);              \\\n    HANDLE_EXT(extDescriptorBuffer);               \\\n    HANDLE_EXT(extDescriptorHeap);                 \\\n    HANDLE_EXT(extExtendedDynamicState3);          \\\n    HANDLE_EXT(extGraphicsPipelineLibrary);        \\\n    HANDLE_EXT(extLineRasterization);              \\\n    HANDLE_EXT(extMultiDraw);                      \\\n    HANDLE_EXT(extRobustness2);                    \\\n    HANDLE_EXT(extSampleLocations);                \\\n    HANDLE_EXT(extTransformFeedback);              \\\n    HANDLE_EXT(extVertexAttributeDivisor);         \\\n    HANDLE_EXT(khrMaintenance5);                   \\\n    HANDLE_EXT(khrMaintenance6);                   \\\n    HANDLE_EXT(khrMaintenance7);                   \\\n    HANDLE_EXT(khrMaintenance9);                   \\\n    HANDLE_EXT(khrMaintenance10);\n\n\n  DxvkDeviceCapabilities::DxvkDeviceCapabilities(\n    const DxvkInstance&               instance,\n          VkPhysicalDevice            adapter,\n    const VkDeviceCreateInfo*         deviceInfo) {\n    // Can't query anything on a Vulkan 1.0 device\n    auto vk = instance.vki();\n    vk->vkGetPhysicalDeviceProperties(adapter, &m_properties.core.properties);\n\n    if (m_properties.core.properties.apiVersion < DxvkVulkanApiVersion)\n      return;\n\n    initSupportedExtensions(instance, adapter, deviceInfo);\n    initSupportedFeatures(instance, adapter, deviceInfo);\n    initDeviceProperties(instance, adapter, deviceInfo);\n    initQueueProperties(instance, adapter, deviceInfo);\n    initMemoryProperties(instance, adapter);\n\n    disableUnusedFeatures(instance);\n\n    enableFeaturesAndExtensions();\n    enableQueues();\n  }\n\n\n  DxvkDeviceCapabilities::~DxvkDeviceCapabilities() {\n\n  }\n\n\n  bool DxvkDeviceCapabilities::queryDeviceExtensions(\n          uint32_t*                   count,\n          VkExtensionProperties*      extensions) const {\n    if (!extensions) {\n      *count = m_extensionList.size();\n      return true;\n    }\n\n    if (*count > m_extensionList.size())\n      *count = m_extensionList.size();\n\n    for (uint32_t i = 0u; i < *count; i++)\n      extensions[i] = *(m_extensionList[i]);\n\n    return *count >= m_extensionList.size();\n  }\n\n\n  bool DxvkDeviceCapabilities::queryDeviceQueues(\n          uint32_t*                   count,\n          VkDeviceQueueCreateInfo*    queues) const {\n    if (!queues) {\n      *count = m_queuesEnabled.size();\n      return true;\n    }\n\n    if (*count > m_queuesEnabled.size())\n      *count = m_queuesEnabled.size();\n\n    bool complete = *count >= m_queuesEnabled.size();\n\n    for (uint32_t i = 0u; i < *count; i++) {\n      const auto& in = m_queuesEnabled[i];\n\n      if (queues[i].pQueuePriorities) {\n        complete = complete && queues[i].queueCount >= in.queueCount;\n\n        for (uint32_t j = 0u; j < in.queueCount && j < queues[i].queueCount; j++)\n          const_cast<float&>(queues[i].pQueuePriorities[j]) = in.pQueuePriorities[j];\n      }\n\n      queues[i].flags = in.flags;\n      queues[i].queueFamilyIndex = in.queueFamilyIndex;\n      queues[i].queueCount = in.queueCount;\n    }\n\n    return complete;\n  }\n\n\n  bool DxvkDeviceCapabilities::queryDeviceFeatures(\n          size_t*                     size,\n          void*                       data) const {\n    if (!data) {\n      *size = sizeof(m_featuresEnabled);\n      return true;\n    }\n\n    if (*size > sizeof(m_featuresEnabled))\n      *size = sizeof(m_featuresEnabled);\n\n    std::memcpy(data, &m_featuresEnabled, *size);\n    return *size >= sizeof(m_featuresEnabled);\n  }\n\n\n  bool DxvkDeviceCapabilities::isSuitable(size_t errorSize, char* error) {\n    auto message = checkDeviceCompatibility();\n\n    if (message && errorSize)\n      std::strncpy(error, message->c_str(), errorSize - 1u);\n\n    return !message;\n  }\n\n\n  void DxvkDeviceCapabilities::logDeviceInfo() {\n    // Assume that known features are ordered by extension\n    const VkExtensionProperties* extension = nullptr;\n\n    std::stringstream stream;\n    stream << m_properties.core.properties.deviceName << \":\" << std::endl\n           << \"  Driver   : \" << m_properties.vk12.driverName << \" \" << m_properties.driverVersion.toString() << std::endl;\n\n    stream << \"Queues:\" << std::endl\n           << \"  Graphics : (\" << m_queueMapping.graphics.family << \", \" << m_queueMapping.graphics.index << \")\" << std::endl\n           << \"  Transfer : (\" << m_queueMapping.transfer.family << \", \" << m_queueMapping.transfer.index << \")\" << std::endl\n           << \"  Sparse   : (\" << m_queueMapping.sparse.family   << \", \" << m_queueMapping.sparse.index   << \")\" << std::endl;\n\n    // Log memory type and heap properties\n    static const std::array<std::pair<VkMemoryPropertyFlagBits, const char*>, 8> s_flags = {{\n      { VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,        \"DEVICE_LOCAL\"        },\n      { VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,        \"HOST_VISIBLE\"        },\n      { VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,       \"HOST_COHERENT\"       },\n      { VK_MEMORY_PROPERTY_HOST_CACHED_BIT,         \"HOST_CACHED\"         },\n      { VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT,    \"LAZILY_ALLOCATED\"    },\n      { VK_MEMORY_PROPERTY_PROTECTED_BIT,           \"PROTECTED\"           },\n      { VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD, \"DEVICE_COHERENT\"     },\n      { VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD, \"DEVICE_UNCACHED\"     },\n    }};\n\n    stream << \"Memory:\" << std::endl;\n\n    for (uint32_t h = 0u; h < m_memory.core.memoryProperties.memoryHeapCount; h++) {\n      const auto& heap = m_memory.core.memoryProperties.memoryHeaps[h];\n      stream << \"  Heap \" << h << \": \";\n\n      if (heap.size >= (1ull << 30u)) {\n        auto size = (heap.size * 100u) >> 30u;\n        stream << (size / 100u) << \".\" << (size % 100u) << \" GiB\";\n      } else {\n        stream << (heap.size >> 20u) << \" MiB\";\n      }\n\n      if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)\n        stream << \" (DEVICE_LOCAL)\";\n\n      stream << std::endl;\n\n      if (m_featuresSupported.extMemoryBudget) {\n        stream << \"  Budget: \";\n\n        if (m_memory.budget.heapBudget[h] >= (1ull << 30u)) {\n          auto budget = (m_memory.budget.heapBudget[h] * 100u) >> 30u;\n          stream << (budget / 100u) << \".\" << (budget % 100u) << \" GiB\";\n        } else  {\n          stream << (m_memory.budget.heapBudget[h] >> 20u) << \" MiB\";\n        }\n\n        stream << std::endl;\n      }\n\n      for (uint32_t t = 0u; t < m_memory.core.memoryProperties.memoryTypeCount; t++) {\n        const auto& type = m_memory.core.memoryProperties.memoryTypes[t];\n\n        if (type.heapIndex != h)\n          continue;\n\n        stream << \"    Type \" << std::setw(2u) << t << \": \";\n\n        const char* prefix = \"\";\n\n        for (const auto& f : s_flags) {\n          if (!(type.propertyFlags & f.first))\n            continue;\n\n          stream << prefix << f.second;\n          prefix = \" | \";\n        }\n\n        if (!type.propertyFlags)\n          stream << \"(None)\";\n\n        stream << std::endl;\n      }\n    }\n\n\n    stream << \"Enabled extensions:\" << std::endl;\n\n    for (const auto& e : m_extensionList)\n      stream << \"  \" << e->extensionName << std::endl;\n\n    stream << \"Enabled features:\" << std::endl;\n\n    for (const auto& f : getFeatureList()) {\n      if (extension != f.extensionEnabled) {\n        extension = f.extensionEnabled;\n\n        if (extension)\n          stream << extension->extensionName << \":\" << std::endl;\n      }\n\n      stream << \"  \" << std::setfill(' ') << std::left << std::setw(30) << f.readableName << \" : \" << uint32_t(*f.featureEnabled) << std::endl;\n    }\n\n    Logger::info(stream.str());\n  }\n\n\n  void DxvkDeviceCapabilities::initSupportedExtensions(\n    const DxvkInstance&               instance,\n          VkPhysicalDevice            adapter,\n    const VkDeviceCreateInfo*         deviceInfo) {\n    auto vk = instance.vki();\n\n    uint32_t extensionCount = 0u;\n    vk->vkEnumerateDeviceExtensionProperties(adapter, nullptr, &extensionCount, nullptr);\n\n    std::vector<VkExtensionProperties> extensions(extensionCount);\n    vk->vkEnumerateDeviceExtensionProperties(adapter, nullptr, &extensionCount, extensions.data());\n\n    // Order extensions by name to accelerate lookup\n    std::sort(extensions.begin(), extensions.end(), vk::SortExtension());\n\n    // If we are importing a device with pre-defined extensions,\n    // filter out any extensions that are not enabled\n    if (deviceInfo) {\n      std::set<VkExtensionProperties, vk::SortExtension> enabledExtensions = { };\n\n      for (uint32_t i = 0u; i < deviceInfo->enabledExtensionCount; i++)\n        enabledExtensions.insert(vk::makeExtension(deviceInfo->ppEnabledExtensionNames[i]));\n\n      extensions.erase(std::remove_if(extensions.begin(), extensions.end(),\n        [&enabledExtensions] (const VkExtensionProperties& a) {\n          return enabledExtensions.find(a) == enabledExtensions.end();\n        }), extensions.end());\n    }\n\n    // If multiple extensions provide the same functionality, remove any\n    // deprecated aliases so that we always use the latest iteration.\n    std::array<std::pair<const char*, const char*>, 1u> aliases = {{\n      { VK_KHR_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME,\n        VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME },\n    }};\n\n    for (const auto& alias : aliases) {\n      auto a = vk::makeExtension(alias.first);\n      auto b = vk::makeExtension(alias.second);\n\n      auto aIter = std::lower_bound(extensions.begin(), extensions.end(), a, vk::SortExtension());\n      auto bIter = std::lower_bound(extensions.begin(), extensions.end(), b, vk::SortExtension());\n\n      if (aIter != extensions.end() && !vk::SortExtension()(a, *aIter)\n       && bIter != extensions.end() && !vk::SortExtension()(b, *bIter))\n        extensions.erase(bIter);\n    }\n\n    // HACK: Use mesh shader extension support to determine whether we're\n    // running on older (pre-Turing) Nvidia GPUs.\n    m_hasMeshShader = std::find_if(extensions.begin(), extensions.end(),\n      [] (const VkExtensionProperties& ext) {\n        return !std::strcmp(ext.extensionName, VK_EXT_MESH_SHADER_EXTENSION_NAME);\n      }) != extensions.end();\n\n    // HACK: Use fmask extension to detect pre-RDNA3 hardware.\n    m_hasFmask = std::find_if(extensions.begin(), extensions.end(),\n      [] (const VkExtensionProperties& ext) {\n        return !std::strcmp(ext.extensionName, VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME);\n      }) != extensions.end();\n\n    // Use the supported spec version as a way to indicate extension support.\n    // We may ignore certain extensions if the spec version is too old.\n    for (const auto& f : getFeatureList()) {\n      if (!f.extensionSupported)\n        continue;\n\n      auto iter = std::lower_bound(extensions.begin(), extensions.end(), *f.extensionSupported, vk::SortExtension());\n\n      if (iter != extensions.end()) {\n        if (!vk::SortExtension()(*f.extensionSupported, *iter))\n          f.extensionSupported->specVersion = std::max(1u, iter->specVersion);\n      }\n    }\n  }\n\n\n  void DxvkDeviceCapabilities::initSupportedFeatures(\n    const DxvkInstance&               instance,\n          VkPhysicalDevice            adapter,\n    const VkDeviceCreateInfo*         deviceInfo) {\n    auto vk = instance.vki();\n\n    chainFeatures(m_extensionsSupported, m_featuresSupported);\n\n    if (deviceInfo) {\n      // Only consider features enabled on the device as supported\n      if (deviceInfo->pEnabledFeatures)\n        m_featuresSupported.core.features = *deviceInfo->pEnabledFeatures;\n\n      copyFeature(deviceInfo->pNext, nullptr, &m_featuresSupported.core);\n\n      #define HANDLE_CORE(name) copyFeature(deviceInfo->pNext, nullptr, &m_featuresSupported.name)\n      #define HANDLE_EXT(name) copyFeature(deviceInfo->pNext, &m_extensionsSupported.name, &m_featuresSupported.name)\n\n      CORE_VERSIONS\n      EXTENSIONS_WITH_FEATURES\n\n      #undef HANDLE_CORE\n      #undef HANDLE_EXT\n    } else {\n      // Query supported features from the physical device\n      vk->vkGetPhysicalDeviceFeatures2(adapter, &m_featuresSupported.core);\n    }\n  }\n\n\n  void DxvkDeviceCapabilities::initDeviceProperties(\n    const DxvkInstance&               instance,\n          VkPhysicalDevice            adapter,\n    const VkDeviceCreateInfo*         deviceInfo) {\n    auto vk = instance.vki();\n\n    chainProperties(m_extensionsSupported, m_properties);\n    vk->vkGetPhysicalDeviceProperties2(adapter, &m_properties.core);\n\n    m_properties.driverVersion = decodeDriverVersion(\n      m_properties.vk12.driverID, m_properties.core.properties.driverVersion);\n  }\n\n\n  void DxvkDeviceCapabilities::initQueueProperties(\n    const DxvkInstance&               instance,\n          VkPhysicalDevice            adapter,\n    const VkDeviceCreateInfo*         deviceInfo) {\n    auto vk = instance.vki();\n\n    uint32_t queueCount = 0u;\n    vk->vkGetPhysicalDeviceQueueFamilyProperties2(adapter, &queueCount, nullptr);\n\n    // Use local array of base structures as the API requires,\n    // then copy the base structure back to the metadata array\n    std::vector<VkQueueFamilyProperties2> queueFamilies(queueCount, { VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2 });\n\n    // Chain extension structs directly into the metadata structure\n    m_queuesAvailable.resize(queueCount);\n\n    for (uint32_t i = 0u; i < queueCount; i++) {\n      auto& base = queueFamilies[i];\n      auto& meta = m_queuesAvailable[i];\n\n      if (m_featuresSupported.khrMaintenance9.maintenance9)\n        meta.ownershipTransfer.pNext = std::exchange(base.pNext, &meta.ownershipTransfer);\n    }\n\n    vk->vkGetPhysicalDeviceQueueFamilyProperties2(adapter, &queueCount, queueFamilies.data());\n\n    for (uint32_t i = 0u; i < queueCount; i++)\n      m_queuesAvailable[i].core = queueFamilies[i];\n\n    if (deviceInfo) {\n      // Only mark queues available that the device has been created with\n      for (uint32_t i = 0u; i < queueCount; i++) {\n        uint32_t queueCount = 0u;\n\n        for (uint32_t j = 0u; j < deviceInfo->queueCreateInfoCount && !queueCount; j++) {\n          if (deviceInfo->pQueueCreateInfos[j].queueFamilyIndex == i)\n            queueCount = deviceInfo->pQueueCreateInfos[j].queueCount;\n        }\n\n        m_queuesAvailable[i].core.queueFamilyProperties.queueCount = queueCount;\n      }\n    }\n  }\n\n\n  void DxvkDeviceCapabilities::initMemoryProperties(\n    const DxvkInstance&               instance,\n          VkPhysicalDevice            adapter) {\n    auto vk = instance.vki();\n\n    if (m_featuresSupported.extMemoryBudget)\n      m_memory.core.pNext = &m_memory.budget;\n\n    vk->vkGetPhysicalDeviceMemoryProperties2(adapter, &m_memory.core);\n  }\n\n\n  void DxvkDeviceCapabilities::disableUnusedFeatures(\n    const DxvkInstance&               instance) {\n    if (m_featuresSupported.extDescriptorHeap.descriptorHeap) {\n      // Only enable descriptor heaps on drivers that are either known to work,\n      // or are maintained well enough that any issues are likely to get fixed\n      bool enableDescriptorHeap = m_properties.vk12.driverID == VK_DRIVER_ID_MESA_RADV\n                               || m_properties.vk12.driverID == VK_DRIVER_ID_MESA_NVK\n                               || m_properties.vk12.driverID == VK_DRIVER_ID_MESA_LLVMPIPE\n                               || m_properties.vk12.driverID == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA\n                               || m_properties.vk12.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY;\n\n      applyTristate(enableDescriptorHeap, instance.options().enableDescriptorHeap);\n\n      if (!enableDescriptorHeap)\n        m_featuresSupported.extDescriptorHeap.descriptorHeap = VK_FALSE;\n    }\n\n    // Descriptor heap deprecates descriptor buffer\n    if (m_featuresSupported.extDescriptorHeap.descriptorHeap)\n      m_featuresSupported.extDescriptorBuffer.descriptorBuffer = VK_FALSE;\n\n    // Descriptor buffers cause perf regressions on some GPUs\n    if (m_featuresSupported.extDescriptorBuffer.descriptorBuffer) {\n      bool enableDescriptorBuffer = m_properties.vk12.driverID == VK_DRIVER_ID_MESA_RADV\n                                 || m_properties.vk12.driverID == VK_DRIVER_ID_MESA_NVK\n                                 || m_properties.vk12.driverID == VK_DRIVER_ID_MESA_LLVMPIPE;\n\n      // Pascal reportedly sees massive perf drops with descriptor buffer\n      if (m_properties.vk12.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY)\n        enableDescriptorBuffer = m_hasMeshShader;\n\n      // On RDNA2 and older, descriptor buffer implicitly disables fmask\n      // on amdvlk, which makes MSAA performance unusable on these GPUs.\n      if (m_properties.vk12.driverID == VK_DRIVER_ID_AMD_OPEN_SOURCE\n       || m_properties.vk12.driverID == VK_DRIVER_ID_AMD_PROPRIETARY)\n        enableDescriptorBuffer = !m_hasFmask;\n\n      applyTristate(enableDescriptorBuffer, instance.options().enableDescriptorBuffer);\n\n      if (!enableDescriptorBuffer)\n        m_featuresSupported.extDescriptorBuffer.descriptorBuffer = VK_FALSE;\n    }\n\n    // Disable unified layouts if disabled via config\n    if (!instance.options().enableUnifiedImageLayout)\n      m_featuresSupported.khrUnifiedImageLayouts.unifiedImageLayouts = VK_FALSE;\n\n    if (env::is32BitHostPlatform()) {\n      // CUDA interop is unnecessary on 32-bit, no games use it\n      m_featuresSupported.nvxBinaryImport = VK_FALSE;\n      m_featuresSupported.nvxImageViewHandle = VK_FALSE;\n\n      // Reflex is broken on 32-bit\n      m_featuresSupported.nvLowLatency2 = VK_FALSE;\n    }\n\n    // EXT_multi_draw is broken on proprietary qcom on some devices\n    if (m_properties.vk12.driverID == VK_DRIVER_ID_QUALCOMM_PROPRIETARY)\n      m_featuresSupported.extMultiDraw.multiDraw = VK_FALSE;\n\n    // If we're running off a device without a sparse binding queue,\n    // disable all the sparse binding features as well\n    uint32_t sparseQueue = findQueueFamily(VK_QUEUE_SPARSE_BINDING_BIT, VK_QUEUE_SPARSE_BINDING_BIT);\n\n    if (sparseQueue == VK_QUEUE_FAMILY_IGNORED\n     || !m_featuresSupported.core.features.sparseBinding\n     || !m_featuresSupported.core.features.sparseResidencyBuffer\n     || !m_featuresSupported.core.features.sparseResidencyImage2D\n     || !m_featuresSupported.core.features.sparseResidencyAliased) {\n      m_featuresSupported.core.features.sparseBinding = VK_FALSE;\n      m_featuresSupported.core.features.sparseResidencyBuffer = VK_FALSE;\n      m_featuresSupported.core.features.sparseResidencyImage2D = VK_FALSE;\n      m_featuresSupported.core.features.sparseResidencyImage3D = VK_FALSE;\n      m_featuresSupported.core.features.sparseResidency2Samples = VK_FALSE;\n      m_featuresSupported.core.features.sparseResidency4Samples = VK_FALSE;\n      m_featuresSupported.core.features.sparseResidency8Samples = VK_FALSE;\n      m_featuresSupported.core.features.sparseResidency16Samples = VK_FALSE;\n      m_featuresSupported.core.features.sparseResidencyAliased = VK_FALSE;\n    }\n\n    // robustness2 is stronger than the Vulkan 1.3 feature\n    if (m_featuresSupported.extRobustness2.robustImageAccess2)\n      m_featuresSupported.vk13.robustImageAccess = VK_FALSE;\n\n    // Vertex attribute divisor is unusable before spec version 3\n    if (m_extensionsSupported.extVertexAttributeDivisor.specVersion < 3u) {\n      m_featuresSupported.extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor = VK_FALSE;\n      m_featuresSupported.extVertexAttributeDivisor.vertexAttributeInstanceRateZeroDivisor = VK_FALSE;\n    }\n\n    // For line rasterization, ensure that the feature set actually makes sense\n    if (!m_featuresSupported.core.features.wideLines || !m_featuresSupported.extLineRasterization.rectangularLines) {\n      m_featuresSupported.core.features.wideLines = VK_FALSE;\n      m_featuresSupported.extLineRasterization.rectangularLines = VK_FALSE;\n      m_featuresSupported.extLineRasterization.smoothLines = VK_FALSE;\n    }\n\n    // Ensure we only enable one of present_id or present_id_2. Prefer the\n    // older versions of the present_id/wait extensions since the newer ones\n    // cause issues with external layers and apparently some Wayland setups\n    // on Mesa for unknown reasons.\n    if (m_featuresSupported.khrPresentId.presentId)\n      m_featuresSupported.khrPresentId2.presentId2 = VK_FALSE;\n\n    // Sanitize features with other feature dependencies\n    if (!m_featuresSupported.core.features.shaderInt16)\n      m_featuresSupported.vk11.storagePushConstant16 = VK_FALSE;\n\n    if (!m_featuresSupported.khrPresentId2.presentId2)\n      m_featuresSupported.khrPresentWait2.presentWait2 = VK_FALSE;\n\n    if (!m_featuresSupported.khrPresentId.presentId)\n      m_featuresSupported.khrPresentWait.presentWait = VK_FALSE;\n\n    if (!m_featuresSupported.khrPresentId.presentId\n     && !m_featuresSupported.khrPresentId2.presentId2)\n      m_featuresSupported.nvLowLatency2 = VK_FALSE;\n  }\n\n\n  void DxvkDeviceCapabilities::enableFeaturesAndExtensions() {\n    // Some extensions functionally work as \"physical device\" extensions\n    // and will not be explicitly enabled during device creation.\n    static const std::array<VkExtensionProperties, 1u> s_passiveExtensions = {{\n      vk::makeExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME),\n    }};\n\n    for (const auto& f : getFeatureList()) {\n      // Enable any supported feature that we know about\n      if ((*f.featureEnabled = *f.featureSupported)) {\n        // Also enable the corresponding extension if we haven't done so yet'\n        if (f.extensionEnabled && !f.extensionEnabled->specVersion) {\n          f.extensionEnabled->specVersion = f.extensionSupported->specVersion;\n\n          auto entry = std::find_if(s_passiveExtensions.begin(), s_passiveExtensions.end(),\n            [&f] (const VkExtensionProperties& passive) {\n              return !std::strncmp(f.extensionEnabled->extensionName, passive.extensionName, sizeof(passive.extensionName));\n            });\n\n          if (entry == s_passiveExtensions.end())\n            m_extensionList.push_back(f.extensionEnabled);\n        }\n      }\n    }\n\n    // Make sure we have a full pNext chain to pass to the device\n    chainFeatures(m_extensionsEnabled, m_featuresEnabled);\n  }\n\n\n  void DxvkDeviceCapabilities::enableQueues() {\n    m_queueMapping.graphics.family = findQueueFamily(\n      VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT,\n      VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT);\n\n    if (m_queueMapping.graphics.family == VK_QUEUE_FAMILY_IGNORED)\n      return;\n\n    uint32_t computeQueue = findQueueFamily(\n      VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT,\n      VK_QUEUE_COMPUTE_BIT);\n\n    if (computeQueue == VK_QUEUE_FAMILY_IGNORED)\n      computeQueue = m_queueMapping.graphics.family;\n\n    m_queueMapping.transfer.family = findQueueFamily(\n      VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT,\n      VK_QUEUE_TRANSFER_BIT);\n\n    if (m_queueMapping.transfer.family == VK_QUEUE_FAMILY_IGNORED)\n      m_queueMapping.transfer.family = computeQueue;\n\n    // Prefer using the graphics queue as a sparse binding queue if possible\n    auto& graphicsQueue = m_queuesAvailable[m_queueMapping.graphics.family].core;\n\n    if (graphicsQueue.queueFamilyProperties.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) {\n      m_queueMapping.sparse.family = m_queueMapping.graphics.family;\n    } else {\n      m_queueMapping.sparse.family = findQueueFamily(\n        VK_QUEUE_SPARSE_BINDING_BIT,\n        VK_QUEUE_SPARSE_BINDING_BIT);\n    }\n\n    // Actually enable all the queues\n    enableQueue(m_queueMapping.graphics);\n    enableQueue(m_queueMapping.transfer);\n    enableQueue(m_queueMapping.sparse);\n\n    // Fix up queue priority pointers\n    uint32_t maxQueueCount = 0u;\n\n    for (auto& q : m_queuesEnabled)\n      maxQueueCount = std::max(maxQueueCount, q.queueCount);\n\n    m_queuePriorities.resize(maxQueueCount, 1.0f);\n\n    for (auto& q : m_queuesEnabled)\n      q.pQueuePriorities = m_queuePriorities.data();\n  }\n\n\n  void DxvkDeviceCapabilities::enableQueue(\n          DxvkDeviceQueueIndex        queue) {\n    if (queue.family == VK_QUEUE_FAMILY_IGNORED)\n      return;\n\n    for (auto& q : m_queuesEnabled) {\n      if (q.queueFamilyIndex == queue.family) {\n        q.queueCount = queue.index + 1u;\n        return;\n      }\n    }\n\n    auto& q = m_queuesEnabled.emplace_back();\n    q.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;\n    q.queueFamilyIndex = queue.family;\n    q.queueCount = queue.index + 1u;\n  }\n\n\n  uint32_t DxvkDeviceCapabilities::findQueueFamily(\n          VkQueueFlags                mask,\n          VkQueueFlags                flags) const {\n    for (uint32_t i = 0; i < m_queuesAvailable.size(); i++) {\n      if ((m_queuesAvailable[i].core.queueFamilyProperties.queueFlags & mask) == flags\n       && (m_queuesAvailable[i].core.queueFamilyProperties.queueCount))\n        return i;\n    }\n\n    return VK_QUEUE_FAMILY_IGNORED;\n  }\n\n\n  std::optional<std::string> DxvkDeviceCapabilities::checkDeviceCompatibility() {\n    if (m_properties.core.properties.apiVersion < DxvkVulkanApiVersion) {\n      return str::format(\"Device does not support Vulkan \",\n        VK_API_VERSION_MAJOR(DxvkVulkanApiVersion), \".\",\n        VK_API_VERSION_MINOR(DxvkVulkanApiVersion));\n    }\n\n    if (m_queueMapping.graphics.family == VK_QUEUE_FAMILY_IGNORED)\n      return std::string(\"Device does not have a graphics queue\");\n\n    for (const auto& f : getFeatureList()) {\n      if (f.featureRequired && !(*f.featureEnabled)) {\n        std::string message = str::format(\"Device does not support required feature '\", f.readableName, \"'\");\n\n        if (f.extensionEnabled)\n          message += str::format(\" (extension: \", f.extensionEnabled->extensionName, \")\");\n\n        return message;\n      }\n    }\n\n    if (!m_featuresEnabled.extDescriptorHeap.descriptorHeap && m_properties.core.properties.limits.maxPushConstantsSize < MaxTotalPushDataSize)\n      return str::format(\"Device does not support \", MaxTotalPushDataSize, \" of push data\");\n\n    return std::nullopt;\n  }\n\n\n  void DxvkDeviceCapabilities::chainFeatures(\n    const DxvkDeviceExtensionInfo&    extensions,\n          DxvkDeviceFeatures&         features) {\n    #define HANDLE_CORE(name) chainFeature(nullptr, &features.core, &features.name)\n    #define HANDLE_EXT(name) chainFeature(&extensions.name, &features.core, &features.name)\n\n    CORE_VERSIONS\n    EXTENSIONS_WITH_FEATURES\n\n    #undef HANDLE_CORE\n    #undef HANDLE_EXT\n  }\n\n\n  void DxvkDeviceCapabilities::chainProperties(\n    const DxvkDeviceExtensionInfo&    extensions,\n          DxvkDeviceInfo&             properties) {\n    #define HANDLE_CORE(name) chainProperties(nullptr, &properties.core, &properties.name)\n    #define HANDLE_EXT(name) chainProperties(&extensions.name, &properties.core, &properties.name)\n\n    CORE_VERSIONS\n    EXTENSIONS_WITH_PROPERTIES\n\n    #undef HANDLE_CORE\n    #undef HANDLE_EXT\n  }\n\n\n  std::vector<DxvkDeviceCapabilities::FeatureEntry> DxvkDeviceCapabilities::getFeatureList() {\n    #define ENABLE_FEATURE(version, name, require)        \\\n      FeatureEntry { nullptr, nullptr,                    \\\n        &m_featuresSupported.version.name,                \\\n        &m_featuresEnabled.version.name,                  \\\n        require, #name }\n\n    #define ENABLE_EXT(ext, require)                      \\\n      FeatureEntry {                                      \\\n        &m_extensionsSupported.ext,                       \\\n        &m_extensionsEnabled.ext,                         \\\n        &m_featuresSupported.ext,                         \\\n        &m_featuresEnabled.ext,                           \\\n        require, #ext }\n\n    #define ENABLE_EXT_FEATURE(ext, name, require)        \\\n      FeatureEntry {                                      \\\n        &m_extensionsSupported.ext,                       \\\n        &m_extensionsEnabled.ext,                         \\\n        &m_featuresSupported.ext.name,                    \\\n        &m_featuresEnabled.ext.name,                      \\\n        require, #name }\n\n    return {{\n      ENABLE_FEATURE(core.features, depthBiasClamp, true),\n      ENABLE_FEATURE(core.features, depthBounds, false),\n      ENABLE_FEATURE(core.features, depthClamp, true),\n      ENABLE_FEATURE(core.features, drawIndirectFirstInstance, false),\n      ENABLE_FEATURE(core.features, dualSrcBlend, true),\n      ENABLE_FEATURE(core.features, fillModeNonSolid, true),\n      ENABLE_FEATURE(core.features, fragmentStoresAndAtomics, false),\n      ENABLE_FEATURE(core.features, fullDrawIndexUint32, true),\n      ENABLE_FEATURE(core.features, geometryShader, true),\n      ENABLE_FEATURE(core.features, imageCubeArray, true),\n      ENABLE_FEATURE(core.features, independentBlend, true),\n      ENABLE_FEATURE(core.features, logicOp, false),\n      ENABLE_FEATURE(core.features, multiDrawIndirect, true),\n      ENABLE_FEATURE(core.features, multiViewport, true),\n      ENABLE_FEATURE(core.features, occlusionQueryPrecise, true),\n      ENABLE_FEATURE(core.features, pipelineStatisticsQuery, false),\n      ENABLE_FEATURE(core.features, robustBufferAccess, true),\n      ENABLE_FEATURE(core.features, sampleRateShading, true),\n      ENABLE_FEATURE(core.features, samplerAnisotropy, false),\n      ENABLE_FEATURE(core.features, shaderClipDistance, true),\n      ENABLE_FEATURE(core.features, shaderCullDistance, true),\n      ENABLE_FEATURE(core.features, shaderFloat64, false),\n      ENABLE_FEATURE(core.features, shaderImageGatherExtended, true),\n      ENABLE_FEATURE(core.features, shaderInt16, false),\n      ENABLE_FEATURE(core.features, shaderInt64, true),\n      ENABLE_FEATURE(core.features, shaderUniformBufferArrayDynamicIndexing, false),\n      ENABLE_FEATURE(core.features, shaderSampledImageArrayDynamicIndexing, true),\n      ENABLE_FEATURE(core.features, shaderStorageBufferArrayDynamicIndexing, false),\n      ENABLE_FEATURE(core.features, shaderStorageImageArrayDynamicIndexing, false),\n      ENABLE_FEATURE(core.features, sparseBinding, false),\n      ENABLE_FEATURE(core.features, sparseResidencyBuffer, false),\n      ENABLE_FEATURE(core.features, sparseResidencyImage2D, false),\n      ENABLE_FEATURE(core.features, sparseResidencyImage3D, false),\n      ENABLE_FEATURE(core.features, sparseResidency2Samples, false),\n      ENABLE_FEATURE(core.features, sparseResidency4Samples, false),\n      ENABLE_FEATURE(core.features, sparseResidency8Samples, false),\n      ENABLE_FEATURE(core.features, sparseResidency16Samples, false),\n      ENABLE_FEATURE(core.features, sparseResidencyAliased, false),\n      ENABLE_FEATURE(core.features, shaderResourceResidency, false),\n      ENABLE_FEATURE(core.features, shaderResourceMinLod, false),\n      ENABLE_FEATURE(core.features, tessellationShader, false),\n      ENABLE_FEATURE(core.features, textureCompressionBC, true),\n      ENABLE_FEATURE(core.features, variableMultisampleRate, false),\n      ENABLE_FEATURE(core.features, vertexPipelineStoresAndAtomics, false),\n      ENABLE_FEATURE(core.features, wideLines, false),\n\n      ENABLE_FEATURE(vk11, shaderDrawParameters, true),\n      ENABLE_FEATURE(vk11, storagePushConstant16, false),\n\n      ENABLE_FEATURE(vk12, bufferDeviceAddress, true),\n      ENABLE_FEATURE(vk12, descriptorIndexing, true),\n      ENABLE_FEATURE(vk12, shaderUniformTexelBufferArrayDynamicIndexing, false),\n      ENABLE_FEATURE(vk12, shaderStorageTexelBufferArrayDynamicIndexing, false),\n      ENABLE_FEATURE(vk12, shaderUniformBufferArrayNonUniformIndexing, false),\n      ENABLE_FEATURE(vk12, shaderSampledImageArrayNonUniformIndexing, false),\n      ENABLE_FEATURE(vk12, shaderStorageBufferArrayNonUniformIndexing, false),\n      ENABLE_FEATURE(vk12, shaderStorageImageArrayNonUniformIndexing, false),\n      ENABLE_FEATURE(vk12, shaderUniformTexelBufferArrayNonUniformIndexing, false),\n      ENABLE_FEATURE(vk12, shaderStorageTexelBufferArrayNonUniformIndexing, false),\n      ENABLE_FEATURE(vk12, descriptorBindingSampledImageUpdateAfterBind, true),\n      ENABLE_FEATURE(vk12, descriptorBindingUpdateUnusedWhilePending, true),\n      ENABLE_FEATURE(vk12, descriptorBindingPartiallyBound, true),\n      ENABLE_FEATURE(vk12, drawIndirectCount, false),\n      ENABLE_FEATURE(vk12, hostQueryReset, true),\n      ENABLE_FEATURE(vk12, runtimeDescriptorArray, true),\n      ENABLE_FEATURE(vk12, samplerFilterMinmax, false),\n      ENABLE_FEATURE(vk12, samplerMirrorClampToEdge, true),\n      ENABLE_FEATURE(vk12, scalarBlockLayout, true),\n      ENABLE_FEATURE(vk12, shaderFloat16, false),\n      ENABLE_FEATURE(vk12, shaderInt8, false),\n      ENABLE_FEATURE(vk12, shaderOutputViewportIndex, false),\n      ENABLE_FEATURE(vk12, shaderOutputLayer, false),\n      ENABLE_FEATURE(vk12, timelineSemaphore, true),\n      ENABLE_FEATURE(vk12, uniformBufferStandardLayout, true),\n      ENABLE_FEATURE(vk12, vulkanMemoryModel, true),\n\n      ENABLE_FEATURE(vk13, computeFullSubgroups, true),\n      ENABLE_FEATURE(vk13, dynamicRendering, true),\n      ENABLE_FEATURE(vk13, maintenance4, true),\n      ENABLE_FEATURE(vk13, robustImageAccess, false),\n      ENABLE_FEATURE(vk13, pipelineCreationCacheControl, false),\n      ENABLE_FEATURE(vk13, shaderDemoteToHelperInvocation, true),\n      ENABLE_FEATURE(vk13, shaderZeroInitializeWorkgroupMemory, true),\n      ENABLE_FEATURE(vk13, subgroupSizeControl, true),\n      ENABLE_FEATURE(vk13, synchronization2, true),\n\n      /* Allows sampling currently bound render targets for client APIs */\n      ENABLE_EXT_FEATURE(extAttachmentFeedbackLoopLayout, attachmentFeedbackLoopLayout, false),\n\n      /* Fix some border color jank due to hardware differences */\n      ENABLE_EXT_FEATURE(extBorderColorSwizzle, borderColorSwizzle, false),\n      ENABLE_EXT_FEATURE(extBorderColorSwizzle, borderColorSwizzleFromImage, false),\n\n      /* Enables client API features */\n      ENABLE_EXT(extConservativeRasterization, false),\n\n      /* Legacy feature exposed in client APIs */\n      ENABLE_EXT_FEATURE(extCustomBorderColor, customBorderColors, false),\n      ENABLE_EXT_FEATURE(extCustomBorderColor, customBorderColorWithoutFormat, false),\n\n      /* Depth clip matches D3D semantics where depth clamp does not */\n      ENABLE_EXT_FEATURE(extDepthClipEnable, depthClipEnable, true),\n\n      /* Controls depth bias behaviour with emulated depth formats */\n      ENABLE_EXT_FEATURE(extDepthBiasControl, depthBiasControl, false),\n      ENABLE_EXT_FEATURE(extDepthBiasControl, leastRepresentableValueForceUnormRepresentation, false),\n      ENABLE_EXT_FEATURE(extDepthBiasControl, floatRepresentation, false),\n      ENABLE_EXT_FEATURE(extDepthBiasControl, depthBiasExact, false),\n\n      /* Deprecated, used when descriptor heap is unavailable */\n      ENABLE_EXT_FEATURE(extDescriptorBuffer, descriptorBuffer, false),\n\n      /* Descriptor heaps for a more efficient binding model */\n      ENABLE_EXT_FEATURE(extDescriptorHeap, descriptorHeap, false),\n\n      /* Dynamic state to further improve the graphics_pipeline_library experience */\n      ENABLE_EXT_FEATURE(extExtendedDynamicState3, extendedDynamicState3AlphaToCoverageEnable, false),\n      ENABLE_EXT_FEATURE(extExtendedDynamicState3, extendedDynamicState3DepthClipEnable, false),\n      ENABLE_EXT_FEATURE(extExtendedDynamicState3, extendedDynamicState3RasterizationSamples, false),\n      ENABLE_EXT_FEATURE(extExtendedDynamicState3, extendedDynamicState3SampleMask, false),\n      ENABLE_EXT_FEATURE(extExtendedDynamicState3, extendedDynamicState3LineRasterizationMode, false),\n      ENABLE_EXT_FEATURE(extExtendedDynamicState3, extendedDynamicState3SampleLocationsEnable, false),\n\n      /* Enables client API features */\n      ENABLE_EXT_FEATURE(extFragmentShaderInterlock, fragmentShaderSampleInterlock, false),\n      ENABLE_EXT_FEATURE(extFragmentShaderInterlock, fragmentShaderPixelInterlock, false),\n\n      /* Windows-only extension to work around driver-side FSE issues */\n      ENABLE_EXT(extFullScreenExclusive, false),\n\n      /* Graphics pipeline libraries for stutter-free gameplay */\n      ENABLE_EXT_FEATURE(extGraphicsPipelineLibrary, graphicsPipelineLibrary, false),\n\n      /* HDR metadata */\n      ENABLE_EXT(extHdrMetadata, false),\n\n      /* Line rasterization features for client APIs */\n      ENABLE_EXT_FEATURE(extLineRasterization, rectangularLines,  false),\n      ENABLE_EXT_FEATURE(extLineRasterization, smoothLines, false),\n\n      /* Memory budget and priority for improved memory management */\n      ENABLE_EXT(extMemoryBudget, false),\n      ENABLE_EXT_FEATURE(extMemoryPriority, memoryPriority, false),\n\n      /* Optionally used to batch consecutive draws */\n      ENABLE_EXT_FEATURE(extMultiDraw, multiDraw, false),\n\n      /* Legacy cubemap for older client APIs */\n      ENABLE_EXT_FEATURE(extNonSeamlessCubeMap, nonSeamlessCubeMap, false),\n\n      /* Enables more dynamic driver-side memory management */\n      ENABLE_EXT_FEATURE(extPageableDeviceLocalMemory, pageableDeviceLocalMemory, false),\n\n      /* Robustness, all features effectively required for correctness */\n      ENABLE_EXT_FEATURE(extRobustness2, robustBufferAccess2, true),\n      ENABLE_EXT_FEATURE(extRobustness2, robustImageAccess2, false),\n      ENABLE_EXT_FEATURE(extRobustness2, nullDescriptor, true),\n\n      /* Sample locations, used to \"disable\" MSAA rendering */\n      ENABLE_EXT(extSampleLocations, false),\n\n      /* Shader module identifier, used for pipeline lifetime management in 32-bit */\n      ENABLE_EXT_FEATURE(extShaderModuleIdentifier, shaderModuleIdentifier, false),\n\n      /* Stencil export, used both internally and in client APIs */\n      ENABLE_EXT(extShaderStencilExport, false),\n\n      /* HDR color space support */\n      ENABLE_EXT(extSwapchainColorSpace, false),\n\n      /* Swapchain maintenance, used to implement proper synchronization\n       * and dynamic present modes to avoid swapchain recreation */\n      ENABLE_EXT_FEATURE(extSwapchainMaintenance1, swapchainMaintenance1, false),\n\n      /* Transform feedback, required for some client APIs */\n      ENABLE_EXT_FEATURE(extTransformFeedback, transformFeedback, false),\n      ENABLE_EXT_FEATURE(extTransformFeedback, geometryStreams, false),\n\n      /* Vertex attribute divisor, used by client APIs */\n      ENABLE_EXT_FEATURE(extVertexAttributeDivisor, vertexAttributeInstanceRateDivisor, false),\n      ENABLE_EXT_FEATURE(extVertexAttributeDivisor, vertexAttributeInstanceRateZeroDivisor, false),\n\n      /* External memory features for wine */\n      ENABLE_EXT(khrExternalMemoryWin32, false),\n      ENABLE_EXT(khrExternalSemaphoreWin32, false),\n\n      /* LOAD_OP_NONE for certain tiler optimizations. Core feature\n       * in Vulkan 1.4, so probably supported by everything we need. */\n      ENABLE_EXT(khrLoadStoreOpNone, true),\n\n      /* Maintenance features, relied on in various parts of the code */\n      ENABLE_EXT_FEATURE(khrMaintenance5, maintenance5, true),\n      ENABLE_EXT_FEATURE(khrMaintenance6, maintenance6, true),\n      ENABLE_EXT_FEATURE(khrMaintenance7, maintenance7, false),\n      ENABLE_EXT_FEATURE(khrMaintenance8, maintenance8, false),\n      ENABLE_EXT_FEATURE(khrMaintenance9, maintenance9, false),\n      ENABLE_EXT_FEATURE(khrMaintenance10, maintenance10, false),\n\n      /* Dependency for graphics pipeline library */\n      ENABLE_EXT(khrPipelineLibrary, true),\n\n      /* Present wait, used for frame pacing and statistics */\n      ENABLE_EXT_FEATURE(khrPresentId, presentId, false),\n      ENABLE_EXT_FEATURE(khrPresentId2, presentId2, false),\n      ENABLE_EXT_FEATURE(khrPresentWait, presentWait, false),\n      ENABLE_EXT_FEATURE(khrPresentWait2, presentWait2, false),\n\n      /* Used for shader compilation in addition to regular float_controls features */\n      ENABLE_EXT_FEATURE(khrShaderFloatControls2, shaderFloatControls2, false),\n\n      /* Subgroup uniform control flow for some built-in shaders */\n      ENABLE_EXT_FEATURE(khrShaderSubgroupUniformControlFlow, shaderSubgroupUniformControlFlow, false),\n\n      /* Untyped pointers, dependency for descriptor heaps */\n      ENABLE_EXT_FEATURE(khrShaderUntypedPointers, shaderUntypedPointers, false),\n\n      /* Swapchain, needed for presentation */\n      ENABLE_EXT(khrSwapchain, true),\n\n      /* Swapchain maintenance, used to implement proper synchronization\n       * and dynamic present modes to avoid swapchain recreation */\n      ENABLE_EXT_FEATURE(khrSwapchainMaintenance1, swapchainMaintenance1, false),\n\n      /* Mutable format used to change srgb-ness of swapchain views */\n      ENABLE_EXT(khrSwapchainMutableFormat, false),\n\n      /* Use GENERAL layout for everything */\n      ENABLE_EXT_FEATURE(khrUnifiedImageLayouts, unifiedImageLayouts, false),\n\n      /* Keyed mutex support in wine */\n      ENABLE_EXT(khrWin32KeyedMutex, false),\n\n      /* Reflex support */\n      ENABLE_EXT(nvLowLatency2, false),\n\n      /* Raw access chains, improves performance on NV */\n      ENABLE_EXT_FEATURE(nvRawAccessChains, shaderRawAccessChains, false),\n\n      /* CUDA interop extensions */\n      ENABLE_EXT(nvxBinaryImport, false),\n      ENABLE_EXT(nvxImageViewHandle, false),\n    }};\n\n    #undef ENABLE_FEATURE\n    #undef ENABLE_EXT\n    #undef ENABLE_EXT_FEATURE\n  }\n\n\n  Version DxvkDeviceCapabilities::decodeDriverVersion(VkDriverId driverId, uint32_t version) {\n    switch (driverId) {\n      case VK_DRIVER_ID_NVIDIA_PROPRIETARY:\n        return Version(\n          (version >> 22) & 0x3ff,\n          (version >> 14) & 0x0ff,\n          (version >>  6) & 0x0ff);\n        break;\n\n      case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:\n        return Version(version >> 14, version & 0x3fff, 0);\n\n      default:\n        return Version(\n          VK_API_VERSION_MAJOR(version),\n          VK_API_VERSION_MINOR(version),\n          VK_API_VERSION_PATCH(version));\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_device_info.h",
    "content": "#pragma once\n\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"dxvk_include.h\"\n\n#include \"../util/util_version.h\"\n\nnamespace dxvk {\n\n  class DxvkInstance;\n\n  /**\n   * \\brief Device info\n   * \n   * Stores core properties and a bunch of extension-specific\n   * properties, if the respective extensions are available.\n   * Structures for unsupported extensions will be undefined,\n   * so before using them, check whether they are supported.\n   */\n  struct DxvkDeviceInfo {\n    Version                                                   driverVersion = { };\n    VkPhysicalDeviceProperties2                               core                            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };\n    VkPhysicalDeviceVulkan11Properties                        vk11                            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES };\n    VkPhysicalDeviceVulkan12Properties                        vk12                            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES };\n    VkPhysicalDeviceVulkan13Properties                        vk13                            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_PROPERTIES };\n    VkPhysicalDeviceConservativeRasterizationPropertiesEXT    extConservativeRasterization    = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT };\n    VkPhysicalDeviceCustomBorderColorPropertiesEXT            extCustomBorderColor            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT };\n    VkPhysicalDeviceDescriptorBufferPropertiesEXT             extDescriptorBuffer             = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT };\n    VkPhysicalDeviceDescriptorHeapPropertiesEXT               extDescriptorHeap               = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT };\n    VkPhysicalDeviceExtendedDynamicState3PropertiesEXT        extExtendedDynamicState3        = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_PROPERTIES_EXT };\n    VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT      extGraphicsPipelineLibrary      = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT };\n    VkPhysicalDeviceLineRasterizationPropertiesEXT            extLineRasterization            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_PROPERTIES_EXT };\n    VkPhysicalDeviceMultiDrawPropertiesEXT                    extMultiDraw                    = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT };\n    VkPhysicalDeviceRobustness2PropertiesEXT                  extRobustness2                  = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT };\n    VkPhysicalDeviceSampleLocationsPropertiesEXT              extSampleLocations              = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT };\n    VkPhysicalDeviceTransformFeedbackPropertiesEXT            extTransformFeedback            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT };\n    VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT       extVertexAttributeDivisor       = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT };\n    VkPhysicalDeviceMaintenance5PropertiesKHR                 khrMaintenance5                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR };\n    VkPhysicalDeviceMaintenance6PropertiesKHR                 khrMaintenance6                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_6_PROPERTIES_KHR };\n    VkPhysicalDeviceMaintenance7PropertiesKHR                 khrMaintenance7                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_7_PROPERTIES_KHR };\n    VkPhysicalDeviceMaintenance9PropertiesKHR                 khrMaintenance9                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_9_PROPERTIES_KHR };\n    VkPhysicalDeviceMaintenance10PropertiesKHR                khrMaintenance10                = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_10_PROPERTIES_KHR };\n  };\n\n\n  /**\n   * \\brief Device features\n   * \n   * Stores core features and extension-specific features.\n   * If the respective extensions are not available, the\n   * extended features will be marked as unsupported.\n   */\n  struct DxvkDeviceFeatures {\n    VkPhysicalDeviceFeatures2                                 core                            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };\n    VkPhysicalDeviceVulkan11Features                          vk11                            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES };\n    VkPhysicalDeviceVulkan12Features                          vk12                            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES };;\n    VkPhysicalDeviceVulkan13Features                          vk13                            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES };;\n    VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT   extAttachmentFeedbackLoopLayout = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT };\n    VkPhysicalDeviceBorderColorSwizzleFeaturesEXT             extBorderColorSwizzle           = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BORDER_COLOR_SWIZZLE_FEATURES_EXT };\n    VkBool32                                                  extConservativeRasterization    = VK_FALSE;\n    VkPhysicalDeviceCustomBorderColorFeaturesEXT              extCustomBorderColor            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT };\n    VkPhysicalDeviceDepthClipEnableFeaturesEXT                extDepthClipEnable              = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT };\n    VkPhysicalDeviceDepthBiasControlFeaturesEXT               extDepthBiasControl             = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_BIAS_CONTROL_FEATURES_EXT };\n    VkPhysicalDeviceDescriptorBufferFeaturesEXT               extDescriptorBuffer             = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT };\n    VkPhysicalDeviceDescriptorHeapFeaturesEXT                 extDescriptorHeap               = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_FEATURES_EXT };\n    VkPhysicalDeviceExtendedDynamicState3FeaturesEXT          extExtendedDynamicState3        = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT };\n    VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT        extFragmentShaderInterlock      = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT };\n    VkBool32                                                  extFullScreenExclusive          = VK_FALSE;\n    VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT        extGraphicsPipelineLibrary      = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT };\n    VkBool32                                                  extHdrMetadata                  = VK_FALSE;\n    VkPhysicalDeviceLineRasterizationFeaturesEXT              extLineRasterization            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT };\n    VkBool32                                                  extMemoryBudget                 = VK_FALSE;\n    VkPhysicalDeviceMemoryPriorityFeaturesEXT                 extMemoryPriority               = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT };\n    VkPhysicalDeviceMultiDrawFeaturesEXT                      extMultiDraw                    = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT };\n    VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT             extNonSeamlessCubeMap           = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT };\n    VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT      extPageableDeviceLocalMemory    = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT };\n    VkPhysicalDeviceRobustness2FeaturesEXT                    extRobustness2                  = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT };\n    VkBool32                                                  extSampleLocations              = VK_FALSE;\n    VkPhysicalDeviceShaderModuleIdentifierFeaturesEXT         extShaderModuleIdentifier       = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MODULE_IDENTIFIER_FEATURES_EXT };\n    VkBool32                                                  extShaderStencilExport          = VK_FALSE;\n    VkBool32                                                  extSwapchainColorSpace          = VK_FALSE;\n    VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT          extSwapchainMaintenance1        = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT };\n    VkPhysicalDeviceTransformFeedbackFeaturesEXT              extTransformFeedback            = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT };\n    VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT         extVertexAttributeDivisor       = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES };\n    VkBool32                                                  khrExternalMemoryWin32          = VK_FALSE;\n    VkBool32                                                  khrExternalSemaphoreWin32       = VK_FALSE;\n    VkBool32                                                  khrLoadStoreOpNone              = VK_FALSE;\n    VkPhysicalDeviceMaintenance5FeaturesKHR                   khrMaintenance5                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES_KHR };\n    VkPhysicalDeviceMaintenance6FeaturesKHR                   khrMaintenance6                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_6_FEATURES_KHR };\n    VkPhysicalDeviceMaintenance7FeaturesKHR                   khrMaintenance7                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_7_FEATURES_KHR };\n    VkPhysicalDeviceMaintenance8FeaturesKHR                   khrMaintenance8                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_8_FEATURES_KHR };\n    VkPhysicalDeviceMaintenance9FeaturesKHR                   khrMaintenance9                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_9_FEATURES_KHR };\n    VkPhysicalDeviceMaintenance10FeaturesKHR                  khrMaintenance10                = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_10_FEATURES_KHR };\n    VkBool32                                                  khrPipelineLibrary              = VK_FALSE;\n    VkPhysicalDevicePresentIdFeaturesKHR                      khrPresentId                    = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR };\n    VkPhysicalDevicePresentId2FeaturesKHR                     khrPresentId2                   = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_2_FEATURES_KHR };\n    VkPhysicalDevicePresentWaitFeaturesKHR                    khrPresentWait                  = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR };\n    VkPhysicalDevicePresentWait2FeaturesKHR                   khrPresentWait2                 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_2_FEATURES_KHR };\n    VkPhysicalDeviceShaderFloatControls2FeaturesKHR           khrShaderFloatControls2         = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT_CONTROLS_2_FEATURES_KHR };\n    VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR khrShaderSubgroupUniformControlFlow = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR };\n    VkPhysicalDeviceShaderUntypedPointersFeaturesKHR          khrShaderUntypedPointers        = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_UNTYPED_POINTERS_FEATURES_KHR };\n    VkBool32                                                  khrSwapchain                    = VK_FALSE;\n    VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR          khrSwapchainMaintenance1        = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR };\n    VkPhysicalDeviceUnifiedImageLayoutsFeaturesKHR            khrUnifiedImageLayouts          = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFIED_IMAGE_LAYOUTS_FEATURES_KHR };\n    VkBool32                                                  khrSwapchainMutableFormat       = VK_FALSE;\n    VkBool32                                                  khrWin32KeyedMutex              = VK_FALSE;\n    VkBool32                                                  nvLowLatency2                   = VK_FALSE;\n    VkPhysicalDeviceRawAccessChainsFeaturesNV                 nvRawAccessChains               = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAW_ACCESS_CHAINS_FEATURES_NV };\n    VkBool32                                                  nvxBinaryImport                 = VK_FALSE;\n    VkBool32                                                  nvxImageViewHandle              = VK_FALSE;\n  };\n\n\n  /**\n   * \\brief Device memory properties\n   */\n  struct DxvkDeviceMemoryInfo {\n    VkPhysicalDeviceMemoryProperties2         core   = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2 };\n    VkPhysicalDeviceMemoryBudgetPropertiesEXT budget = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };\n  };\n\n\n  /**\n   * \\brief Vulkan extension info\n   */\n  struct DxvkDeviceExtensionInfo {\n    VkExtensionProperties extAttachmentFeedbackLoopLayout   = vk::makeExtension(VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME);\n    VkExtensionProperties extBorderColorSwizzle             = vk::makeExtension(VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME);\n    VkExtensionProperties extConservativeRasterization      = vk::makeExtension(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME);\n    VkExtensionProperties extCustomBorderColor              = vk::makeExtension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);\n    VkExtensionProperties extDepthClipEnable                = vk::makeExtension(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME);\n    VkExtensionProperties extDepthBiasControl               = vk::makeExtension(VK_EXT_DEPTH_BIAS_CONTROL_EXTENSION_NAME);\n    VkExtensionProperties extDescriptorBuffer               = vk::makeExtension(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME);\n    VkExtensionProperties extDescriptorHeap                 = vk::makeExtension(VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);\n    VkExtensionProperties extExtendedDynamicState3          = vk::makeExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);\n    VkExtensionProperties extFragmentShaderInterlock        = vk::makeExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME);\n    VkExtensionProperties extFullScreenExclusive            = vk::makeExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME);\n    VkExtensionProperties extGraphicsPipelineLibrary        = vk::makeExtension(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME);\n    VkExtensionProperties extHdrMetadata                    = vk::makeExtension(VK_EXT_HDR_METADATA_EXTENSION_NAME);\n    VkExtensionProperties extLineRasterization              = vk::makeExtension(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);\n    VkExtensionProperties extMemoryBudget                   = vk::makeExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);\n    VkExtensionProperties extMemoryPriority                 = vk::makeExtension(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME);\n    VkExtensionProperties extMultiDraw                      = vk::makeExtension(VK_EXT_MULTI_DRAW_EXTENSION_NAME);\n    VkExtensionProperties extNonSeamlessCubeMap             = vk::makeExtension(VK_EXT_NON_SEAMLESS_CUBE_MAP_EXTENSION_NAME);\n    VkExtensionProperties extPageableDeviceLocalMemory      = vk::makeExtension(VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME);\n    VkExtensionProperties extRobustness2                    = vk::makeExtension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);\n    VkExtensionProperties extSampleLocations                = vk::makeExtension(VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME);\n    VkExtensionProperties extShaderModuleIdentifier         = vk::makeExtension(VK_EXT_SHADER_MODULE_IDENTIFIER_EXTENSION_NAME);\n    VkExtensionProperties extShaderStencilExport            = vk::makeExtension(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);\n    VkExtensionProperties extSwapchainColorSpace            = vk::makeExtension(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);\n    VkExtensionProperties extSwapchainMaintenance1          = vk::makeExtension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME);\n    VkExtensionProperties extTransformFeedback              = vk::makeExtension(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);\n    VkExtensionProperties extVertexAttributeDivisor         = vk::makeExtension(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);\n    VkExtensionProperties khrExternalMemoryWin32            = vk::makeExtension(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);\n    VkExtensionProperties khrExternalSemaphoreWin32         = vk::makeExtension(VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME);\n    VkExtensionProperties khrLoadStoreOpNone                = vk::makeExtension(VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME);\n    VkExtensionProperties khrMaintenance5                   = vk::makeExtension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);\n    VkExtensionProperties khrMaintenance6                   = vk::makeExtension(VK_KHR_MAINTENANCE_6_EXTENSION_NAME);\n    VkExtensionProperties khrMaintenance7                   = vk::makeExtension(VK_KHR_MAINTENANCE_7_EXTENSION_NAME);\n    VkExtensionProperties khrMaintenance8                   = vk::makeExtension(VK_KHR_MAINTENANCE_8_EXTENSION_NAME);\n    VkExtensionProperties khrMaintenance9                   = vk::makeExtension(VK_KHR_MAINTENANCE_9_EXTENSION_NAME);\n    VkExtensionProperties khrMaintenance10                  = vk::makeExtension(VK_KHR_MAINTENANCE_10_EXTENSION_NAME);\n    VkExtensionProperties khrPipelineLibrary                = vk::makeExtension(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME);\n    VkExtensionProperties khrPresentId                      = vk::makeExtension(VK_KHR_PRESENT_ID_EXTENSION_NAME);\n    VkExtensionProperties khrPresentId2                     = vk::makeExtension(VK_KHR_PRESENT_ID_2_EXTENSION_NAME);\n    VkExtensionProperties khrPresentWait                    = vk::makeExtension(VK_KHR_PRESENT_WAIT_EXTENSION_NAME);\n    VkExtensionProperties khrPresentWait2                   = vk::makeExtension(VK_KHR_PRESENT_WAIT_2_EXTENSION_NAME);\n    VkExtensionProperties khrShaderFloatControls2           = vk::makeExtension(VK_KHR_SHADER_FLOAT_CONTROLS_2_EXTENSION_NAME);\n    VkExtensionProperties khrShaderSubgroupUniformControlFlow = vk::makeExtension(VK_KHR_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_EXTENSION_NAME);\n    VkExtensionProperties khrShaderUntypedPointers          = vk::makeExtension(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);\n    VkExtensionProperties khrSwapchain                      = vk::makeExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);\n    VkExtensionProperties khrSwapchainMaintenance1          = vk::makeExtension(VK_KHR_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME);\n    VkExtensionProperties khrSwapchainMutableFormat         = vk::makeExtension(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);\n    VkExtensionProperties khrUnifiedImageLayouts            = vk::makeExtension(VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME);\n    VkExtensionProperties khrWin32KeyedMutex                = vk::makeExtension(VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME);\n    VkExtensionProperties nvLowLatency2                     = vk::makeExtension(VK_NV_LOW_LATENCY_2_EXTENSION_NAME);\n    VkExtensionProperties nvRawAccessChains                 = vk::makeExtension(VK_NV_RAW_ACCESS_CHAINS_EXTENSION_NAME);\n    VkExtensionProperties nvxBinaryImport                   = vk::makeExtension(VK_NVX_BINARY_IMPORT_EXTENSION_NAME);\n    VkExtensionProperties nvxImageViewHandle                = vk::makeExtension(VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME);\n  };\n\n\n  /**\n   * \\brief Queue family properties with extension structs\n   */\n  struct DxvkDeviceQueueInfo {\n    VkQueueFamilyProperties2                    core              = { VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2 };\n    VkQueueFamilyOwnershipTransferPropertiesKHR ownershipTransfer = { VK_STRUCTURE_TYPE_QUEUE_FAMILY_OWNERSHIP_TRANSFER_PROPERTIES_KHR };\n  };\n\n\n  /**\n   * \\brief Queue family and index\n   */\n  struct DxvkDeviceQueueIndex {\n    uint32_t family = VK_QUEUE_FAMILY_IGNORED;\n    uint32_t index  = 0u;\n  };\n\n\n  /**\n   * \\brief Queue mapping\n   */\n  struct DxvkDeviceQueueMapping {\n    DxvkDeviceQueueIndex graphics;\n    DxvkDeviceQueueIndex transfer;\n    DxvkDeviceQueueIndex sparse;\n  };\n\n\n  /**\n   * \\brief Device capability info\n   *\n   * Stores supported extensions, features and device properties for any\n   * given adapter and handles feature enablement for device creation.\n   */\n  class DxvkDeviceCapabilities {\n\n  public:\n\n    DxvkDeviceCapabilities(\n      const DxvkInstance&               instance,\n            VkPhysicalDevice            adapter,\n      const VkDeviceCreateInfo*         deviceInfo);\n\n    DxvkDeviceCapabilities(const DxvkDeviceCapabilities&) = delete;\n\n    DxvkDeviceCapabilities& operator = (const DxvkDeviceCapabilities&) = delete;\n\n    ~DxvkDeviceCapabilities();\n\n    /**\n     * \\brief Queries device features\n     * \\returns Enabled or supported features\n     */\n    const auto& getFeatures() const {\n      return m_featuresEnabled;\n    }\n\n    /**\n     * \\brief Queries device properties\n     * \\returns Device properties\n     */\n    const auto& getProperties() const {\n      return m_properties;\n    }\n\n    /**\n     * \\brief Queries memory properties\n     * \\returns Device memory properties\n     */\n    const auto& getMemoryInfo() const {\n      return m_memory;\n    }\n\n    /**\n     * \\brief Queries queue properties for a valid queue family\n     *\n     * Must only be used to query queues that will actually be\n     * used by DXVK, thus no function to query the queue family\n     * count is provided.\n     * \\param [in] family Queue family index\n     * \\returns Queue properties\n     */\n    const auto& getQueueProperties(uint32_t family) const {\n      return m_queuesAvailable[family];\n    }\n\n    /**\n     * \\brief Queries queue family mapping\n     * \\returns Assigned queue families and indices\n     */\n    DxvkDeviceQueueMapping getQueueMapping() const {\n      return m_queueMapping;\n    }\n\n    /**\n     * \\brief Queries extensions to enable\n     *\n     * All returned extensions \\e must be enabled when\n     * using an external Vulkan device for DXVK.\n     * \\param [in,out] count Number of extensions\n     * \\param [out] extensions Extension properties\n     * \\returns \\c true on success, \\c false if the extension array is too small.\n     */\n    bool queryDeviceExtensions(\n            uint32_t*                   count,\n            VkExtensionProperties*      extensions) const;\n\n    /**\n     * \\brief Queries queue create infos\n     *\n     * Writes an array of queues that can be used to create a Vulkan device\n     * compatible with DXVK. Applications are free to add or remove queues\n     * as they wish, however disabling queues may reduce performance, and at\n     * least one queue \\e must support both graphics and, compute operations.\n     * For each written member of \\c queues, if \\c pQueuePriorities is non-null,\n     * it must point to an array of \\c queueCount floats that can be \\e written.\n     * As a result, this function needs to be called up to three times to query\n     * queue properties.\n     * \\param [in,out] count Number of queues to enable\n     * \\param [out] queues Queue properties\n     * \\returns \\c true on success, \\c false if the queue array is too small.\n     */\n    bool queryDeviceQueues(\n            uint32_t*                   count,\n            VkDeviceQueueCreateInfo*    queues) const;\n\n    /**\n     * \\brief Queries device features to enable\n     *\n     * Returns a blob of memory containing feature structs, led by a single\n     * \\c VkPhysicalDeviceFeatures2 structure at the start. The \\c pNext\n     * chain includes all feature structs that are both known to DXVK and\n     * supported by the device. Applications can enable additional features\n     * by scanning feature structs by their \\c sType, and change the \\c pNext\n     * chain to insert feature structs that DXVK is not aware of.\n     * \\param [in,out] size Memory data size, in bytes\n     * \\param [out] data Pointer to meory blob of \\c size bytes\n     * \\returns \\c true on success, \\c false if the blob at \\c data is too small.\n     */\n    bool queryDeviceFeatures(\n            size_t*                     size,\n            void*                       data) const;\n\n    /**\n     * \\brief Checks whether adapter supports all features\n     *\n     * \\param [in] errorSize Size of error string\n     * \\param [out] error Optional pointer to descriptive error\n     * \\returns \\c true if the device supports all required features\n     *    and can be used with DXVK, \\c false otherwise. If \\c false,\n     *    and if \\c error is not null, a string describing which\n     *    feature or extension is missing will be written there.\n     */\n    bool isSuitable(size_t errorSize, char* error);\n\n    /**\n     * \\brief Logs all enabled extensions and features\n     */\n    void logDeviceInfo();\n\n  private:\n\n    struct FeatureEntry {\n      VkExtensionProperties*  extensionSupported  = nullptr;\n      VkExtensionProperties*  extensionEnabled    = nullptr;\n      VkBool32*               featureSupported    = nullptr;\n      VkBool32*               featureEnabled      = nullptr;\n      VkBool32                featureRequired     = VK_FALSE;\n      const char*             readableName        = nullptr;\n    };\n\n    DxvkDeviceInfo                        m_properties          = { };\n\n    DxvkDeviceFeatures                    m_featuresSupported   = { };\n    DxvkDeviceFeatures                    m_featuresEnabled     = { };\n\n    DxvkDeviceExtensionInfo               m_extensionsSupported = { };\n    DxvkDeviceExtensionInfo               m_extensionsEnabled   = { };\n\n    DxvkDeviceMemoryInfo                  m_memory = { };\n\n    DxvkDeviceQueueMapping                m_queueMapping = { };\n\n    bool                                  m_hasMeshShader = false;\n    bool                                  m_hasFmask = false;\n\n    std::vector<const VkExtensionProperties*> m_extensionList;\n\n    std::vector<DxvkDeviceQueueInfo>      m_queuesAvailable;\n    std::vector<VkDeviceQueueCreateInfo>  m_queuesEnabled;\n    std::vector<float>                    m_queuePriorities;\n\n    void initSupportedExtensions(\n      const DxvkInstance&               instance,\n            VkPhysicalDevice            adapter,\n      const VkDeviceCreateInfo*         deviceInfo);\n\n    void initSupportedFeatures(\n      const DxvkInstance&               instance,\n            VkPhysicalDevice            adapter,\n      const VkDeviceCreateInfo*         deviceInfo);\n\n    void initDeviceProperties(\n      const DxvkInstance&               instance,\n            VkPhysicalDevice            adapter,\n      const VkDeviceCreateInfo*         deviceInfo);\n\n    void initQueueProperties(\n      const DxvkInstance&               instance,\n            VkPhysicalDevice            adapter,\n      const VkDeviceCreateInfo*         deviceInfo);\n\n    void initMemoryProperties(\n      const DxvkInstance&               instance,\n            VkPhysicalDevice            adapter);\n\n    void disableUnusedFeatures(\n      const DxvkInstance&               instance);\n\n    void enableFeaturesAndExtensions();\n\n    void enableQueues();\n\n    void enableQueue(\n            DxvkDeviceQueueIndex        queue);\n\n    uint32_t findQueueFamily(\n            VkQueueFlags                mask,\n            VkQueueFlags                flags) const;\n\n    std::optional<std::string> checkDeviceCompatibility();\n\n    void chainFeatures(\n      const DxvkDeviceExtensionInfo&    extensions,\n            DxvkDeviceFeatures&         features);\n\n    void chainProperties(\n      const DxvkDeviceExtensionInfo&    extensions,\n            DxvkDeviceInfo&             properties);\n\n    std::vector<FeatureEntry> getFeatureList();\n\n    static Version decodeDriverVersion(VkDriverId driverId, uint32_t version);\n\n    template<typename T>\n    static void copyFeature(\n      const void*                       chain,\n      const VkExtensionProperties*      extension,\n            T*                          feature) {\n      auto in = vk::scanChain(chain, feature->sType);\n\n      if (in) {\n        auto next = feature->pNext;\n\n        *feature = *reinterpret_cast<const T*>(in);\n        feature->pNext = next;\n      }\n    }\n\n    static void copyFeature(\n      const void*                       chain,\n      const VkExtensionProperties*      extension,\n            VkBool32*                   feature) {\n      *feature = !extension || extension->specVersion;\n    }\n\n    template<typename T>\n    static void chainFeature(\n      const VkExtensionProperties*      extension,\n            VkPhysicalDeviceFeatures2*  chain,\n            T*                          feature) {\n      if (!extension || extension->specVersion)\n        feature->pNext = std::exchange(chain->pNext, feature);\n    }\n\n    static void chainFeature(\n      const VkExtensionProperties*      extension,\n            VkPhysicalDeviceFeatures2*  chain,\n            VkBool32*                   feature) {\n      if (!extension || extension->specVersion)\n        *feature = VK_TRUE;\n    }\n\n    template<typename T>\n    static void chainProperties(\n      const VkExtensionProperties*      extension,\n            VkPhysicalDeviceProperties2* chain,\n            T*                          property) {\n      if (!extension || extension->specVersion)\n        property->pNext = std::exchange(chain->pNext, property);\n    }\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_extension_provider.h",
    "content": "#pragma once\n\n#include \"dxvk_include.h\"\n\n#include <vector>\n#include <string>\n\nnamespace dxvk {\n\n  class DxvkInstance;\n  class DxvkExtensionProvider;\n\n  using DxvkExtensionList = std::vector<VkExtensionProperties>;\n\n  /**\n   * \\brief Extension provider base\n   *\n   * Abstract interface for extension\n   * providers\n   */\n  class DxvkExtensionProvider {\n\n  public:\n\n    /**\n     * \\brief Extension provider name\n     * \\returns The extension provider's name\n     */\n    virtual std::string_view getName() = 0;\n\n    /**\n     * \\brief Query instance extensions\n     * \\returns Instance extensions\n     */\n    virtual DxvkExtensionList getInstanceExtensions() = 0;\n\n    /**\n     * \\brief Query device extensions\n     * \n     * Retrieves the extensions required for a specific\n     * physical device. The adapter index should remain\n     * the same across multiple Vulkan instances.\n     * \\param [in] adapterId Adapter index\n     */\n    virtual DxvkExtensionList getDeviceExtensions(\n            uint32_t      adapterId) = 0;\n    \n    /**\n     * \\brief Initializes instance extension set\n     * \n     * Should be called before creating\n     * the first Vulkan instance.\n     */\n    virtual void initInstanceExtensions() = 0;\n\n    /**\n     * \\brief Initializes device extension sets\n     * \n     * Should be called after setting\n     * up the Vulkan physical devices.\n     * \\param [in] instance DXVK instance\n     */\n    virtual void initDeviceExtensions(\n      const DxvkInstance* instance) = 0;\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_fence.cpp",
    "content": "#include \"dxvk_device.h\"\n#include \"dxvk_fence.h\"\n\nnamespace dxvk {\n\n  DxvkFence::DxvkFence(\n          DxvkDevice*           device,\n    const DxvkFenceCreateInfo&  info)\n  : m_vkd(device->vkd()), m_info(info) {\n    VkSemaphoreTypeCreateInfo typeInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO };\n    typeInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;\n    typeInfo.initialValue = info.initialValue;\n    \n    VkExportSemaphoreCreateInfo exportInfo = { VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO };\n    exportInfo.handleTypes = info.sharedType;\n\n    VkExternalSemaphoreFeatureFlags externalFeatures = 0;\n\n    if (info.sharedType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM) {\n      auto vki = device->adapter()->vki();\n\n      VkPhysicalDeviceExternalSemaphoreInfo externalInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, &typeInfo };\n      externalInfo.handleType = info.sharedType;\n\n      VkExternalSemaphoreProperties externalProperties = { VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES };\n      vki->vkGetPhysicalDeviceExternalSemaphoreProperties(\n        device->adapter()->handle(), &externalInfo, &externalProperties);\n\n      externalFeatures = externalProperties.externalSemaphoreFeatures;\n\n      if (externalFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT)\n        typeInfo.pNext = &exportInfo;\n      else\n        Logger::warn(str::format(\"Exporting semaphores of type \", info.sharedType, \" not supported by device\"));\n    }\n\n    VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &typeInfo };\n\n    VkResult vr = m_vkd->vkCreateSemaphore(m_vkd->device(),\n      &semaphoreInfo, nullptr, &m_semaphore);\n\n    if (vr != VK_SUCCESS)\n      throw DxvkError(\"Failed to create timeline semaphore\");\n\n    if (info.sharedHandle != INVALID_HANDLE_VALUE) {\n      if (externalFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT) {\n        VkImportSemaphoreWin32HandleInfoKHR importInfo = { VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR };\n        importInfo.semaphore = m_semaphore;\n        importInfo.handleType = info.sharedType;\n        importInfo.handle = info.sharedHandle;\n\n        vr = m_vkd->vkImportSemaphoreWin32HandleKHR(m_vkd->device(), &importInfo);\n        if (vr != VK_SUCCESS)\n          throw DxvkError(\"Failed to import timeline semaphore\");\n      } else {\n        Logger::warn(str::format(\"Importing semaphores of type \", info.sharedType, \" not supported by device\"));\n      }\n    }\n\n    if (info.sharedType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM)\n      initKmtHandles();\n  }\n\n\n  DxvkFence::~DxvkFence() {\n    if (m_thread.joinable()) {\n      {\n        std::unique_lock<dxvk::mutex> lock(m_mutex);\n        m_running = false;\n        m_condVar.notify_one();\n      }\n      m_thread.join();\n    }\n    if (m_kmtLocal) {\n      D3DKMT_DESTROYSYNCHRONIZATIONOBJECT desc = { };\n      desc.hSyncObject = m_kmtLocal;\n      D3DKMTDestroySynchronizationObject(&desc);\n    }\n    m_vkd->vkDestroySemaphore(m_vkd->device(), m_semaphore, nullptr);\n  }\n\n\n  void DxvkFence::enqueueWait(uint64_t value, DxvkFenceEvent&& event) {\n    if (value > getValue()) {\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n      m_queue.emplace(value, std::move(event));\n\n      if (!m_running) {\n        m_running = true;\n        m_thread = dxvk::thread([this] { run(); });\n      } else {\n        m_condVar.notify_one();\n      }\n      lock.unlock();\n    } else {\n      event();\n    }\n  }\n\n  void DxvkFence::wait(uint64_t value) {\n    VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };\n    waitInfo.semaphoreCount = 1;\n    waitInfo.pSemaphores = &m_semaphore;\n    waitInfo.pValues = &value;\n    VkResult vr = m_vkd->vkWaitSemaphores(\n      m_vkd->device(), &waitInfo, ~0ull);\n\n    if (vr != VK_SUCCESS) {\n      Logger::err(str::format(\"Failed to wait for semaphore: \", vr));\n    }\n  }\n\n  void DxvkFence::run() {\n    uint64_t value = 0ull;\n\n    VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };\n    waitInfo.semaphoreCount = 1;\n    waitInfo.pSemaphores = &m_semaphore;\n    waitInfo.pValues = &value;\n\n    while (true) {\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n      m_condVar.wait(lock, [&]() { return !m_queue.empty() || !m_running; });\n\n      // Query actual semaphore value and start from there, so that\n      // we can skip over large increments in the semaphore value\n      VkResult vr = m_vkd->vkGetSemaphoreCounterValue(m_vkd->device(), m_semaphore, &value);\n\n      if (vr != VK_SUCCESS) {\n        Logger::err(str::format(\"Failed to query semaphore value: \", vr));\n        return;\n      }\n\n      // Signal all enqueued events whose value is not greater than\n      // the current semaphore value\n      while (!m_queue.empty() && m_queue.top().value <= value) {\n        m_queue.top().event();\n        m_queue.pop();\n      }\n\n      if (!m_running)\n        return;\n\n      if (m_queue.empty())\n        continue;\n\n      lock.unlock();\n\n      // Wait for the semaphore to be singaled again and update state.\n      // The timeout is unfortunate, but we can't always know when a\n      // signal operation has been recorded, and the alternative would\n      // be to create a teardown semaphore and use WAIT_ANY, which may\n      // be fall back to a busy-wait loop on some drivers.\n      value += 1;\n\n      vr = m_vkd->vkWaitSemaphores(\n        m_vkd->device(), &waitInfo, 10'000'000ull);\n\n      if (vr != VK_SUCCESS && vr != VK_TIMEOUT) {\n        Logger::err(str::format(\"Failed to wait for semaphore: \", vr));\n        return;\n      }\n    }\n  }\n\n  uint64_t DxvkFence::getValue() {\n    uint64_t value = 0;\n    VkResult vr = m_vkd->vkGetSemaphoreCounterValue(m_vkd->device(), m_semaphore, &value);\n    if (vr != VK_SUCCESS) {\n      Logger::err(str::format(\"Failed to query semaphore value: \", vr));\n    }\n    return value;\n  }\n\n  HANDLE DxvkFence::sharedHandle() const {\n    if (m_info.sharedType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM)\n      return INVALID_HANDLE_VALUE;\n\n    VkSemaphoreGetWin32HandleInfoKHR win32HandleInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR };\n    win32HandleInfo.semaphore = m_semaphore;\n    win32HandleInfo.handleType = m_info.sharedType;\n\n    HANDLE sharedHandle = INVALID_HANDLE_VALUE;\n    VkResult vr = m_vkd->vkGetSemaphoreWin32HandleKHR(m_vkd->device(), &win32HandleInfo, &sharedHandle);\n\n    if (vr != VK_SUCCESS)\n      Logger::err(str::format(\"Failed to get semaphore handle: \", vr));\n\n    return sharedHandle;\n  }\n\n\n  void DxvkFence::initKmtHandles() {\n#ifdef _WIN32\n    VkSemaphoreGetWin32HandleInfoKHR win32HandleInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR };\n    win32HandleInfo.semaphore = m_semaphore;\n    win32HandleInfo.handleType = m_info.sharedType;\n\n    HANDLE sharedHandle = INVALID_HANDLE_VALUE;\n    VkResult vr = m_vkd->vkGetSemaphoreWin32HandleKHR(m_vkd->device(), &win32HandleInfo, &sharedHandle);\n    if (vr != VK_SUCCESS) {\n      Logger::err(str::format(\"Failed to get semaphore handle: \", vr));\n    } else if (m_info.sharedType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) {\n      D3DKMT_OPENSYNCHRONIZATIONOBJECT desc = { };\n      desc.hSharedHandle = HandleToULong(sharedHandle);\n\n      if (D3DKMTOpenSynchronizationObject(&desc)) {\n        Logger::warn(\"DxvkFence::DxvkFence: Failed to open shared D3DKMT handle\");\n      } else {\n        m_kmtLocal = desc.hSyncObject;\n        m_kmtGlobal = desc.hSharedHandle;\n      }\n    } else {\n      D3DKMT_OPENSYNCOBJECTFROMNTHANDLE desc = { };\n      desc.hNtHandle = sharedHandle;\n\n      if (D3DKMTOpenSyncObjectFromNtHandle(&desc)) {\n        Logger::warn(\"DxvkFence::DxvkFence: Failed to open shared NT handle\");\n      } else {\n        m_kmtLocal = desc.hSyncObject;\n      }\n      CloseHandle(sharedHandle);\n    }\n#else\n    Logger::warn(\"DxvkFence::initKmtHandles: Not implemented on this platform\");\n#endif\n  }\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_fence.h",
    "content": "#pragma once\n\n#include <functional>\n#include <queue>\n#include <utility>\n#include <vector>\n\n#include \"dxvk_include.h\"\n#include \"dxvk_adapter.h\"\n\n#include \"../util/thread.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n  class DxvkFence;\n\n  using DxvkFenceEvent = std::function<void ()>;\n\n  /**\n   * \\brief Fence create info\n   */\n  struct DxvkFenceCreateInfo {\n    uint64_t        initialValue;\n    VkExternalSemaphoreHandleTypeFlagBits sharedType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;\n    union {\n      // When we want to implement this on non-Windows platforms,\n      // we could add a `int fd` here, etc.\n      HANDLE          sharedHandle = INVALID_HANDLE_VALUE;\n    };\n  };\n\n  /**\n   * \\brief Fence-value pair\n   */\n  struct DxvkFenceValuePair {\n    DxvkFenceValuePair() { }\n    DxvkFenceValuePair(Rc<DxvkFence>&& fence_, uint64_t value_)\n    : fence(std::move(fence_)), value(value_) { }\n    DxvkFenceValuePair(const Rc<DxvkFence>& fence_, uint64_t value_)\n    : fence(fence_), value(value_) { }\n\n    Rc<DxvkFence> fence;\n    uint64_t value;\n  };\n\n  /**\n   * \\brief Fence\n   *\n   * Wrapper around Vulkan timeline semaphores that\n   * can signal an event when the value changes.\n   */\n  class DxvkFence : public RcObject {\n\n  public:\n\n    DxvkFence(\n            DxvkDevice*           device,\n      const DxvkFenceCreateInfo&  info);\n\n    ~DxvkFence();\n\n    /**\n     * \\brief Semaphore handle\n     */\n    VkSemaphore handle() const {\n      return m_semaphore;\n    }\n    \n    /**\n     * \\brief D3DKMT sync object local handle\n     * \\returns The sync object D3DKMT local handle\n     * \\returns \\c 0 if fence is not shared\n     */\n    D3DKMT_HANDLE kmtLocal() const {\n      return m_kmtLocal;\n    }\n\n    /**\n     * \\brief D3DKMT sync object global handle\n     * \\returns The sync object D3DKMT global handle\n     * \\returns \\c 0 if sync object is not shared or shared with NT handle\n     */\n    D3DKMT_HANDLE kmtGlobal() const {\n      return m_kmtGlobal;\n    }\n\n    /**\n     * \\brief Retrieves current semaphore value\n     * \\returns Current semaphore value\n     */\n    uint64_t getValue();\n\n    /**\n     * \\brief Enqueues semaphore wait\n     *\n     * Signals the given event when the\n     * semaphore reaches the given value.\n     * \\param [in] value Enqueue value\n     * \\param [in] event Callback\n     */\n    void enqueueWait(uint64_t value, DxvkFenceEvent&& event);\n\n    /**\n     * \\brief Create a new shared handle to timeline semaphore backing the fence\n     * \\returns The shared handle with the type given by DxvkFenceCreateInfo::sharedType\n     */\n    HANDLE sharedHandle() const;\n\n    /*\n     * \\brief Waits for the given value\n     *\n     * Blocks the calling thread until\n     * the fence reaches the given value.\n     * \\param [in] value Value to wait for\n    */\n    void wait(uint64_t value);\n\n  private:\n\n    struct QueueItem {\n      QueueItem() { }\n      QueueItem(uint64_t v, DxvkFenceEvent&& e)\n      : value(v), event(std::move(e)) { }\n\n      uint64_t        value;\n      DxvkFenceEvent  event;\n\n      bool operator == (const QueueItem& item) const { return value == item.value; }\n      bool operator != (const QueueItem& item) const { return value != item.value; }\n      bool operator <  (const QueueItem& item) const { return value <  item.value; }\n      bool operator <= (const QueueItem& item) const { return value <= item.value; }\n      bool operator >  (const QueueItem& item) const { return value >  item.value; }\n      bool operator >= (const QueueItem& item) const { return value >= item.value; }\n    };\n\n    Rc<vk::DeviceFn>                m_vkd;\n    DxvkFenceCreateInfo             m_info;\n    VkSemaphore                     m_semaphore;\n    D3DKMT_HANDLE                   m_kmtLocal = 0;\n    D3DKMT_HANDLE                   m_kmtGlobal = 0;\n\n    std::priority_queue<QueueItem>  m_queue;\n    bool                            m_running    = false;\n\n    dxvk::mutex                     m_mutex;\n    dxvk::condition_variable        m_condVar;\n    dxvk::thread                    m_thread;\n\n    void run();\n\n    void initKmtHandles();\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_format.cpp",
    "content": "#include \"dxvk_format.h\"\n\nnamespace dxvk {\n\n  constexpr VkColorComponentFlags RGBA = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n  constexpr VkColorComponentFlags RGB  = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT;\n  constexpr VkColorComponentFlags RG   = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT;\n  constexpr VkColorComponentFlags R    = VK_COLOR_COMPONENT_R_BIT;\n  constexpr VkColorComponentFlags A    = VK_COLOR_COMPONENT_A_BIT;\n\n  const std::array<DxvkFormatInfo, DxvkFormatCount> g_formatInfos = {{\n    // VK_FORMAT_UNDEFINED\n    { },\n    \n    // VK_FORMAT_R4G4_UNORM_PACK8\n    { 1, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R4G4B4A4_UNORM_PACK16\n    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B4G4R4A4_UNORM_PACK16\n    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R5G6B5_UNORM_PACK16\n    { 2, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B5G6R5_UNORM_PACK16\n    { 2, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R5G5B5A1_UNORM_PACK16\n    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B5G5R5A1_UNORM_PACK16\n    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A1R5G5B5_UNORM_PACK16\n    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8_UNORM\n    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8_SNORM\n    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8_USCALED\n    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8_SSCALED\n    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8_UINT\n    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R8_SINT\n    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R8_SRGB\n    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::ColorSpaceSrgb },\n    \n    // VK_FORMAT_R8G8_UNORM\n    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8_SNORM\n    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8_USCALED\n    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8_SSCALED\n    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8_UINT\n    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R8G8_SINT\n    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R8G8_SRGB\n    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::ColorSpaceSrgb },\n    \n    // VK_FORMAT_R8G8B8_UNORM\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8B8_SNORM\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8B8_USCALED\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8B8_SSCALED\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8B8_UINT\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R8G8B8_SINT\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R8G8B8_SRGB\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::ColorSpaceSrgb },\n    \n    // VK_FORMAT_B8G8R8_UNORM\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B8G8R8_SNORM\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B8G8R8_USCALED\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B8G8R8_SSCALED\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B8G8R8_UINT\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_B8G8R8_SINT\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_B8G8R8_SRGB\n    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::ColorSpaceSrgb },\n    \n    // VK_FORMAT_R8G8B8A8_UNORM\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8B8A8_SNORM\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8B8A8_USCALED\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8B8A8_SSCALED\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R8G8B8A8_UINT\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R8G8B8A8_SINT\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R8G8B8A8_SRGB\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::ColorSpaceSrgb },\n    \n    // VK_FORMAT_B8G8R8A8_UNORM\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B8G8R8A8_SNORM\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B8G8R8A8_USCALED\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B8G8R8A8_SSCALED\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B8G8R8A8_UINT\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_B8G8R8A8_SINT\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_B8G8R8A8_SRGB\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::ColorSpaceSrgb },\n    \n    // VK_FORMAT_A8B8G8R8_UNORM_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A8B8G8R8_SNORM_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A8B8G8R8_USCALED_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A8B8G8R8_SSCALED_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A8B8G8R8_UINT_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_A8B8G8R8_SINT_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_A8B8G8R8_SRGB_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::ColorSpaceSrgb },\n    \n    // VK_FORMAT_A2R10G10B10_UNORM_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A2R10G10B10_SNORM_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A2R10G10B10_USCALED_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A2R10G10B10_SSCALED_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A2R10G10B10_UINT_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_A2R10G10B10_SINT_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_A2B10G10R10_UNORM_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A2B10G10R10_SNORM_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A2B10G10R10_USCALED_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A2B10G10R10_SSCALED_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_A2B10G10R10_UINT_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_A2B10G10R10_SINT_PACK32\n    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R16_UNORM\n    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16_SNORM\n    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16_USCALED\n    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16_SSCALED\n    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16_UINT\n    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R16_SINT\n    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R16_SFLOAT\n    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16_UNORM\n    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16_SNORM\n    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16_USCALED\n    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16_SSCALED\n    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16_UINT\n    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R16G16_SINT\n    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R16G16_SFLOAT\n    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16_UNORM\n    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16_SNORM\n    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16_USCALED\n    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16_SSCALED\n    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16_UINT\n    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R16G16B16_SINT\n    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R16G16B16_SFLOAT\n    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16A16_UNORM\n    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16A16_SNORM\n    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16A16_USCALED\n    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16A16_SSCALED\n    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R16G16B16A16_UINT\n    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R16G16B16A16_SINT\n    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R16G16B16A16_SFLOAT\n    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R32_UINT\n    { 4, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R32_SINT\n    { 4, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R32_SFLOAT\n    { 4, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R32G32_UINT\n    { 8, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R32G32_SINT\n    { 8, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R32G32_SFLOAT\n    { 8, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R32G32B32_UINT\n    { 12, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R32G32B32_SINT\n    { 12, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R32G32B32_SFLOAT\n    { 12, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R32G32B32A32_UINT\n    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R32G32B32A32_SINT\n    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R32G32B32A32_SFLOAT\n    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R64_UINT\n    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R64_SINT\n    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R64_SFLOAT\n    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R64G64_UINT\n    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R64G64_SINT\n    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R64G64_SFLOAT\n    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R64G64B64_UINT\n    { 24, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R64G64B64_SINT\n    { 24, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R64G64B64_SFLOAT\n    { 24, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_R64G64B64A64_UINT\n    { 32, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledUInt },\n    \n    // VK_FORMAT_R64G64B64A64_SINT\n    { 32, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::SampledSInt },\n    \n    // VK_FORMAT_R64G64B64A64_SFLOAT\n    { 32, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_B10G11R11_UFLOAT_PACK32\n    { 4, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_E5B9G9R9_UFLOAT_PACK32\n    { 4, RGB, VK_IMAGE_ASPECT_COLOR_BIT },\n    \n    // VK_FORMAT_D16_UNORM\n    { 2, 0, VK_IMAGE_ASPECT_DEPTH_BIT },\n    \n    // VK_FORMAT_X8_D24_UNORM_PACK32\n    { 4, 0, VK_IMAGE_ASPECT_DEPTH_BIT },\n    \n    // VK_FORMAT_D32_SFLOAT\n    { 4, 0, VK_IMAGE_ASPECT_DEPTH_BIT },\n    \n    // VK_FORMAT_S8_UINT\n    { 1, 0, VK_IMAGE_ASPECT_STENCIL_BIT },\n    \n    // VK_FORMAT_D16_UNORM_S8_UINT\n    { 4, 0, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT },\n    \n    // VK_FORMAT_D24_UNORM_S8_UINT\n    { 4, 0, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT },\n    \n    // VK_FORMAT_D32_SFLOAT_S8_UINT\n    { 8, 0, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT },\n    \n    // VK_FORMAT_BC1_RGB_UNORM_BLOCK\n    { 8, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC1_RGB_SRGB_BLOCK\n    { 8, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlags(\n        DxvkFormatFlag::BlockCompressed,\n        DxvkFormatFlag::ColorSpaceSrgb),\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC1_RGBA_UNORM_BLOCK\n    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC1_RGBA_SRGB_BLOCK\n    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlags(\n        DxvkFormatFlag::BlockCompressed,\n        DxvkFormatFlag::ColorSpaceSrgb),\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC2_UNORM_BLOCK\n    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC2_SRGB_BLOCK\n    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlags(\n        DxvkFormatFlag::BlockCompressed,\n        DxvkFormatFlag::ColorSpaceSrgb),\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC3_UNORM_BLOCK\n    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC3_SRGB_BLOCK\n    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlags(\n        DxvkFormatFlag::BlockCompressed,\n        DxvkFormatFlag::ColorSpaceSrgb),\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC4_UNORM_BLOCK\n    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC4_SNORM_BLOCK\n    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC5_UNORM_BLOCK\n    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC5_SNORM_BLOCK\n    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC6H_UFLOAT_BLOCK\n    { 16, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC6H_SFLOAT_BLOCK\n    { 16, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC7_UNORM_BLOCK\n    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_BC7_SRGB_BLOCK\n    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlags(\n        DxvkFormatFlag::BlockCompressed,\n        DxvkFormatFlag::ColorSpaceSrgb),\n      VkExtent3D { 4, 4, 1 } },\n    \n    // VK_FORMAT_G8B8G8R8_422_UNORM_KHR\n    { 4, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 2, 1, 1 } },\n    \n    // VK_FORMAT_B8G8R8G8_422_UNORM_KHR\n    { 4, RGB, VK_IMAGE_ASPECT_COLOR_BIT,\n      DxvkFormatFlag::BlockCompressed,\n      VkExtent3D { 2, 1, 1 } },\n\n    // VK_FORMAT_A4R4G4B4_UNORM_PACK16\n    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n\n    // VK_FORMAT_A4B4G4R4_UNORM_PACK16\n    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n\n    // VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM\n    { 8, RGB, VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT,\n      DxvkFormatFlag::MultiPlane, VkExtent3D { 1, 1, 1 },\n      { DxvkPlaneFormatInfo { 1, { 1, 1 } },\n        DxvkPlaneFormatInfo { 1, { 2, 2 } },\n        DxvkPlaneFormatInfo { 1, { 2, 2 } } } },\n\n    // VK_FORMAT_G8_B8R8_2PLANE_420_UNORM\n    { 6, RGB, VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT,\n      DxvkFormatFlag::MultiPlane, VkExtent3D { 1, 1, 1 },\n      { DxvkPlaneFormatInfo { 1, { 1, 1 } },\n        DxvkPlaneFormatInfo { 2, { 2, 2 } } } },\n\n    // VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16\n    { 12, RGB, VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT,\n      DxvkFormatFlag::MultiPlane, VkExtent3D { 1, 1, 1 },\n      { DxvkPlaneFormatInfo { 2, { 1, 1 } },\n        DxvkPlaneFormatInfo { 4, { 2, 2 } } } },\n\n    // VK_FORMAT_G16_B16R16_2PLANE_420_UNORM\n    { 12, RGB, VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT,\n      DxvkFormatFlag::MultiPlane, VkExtent3D { 1, 1, 1 },\n      { DxvkPlaneFormatInfo { 2, { 1, 1 } },\n        DxvkPlaneFormatInfo { 4, { 2, 2 } } } },\n\n    // VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR\n    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },\n\n    // VK_FORMAT_A8_UNORM_KHR\n    { 1, A, VK_IMAGE_ASPECT_COLOR_BIT },\n  }};\n  \n  \n  const std::array<std::pair<VkFormat, VkFormat>, 7> g_formatGroups = {{\n    { VK_FORMAT_UNDEFINED,                  VK_FORMAT_BC7_SRGB_BLOCK                                            },\n    { VK_FORMAT_G8B8G8R8_422_UNORM_KHR,     VK_FORMAT_B8G8R8G8_422_UNORM_KHR                                    },\n    { VK_FORMAT_A4R4G4B4_UNORM_PACK16,      VK_FORMAT_A4B4G4R4_UNORM_PACK16                                     },\n    { VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,  VK_FORMAT_G8_B8R8_2PLANE_420_UNORM                                  },\n    { VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16  },\n    { VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, VK_FORMAT_G16_B16R16_2PLANE_420_UNORM                              },\n    { VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR,  VK_FORMAT_A8_UNORM_KHR                                              },\n  }};\n  \n  \n  const DxvkFormatInfo* lookupFormatInfoSlow(VkFormat format) {\n    uint32_t indexOffset = 0;\n    \n    for (const auto& group : g_formatGroups) {\n      if (format >= group.first && format <= group.second) {\n        uint32_t index = uint32_t(format) - uint32_t(group.first);\n        return &g_formatInfos[indexOffset + index];\n      } else {\n        indexOffset += uint32_t(group.second)\n                     - uint32_t(group.first) + 1;\n      }\n    }\n    \n    return nullptr;\n  }\n  \n}"
  },
  {
    "path": "src/dxvk/dxvk_format.h",
    "content": "#pragma once\n\n#include \"dxvk_include.h\"\n\nnamespace dxvk {\n  \n  enum class DxvkFormatFlag {\n    BlockCompressed = 0,  ///< Image format is block compressed\n    SampledUInt     = 1,  ///< Sampled type is an unsigned integer type\n    SampledSInt     = 2,  ///< Sampled type is a signed integer type\n    ColorSpaceSrgb  = 3,  ///< Non-linear SRGB color format\n    MultiPlane      = 4,  ///< Multi-plane format\n  };\n  \n  using DxvkFormatFlags = Flags<DxvkFormatFlag>;\n\n  /**\n   * \\brief Format support info\n   */\n  struct DxvkFormatFeatures {\n    VkFormatFeatureFlags2 optimal;\n    VkFormatFeatureFlags2 linear;\n    VkFormatFeatureFlags2 buffer;\n  };\n\n  /**\n   * \\brief Format support limits for a given set of image usage flags\n   */\n  struct DxvkFormatLimits {\n    VkExtent3D                  maxExtent;\n    uint32_t                    maxMipLevels;\n    uint32_t                    maxArrayLayers;\n    VkSampleCountFlags          sampleCounts;\n    VkDeviceSize                maxResourceSize;\n    VkExternalMemoryFeatureFlags externalFeatures;\n  };\n\n  /**\n   * \\brief Format query info\n   */\n  struct DxvkFormatQuery {\n    VkFormat                    format;\n    VkImageType                 type;\n    VkImageTiling               tiling;\n    VkImageUsageFlags           usage;\n    VkImageCreateFlags          flags;\n    VkExternalMemoryHandleTypeFlagBits handleType;\n  };\n\n  /**\n   * \\brief Planar format info\n   */\n  struct DxvkPlaneFormatInfo {\n    /// Byte size of a pixel in the current plane\n    VkDeviceSize elementSize = 0;\n    /// Number of image pixels covered by a\n    /// single pixel in the current plane\n    VkExtent2D blockSize = { 1, 1 };\n  };\n\n  /**\n   * \\brief Format info structure\n   * \n   * Provides some useful information\n   * about a Vulkan image format. \n   */\n  struct DxvkFormatInfo {\n    /// Size of an element in this format. For compressed\n    /// formats, this is the size of a block, in bytes.\n    VkDeviceSize elementSize = 0;\n    \n    /// Available component mask\n    VkColorComponentFlags componentMask = 0;\n    \n    /// Available image aspect flags\n    VkImageAspectFlags aspectMask = 0;\n    \n    /// Some other format info flags\n    DxvkFormatFlags flags = 0;\n    \n    /// Size, in pixels, of a compressed block. For\n    /// non-block formats, all these values are 1.\n    VkExtent3D blockSize = { 1, 1, 1 };\n\n    /// Plane info for multi-planar formats\n    std::array<DxvkPlaneFormatInfo, 3> planes;\n  };\n\n  /// Number of formats defined in lookup table  \n  constexpr size_t DxvkFormatCount = 157;\n\n  /// Format lookup table\n  extern const std::array<DxvkFormatInfo, DxvkFormatCount> g_formatInfos;\n\n  /**\n   * \\brief Looks up format info\n   *\n   * \\param [in] format Format to look up\n   * \\returns Info for the given format\n   */\n  const DxvkFormatInfo* lookupFormatInfoSlow(VkFormat format);\n\n  /**\n   * \\brief Queries image format info\n   *\n   * Provides a fast path for the most common base formats.\n   * \\param [in] format Format to look up\n   * \\returns Info for the given format\n   */\n  inline const DxvkFormatInfo* lookupFormatInfo(VkFormat format) {\n    if (likely(format <= VK_FORMAT_BC7_SRGB_BLOCK))\n      return &g_formatInfos[uint32_t(format)];\n    else\n      return lookupFormatInfoSlow(format);\n  }\n\n  /**\n   * \\brief Queries default resolve mode for format\n   *\n   * For depth-stencil formats, this will return SAMPLE_ZERO.\n   * \\param [in] format Format to look up\n   * \\returns Default resolve mode\n   */\n  inline VkResolveModeFlagBits getDefaultResolveMode(const DxvkFormatInfo* format) {\n    if ((format->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n     || (format->flags.any(DxvkFormatFlag::SampledSInt, DxvkFormatFlag::SampledUInt)))\n      return VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;\n\n    return VK_RESOLVE_MODE_AVERAGE_BIT;\n  }\n\n  inline VkResolveModeFlagBits getDefaultResolveMode(VkFormat format) {\n    return getDefaultResolveMode(lookupFormatInfo(format));\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_framebuffer.cpp",
    "content": "#include \"dxvk_framebuffer.h\"\n\nnamespace dxvk {\n  \n  DxvkFramebufferInfo::DxvkFramebufferInfo() {\n\n  }\n\n\n  DxvkFramebufferInfo::DxvkFramebufferInfo(\n    const DxvkRenderTargets&      renderTargets,\n    const DxvkFramebufferSize&    defaultSize)\n  : m_renderTargets (renderTargets),\n    m_renderSize    (computeRenderSize(defaultSize)) {\n\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      if (m_renderTargets.color[i].view != nullptr) {\n        m_attachments[m_attachmentCount++] = i;\n        m_sampleCount = m_renderTargets.color[i].view->image()->info().sampleCount;\n      }\n    }\n\n    if (m_renderTargets.depth.view != nullptr) {\n      m_attachments[m_attachmentCount++] = -1;\n      m_sampleCount = m_renderTargets.depth.view->image()->info().sampleCount;\n    }\n  }\n\n\n  DxvkFramebufferInfo::~DxvkFramebufferInfo() {\n\n  }\n\n\n  int32_t DxvkFramebufferInfo::findAttachment(const Rc<DxvkImageView>& view) const {\n    for (uint32_t i = 0; i < m_attachmentCount; i++) {\n      if (getAttachment(i).view->matchesView(view))\n        return int32_t(i);\n    }\n\n    return -1;\n  }\n\n\n  bool DxvkFramebufferInfo::isFullSize(const Rc<DxvkImageView>& view) const {\n    return m_renderSize.width  == view->mipLevelExtent(0).width\n        && m_renderSize.height == view->mipLevelExtent(0).height\n        && m_renderSize.layers == view->info().layerCount;\n  }\n\n\n  bool DxvkFramebufferInfo::isWritable(uint32_t attachmentIndex, VkImageAspectFlags aspects) const {\n    const auto& attachment = getAttachment(attachmentIndex);\n\n    if (!attachment.view)\n      return false;\n\n    /* Check the layout that the view was created for, not the view that we\n     * actually selected for rendering since that may lose information about\n     * the writable aspects. */\n    VkImageAspectFlags writableAspects = vk::getWritableAspectsForLayout(attachment.view->info().layout);\n    return (writableAspects & aspects) == aspects;\n  }\n\n\n  DxvkRtInfo DxvkFramebufferInfo::getRtInfo() const {\n    VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED;\n    VkImageAspectFlags depthStencilReadOnlyAspects = 0;\n\n    if (m_renderTargets.depth.view) {\n      depthStencilFormat = m_renderTargets.depth.view->info().format;\n      depthStencilReadOnlyAspects = m_renderTargets.depth.view->formatInfo()->aspectMask\n        & ~vk::getWritableAspectsForLayout(m_renderTargets.depth.view->info().layout);\n    }\n\n    std::array<VkFormat, MaxNumRenderTargets> colorFormats = { };\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      if (m_renderTargets.color[i].view != nullptr)\n        colorFormats[i] = m_renderTargets.color[i].view->info().format;\n    }\n\n    return DxvkRtInfo(MaxNumRenderTargets, colorFormats.data(),\n      depthStencilFormat, depthStencilReadOnlyAspects);\n  }\n\n\n  DxvkFramebufferSize DxvkFramebufferInfo::computeRenderSize(\n    const DxvkFramebufferSize& defaultSize) const {\n    // Some games bind render targets of a different size and\n    // expect it to work, so we'll compute the minimum size\n    DxvkFramebufferSize minSize = defaultSize;\n\n    if (m_renderTargets.depth.view != nullptr) {\n      DxvkFramebufferSize depthSize = this->computeRenderTargetSize(m_renderTargets.depth.view);\n      minSize.width  = std::min(minSize.width,  depthSize.width);\n      minSize.height = std::min(minSize.height, depthSize.height);\n      minSize.layers = std::min(minSize.layers, depthSize.layers);\n    }\n\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      if (m_renderTargets.color[i].view != nullptr) {\n        DxvkFramebufferSize colorSize = this->computeRenderTargetSize(m_renderTargets.color[i].view);\n        minSize.width  = std::min(minSize.width,  colorSize.width);\n        minSize.height = std::min(minSize.height, colorSize.height);\n        minSize.layers = std::min(minSize.layers, colorSize.layers);\n      }\n    }\n\n    return minSize;\n  }\n\n\n  DxvkFramebufferSize DxvkFramebufferInfo::computeRenderTargetSize(\n    const Rc<DxvkImageView>& renderTarget) const {\n    auto extent = renderTarget->mipLevelExtent(0);\n    auto layers = renderTarget->info().layerCount;\n    return DxvkFramebufferSize { extent.width, extent.height, layers };\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_framebuffer.h",
    "content": "#pragma once\n\n#include \"dxvk_image.h\"\n#include \"dxvk_graphics_state.h\"\n#include \"dxvk_pipelayout.h\"\n#include \"dxvk_renderpass.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Framebuffer size\n   * \n   * Stores the width, height and number of layers\n   * of a framebuffer. This can be used in case a\n   * framebuffer does not have any attachments.\n   */\n  struct DxvkFramebufferSize {\n    uint32_t width;\n    uint32_t height;\n    uint32_t layers;\n  };\n\n  \n  /**\n   * \\brief Framebuffer attachment\n   */\n  struct DxvkAttachment {\n    Rc<DxvkImageView> view = nullptr;\n\n    bool operator == (const DxvkAttachment& other) const { return view == other.view; }\n    bool operator != (const DxvkAttachment& other) const { return view != other.view; }\n  };\n  \n  \n  /**\n   * \\brief Render targets\n   * \n   * Stores all depth-stencil and color\n   * attachments attached to a framebuffer.\n   */\n  struct DxvkRenderTargets {\n    DxvkAttachment depth;\n    DxvkAttachment color[MaxNumRenderTargets];\n\n    bool operator == (const DxvkRenderTargets& other) const {\n      bool eq = depth == other.depth;\n\n      for (uint32_t i = 0; i < MaxNumRenderTargets && eq; i++)\n        eq = color[i] == other.color[i];\n\n      return eq;\n    }\n\n    bool operator != (const DxvkRenderTargets& other) const {\n      return !(this->operator == (other));\n    }\n  };\n\n\n  /**\n   * \\brief Render target layouts\n   */\n  struct DxvkRenderTargetLayouts {\n    VkImageLayout color[MaxNumRenderTargets];\n    VkImageLayout depth;\n  };\n\n\n  /**\n   * \\brief Rendering info\n   */\n  struct DxvkRenderingInfo {\n    std::array<VkRenderingAttachmentFlagsInfoKHR, MaxNumRenderTargets> colorAttachmentFlags = { };\n    std::array<VkAttachmentFeedbackLoopInfoEXT, MaxNumRenderTargets> colorFeedbackLoop = { };\n    std::array<VkRenderingAttachmentInfo, MaxNumRenderTargets> color = { };\n    VkAttachmentFeedbackLoopInfoEXT depthStencilFeedbackLoop = { };\n    VkRenderingAttachmentInfo depth = { };\n    VkRenderingAttachmentInfo stencil = { };\n    VkRenderingInfo rendering = { };\n  };\n\n\n  /**\n   * \\brief Framebuffer info\n   *\n   * Stores metadata about the current framebuffer,\n   * without actually creating a framebuffer object.\n   */\n  class DxvkFramebufferInfo {\n\n  public:\n\n    DxvkFramebufferInfo();\n\n    DxvkFramebufferInfo(\n      const DxvkRenderTargets&      renderTargets,\n      const DxvkFramebufferSize&    defaultSize);\n\n    ~DxvkFramebufferInfo();\n\n    /**\n     * \\brief Retrieves all attachments\n     * \\returns Render targets\n     */\n    const DxvkRenderTargets& attachments() const {\n      return m_renderTargets;\n    }\n\n    /**\n     * \\brief Framebuffer size\n     * \\returns Framebuffer size\n     */\n    DxvkFramebufferSize size() const {\n      return m_renderSize;\n    }\n\n    /**\n     * \\brief Framebuffer sample count\n     *\n     * Returns the sample count of the color\n     * and depth-stencil attachments, or 0 if\n     * there are no attachments.\n     * \\returns Sample count\n     */\n    VkSampleCountFlags getSampleCount() const {\n      return m_sampleCount;\n    }\n\n    /**\n     * \\brief Depth-stencil target\n     * \\returns Depth-stencil target\n     */\n    const DxvkAttachment& getDepthTarget() const {\n      return m_renderTargets.depth;\n    }\n\n    /**\n     * \\brief Color target\n     *\n     * \\param [in] id Target Index\n     * \\returns The color target\n     */\n    const DxvkAttachment& getColorTarget(uint32_t id) const {\n      return m_renderTargets.color[id];\n    }\n\n    /**\n     * \\brief Number of framebuffer attachment\n     * \\returns Total attachment count\n     */\n    uint32_t numAttachments() const {\n      return m_attachmentCount;\n    }\n\n    /**\n     * \\brief Queries color attachment index of a given attachment\n     * \\returns The index, or -1 if the given attachment is the depth attachment\n     */\n    int32_t getColorAttachmentIndex(uint32_t id) const {\n      return m_attachments[id];\n    }\n\n    /**\n     * \\brief Retrieves attachment by index\n     *\n     * \\param [in] id Framebuffer attachment ID\n     * \\returns The framebuffer attachment\n     */\n    const DxvkAttachment& getAttachment(uint32_t id) const {\n      int32_t idx = getColorAttachmentIndex(id);\n      return idx < 0 ? m_renderTargets.depth : m_renderTargets.color[idx];\n    }\n\n    /**\n     * \\brief Finds attachment index by view\n     *\n     * Color attachments start at 0\n     * \\param [in] view Image view\n     * \\returns Attachment index\n     */\n    int32_t findAttachment(const Rc<DxvkImageView>& view) const;\n\n    /**\n     * \\brief Checks whether view and framebuffer sizes match\n     *\n     * Tests whether the size of the framebuffer is the same\n     * as the size of one of its views. This may be \\c false\n     * when mixing attachments with mismatched dimensions.\n     * \\param [in] view Image view to test\n     * \\returns \\c true if \\c view has the same size as\n     *          the framebuffer.\n     */\n    bool isFullSize(const Rc<DxvkImageView>& view) const;\n\n    /**\n     * \\brief Checks whether an attachment is writable\n     *\n     * Needed for certain clear optimizations.\n     * \\param [in] attachmentIndex Attachment to check\n     * \\param [in] aspects Aspect mask to check\n     * \\returns \\c true if all aspects can be written for the given attachment\n     */\n    bool isWritable(uint32_t attachmentIndex, VkImageAspectFlags aspects) const;\n\n    /**\n     * \\brief Generates render target state\n     * \\returns Render target state info\n     */\n    DxvkRtInfo getRtInfo() const;\n\n  private:\n\n    DxvkRenderTargets   m_renderTargets;\n    DxvkFramebufferSize m_renderSize  = { 0u, 0u, 0u };\n    VkSampleCountFlags  m_sampleCount = 0;\n\n    uint32_t                                     m_attachmentCount = 0;\n    std::array<int32_t, MaxNumRenderTargets + 1> m_attachments;\n\n    DxvkFramebufferSize computeRenderSize(\n      const DxvkFramebufferSize& defaultSize) const;\n\n    DxvkFramebufferSize computeRenderTargetSize(\n      const Rc<DxvkImageView>& renderTarget) const;\n\n  };\n\n\n  /**\n   * \\brief Attachment mask\n   *\n   * Convenience class to track attachment access.\n   */\n  class DxvkAttachmentMask {\n    constexpr static uint32_t ColorRead     = 1u << 0;\n    constexpr static uint32_t ColorWrite    = 1u << 8;\n    constexpr static uint32_t DepthRead     = 1u << 16;\n    constexpr static uint32_t DepthWrite    = 1u << 17;\n    constexpr static uint32_t StencilRead   = 1u << 18;\n    constexpr static uint32_t StencilWrite  = 1u << 19;\n  public:\n\n    DxvkAttachmentMask() = default;\n\n    DxvkAccess getColorAccess(uint32_t index) const {\n      return getAccess(ColorRead << index, ColorWrite << index);\n    }\n\n    DxvkAccess getDepthAccess() const {\n      return getAccess(DepthRead, DepthWrite);\n    }\n\n    DxvkAccess getStencilAccess() const {\n      return getAccess(StencilRead, StencilWrite);\n    }\n\n    void trackColorRead(uint32_t index) {\n      m_mask |= ColorRead << index;\n    }\n\n    void trackColorWrite(uint32_t index) {\n      m_mask |= ColorWrite << index;\n    }\n\n    void trackDepthRead() {\n      m_mask |= DepthRead;\n    }\n\n    void trackDepthWrite() {\n      m_mask |= DepthWrite;\n    }\n\n    void trackStencilRead() {\n      m_mask |= StencilRead;\n    }\n\n    void trackStencilWrite() {\n      m_mask |= StencilWrite;\n    }\n\n    void unifyDepthStencilAccess() {\n      if (m_mask & (DepthRead | StencilRead))\n        m_mask |= DepthRead | StencilRead;\n      if (m_mask & (DepthWrite | StencilWrite))\n        m_mask |= DepthWrite | StencilWrite;\n    }\n\n    void merge(const DxvkAttachmentMask& other) {\n      m_mask |= other.m_mask;\n    }\n\n    void clear() {\n      m_mask = 0u;\n    }\n\n  private:\n\n    uint32_t m_mask = 0u;\n\n    DxvkAccess getAccess(uint32_t readBit, uint32_t writeBit) const {\n      if (m_mask & writeBit)\n        return DxvkAccess::Write;\n      else if (m_mask & readBit)\n        return DxvkAccess::Read;\n      else\n        return DxvkAccess::None;\n    }\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_gpu_event.cpp",
    "content": "#include \"dxvk_gpu_event.h\"\n#include \"dxvk_device.h\"\n\nnamespace dxvk {\n\n  DxvkGpuEvent::DxvkGpuEvent(\n          DxvkGpuEventPool*           parent)\n  : m_pool(parent) {\n    auto vk = m_pool->m_vkd;\n\n    VkEventCreateInfo info = { VK_STRUCTURE_TYPE_EVENT_CREATE_INFO };\n    VkResult vr = vk->vkCreateEvent(vk->device(), &info, nullptr, &m_event);\n\n    if (vr != VK_SUCCESS)\n      throw DxvkError(str::format(\"Failed to create event: \", vr));\n  }\n\n\n  DxvkGpuEvent::~DxvkGpuEvent() {\n    auto vk = m_pool->m_vkd;\n    vk->vkDestroyEvent(vk->device(), m_event, nullptr);\n  }\n\n\n  void DxvkGpuEvent::free() {\n    m_pool->freeEvent(this);\n  }\n\n\n\n  DxvkEvent::DxvkEvent(const Rc<DxvkDevice>& device)\n  : m_device(device) { }\n\n\n  DxvkEvent::~DxvkEvent() {\n\n  }\n\n\n  DxvkGpuEventStatus DxvkEvent::test() {\n    std::lock_guard lock(m_mutex);\n\n    if (m_status == VK_EVENT_SET)\n      return DxvkGpuEventStatus::Signaled;\n\n    if (!m_gpuEvent)\n      return DxvkGpuEventStatus::Invalid;\n\n    // Query current event status and recycle\n    // it as soon as a signal is observed.\n    auto vk = m_device->vkd();\n\n    m_status = vk->vkGetEventStatus(\n      vk->device(), m_gpuEvent->handle());\n\n    switch (m_status) {\n      case VK_EVENT_SET:\n        m_gpuEvent = nullptr;\n        return DxvkGpuEventStatus::Signaled;\n\n      case VK_EVENT_RESET:\n        return DxvkGpuEventStatus::Pending;\n\n      default:\n        return DxvkGpuEventStatus::Invalid;\n    }\n  }\n\n\n  void DxvkEvent::assignGpuEvent(\n          Rc<DxvkGpuEvent>             event) {\n    std::lock_guard lock(m_mutex);\n\n    m_gpuEvent = std::move(event);\n    m_status = VK_NOT_READY;\n  }\n\n\n\n\n  DxvkGpuEventPool::DxvkGpuEventPool(const DxvkDevice* device)\n  : m_vkd(device->vkd()) { }\n\n\n  DxvkGpuEventPool::~DxvkGpuEventPool() {\n    for (auto e : m_freeEvents)\n      delete e;\n  }\n\n  \n  Rc<DxvkGpuEvent> DxvkGpuEventPool::allocEvent() {\n    std::lock_guard lock(m_mutex);\n\n    Rc<DxvkGpuEvent> event;\n\n    if (m_freeEvents.empty()) {\n      event = new DxvkGpuEvent(this);\n    } else {\n      event = m_freeEvents.back();\n      m_freeEvents.pop_back();\n    }\n\n    m_vkd->vkResetEvent(m_vkd->device(), event->handle());\n    return event;\n  }\n\n\n  void DxvkGpuEventPool::freeEvent(DxvkGpuEvent* event) {\n    std::lock_guard lock(m_mutex);\n    m_freeEvents.push_back(event);\n  }\n\n}"
  },
  {
    "path": "src/dxvk/dxvk_gpu_event.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <vector>\n\n#include \"dxvk_include.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n  class DxvkGpuEventPool;\n\n  /**\n   * \\brief Event status\n   * \n   * Reports whether the event is in\n   * a signaled or unsignaled state.\n   */\n  enum class DxvkGpuEventStatus : uint32_t {\n    Invalid   = 0,\n    Pending   = 1,\n    Signaled  = 2,\n  };\n\n\n  /**\n   * \\brief Event handle\n   *\n   * Stores the event handle itself as well\n   * as a pointer to the pool that the event\n   * was allocated from.\n   */\n  class DxvkGpuEvent {\n\n  public:\n\n    explicit DxvkGpuEvent(\n            DxvkGpuEventPool*           parent);\n\n    ~DxvkGpuEvent();\n\n    /**\n     * \\brief Increments ref count\n     */\n    force_inline void incRef() {\n      m_refs.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    /**\n     * \\brief Decrements ref count\n     *\n     * Returns event to the pool if no further\n     * references exist for this event.\n     */\n    force_inline void decRef() {\n      if (m_refs.fetch_sub(1u, std::memory_order_release) == 1u)\n        free();\n    }\n\n    /**\n     * \\brief Queries event handle\n     * \\returns Event handle\n     */\n    VkEvent handle() const {\n      return m_event;\n    }\n\n  private:\n\n    DxvkGpuEventPool*     m_pool  = nullptr;\n    VkEvent               m_event = VK_NULL_HANDLE;\n\n    std::atomic<uint32_t> m_refs  = { 0u };\n\n    void free();\n\n  };\n\n\n  /**\n   * \\brief GPU event\n   *\n   * An event managed by the GPU which allows\n   * the application to check whether a specific\n   * command has completed execution.\n   */\n  class DxvkEvent {\n    friend class DxvkContext;\n  public:\n\n    DxvkEvent(const Rc<DxvkDevice>& device);\n    ~DxvkEvent();\n\n    /**\n     * \\brief Increments reference count\n     */\n    force_inline void incRef() {\n      m_refCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    /**\n     * \\brief Decrements reference count\n     * Frees the event as necessary.\n     */\n    force_inline void decRef() {\n      if (m_refCount.fetch_sub(1u, std::memory_order_release) == 1u)\n        delete this;\n    }\n\n    /**\n     * \\brief Retrieves event status\n     * \n     * Only valid after the event has been\n     * recorded intro a command buffer.\n     * \\returns Event status\n     */\n    DxvkGpuEventStatus test();\n\n  private:\n\n    std::atomic<uint32_t> m_refCount = { 0u };\n\n    sync::Spinlock    m_mutex;\n    VkResult          m_status = VK_NOT_READY;\n\n    Rc<DxvkDevice>    m_device;\n    Rc<DxvkGpuEvent>  m_gpuEvent;\n\n    void assignGpuEvent(\n            Rc<DxvkGpuEvent>            event);\n\n  };\n\n\n  /**\n   * \\brief Event pool\n   * \n   * Thread-safe event allocator that provides\n   * a way to create and recycle Vulkan events.\n   */\n  class DxvkGpuEventPool {\n    friend class DxvkGpuEvent;\n  public:\n\n    DxvkGpuEventPool(const DxvkDevice* device);\n    ~DxvkGpuEventPool();\n\n    /**\n     * \\brief Allocates an event\n     * \n     * Either returns a recycled event, or\n     * creates a new one if necessary. The\n     * state of the event is undefined.\n     * \\returns An event handle\n     */\n    Rc<DxvkGpuEvent> allocEvent();\n\n    /**\n     * \\brief Recycles an event\n     * \n     * \\param [in] event Event object to free\n     */\n    void freeEvent(DxvkGpuEvent* event);\n\n  private:\n\n    Rc<vk::DeviceFn>            m_vkd;\n\n    dxvk::mutex                 m_mutex;\n    std::vector<DxvkGpuEvent*>  m_freeEvents;\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_gpu_query.cpp",
    "content": "#include <utility>\n\n#include \"dxvk_cmdlist.h\"\n#include \"dxvk_device.h\"\n#include \"dxvk_gpu_query.h\"\n\nnamespace dxvk {\n\n  void DxvkGpuQuery::free() {\n    m_allocator->freeQuery(this);\n  }\n\n\n\n\n  DxvkQuery::DxvkQuery(\n    const Rc<DxvkDevice>&             device,\n          VkQueryType                 type,\n          VkQueryControlFlags         flags,\n          uint32_t                    index)\n  : m_device(device), m_type(type), m_flags(flags), m_index(index) {\n\n  }\n  \n  \n  DxvkQuery::~DxvkQuery() {\n\n  }\n\n\n  DxvkGpuQueryStatus DxvkQuery::getData(DxvkQueryData& queryData) {\n    queryData = DxvkQueryData();\n\n    // Callers must ensure that no begin call is pending when\n    // calling this. Given that, once the query is ended, we\n    // know that no other thread will access query state.\n    std::lock_guard lock(m_mutex);\n\n    if (!m_ended)\n      return DxvkGpuQueryStatus::Invalid;\n\n    // Accumulate query data from all available queries\n    DxvkGpuQueryStatus status = accumulateQueryDataLocked();\n\n    // Treat non-precise occlusion queries as available\n    // if we already know the result will be non-zero\n    if ((status == DxvkGpuQueryStatus::Pending)\n     && (m_type == VK_QUERY_TYPE_OCCLUSION)\n     && !(m_flags & VK_QUERY_CONTROL_PRECISE_BIT)\n     && (m_queryData.occlusion.samplesPassed))\n      status = DxvkGpuQueryStatus::Available;\n\n    // Write back accumulated query data if the result is useful\n    if (status == DxvkGpuQueryStatus::Available)\n      queryData = m_queryData;\n\n    return status;\n  }\n\n\n  void DxvkQuery::begin() {\n    std::lock_guard lock(m_mutex);\n    m_queries.clear();\n    m_queryData = { };\n    m_ended = false;\n  }\n\n\n  void DxvkQuery::end() {\n    std::lock_guard lock(m_mutex);\n    m_ended = true;\n  }\n\n\n  void DxvkQuery::addGpuQuery(Rc<DxvkGpuQuery> query) {\n    // Already accumulate available queries here in case\n    // we already allocated a large number of queries\n    std::lock_guard lock(m_mutex);\n\n    if (m_queries.size() >= m_queries.EmbeddedCapacity)\n      accumulateQueryDataLocked();\n\n    m_queries.push_back(std::move(query));\n  }\n\n\n  DxvkGpuQueryStatus DxvkQuery::accumulateQueryDataForGpuQueryLocked(\n    const Rc<DxvkGpuQuery>&           query) {\n    auto vk = m_device->vkd();\n\n    DxvkQueryData tmpData = { };\n\n    // Try to copy query data to temporary structure\n    std::pair<VkQueryPool, uint32_t> handle = query->getQuery();\n\n    VkResult result = vk->vkGetQueryPoolResults(\n      vk->device(), handle.first, handle.second, 1,\n      sizeof(DxvkQueryData), &tmpData,\n      sizeof(DxvkQueryData), VK_QUERY_RESULT_64_BIT);\n\n    if (result == VK_NOT_READY)\n      return DxvkGpuQueryStatus::Pending;\n    else if (result != VK_SUCCESS)\n      return DxvkGpuQueryStatus::Failed;\n\n    // Add numbers to the destination structure\n    switch (m_type) {\n      case VK_QUERY_TYPE_OCCLUSION:\n        m_queryData.occlusion.samplesPassed += tmpData.occlusion.samplesPassed;\n        break;\n\n      case VK_QUERY_TYPE_TIMESTAMP:\n        m_queryData.timestamp.time = tmpData.timestamp.time;\n        break;\n\n      case VK_QUERY_TYPE_PIPELINE_STATISTICS:\n        m_queryData.statistic.iaVertices       += tmpData.statistic.iaVertices;\n        m_queryData.statistic.iaPrimitives     += tmpData.statistic.iaPrimitives;\n        m_queryData.statistic.vsInvocations    += tmpData.statistic.vsInvocations;\n        m_queryData.statistic.gsInvocations    += tmpData.statistic.gsInvocations;\n        m_queryData.statistic.gsPrimitives     += tmpData.statistic.gsPrimitives;\n        m_queryData.statistic.clipInvocations  += tmpData.statistic.clipInvocations;\n        m_queryData.statistic.clipPrimitives   += tmpData.statistic.clipPrimitives;\n        m_queryData.statistic.fsInvocations    += tmpData.statistic.fsInvocations;\n        m_queryData.statistic.tcsPatches       += tmpData.statistic.tcsPatches;\n        m_queryData.statistic.tesInvocations   += tmpData.statistic.tesInvocations;\n        m_queryData.statistic.csInvocations    += tmpData.statistic.csInvocations;\n        break;\n      \n      case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT:\n        m_queryData.xfbStream.primitivesWritten += tmpData.xfbStream.primitivesWritten;\n        m_queryData.xfbStream.primitivesNeeded  += tmpData.xfbStream.primitivesNeeded;\n        break;\n      \n      default:\n        Logger::err(str::format(\"DXVK: Unhandled query type: \", m_type));\n        return DxvkGpuQueryStatus::Invalid;\n    }\n    \n    return DxvkGpuQueryStatus::Available;\n  }\n\n\n  DxvkGpuQueryStatus DxvkQuery::accumulateQueryDataLocked() {\n    DxvkGpuQueryStatus status = DxvkGpuQueryStatus::Available;\n\n    // Process available queries and return them to the\n    // allocator if possible. This may help reduce the\n    // number of Vulkan queries in flight.\n    size_t queriesAvailable = 0;\n\n    while (queriesAvailable < m_queries.size()) {\n      status = accumulateQueryDataForGpuQueryLocked(m_queries[queriesAvailable]);\n\n      if (status != DxvkGpuQueryStatus::Available)\n        break;\n\n      queriesAvailable += 1;\n    }\n\n    if (queriesAvailable) {\n      for (size_t i = queriesAvailable; i < m_queries.size(); i++)\n        m_queries[i - queriesAvailable] = m_queries[i];\n\n      m_queries.resize(m_queries.size() - queriesAvailable);\n    }\n\n    return status;\n  }\n  \n  \n  \n  \n  DxvkGpuQueryAllocator::DxvkGpuQueryAllocator(\n          DxvkDevice*                 device,\n          VkQueryType                 queryType,\n          uint32_t                    queryPoolSize)\n  : m_device        (device),\n    m_queryType     (queryType),\n    m_queryPoolSize (queryPoolSize) {\n\n  }\n\n  \n  DxvkGpuQueryAllocator::~DxvkGpuQueryAllocator() {\n    auto vk = m_device->vkd();\n\n    for (auto& p : m_pools) {\n      vk->vkDestroyQueryPool(vk->device(), p.pool, nullptr);\n      delete[] p.queries;\n    }\n  }\n\n  \n  Rc<DxvkGpuQuery> DxvkGpuQueryAllocator::allocQuery() {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    if (!m_free)\n      createQueryPool();\n\n    return std::exchange(m_free, m_free->m_next);\n  }\n\n\n  void DxvkGpuQueryAllocator::freeQuery(DxvkGpuQuery* query) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    query->m_next = std::exchange(m_free, query);\n  }\n\n\n  void DxvkGpuQueryAllocator::createQueryPool() {\n    auto vk = m_device->vkd();\n\n    VkQueryPoolCreateInfo info = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };\n    info.queryType  = m_queryType;\n    info.queryCount = m_queryPoolSize;\n\n    if (m_queryType == VK_QUERY_TYPE_PIPELINE_STATISTICS) {\n      info.pipelineStatistics\n        = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT\n        | VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT;\n    }\n\n    VkQueryPool queryPool = VK_NULL_HANDLE;\n\n    if (vk->vkCreateQueryPool(vk->device(), &info, nullptr, &queryPool)) {\n      Logger::err(str::format(\"DXVK: Failed to create query pool (\", m_queryType, \"; \", m_queryPoolSize, \")\"));\n      return;\n    }\n\n    auto& pool = m_pools.emplace_back();\n    pool.pool = queryPool;\n    pool.queries = new DxvkGpuQuery [m_queryPoolSize];\n\n    for (uint32_t i = 0; i < m_queryPoolSize; i++) {\n      auto& query = pool.queries[i];\n      query.m_allocator = this;\n      query.m_pool = queryPool;\n      query.m_index = i;\n\n      if (i + 1u < m_queryPoolSize)\n        query.m_next = &pool.queries[i + 1u];\n    }\n\n    m_free = &pool.queries[0u];\n  }\n\n\n\n\n  DxvkGpuQueryPool::DxvkGpuQueryPool(DxvkDevice* device)\n  : m_occlusion(device, VK_QUERY_TYPE_OCCLUSION,                     16384),\n    m_statistic(device, VK_QUERY_TYPE_PIPELINE_STATISTICS,           1024),\n    m_timestamp(device, VK_QUERY_TYPE_TIMESTAMP,                     1024),\n    m_xfbStream(device, VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 1024) {\n    \n  }\n  \n\n  DxvkGpuQueryPool::~DxvkGpuQueryPool() {\n\n  }\n\n  \n  Rc<DxvkGpuQuery> DxvkGpuQueryPool::allocQuery(VkQueryType type) {\n    switch (type) {\n      case VK_QUERY_TYPE_OCCLUSION:\n        return m_occlusion.allocQuery();\n      case VK_QUERY_TYPE_PIPELINE_STATISTICS:\n        return m_statistic.allocQuery();\n      case VK_QUERY_TYPE_TIMESTAMP:\n        return m_timestamp.allocQuery();\n      case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT:\n        return m_xfbStream.allocQuery();\n      default:\n        Logger::err(str::format(\"DXVK: Unhandled query type: \", type));\n        return nullptr;\n    }\n  }\n\n\n\n\n  DxvkGpuQueryManager::DxvkGpuQueryManager(DxvkGpuQueryPool& pool)\n  : m_pool(&pool) {\n\n  }\n\n  \n  DxvkGpuQueryManager::~DxvkGpuQueryManager() {\n\n  }\n\n\n  void DxvkGpuQueryManager::enableQuery(\n    const Rc<DxvkCommandList>&  cmd,\n    const Rc<DxvkQuery>&        query) {\n    query->begin();\n\n    uint32_t index = getQueryTypeIndex(query->type(), query->index());\n\n    m_activeQueries[index].queries.push_back(query);\n\n    if (m_activeTypes & getQueryTypeBit(query->type()))\n      restartQueries(cmd, query->type(), query->index());\n  }\n\n  \n  void DxvkGpuQueryManager::disableQuery(\n    const Rc<DxvkCommandList>&  cmd,\n    const Rc<DxvkQuery>&        query) {\n    uint32_t index = getQueryTypeIndex(query->type(), query->index());\n\n    for (auto& q : m_activeQueries[index].queries) {\n      if (q == query) {\n        q = std::move(m_activeQueries[index].queries.back());\n        m_activeQueries[index].queries.pop_back();\n        break;\n      }\n    }\n\n    if (m_activeTypes & getQueryTypeBit(query->type()))\n      restartQueries(cmd, query->type(), query->index());\n\n    query->end();\n  }\n\n\n  void DxvkGpuQueryManager::writeTimestamp(\n    const Rc<DxvkCommandList>&  cmd,\n    const Rc<DxvkQuery>&        query) {\n    Rc<DxvkGpuQuery> q = m_pool->allocQuery(query->type());\n\n    query->begin();\n    query->addGpuQuery(q);\n    query->end();\n\n    std::pair<VkQueryPool, uint32_t> handle = q->getQuery();\n\n    cmd->resetQuery(handle.first, handle.second);\n\n    cmd->cmdWriteTimestamp(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,\n      handle.first, handle.second);\n\n    cmd->track(std::move(q));\n  }\n\n\n  void DxvkGpuQueryManager::beginQueries(\n    const Rc<DxvkCommandList>&  cmd,\n          VkQueryType           type) {\n    m_activeTypes |= getQueryTypeBit(type);\n\n    if (likely(type != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)) {\n      restartQueries(cmd, type, 0);\n    } else {\n      for (uint32_t i = 0; i < 4; i++)\n        restartQueries(cmd, type, i);\n    }\n  }\n\n    \n  void DxvkGpuQueryManager::endQueries(\n    const Rc<DxvkCommandList>&  cmd,\n          VkQueryType           type) {\n    m_activeTypes &= ~getQueryTypeBit(type);\n\n    if (likely(type != VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)) {\n      restartQueries(cmd, type, 0);\n    } else {\n      for (uint32_t i = 0; i < 4; i++)\n        restartQueries(cmd, type, i);\n    }\n  }\n\n\n  void DxvkGpuQueryManager::restartQueries(\n    const Rc<DxvkCommandList>&  cmd,\n          VkQueryType           type,\n          uint32_t              index) {\n    auto& array = m_activeQueries[getQueryTypeIndex(type, index)];\n\n    // End active GPU query for the given type and index\n    if (array.gpuQuery) {\n      auto handle = array.gpuQuery->getQuery();\n\n      if (type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)\n        cmd->cmdEndQueryIndexed(handle.first, handle.second, index);\n      else\n        cmd->cmdEndQuery(handle.first, handle.second);\n\n      array.gpuQuery = nullptr;\n    }\n\n    // If the query type is still active, allocate, reset and begin\n    // a new GPU query and assign it to all virtual queries.\n    if ((m_activeTypes & getQueryTypeBit(type)) && !array.queries.empty()) {\n      array.gpuQuery = m_pool->allocQuery(type);\n      auto handle = array.gpuQuery->getQuery();\n\n      // If any active occlusion query has the precise flag set, we need\n      // to respect it, otherwise just use a regular occlusion query.\n      VkQueryControlFlags flags = 0u;\n\n      for (const auto& q : array.queries) {\n        flags |= q->flags();\n        q->addGpuQuery(array.gpuQuery);\n      }\n\n      // Actually reset and begin the query\n      cmd->resetQuery(handle.first, handle.second);\n\n      if (type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)\n        cmd->cmdBeginQueryIndexed(handle.first, handle.second, flags, index);\n      else\n        cmd->cmdBeginQuery(handle.first, handle.second, flags);\n\n      cmd->track(array.gpuQuery);\n    }\n  }\n\n\n  uint32_t DxvkGpuQueryManager::getQueryTypeBit(\n          VkQueryType           type) {\n    return 1u << getQueryTypeIndex(type, 0u);\n  }\n\n\n  uint32_t DxvkGpuQueryManager::getQueryTypeIndex(\n          VkQueryType           type,\n          uint32_t              index) {\n    switch (type) {\n      case VK_QUERY_TYPE_OCCLUSION:                     return 0u;\n      case VK_QUERY_TYPE_PIPELINE_STATISTICS:           return 1u;\n      case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT: return 2u + index;\n      default:                                          return 0u;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_gpu_query.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <mutex>\n#include <list>\n#include <vector>\n\n#include \"../util/util_small_vector.h\"\n\n#include \"dxvk_include.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n  class DxvkCommandList;\n\n  class DxvkGpuQueryPool;\n  class DxvkGpuQueryAllocator;\n\n  /**\n   * \\brief Query status\n   * \n   * Reports whether a query is in\n   * signaled or unsignaled state.\n   */\n  enum class DxvkGpuQueryStatus : uint32_t {\n    Invalid   = 0,\n    Pending   = 1,\n    Available = 2,\n    Failed    = 3,\n  };\n\n\n  /**\n   * \\brief Occlusion query data\n   * \n   * Stores the number of samples\n   * that passes fragment tests.\n   */\n  struct DxvkQueryOcclusionData {\n    uint64_t samplesPassed;\n  };\n  \n  /**\n   * \\brief Timestamp data\n   * \n   * Stores a GPU time stamp.\n   */\n  struct DxvkQueryTimestampData {\n    uint64_t time;\n  };\n  \n  /**\n   * \\brief Pipeline statistics\n   * \n   * Stores the counters for\n   * pipeline statistics queries.\n   */\n  struct DxvkQueryStatisticData {\n    uint64_t iaVertices;\n    uint64_t iaPrimitives;\n    uint64_t vsInvocations;\n    uint64_t gsInvocations;\n    uint64_t gsPrimitives;\n    uint64_t clipInvocations;\n    uint64_t clipPrimitives;\n    uint64_t fsInvocations;\n    uint64_t tcsPatches;\n    uint64_t tesInvocations;\n    uint64_t csInvocations;\n  };\n\n  /**\n   * \\brief Transform feedback stream query\n   * \n   * Stores the number of primitives written to the\n   * buffer, as well as the number of primitives\n   * generated. The latter can be used to check for\n   * overflow.\n   */\n  struct DxvkQueryXfbStreamData {\n    uint64_t primitivesWritten;\n    uint64_t primitivesNeeded;\n  };\n  \n  /**\n   * \\brief Query data\n   * \n   * A union that stores query data. Select an\n   * appropriate member based on the query type.\n   */\n  union DxvkQueryData {\n    DxvkQueryOcclusionData occlusion;\n    DxvkQueryTimestampData timestamp;\n    DxvkQueryStatisticData statistic;\n    DxvkQueryXfbStreamData xfbStream;\n  };\n\n\n  /**\n   * \\brief Query handle\n   * \n   * Stores the query allocator, as well as\n   * the actual pool and query index. Since\n   * query pools have to be reset on the GPU,\n   * this also comes with a reset event.\n   */\n  class DxvkGpuQuery {\n    friend class DxvkGpuQueryAllocator;\n  public:\n\n    /**\n     * \\brief Increments query reference count\n     */\n    force_inline void incRef() {\n      m_refCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    /**\n     * \\brief Decrements query reference count\n     * Returns the query to its allocator if necessary.\n     */\n    force_inline void decRef() {\n      if (m_refCount.fetch_sub(1u, std::memory_order_release) == 1u)\n        free();\n    }\n\n    /**\n     * \\brief Retrieves query pool and index\n     * \\returns Query pool handle and query index\n     */\n    std::pair<VkQueryPool, uint32_t> getQuery() const {\n      return std::make_pair(m_pool, m_index);\n    }\n\n  private:\n\n    DxvkGpuQueryAllocator*  m_allocator = nullptr;\n    DxvkGpuQuery*           m_next      = nullptr;\n\n    VkQueryPool             m_pool      = VK_NULL_HANDLE;\n    uint32_t                m_index     = 0u;\n\n    std::atomic<uint32_t>   m_refCount  = { 0u };\n\n    void free();\n\n  };\n\n\n  /**\n   * \\brief Virtual query object\n   *\n   * References an arbitrary number of Vulkan queries to\n   * get feedback from the GPU. Vulkan queries can be used\n   * by multiple virtual queries in case of overlap.\n   */\n  class DxvkQuery {\n    friend class DxvkGpuQueryManager;\n  public:\n\n    DxvkQuery(\n      const Rc<DxvkDevice>&             device,\n            VkQueryType                 type,\n            VkQueryControlFlags         flags,\n            uint32_t                    index);\n    \n    ~DxvkQuery();\n\n    /**\n     * \\brief Increments reference count\n     */\n    force_inline void incRef() {\n      m_refCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    /**\n     * \\brief Decrements reference count\n     */\n    force_inline void decRef() {\n      if (m_refCount.fetch_sub(1u, std::memory_order_release) == 1u)\n        delete this;\n    }\n\n    /**\n     * \\brief Query type\n     * \\returns Query type\n     */\n    VkQueryType type() const {\n      return m_type;\n    }\n\n    /**\n     * \\brief Query control flags\n     * \\returns Query control flags\n     */\n    VkQueryControlFlags flags() const {\n      return m_flags;\n    }\n\n    /**\n     * \\brief Query index\n     * \n     * Only valid for indexed query types.\n     * For non-zero values, indexed query\n     * functions must be used.\n     * \\returns Query index\n     */\n    uint32_t index() const {\n      return m_index;\n    }\n\n    /**\n     * \\brief Retrieves query data\n     * \n     * If all query data is available, this will\n     * return \\c DxvkGpuQueryStatus::Signaled, and\n     * the destination structure will be filled\n     * with the data retrieved from all associated\n     * query handles.\n     * \\param [out] queryData Query data\n     * \\returns Current query status\n     */\n    DxvkGpuQueryStatus getData(\n            DxvkQueryData&      queryData);\n\n    /**\n     * \\brief Begins query\n     *\n     * Invalidates previously retrieved data.\n     */\n    void begin();\n\n    /**\n     * \\brief Ends query\n     *\n     * Sets query into pending state. Calling\n     * \\c getData is legal after calling this.\n     */\n    void end();\n\n  private:\n\n    std::atomic<uint32_t> m_refCount = { 0u };\n\n    Rc<DxvkDevice>      m_device;\n\n    VkQueryType         m_type  = VK_QUERY_TYPE_MAX_ENUM;\n    VkQueryControlFlags m_flags = 0u;\n    uint32_t            m_index = 0u;\n    bool                m_ended = false;\n\n    sync::Spinlock      m_mutex;\n    DxvkQueryData       m_queryData = { };\n\n    small_vector<Rc<DxvkGpuQuery>, 8> m_queries;\n\n    DxvkGpuQueryStatus accumulateQueryDataForGpuQueryLocked(\n      const Rc<DxvkGpuQuery>&           query);\n\n    DxvkGpuQueryStatus accumulateQueryDataLocked();\n\n    void addGpuQuery(\n            Rc<DxvkGpuQuery>            query);\n\n  };\n\n\n  /**\n   * \\brief Query allocator\n   * \n   * Creates query pools and allocates\n   * queries for a single query type. \n   */\n  class DxvkGpuQueryAllocator {\n\n  public:\n\n    DxvkGpuQueryAllocator(\n            DxvkDevice*                 device,\n            VkQueryType                 queryType,\n            uint32_t                    queryPoolSize);\n\n    ~DxvkGpuQueryAllocator();\n\n    /**\n     * \\brief Allocates a query\n     * \n     * If possible, this returns a free query from an existing\n     * query pool. Otherwise, a new query pool will be created.\n     * \\returns Query handle\n     */\n    Rc<DxvkGpuQuery> allocQuery();\n\n    /**\n     * \\brief Recycles a query\n     * \n     * Returns a query back to the allocator so that it can be\n     * reused. The query must not be in pending state.\n     * \\param [in] query Query object to recycle\n     */\n    void freeQuery(\n            DxvkGpuQuery*               query);\n\n  private:\n\n    struct Pool {\n      VkQueryPool   pool    = VK_NULL_HANDLE;\n      DxvkGpuQuery* queries = nullptr;\n    };\n\n    DxvkDevice*       m_device        = nullptr;\n    VkQueryType       m_queryType     = VK_QUERY_TYPE_MAX_ENUM;\n    uint32_t          m_queryPoolSize = 0u;\n\n    dxvk::mutex       m_mutex;\n    std::list<Pool>   m_pools;\n\n    DxvkGpuQuery*     m_free = nullptr;\n\n    void createQueryPool();\n\n  };\n\n\n  /**\n   * \\brief Query pool\n   * \n   * Small wrapper class that manages query\n   * allocators for all supported query types,\n   */\n  class DxvkGpuQueryPool {\n\n  public:\n\n    DxvkGpuQueryPool(DxvkDevice* device);\n    \n    ~DxvkGpuQueryPool();\n    \n    /**\n     * \\brief Allocates a single query\n     * \n     * \\param [in] type Query type\n     * \\returns Handle to the allocated query\n     */\n    Rc<DxvkGpuQuery> allocQuery(VkQueryType type);\n\n  private:\n\n    DxvkGpuQueryAllocator m_occlusion;\n    DxvkGpuQueryAllocator m_statistic;\n    DxvkGpuQueryAllocator m_timestamp;\n    DxvkGpuQueryAllocator m_xfbStream;\n\n  };\n\n\n  /**\n   * \\brief Query manager\n   * \n   * Keeps track of enabled and disabled queries\n   * and assigns Vulkan queries to them as needed.\n   */\n  class DxvkGpuQueryManager {\n    constexpr static uint32_t MaxQueryTypes = 6u;\n  public:\n\n    DxvkGpuQueryManager(DxvkGpuQueryPool& pool);\n    \n    ~DxvkGpuQueryManager();\n\n    /**\n     * \\brief Enables a query\n     * \n     * This will also immediately begin the\n     * query in case the query type is active.\n     * \\param [in] cmd Command list\n     * \\param [in] query Query to allocate\n     */\n    void enableQuery(\n      const Rc<DxvkCommandList>&  cmd,\n      const Rc<DxvkQuery>&        query);\n    \n    /**\n     * \\brief Disables a query\n     * \n     * This will also immediately end the\n     * query in case the query type is active.\n     * \\param [in] cmd Command list\n     * \\param [in] query Query to allocate\n     */\n    void disableQuery(\n      const Rc<DxvkCommandList>&  cmd,\n      const Rc<DxvkQuery>&        query);\n    \n    /**\n     * \\brief Signals a time stamp query\n     * \n     * Timestamp queries are not scoped.\n     * \\param [in] cmd Command list\n     * \\param [in] query Query to allocate\n     */\n    void writeTimestamp(\n      const Rc<DxvkCommandList>&  cmd,\n      const Rc<DxvkQuery>&        query);\n\n    /**\n     * \\brief Begins queries of a given type\n     * \n     * Makes a query type \\e active. Begins\n     * all enabled queries of this type.\n     * \\param [in] cmd Command list\n     * \\param [in] type Query type\n     */\n    void beginQueries(\n      const Rc<DxvkCommandList>&  cmd,\n            VkQueryType           type);\n    \n    /**\n     * \\brief Ends queries of a given type\n     * \n     * Makes a query type \\e inactive. Ends\n     * all enabled queries of this type.\n     * \\param [in] cmd Command list\n     * \\param [in] type Query type\n     */\n    void endQueries(\n      const Rc<DxvkCommandList>&  cmd,\n            VkQueryType           type);\n\n  private:\n\n    struct QuerySet {\n      Rc<DxvkGpuQuery>            gpuQuery;\n      std::vector<Rc<DxvkQuery>>  queries;\n    };\n\n    DxvkGpuQueryPool*             m_pool        = nullptr;\n    uint32_t                      m_activeTypes = 0u;\n\n    std::array<QuerySet, MaxQueryTypes> m_activeQueries = { };\n\n    void restartQueries(\n      const Rc<DxvkCommandList>&  cmd,\n            VkQueryType           type,\n            uint32_t              index);\n\n    static uint32_t getQueryTypeBit(\n            VkQueryType           type);\n\n    static uint32_t getQueryTypeIndex(\n            VkQueryType           type,\n            uint32_t              index);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_graphics.cpp",
    "content": "#include <iomanip>\n\n#include \"../util/util_time.h\"\n\n#include \"dxvk_device.h\"\n#include \"dxvk_graphics.h\"\n#include \"dxvk_pipemanager.h\"\n\nnamespace dxvk {\n\n  VkPrimitiveTopology determineGsInputTopology(\n          VkPrimitiveTopology            shader,\n          VkPrimitiveTopology            state) {\n    switch (state) {\n      case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:\n        return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;\n\n      case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:\n      case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:\n        if (shader == VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY)\n          return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;\n        [[fallthrough]];\n\n      case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:\n      case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:\n        return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;\n\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:\n        if (shader == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY)\n          return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;\n        [[fallthrough]];\n\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:\n        return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n\n      default:\n        Logger::err(str::format(\"Unhandled primitive topology \", state));\n        return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;\n    }\n  }\n\n\n  VkPrimitiveTopology determinePreGsTopology(\n    const DxvkGraphicsPipelineShaders&      shaders,\n    const DxvkGraphicsPipelineStateInfo&    state) {\n    if (shaders.tcs && shaders.tcs->metadata().flags.test(DxvkShaderFlag::TessellationPoints))\n      return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;\n\n    if (shaders.tes)\n      return shaders.tes->metadata().outputTopology;\n\n    return state.ia.primitiveTopology();\n  }\n\n\n  VkPrimitiveTopology determinePipelineTopology(\n    const DxvkGraphicsPipelineShaders&      shaders,\n    const DxvkGraphicsPipelineStateInfo&    state) {\n    if (shaders.gs)\n      return shaders.gs->metadata().outputTopology;\n\n    return determinePreGsTopology(shaders, state);\n  }\n\n\n  DxvkGraphicsPipelineVertexInputState::DxvkGraphicsPipelineVertexInputState() {\n    \n  }\n\n\n  DxvkGraphicsPipelineVertexInputState::DxvkGraphicsPipelineVertexInputState(\n    const DxvkDevice*                     device,\n    const DxvkGraphicsPipelineStateInfo&  state,\n    const DxvkGraphicsPipelineShaders&    shaders) {\n    std::array<uint32_t, MaxNumVertexBindings> viBindingMap = { };\n\n    iaInfo.topology               = state.ia.primitiveTopology();\n    iaInfo.primitiveRestartEnable = state.ia.primitiveRestart();\n\n    uint32_t attrMask = shaders.vs->metadata().inputs.computeMask();\n    uint32_t bindingMask = 0;\n\n    // Find out which bindings are used based on the attribute mask\n    for (uint32_t i = 0; i < state.il.attributeCount(); i++) {\n      if (attrMask & (1u << state.ilAttributes[i].location()))\n        bindingMask |= 1u << state.ilAttributes[i].binding();\n    }\n\n    // Process vertex bindings. We will compact binding numbers on\n    // the fly so that vertex buffers can be updated more easily.\n    uint32_t bindingCount = 0;\n\n    for (uint32_t i = 0; i < state.il.bindingCount(); i++) {\n      uint32_t bindingIndex = state.ilBindings[i].binding();\n\n      if (bindingMask & (1u << bindingIndex)) {\n        viBindingMap[bindingIndex] = i;\n\n        VkVertexInputBindingDescription& binding = viBindings[bindingCount++];\n        binding.binding = i;\n        binding.stride = state.ilBindings[i].stride();\n        binding.inputRate = state.ilBindings[i].inputRate();\n\n        if (state.ilBindings[i].inputRate() == VK_VERTEX_INPUT_RATE_INSTANCE\n         && state.ilBindings[i].divisor()   != 1) {\n          VkVertexInputBindingDivisorDescriptionEXT& divisor = viDivisors[viDivisorInfo.vertexBindingDivisorCount++];\n          divisor.binding = i;\n          divisor.divisor = state.ilBindings[i].divisor();\n        }\n      }\n    }\n\n    if (bindingCount) {\n      bool supportsDivisor = device->features().extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor;\n\n      viInfo.vertexBindingDescriptionCount = bindingCount;\n      viInfo.pVertexBindingDescriptions = viBindings.data();\n\n      if (viDivisorInfo.vertexBindingDivisorCount && supportsDivisor) {\n        viDivisorInfo.pVertexBindingDivisors = viDivisors.data();\n        viInfo.pNext = &viDivisorInfo;\n      }\n    }\n\n    // Process vertex attributes, filtering out unused ones\n    uint32_t attrCount = 0;\n\n    for (uint32_t i = 0; i < state.il.attributeCount(); i++) {\n      if (attrMask & (1u << state.ilAttributes[i].location())) {\n        VkVertexInputAttributeDescription& attr = viAttributes[attrCount++];\n        attr.location = state.ilAttributes[i].location();\n        attr.binding = viBindingMap[state.ilAttributes[i].binding()];\n        attr.format = state.ilAttributes[i].format();\n        attr.offset = state.ilAttributes[i].offset();\n      }\n    }\n\n    if (attrCount) {\n      viInfo.vertexAttributeDescriptionCount = attrCount;\n      viInfo.pVertexAttributeDescriptions = viAttributes.data();\n    }\n\n    // We need to be consistent with the pipeline state vector since\n    // the normalized state may otherwise change beavhiour here.\n    viUseDynamicVertexStrides = state.useDynamicVertexStrides();\n  }\n\n\n  bool DxvkGraphicsPipelineVertexInputState::eq(const DxvkGraphicsPipelineVertexInputState& other) const {\n    bool eq = iaInfo.topology                         == other.iaInfo.topology\n           && iaInfo.primitiveRestartEnable           == other.iaInfo.primitiveRestartEnable\n           && viInfo.vertexBindingDescriptionCount    == other.viInfo.vertexBindingDescriptionCount\n           && viInfo.vertexAttributeDescriptionCount  == other.viInfo.vertexAttributeDescriptionCount\n           && viDivisorInfo.vertexBindingDivisorCount == other.viDivisorInfo.vertexBindingDivisorCount\n           && viUseDynamicVertexStrides               == other.viUseDynamicVertexStrides;\n\n    for (uint32_t i = 0; i < viInfo.vertexBindingDescriptionCount && eq; i++) {\n      const auto& a = viBindings[i];\n      const auto& b = other.viBindings[i];\n\n      eq = a.binding    == b.binding\n        && a.stride     == b.stride\n        && a.inputRate  == b.inputRate;\n    }\n\n    for (uint32_t i = 0; i < viInfo.vertexAttributeDescriptionCount && eq; i++) {\n      const auto& a = viAttributes[i];\n      const auto& b = other.viAttributes[i];\n\n      eq = a.location   == b.location\n        && a.binding    == b.binding\n        && a.format     == b.format\n        && a.offset     == b.offset;\n    }\n\n    for (uint32_t i = 0; i < viDivisorInfo.vertexBindingDivisorCount; i++) {\n      const auto& a = viDivisors[i];\n      const auto& b = other.viDivisors[i];\n\n      eq = a.binding    == b.binding\n        && a.divisor    == b.divisor;\n    }\n\n    return eq;\n  }\n\n\n  size_t DxvkGraphicsPipelineVertexInputState::hash() const {\n    DxvkHashState hash;\n    hash.add(uint32_t(iaInfo.topology));\n    hash.add(uint32_t(iaInfo.primitiveRestartEnable));\n    hash.add(uint32_t(viInfo.vertexBindingDescriptionCount));\n    hash.add(uint32_t(viInfo.vertexAttributeDescriptionCount));\n    hash.add(uint32_t(viDivisorInfo.vertexBindingDivisorCount));\n    hash.add(uint32_t(viUseDynamicVertexStrides));\n\n    for (uint32_t i = 0; i < viInfo.vertexBindingDescriptionCount; i++) {\n      hash.add(uint32_t(viBindings[i].binding));\n      hash.add(uint32_t(viBindings[i].stride));\n      hash.add(uint32_t(viBindings[i].inputRate));\n    }\n\n    for (uint32_t i = 0; i < viInfo.vertexAttributeDescriptionCount; i++) {\n      hash.add(uint32_t(viAttributes[i].location));\n      hash.add(uint32_t(viAttributes[i].binding));\n      hash.add(uint32_t(viAttributes[i].format));\n      hash.add(uint32_t(viAttributes[i].offset));\n    }\n\n    for (uint32_t i = 0; i < viDivisorInfo.vertexBindingDivisorCount; i++) {\n      hash.add(uint32_t(viDivisors[i].binding));\n      hash.add(uint32_t(viDivisors[i].divisor));\n    }\n\n    return hash;\n  }\n\n\n  DxvkGraphicsPipelineVertexInputLibrary::DxvkGraphicsPipelineVertexInputLibrary(\n          DxvkDevice*                           device,\n    const DxvkGraphicsPipelineVertexInputState& state)\n  : m_device(device) {\n    auto vk = m_device->vkd();\n\n    VkDynamicState dynamicState = VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE;\n    VkPipelineDynamicStateCreateInfo dyInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };\n\n    if (state.viUseDynamicVertexStrides) {\n      dyInfo.dynamicStateCount = 1;\n      dyInfo.pDynamicStates = &dynamicState;\n    }\n\n    VkPipelineCreateFlags2CreateInfo flags = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO };\n    flags.flags = VK_PIPELINE_CREATE_2_LIBRARY_BIT_KHR;\n\n    if (m_device->canUseDescriptorHeap())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (m_device->canUseDescriptorBuffer())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkGraphicsPipelineLibraryCreateInfoEXT libInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, &flags };\n    libInfo.flags             = VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT;\n\n    VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo };\n    info.pVertexInputState    = &state.viInfo;\n    info.pInputAssemblyState  = &state.iaInfo;\n    info.pDynamicState        = &dyInfo;\n    info.basePipelineIndex    = -1;\n\n    VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(),\n      VK_NULL_HANDLE, 1, &info, nullptr, &m_pipeline);\n\n    if (vr)\n      throw DxvkError(\"Failed to create vertex input pipeline library\");\n  }\n\n\n  DxvkGraphicsPipelineVertexInputLibrary::~DxvkGraphicsPipelineVertexInputLibrary() {\n    auto vk = m_device->vkd();\n\n    vk->vkDestroyPipeline(vk->device(), m_pipeline, nullptr);\n  }\n\n\n  DxvkGraphicsPipelineFragmentOutputState::DxvkGraphicsPipelineFragmentOutputState() {\n\n  }\n\n\n  DxvkGraphicsPipelineFragmentOutputState::DxvkGraphicsPipelineFragmentOutputState(\n    const DxvkDevice*                     device,\n    const DxvkGraphicsPipelineStateInfo&  state,\n    const DxvkGraphicsPipelineShaders&    shaders) {\n    // Set up color formats and attachment blend states. Disable the write\n    // mask for any attachment that the fragment shader does not write to.\n    uint32_t fsOutputMask = shaders.fs ? shaders.fs->metadata().outputs.computeMask() : 0u;\n\n    // Dual-source blending can only write to one render target\n    if (state.useDualSourceBlending())\n      fsOutputMask &= 0x1;\n\n    const VkColorComponentFlags rgbaWriteMask\n      = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT\n      | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n\n    cbInfo.logicOpEnable  = state.om.enableLogicOp();\n    cbInfo.logicOp        = state.om.logicOp();\n\n    feedbackLoop = state.om.feedbackLoop();\n\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      rtColorFormats[i] = state.rt.getColorFormat(i);\n\n      if (rtColorFormats[i]) {\n        rtInfo.colorAttachmentCount = i + 1;\n\n        auto formatInfo = lookupFormatInfo(rtColorFormats[i]);\n\n        if ((fsOutputMask & (1 << i)) && formatInfo) {\n          VkColorComponentFlags writeMask = state.omBlend[i].colorWriteMask();\n\n          if (writeMask != rgbaWriteMask) {\n            writeMask = util::remapComponentMask(\n              state.omBlend[i].colorWriteMask(), state.omSwizzle[i].mapping());\n          }\n\n          writeMask &= formatInfo->componentMask;\n\n          if (writeMask == formatInfo->componentMask)\n            writeMask = rgbaWriteMask;\n\n          if (writeMask) {\n            cbAttachments[i] = state.omBlend[i].state();\n            cbAttachments[i].colorWriteMask = writeMask;\n\n            // If we're rendering to an emulated alpha-only render target, fix up blending\n            if (cbAttachments[i].blendEnable && formatInfo->componentMask == VK_COLOR_COMPONENT_R_BIT && state.omSwizzle[i].rIndex() == 3) {\n              cbAttachments[i].srcColorBlendFactor = util::remapAlphaToColorBlendFactor(\n                std::exchange(cbAttachments[i].srcAlphaBlendFactor, VK_BLEND_FACTOR_ONE));\n              cbAttachments[i].dstColorBlendFactor = util::remapAlphaToColorBlendFactor(\n                std::exchange(cbAttachments[i].dstAlphaBlendFactor, VK_BLEND_FACTOR_ZERO));\n              cbAttachments[i].colorBlendOp =\n                std::exchange(cbAttachments[i].alphaBlendOp, VK_BLEND_OP_ADD);\n            }\n          }\n        }\n      }\n    }\n\n    if (rtInfo.colorAttachmentCount) {\n      rtInfo.pColorAttachmentFormats = rtColorFormats.data();\n\n      cbInfo.attachmentCount = rtInfo.colorAttachmentCount;\n      cbInfo.pAttachments = cbAttachments.data();\n    }\n\n    // Set up depth-stencil format accordingly.\n    VkFormat rtDepthFormat = state.rt.getDepthStencilFormat();\n\n    if (rtDepthFormat) {\n      auto rtDepthFormatInfo = lookupFormatInfo(rtDepthFormat);\n\n      if (rtDepthFormatInfo->aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)\n        rtInfo.depthAttachmentFormat = rtDepthFormat;\n\n      if (rtDepthFormatInfo->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)\n        rtInfo.stencilAttachmentFormat = rtDepthFormat;\n    }\n\n    // Set up multisample state based on shader info as well\n    // as rasterization state and render target sample counts.\n    msInfo.rasterizationSamples = VkSampleCountFlagBits(state.ms.sampleCount());\n\n    if (!msInfo.rasterizationSamples) {\n      msInfo.rasterizationSamples = state.rs.sampleCount()\n        ? VkSampleCountFlagBits(state.rs.sampleCount())\n        : VK_SAMPLE_COUNT_1_BIT;\n    }\n\n    if (shaders.fs && shaders.fs->metadata().flags.test(DxvkShaderFlag::HasSampleRateShading)) {\n      msInfo.sampleShadingEnable  = VK_TRUE;\n      msInfo.minSampleShading     = 1.0f;\n    }\n\n    // Alpha to coverage is not supported with sample mask exports.\n    cbUseDynamicAlphaToCoverage = !shaders.fs || !shaders.fs->metadata().flags.test(DxvkShaderFlag::ExportsSampleMask);\n\n    msSampleMask                  = state.ms.sampleMask() & ((1u << msInfo.rasterizationSamples) - 1);\n    msInfo.pSampleMask            = &msSampleMask;\n    msInfo.alphaToCoverageEnable  = state.ms.enableAlphaToCoverage() && cbUseDynamicAlphaToCoverage;\n\n    // We need to be fully consistent with the pipeline state here, and\n    // while we could consistently infer it, just don't take any chances\n    cbUseDynamicBlendConstants = state.useDynamicBlendConstants();\n  }\n\n\n  bool DxvkGraphicsPipelineFragmentOutputState::eq(const DxvkGraphicsPipelineFragmentOutputState& other) const {\n    bool eq = rtInfo.colorAttachmentCount     == other.rtInfo.colorAttachmentCount\n           && rtInfo.depthAttachmentFormat    == other.rtInfo.depthAttachmentFormat\n           && rtInfo.stencilAttachmentFormat  == other.rtInfo.stencilAttachmentFormat\n           && cbInfo.logicOpEnable            == other.cbInfo.logicOpEnable\n           && cbInfo.logicOp                  == other.cbInfo.logicOp\n           && cbInfo.attachmentCount          == other.cbInfo.attachmentCount\n           && msInfo.rasterizationSamples     == other.msInfo.rasterizationSamples\n           && msInfo.sampleShadingEnable      == other.msInfo.sampleShadingEnable\n           && msInfo.minSampleShading         == other.msInfo.minSampleShading\n           && msInfo.alphaToCoverageEnable    == other.msInfo.alphaToCoverageEnable\n           && msInfo.alphaToOneEnable         == other.msInfo.alphaToOneEnable\n           && msSampleMask                    == other.msSampleMask\n           && cbUseDynamicBlendConstants      == other.cbUseDynamicBlendConstants\n           && cbUseDynamicAlphaToCoverage     == other.cbUseDynamicAlphaToCoverage\n           && feedbackLoop                    == other.feedbackLoop;\n\n    for (uint32_t i = 0; i < rtInfo.colorAttachmentCount && eq; i++)\n      eq = rtColorFormats[i] == other.rtColorFormats[i];\n\n    for (uint32_t i = 0; i < cbInfo.attachmentCount && eq; i++) {\n      const auto& a = cbAttachments[i];\n      const auto& b = other.cbAttachments[i];\n\n      eq = a.blendEnable    == b.blendEnable\n        && a.colorWriteMask == b.colorWriteMask;\n\n      if (a.blendEnable && eq) {\n        eq = a.srcColorBlendFactor == b.srcColorBlendFactor\n          && a.dstColorBlendFactor == b.dstColorBlendFactor\n          && a.colorBlendOp        == b.colorBlendOp\n          && a.srcAlphaBlendFactor == b.srcAlphaBlendFactor\n          && a.dstAlphaBlendFactor == b.dstAlphaBlendFactor\n          && a.alphaBlendOp        == b.alphaBlendOp;\n      }\n    }\n\n    return eq;\n  }\n\n\n  size_t DxvkGraphicsPipelineFragmentOutputState::hash() const {\n    DxvkHashState hash;\n    hash.add(uint32_t(rtInfo.colorAttachmentCount));\n    hash.add(uint32_t(rtInfo.depthAttachmentFormat));\n    hash.add(uint32_t(rtInfo.stencilAttachmentFormat));\n    hash.add(uint32_t(cbInfo.logicOpEnable));\n    hash.add(uint32_t(cbInfo.logicOp));\n    hash.add(uint32_t(cbInfo.attachmentCount));\n    hash.add(uint32_t(msInfo.rasterizationSamples));\n    hash.add(uint32_t(msInfo.alphaToCoverageEnable));\n    hash.add(uint32_t(msInfo.alphaToOneEnable));\n    hash.add(uint32_t(msSampleMask));\n    hash.add(uint32_t(cbUseDynamicBlendConstants));\n    hash.add(uint32_t(cbUseDynamicAlphaToCoverage));\n    hash.add(uint32_t(feedbackLoop));\n\n    for (uint32_t i = 0; i < rtInfo.colorAttachmentCount; i++)\n      hash.add(uint32_t(rtColorFormats[i]));\n\n    for (uint32_t i = 0; i < cbInfo.attachmentCount; i++) {\n      hash.add(uint32_t(cbAttachments[i].blendEnable));\n      hash.add(uint32_t(cbAttachments[i].colorWriteMask));\n\n      if (cbAttachments[i].blendEnable) {\n        hash.add(uint32_t(cbAttachments[i].srcColorBlendFactor));\n        hash.add(uint32_t(cbAttachments[i].dstColorBlendFactor));\n        hash.add(uint32_t(cbAttachments[i].colorBlendOp));\n        hash.add(uint32_t(cbAttachments[i].srcAlphaBlendFactor));\n        hash.add(uint32_t(cbAttachments[i].dstAlphaBlendFactor));\n        hash.add(uint32_t(cbAttachments[i].alphaBlendOp));\n      }\n    }\n\n    return hash;\n  }\n\n\n  DxvkGraphicsPipelineFragmentOutputLibrary::DxvkGraphicsPipelineFragmentOutputLibrary(\n          DxvkDevice*                               device,\n    const DxvkGraphicsPipelineFragmentOutputState&  state)\n  : m_device(device) {\n    auto vk = m_device->vkd();\n\n    small_vector<VkDynamicState, 8> dynamicStates = { };\n\n    bool hasDynamicMultisampleState = state.msInfo.sampleShadingEnable\n      && m_device->features().extExtendedDynamicState3.extendedDynamicState3RasterizationSamples\n      && m_device->features().extExtendedDynamicState3.extendedDynamicState3SampleMask;\n\n    bool hasDynamicAlphaToCoverage = hasDynamicMultisampleState && state.cbUseDynamicAlphaToCoverage\n      && device->features().extExtendedDynamicState3.extendedDynamicState3AlphaToCoverageEnable;\n\n    bool hasDynamicSampleLocations = m_device->canUseSampleLocations(0u);\n\n    if (hasDynamicMultisampleState) {\n      dynamicStates.push_back(VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT);\n      dynamicStates.push_back(VK_DYNAMIC_STATE_SAMPLE_MASK_EXT);\n    }\n\n    if (hasDynamicAlphaToCoverage)\n      dynamicStates.push_back(VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT);\n\n    if (hasDynamicSampleLocations) {\n      dynamicStates.push_back(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_ENABLE_EXT);\n      dynamicStates.push_back(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT);\n    }\n\n    if (state.cbUseDynamicBlendConstants)\n      dynamicStates.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS);\n\n    VkPipelineDynamicStateCreateInfo dyInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };\n\n    if (!dynamicStates.empty()) {\n      dyInfo.dynamicStateCount = dynamicStates.size();\n      dyInfo.pDynamicStates = dynamicStates.data();\n    }\n\n    // Fix up multisample state based on dynamic state. Needed to\n    // silence validation errors in case we hit the full EDS3 path.\n    VkPipelineMultisampleStateCreateInfo msInfo = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };\n    msInfo.sampleShadingEnable = state.msInfo.sampleShadingEnable;\n    msInfo.minSampleShading = state.msInfo.minSampleShading;\n\n    if (!hasDynamicMultisampleState) {\n      msInfo.rasterizationSamples = state.msInfo.rasterizationSamples;\n      msInfo.pSampleMask = state.msInfo.pSampleMask;\n    }\n\n    if (!hasDynamicAlphaToCoverage)\n      msInfo.alphaToCoverageEnable = state.msInfo.alphaToCoverageEnable;\n\n    VkPipelineCreateFlags2CreateInfo flags = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO, &state.rtInfo };\n    flags.flags = VK_PIPELINE_CREATE_2_LIBRARY_BIT_KHR;\n\n    if (state.feedbackLoop & VK_IMAGE_ASPECT_COLOR_BIT)\n      flags.flags |= VK_PIPELINE_CREATE_2_COLOR_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;\n\n    if (state.feedbackLoop & VK_IMAGE_ASPECT_DEPTH_BIT)\n      flags.flags |= VK_PIPELINE_CREATE_2_DEPTH_STENCIL_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;\n\n    if (m_device->canUseDescriptorHeap())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (m_device->canUseDescriptorBuffer())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkGraphicsPipelineLibraryCreateInfoEXT libInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, &flags };\n    libInfo.flags             = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT;\n\n    VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo };\n    info.pColorBlendState     = &state.cbInfo;\n    info.pMultisampleState    = &msInfo;\n    info.pDynamicState        = &dyInfo;\n    info.basePipelineIndex    = -1;\n\n    VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(),\n      VK_NULL_HANDLE, 1, &info, nullptr, &m_pipeline);\n\n    if (vr)\n      throw DxvkError(\"Failed to create vertex input pipeline library\");\n  }\n\n\n  DxvkGraphicsPipelineFragmentOutputLibrary::~DxvkGraphicsPipelineFragmentOutputLibrary() {\n    auto vk = m_device->vkd();\n\n    vk->vkDestroyPipeline(vk->device(), m_pipeline, nullptr);\n  }\n\n\n  DxvkGraphicsPipelinePreRasterizationState::DxvkGraphicsPipelinePreRasterizationState() {\n    \n  }\n\n\n  DxvkGraphicsPipelinePreRasterizationState::DxvkGraphicsPipelinePreRasterizationState(\n    const DxvkDevice*                     device,\n    const DxvkGraphicsPipelineStateInfo&  state,\n    const DxvkGraphicsPipelineShaders&    shaders) {\n    // Set up tessellation state\n    tsInfo.patchControlPoints = state.ia.patchVertexCount();\n    \n    // Set up basic rasterization state\n    rsInfo.depthClampEnable         = VK_TRUE;\n    rsInfo.polygonMode              = state.rs.polygonMode();\n    rsInfo.lineWidth                = 1.0f;\n\n    // Set up rasterized stream depending on geometry shader state.\n    // Rasterizing stream 0 is default behaviour in all situations.\n    int32_t streamIndex = shaders.gs ? shaders.gs->metadata().rasterizedStream : 0;\n\n    if (streamIndex > 0) {\n      rsXfbStreamInfo.pNext = std::exchange(rsInfo.pNext, &rsXfbStreamInfo);\n      rsXfbStreamInfo.rasterizationStream = uint32_t(streamIndex);\n    } else if (streamIndex < 0) {\n      rsInfo.rasterizerDiscardEnable = VK_TRUE;\n    }\n\n    // Set up depth clip state. Require depth clip support,\n    // this is *not* equivalent to disabling depth clamp.\n    rsDepthClipInfo.pNext = std::exchange(rsInfo.pNext, &rsDepthClipInfo);\n    rsDepthClipInfo.depthClipEnable = state.rs.depthClipEnable();\n\n    // Set up conservative rasterization if requested by the application.\n    if (state.rs.conservativeMode() != VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT) {\n      rsConservativeInfo.pNext = std::exchange(rsInfo.pNext, &rsConservativeInfo);\n      rsConservativeInfo.conservativeRasterizationMode = state.rs.conservativeMode();\n      rsConservativeInfo.extraPrimitiveOverestimationSize = 0.0f;\n    }\n\n    // Set up line rasterization mode as requested by the application.\n    if (state.rs.lineMode() != VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT && isLineRendering(shaders, state)) {\n      rsLineInfo.pNext = std::exchange(rsInfo.pNext, &rsLineInfo);\n      rsLineInfo.lineRasterizationMode = state.rs.lineMode();\n\n      if (rsLineInfo.lineRasterizationMode == VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT) {\n        // This line width matches expected D3D behaviour, hard-code this\n        // so that we don't need to introduce an extra bit of render state.\n        rsInfo.lineWidth = 1.4f;\n      } else {\n        // Vulkan does not allow alphaToCoverage or sample rate shading\n        // in combination with smooth lines. Override the line mode to\n        // rectangular to fix this, but keep the width fixed at 1.0.\n        bool needsOverride = state.ms.enableAlphaToCoverage()\n          || (shaders.fs && shaders.fs->metadata().flags.test(DxvkShaderFlag::HasSampleRateShading));\n\n        if (needsOverride)\n          rsLineInfo.lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT;\n      }\n    }\n  }\n\n\n\n  bool DxvkGraphicsPipelinePreRasterizationState::eq(const DxvkGraphicsPipelinePreRasterizationState& other) const {\n    bool eq = tsInfo.patchControlPoints == other.tsInfo.patchControlPoints;\n\n    if (eq) {\n      eq = rsInfo.depthClampEnable         == other.rsInfo.depthClampEnable\n        && rsInfo.rasterizerDiscardEnable  == other.rsInfo.rasterizerDiscardEnable\n        && rsInfo.polygonMode              == other.rsInfo.polygonMode\n        && rsInfo.depthBiasEnable          == other.rsInfo.depthBiasEnable\n        && rsInfo.lineWidth                == other.rsInfo.lineWidth;\n    }\n\n    if (eq)\n      eq = rsXfbStreamInfo.rasterizationStream == other.rsXfbStreamInfo.rasterizationStream;\n\n    if (eq)\n      eq = rsDepthClipInfo.depthClipEnable == other.rsDepthClipInfo.depthClipEnable;\n\n    if (eq) {\n      eq = rsConservativeInfo.conservativeRasterizationMode    == other.rsConservativeInfo.conservativeRasterizationMode\n        && rsConservativeInfo.extraPrimitiveOverestimationSize == other.rsConservativeInfo.extraPrimitiveOverestimationSize;\n    }\n\n    if (eq)\n      eq = rsLineInfo.lineRasterizationMode == other.rsLineInfo.lineRasterizationMode;\n\n    return eq;\n  }\n\n\n  size_t DxvkGraphicsPipelinePreRasterizationState::hash() const {\n    DxvkHashState hash;\n    hash.add(tsInfo.patchControlPoints);\n\n    hash.add(rsInfo.depthClampEnable);\n    hash.add(rsInfo.rasterizerDiscardEnable);\n    hash.add(rsInfo.polygonMode);\n    hash.add(rsInfo.depthBiasEnable);\n    hash.add(bit::cast<uint32_t>(rsInfo.lineWidth));\n\n    hash.add(rsXfbStreamInfo.rasterizationStream);\n\n    hash.add(rsDepthClipInfo.depthClipEnable);\n\n    hash.add(rsConservativeInfo.conservativeRasterizationMode);\n    hash.add(bit::cast<uint32_t>(rsConservativeInfo.extraPrimitiveOverestimationSize));\n\n    hash.add(rsLineInfo.lineRasterizationMode);\n    return hash;\n  }\n\n\n  bool DxvkGraphicsPipelinePreRasterizationState::isLineRendering(\n    const DxvkGraphicsPipelineShaders&    shaders,\n    const DxvkGraphicsPipelineStateInfo&  state) {\n    VkPrimitiveTopology topology = determinePipelineTopology(shaders, state);\n\n    if (state.rs.polygonMode() == VK_POLYGON_MODE_LINE) {\n      return topology != VK_PRIMITIVE_TOPOLOGY_POINT_LIST\n          && topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;\n    }\n\n    return topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST\n        || topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP\n        || topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY\n        || topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;\n  }\n\n\n  DxvkGraphicsPipelineFragmentShaderState::DxvkGraphicsPipelineFragmentShaderState() {\n\n  }\n\n\n  DxvkGraphicsPipelineFragmentShaderState::DxvkGraphicsPipelineFragmentShaderState(\n    const DxvkDevice*                     device,\n    const DxvkGraphicsPipelineStateInfo&  state) {\n\n  }\n\n\n  bool DxvkGraphicsPipelineFragmentShaderState::eq(const DxvkGraphicsPipelineFragmentShaderState& other) const {\n    bool eq = dsInfo.depthTestEnable       == other.dsInfo.depthTestEnable\n           && dsInfo.depthBoundsTestEnable == other.dsInfo.depthBoundsTestEnable\n           && dsInfo.stencilTestEnable     == other.dsInfo.stencilTestEnable;\n\n    if (eq && dsInfo.depthTestEnable) {\n      eq = dsInfo.depthWriteEnable == other.dsInfo.depthWriteEnable\n        && dsInfo.depthCompareOp   == other.dsInfo.depthCompareOp;\n    }\n\n    if (eq && dsInfo.stencilTestEnable) {\n      eq = dsInfo.front.failOp      == other.dsInfo.front.failOp\n        && dsInfo.front.passOp      == other.dsInfo.front.passOp\n        && dsInfo.front.depthFailOp == other.dsInfo.front.depthFailOp\n        && dsInfo.front.compareOp   == other.dsInfo.front.compareOp\n        && dsInfo.front.compareMask == other.dsInfo.front.compareMask\n        && dsInfo.front.writeMask   == other.dsInfo.front.writeMask\n        && dsInfo.back.failOp       == other.dsInfo.back.failOp\n        && dsInfo.back.passOp       == other.dsInfo.back.passOp\n        && dsInfo.back.depthFailOp  == other.dsInfo.back.depthFailOp\n        && dsInfo.back.compareOp    == other.dsInfo.back.compareOp\n        && dsInfo.back.compareMask  == other.dsInfo.back.compareMask\n        && dsInfo.back.writeMask    == other.dsInfo.back.writeMask;\n    }\n\n    return eq;\n  }\n\n\n  size_t DxvkGraphicsPipelineFragmentShaderState::hash() const {\n    DxvkHashState hash;\n    hash.add(dsInfo.depthTestEnable);\n    hash.add(dsInfo.depthBoundsTestEnable);\n    hash.add(dsInfo.stencilTestEnable);\n\n    if (dsInfo.depthTestEnable) {\n      hash.add(dsInfo.depthWriteEnable);\n      hash.add(dsInfo.depthCompareOp);\n    }\n\n    if (dsInfo.stencilTestEnable) {\n      hash.add(dsInfo.front.failOp);\n      hash.add(dsInfo.front.passOp);\n      hash.add(dsInfo.front.depthFailOp);\n      hash.add(dsInfo.front.compareOp);\n      hash.add(dsInfo.front.compareMask);\n      hash.add(dsInfo.front.writeMask);\n      hash.add(dsInfo.back.failOp);\n      hash.add(dsInfo.back.passOp);\n      hash.add(dsInfo.back.depthFailOp);\n      hash.add(dsInfo.back.compareOp);\n      hash.add(dsInfo.back.compareMask);\n      hash.add(dsInfo.back.writeMask);\n    }\n\n    return hash;\n  }\n\n\n  DxvkGraphicsPipelineDynamicState::DxvkGraphicsPipelineDynamicState() {\n    \n  }\n\n\n  DxvkGraphicsPipelineDynamicState::DxvkGraphicsPipelineDynamicState(\n    const DxvkDevice*                     device,\n    const DxvkGraphicsPipelineStateInfo&  state,\n          DxvkGraphicsPipelineFlags       flags) {\n    dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT;\n    dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT;\n\n    if (!flags.test(DxvkGraphicsPipelineFlag::HasRasterizerDiscard)) {\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BIAS;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_CULL_MODE;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_FRONT_FACE;\n    }\n\n    if (state.useDynamicVertexStrides())\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE;\n\n    if (state.useDynamicDepthBounds() && device->features().core.features.depthBounds) {\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BOUNDS;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE;\n    }\n\n    if (state.useDynamicBlendConstants())\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;\n\n    if (state.useDynamicDepthTest()) {\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_COMPARE_OP;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE;\n    }\n\n    if (state.useDynamicStencilTest()) {\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_OP;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;\n    }\n\n    if (state.useSampleLocations() && device->canUseSampleLocations(0u)) {\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_ENABLE_EXT;\n      dyStates[dyInfo.dynamicStateCount++] = VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT;\n    }\n\n    if (dyInfo.dynamicStateCount)\n      dyInfo.pDynamicStates = dyStates.data();\n  }\n\n\n  bool DxvkGraphicsPipelineDynamicState::eq(const DxvkGraphicsPipelineDynamicState& other) const {\n    bool eq = dyInfo.dynamicStateCount == other.dyInfo.dynamicStateCount;\n\n    for (uint32_t i = 0; i < dyInfo.dynamicStateCount && eq; i++)\n      eq = dyStates[i] == other.dyStates[i];\n\n    return eq;\n  }\n\n\n  size_t DxvkGraphicsPipelineDynamicState::hash() const {\n    DxvkHashState hash;\n    hash.add(dyInfo.dynamicStateCount);\n\n    for (uint32_t i = 0; i < dyInfo.dynamicStateCount; i++)\n      hash.add(dyStates[i]);\n\n    return hash;\n  }\n\n\n  DxvkGraphicsPipelineShaderState::DxvkGraphicsPipelineShaderState() {\n\n  }\n\n\n  DxvkGraphicsPipelineShaderState::DxvkGraphicsPipelineShaderState(\n    const DxvkGraphicsPipelineShaders&    shaders,\n    const DxvkGraphicsPipelineStateInfo&  state)\n  : vsInfo  (getLinkage(shaders, shaders.vs, state)),\n    tcsInfo (getLinkage(shaders, shaders.tcs, state)),\n    tesInfo (getLinkage(shaders, shaders.tes, state)),\n    gsInfo  (getLinkage(shaders, shaders.gs, state)),\n    fsInfo  (getLinkage(shaders, shaders.fs, state)) {\n\n  }\n\n\n  bool DxvkGraphicsPipelineShaderState::eq(const DxvkGraphicsPipelineShaderState& other) const {\n    return vsInfo.eq(other.vsInfo)\n        && tcsInfo.eq(other.tcsInfo)\n        && tesInfo.eq(other.tesInfo)\n        && gsInfo.eq(other.gsInfo)\n        && fsInfo.eq(other.fsInfo);\n  }\n\n\n  size_t DxvkGraphicsPipelineShaderState::hash() const {\n    DxvkHashState hash;\n    hash.add(vsInfo.hash());\n    hash.add(tcsInfo.hash());\n    hash.add(tesInfo.hash());\n    hash.add(gsInfo.hash());\n    hash.add(fsInfo.hash());\n    return hash;\n  }\n\n\n  DxvkShaderLinkage DxvkGraphicsPipelineShaderState::getLinkage(\n    const DxvkGraphicsPipelineShaders&    shaders,\n    const Rc<DxvkShader>&                 shader,\n    const DxvkGraphicsPipelineStateInfo&  state) {\n    DxvkShaderLinkage info;\n\n    if (!shader)\n      return info;\n\n    // Fix up fragment shader outputs for dual-source blending\n    const DxvkShaderMetadata& shaderMeta = shader->metadata();\n\n    if (shaderMeta.stage == VK_SHADER_STAGE_FRAGMENT_BIT) {\n      auto fsOutputMask = shaderMeta.outputs.computeMask();\n\n      info.fsDualSrcBlend = state.useDualSourceBlending();\n      info.fsFlatShading = state.rs.flatShading() && shader->metadata().flatShadingInputs;\n\n      for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n        if ((fsOutputMask & (1u << i)) && state.writesRenderTarget(i))\n          info.rtSwizzles[i] = state.omSwizzle[i].mapping();\n      }\n    }\n\n    // Deal with undefined shader inputs\n    if (shaderMeta.stage == VK_SHADER_STAGE_VERTEX_BIT) {\n      uint32_t attributeMask = 0u;\n\n      for (uint32_t i = 0; i < state.il.attributeCount(); i++)\n        attributeMask |= 1u << state.ilAttributes[i].location();\n\n      info.prevStageOutputs = DxvkShaderIo::forVertexBindings(attributeMask);\n    } else {\n      auto prevStage = getPrevStageShader(shaders, shaderMeta.stage);\n\n      info.prevStage = prevStage->metadata().stage;\n      info.prevStageOutputs = prevStage->metadata().outputs;\n\n      info.semanticIo = prevStage->metadata().flags.test(DxvkShaderFlag::SemanticIo);\n    }\n\n    // Fix up input topology for geometry shaders as necessary\n    if (shaderMeta.stage == VK_SHADER_STAGE_GEOMETRY_BIT) {\n      VkPrimitiveTopology iaTopology = determinePreGsTopology(shaders, state);\n      info.inputTopology = determineGsInputTopology(shaderMeta.inputTopology, iaTopology);\n    }\n\n    return info;\n  }\n\n\n  Rc<DxvkShader> DxvkGraphicsPipelineShaderState::getPrevStageShader(\n    const DxvkGraphicsPipelineShaders&    shaders,\n    const VkShaderStageFlagBits           stage) {\n    if (stage == VK_SHADER_STAGE_VERTEX_BIT)\n      return nullptr;\n\n    if (stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)\n      return shaders.tcs;\n\n    Rc<DxvkShader> result = shaders.vs;\n\n    if (stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)\n      return result;\n\n    if (shaders.tes != nullptr)\n      result = shaders.tes;\n\n    if (stage == VK_SHADER_STAGE_GEOMETRY_BIT)\n      return result;\n\n    if (shaders.gs != nullptr)\n      result = shaders.gs;\n\n    return result;\n  }\n\n\n  DxvkPipelineSpecConstantState::DxvkPipelineSpecConstantState() {\n\n  }\n\n\n  DxvkPipelineSpecConstantState::DxvkPipelineSpecConstantState(\n          uint32_t                        mask,\n    const DxvkScInfo&                     state) {\n    for (uint32_t i = 0; i < MaxNumSpecConstants; i++) {\n      if (mask & (1u << i))\n        addConstant(i, state.specConstants[i]);\n    }\n\n    if (mask & (1u << MaxNumSpecConstants))\n      addConstant(MaxNumSpecConstants, VK_TRUE);\n\n    if (scInfo.mapEntryCount) {\n      scInfo.pMapEntries = scConstantMap.data();\n      scInfo.dataSize = scInfo.mapEntryCount * sizeof(uint32_t);\n      scInfo.pData = scConstantData.data();\n    }\n  }\n\n\n  bool DxvkPipelineSpecConstantState::eq(const DxvkPipelineSpecConstantState& other) const {\n    bool eq = scInfo.mapEntryCount == other.scInfo.mapEntryCount;\n\n    for (uint32_t i = 0; i < scInfo.mapEntryCount && eq; i++) {\n      eq = scConstantMap[i].constantID == other.scConstantMap[i].constantID\n        && scConstantData[i]           == other.scConstantData[i];\n    }\n\n    return eq;\n  }\n\n\n  size_t DxvkPipelineSpecConstantState::hash() const {\n    DxvkHashState hash;\n    hash.add(scInfo.mapEntryCount);\n\n    for (uint32_t i = 0; i < scInfo.mapEntryCount; i++) {\n      hash.add(scConstantMap[i].constantID);\n      hash.add(scConstantData[i]);\n    }\n\n    return hash;\n  }\n\n\n  void DxvkPipelineSpecConstantState::addConstant(uint32_t id, uint32_t value) {\n    if (value) {\n      uint32_t index = scInfo.mapEntryCount++;\n\n      scConstantMap[index].constantID = id;\n      scConstantMap[index].offset = sizeof(uint32_t) * index;\n      scConstantMap[index].size = sizeof(uint32_t);\n\n      scConstantData[index] = value;\n    }\n  }\n\n\n  DxvkGraphicsPipeline::DxvkGraphicsPipeline(\n          DxvkDevice*                 device,\n          DxvkPipelineManager*        pipeMgr,\n          DxvkGraphicsPipelineShaders shaders,\n          DxvkShaderPipelineLibrary*  vsLibrary,\n          DxvkShaderPipelineLibrary*  fsLibrary)\n  : m_device        (device),\n    m_manager       (pipeMgr),\n    m_workers       (&pipeMgr->m_workers),\n    m_stats         (&pipeMgr->m_stats),\n    m_shaders       (std::move(shaders)),\n    m_layout        (device, pipeMgr, buildPipelineLayout()),\n    m_barrier       (m_layout.getGlobalBarrier()),\n    m_vsLibrary     (vsLibrary),\n    m_fsLibrary     (fsLibrary),\n    m_debugName     (createDebugName()) {\n    m_vsIn  = m_shaders.vs != nullptr ? m_shaders.vs->metadata().inputs.computeMask() : 0u;\n    m_fsOut = m_shaders.fs != nullptr ? m_shaders.fs->metadata().outputs.computeMask() : 0u;\n    m_specConstantMask = this->computeSpecConstantMask();\n\n    if (m_shaders.gs != nullptr) {\n      if (m_shaders.gs->metadata().flags.test(DxvkShaderFlag::HasTransformFeedback)) {\n        m_flags.set(DxvkGraphicsPipelineFlag::HasTransformFeedback);\n\n        m_barrier.stages |= VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT;\n        m_barrier.access |= VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT\n                         |  VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT\n                         |  VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT;\n      }\n\n      if (m_shaders.gs->metadata().rasterizedStream < 0)\n        m_flags.set(DxvkGraphicsPipelineFlag::HasRasterizerDiscard);\n    }\n    \n    if (m_barrier.access & VK_ACCESS_SHADER_WRITE_BIT) {\n      m_flags.set(DxvkGraphicsPipelineFlag::HasStorageDescriptors);\n\n      if (m_layout.getHazardousStageMask())\n        m_flags.set(DxvkGraphicsPipelineFlag::UnrollMergedDraws);\n    }\n\n    if (m_shaders.fs != nullptr) {\n      if (m_shaders.fs->metadata().flags.test(DxvkShaderFlag::HasSampleRateShading))\n        m_flags.set(DxvkGraphicsPipelineFlag::HasSampleRateShading);\n      if (m_shaders.fs->metadata().flags.test(DxvkShaderFlag::ExportsSampleMask))\n        m_flags.set(DxvkGraphicsPipelineFlag::HasSampleMaskExport);\n    }\n  }\n  \n  \n  DxvkGraphicsPipeline::~DxvkGraphicsPipeline() {\n    this->destroyBasePipelines();\n    this->destroyOptimizedPipelines();\n  }\n  \n  \n  DxvkGlobalPipelineBarrier DxvkGraphicsPipeline::getGlobalBarrier(\n    const DxvkGraphicsPipelineStateInfo&    state) const {\n    DxvkGlobalPipelineBarrier barrier = m_barrier;\n\n    if (state.il.bindingCount()) {\n      barrier.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;\n      barrier.access |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;\n    }\n\n    return barrier;\n  }\n\n\n  DxvkGraphicsPipelineHandle DxvkGraphicsPipeline::getPipelineHandle(\n    const DxvkGraphicsPipelineStateInfo& state) {\n    DxvkGraphicsPipelineInstance* instance = this->findInstance(state);\n\n    if (unlikely(!instance)) {\n      // Exit early if the state vector is invalid\n      if (!this->validatePipelineState(state, true))\n        return DxvkGraphicsPipelineHandle();\n\n      // Prevent other threads from adding new instances and check again\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n      instance = this->findInstance(state);\n\n      if (!instance) {\n        // Keep pipeline object locked, at worst we're going to stall\n        // a state cache worker and the current thread needs priority.\n        bool canCreateBasePipeline = this->canCreateBasePipeline(state);\n        instance = this->createInstance(state, canCreateBasePipeline);\n\n        // Unlock here since we may dispatch the pipeline to a worker,\n        // which will then acquire it to increment the use counter.\n        lock.unlock();\n\n        // If necessary, compile an optimized pipeline variant\n        if (!instance->fastHandle.load())\n          m_workers->compileGraphicsPipeline(this, state, DxvkPipelinePriority::Low);\n      }\n    }\n\n    return instance->getHandle();\n  }\n\n\n  void DxvkGraphicsPipeline::compilePipeline(\n    const DxvkGraphicsPipelineStateInfo& state) {\n    if (m_device->config().enableGraphicsPipelineLibrary == Tristate::True)\n      return;\n\n    // Try to find an existing instance that contains a base pipeline\n    DxvkGraphicsPipelineInstance* instance = this->findInstance(state);\n\n    if (!instance) {\n      // Exit early if the state vector is invalid\n      if (!this->validatePipelineState(state, false))\n        return;\n\n      // Do not compile if this pipeline can be fast linked. This essentially\n      // disables the state cache for pipelines that do not benefit from it.\n      if (this->canCreateBasePipeline(state))\n        return;\n\n      // Prevent other threads from adding new instances and check again\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n      instance = this->findInstance(state);\n\n      if (!instance)\n        instance = this->createInstance(state, false);\n    }\n\n    // Exit if another thread is already compiling\n    // an optimized version of this pipeline\n    if (instance->isCompiling.load()\n     || instance->isCompiling.exchange(VK_TRUE, std::memory_order_acquire))\n      return;\n\n    VkPipeline pipeline = this->getOptimizedPipeline(state);\n    instance->fastHandle.store(pipeline, std::memory_order_release);\n\n    // Log pipeline state on error\n    if (!pipeline)\n      this->logPipelineState(LogLevel::Error, state);\n  }\n\n\n  void DxvkGraphicsPipeline::acquirePipeline() {\n    if (!m_device->mustTrackPipelineLifetime())\n      return;\n\n    // We need to lock here to make sure that any ongoing pipeline\n    // destruction finishes before the calling thread can access the\n    // pipeline, and that no pipelines get destroyed afterwards.\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n    m_useCount += 1;\n  }\n\n\n  void DxvkGraphicsPipeline::releasePipeline() {\n    if (!m_device->mustTrackPipelineLifetime())\n      return;\n\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n    if (!(--m_useCount)) {\n      // Don't destroy base pipelines if that's all we're going to\n      // use, since that would pretty much ruin the experience.\n      if (m_device->config().enableGraphicsPipelineLibrary == Tristate::True)\n        return;\n\n      // Exit early if there's nothing to do\n      if (m_basePipelines.empty())\n        return;\n\n      // Remove any base pipeline references, but\n      // keep the optimized pipelines around.\n      m_pipelines.forEach([] (DxvkGraphicsPipelineInstance& e) {\n        e.baseHandle.store(VK_NULL_HANDLE);\n      });\n\n      // Destroy the actual Vulkan pipelines\n      this->destroyBasePipelines();\n    }\n  }\n\n\n  DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::createInstance(\n    const DxvkGraphicsPipelineStateInfo& state,\n          bool                           doCreateBasePipeline) {\n    VkPipeline baseHandle = VK_NULL_HANDLE;\n    VkPipeline fastHandle = VK_NULL_HANDLE;\n\n    if (doCreateBasePipeline)\n      baseHandle = this->getBasePipeline(state);\n\n    // Fast-linking may fail in some situations\n    if (!baseHandle)\n      fastHandle = this->getOptimizedPipeline(state);\n\n    // Log pipeline state if requested, or on failure\n    if (!fastHandle && !baseHandle)\n      this->logPipelineState(LogLevel::Error, state);\n\n    m_stats->numGraphicsPipelines += 1;\n    return m_pipelines.add(state, baseHandle, fastHandle, computeAttachmentMask(state));\n  }\n  \n  \n  DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::findInstance(\n    const DxvkGraphicsPipelineStateInfo& state) {\n    return m_pipelines.find(state);\n  }\n  \n  \n  bool DxvkGraphicsPipeline::canCreateBasePipeline(\n    const DxvkGraphicsPipelineStateInfo& state) const {\n    if (!m_device->canUseGraphicsPipelineLibrary())\n      return false;\n\n    // We do not implement setting certain rarely used render\n    // states dynamically since they are generally not used\n    bool isLineRendering = DxvkGraphicsPipelinePreRasterizationState::isLineRendering(m_shaders, state);\n\n    if (state.rs.polygonMode() != VK_POLYGON_MODE_FILL\n     || state.rs.conservativeMode() != VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT\n     || (state.rs.lineMode() != VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT && isLineRendering))\n      return false;\n\n    // Depth clip is assumed to be enabled. If the driver does not\n    // support dynamic depth clip, we'd have to late-compile anyway\n    // unless the pipeline is used multiple times.\n    if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable\n     && !state.rs.depthClipEnable())\n      return false;\n\n    // If the vertex shader uses any input locations not provided by\n    // the input layout, we need to patch the shader.\n    uint32_t ilAttributeMask = 0u;\n\n    for (uint32_t i = 0; i < state.il.attributeCount(); i++)\n      ilAttributeMask |= 1u << state.ilAttributes[i].location();\n\n    if ((m_vsIn & ilAttributeMask) != m_vsIn)\n      return false;\n\n    if (m_shaders.gs != nullptr) {\n      // If the geometry shader's input topology is not compatible with\n      // the topology set to the pipeline, we need to patch the GS.\n      VkPrimitiveTopology iaTopology = determinePreGsTopology(m_shaders, state);\n      VkPrimitiveTopology gsTopology = m_shaders.gs->metadata().inputTopology;\n\n      if (determineGsInputTopology(gsTopology, iaTopology) != gsTopology)\n        return false;\n    }\n\n    if (m_shaders.tcs != nullptr) {\n      // If tessellation shaders are present, the input patch\n      // vertex count must match the shader's definition.\n      if (m_shaders.tcs->metadata().patchVertexCount != state.ia.patchVertexCount())\n        return false;\n    }\n\n    if (m_shaders.fs != nullptr) {\n      // If the fragment shader has inputs not produced by the last\n      // pre-rasterization stage, we need to patch the fragment shader\n      const auto& fsInputs = m_shaders.fs->metadata().inputs;\n      auto* preRasterStage = m_shaders.vs.ptr();\n\n      if (m_shaders.gs)\n        preRasterStage = m_shaders.gs.ptr();\n      else if (m_shaders.tes != nullptr)\n        preRasterStage = m_shaders.tes.ptr();\n\n      bool semanticIo = preRasterStage->metadata().flags.test(DxvkShaderFlag::SemanticIo)\n                     && m_shaders.fs->metadata().flags.test(DxvkShaderFlag::SemanticIo);\n\n      if (!DxvkShaderIo::checkStageCompatibility(VK_SHADER_STAGE_FRAGMENT_BIT, fsInputs,\n          preRasterStage->metadata().stage, preRasterStage->metadata().outputs, semanticIo))\n        return false;\n\n      // Dual-source blending requires patching the fragment shader\n      if (state.useDualSourceBlending())\n        return false;\n\n      // Flat shading requires patching the fragment shader\n      if (state.rs.flatShading() && m_shaders.fs->metadata().flatShadingInputs)\n        return false;\n\n      // If dynamic multisample state is not supported and sample shading\n      // is enabled, the library is compiled with a sample count of 1.\n      if (m_shaders.fs->metadata().flags.test(DxvkShaderFlag::HasSampleRateShading)) {\n        bool canUseDynamicMultisampleState =\n          m_device->features().extExtendedDynamicState3.extendedDynamicState3RasterizationSamples &&\n          m_device->features().extExtendedDynamicState3.extendedDynamicState3SampleMask;\n\n        bool canUseDynamicAlphaToCoverage = canUseDynamicMultisampleState &&\n          m_device->features().extExtendedDynamicState3.extendedDynamicState3AlphaToCoverageEnable;\n\n        if (!canUseDynamicMultisampleState\n         && (state.ms.sampleCount() != VK_SAMPLE_COUNT_1_BIT\n          || state.ms.sampleMask() == 0))\n          return false;\n\n        if (!canUseDynamicAlphaToCoverage\n         && (state.ms.enableAlphaToCoverage())\n         && !m_shaders.fs->metadata().flags.test(DxvkShaderFlag::ExportsSampleMask))\n          return false;\n      }\n    }\n\n    // Remapping fragment shader outputs would require spec constants\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      if ((m_fsOut & (1u << i)) && state.writesRenderTarget(i)\n       && !util::isIdentityMapping(state.omSwizzle[i].mapping()))\n        return false;\n    }\n\n    return true;\n  }\n\n\n  VkPipeline DxvkGraphicsPipeline::getBasePipeline(\n    const DxvkGraphicsPipelineStateInfo& state) {\n    DxvkGraphicsPipelineVertexInputState    viState(m_device, state, m_shaders);\n    DxvkGraphicsPipelineFragmentOutputState foState(m_device, state, m_shaders);\n\n    DxvkGraphicsPipelineBaseInstanceKey key;\n    key.viLibrary = m_manager->createVertexInputLibrary(viState);\n    key.foLibrary = m_manager->createFragmentOutputLibrary(foState);\n\n    auto entry = m_basePipelines.find(key);\n    if (entry != m_basePipelines.end())\n      return entry->second;\n\n    VkPipeline handle = createBasePipeline(key);\n    m_basePipelines.insert({ key, handle });\n    return handle;\n  }\n\n\n  VkPipeline DxvkGraphicsPipeline::createBasePipeline(\n    const DxvkGraphicsPipelineBaseInstanceKey& key) const {\n    auto vk = m_device->vkd();\n\n    DxvkShaderPipelineLibraryHandle vs = m_vsLibrary->acquirePipelineHandle();\n    DxvkShaderPipelineLibraryHandle fs = m_fsLibrary->acquirePipelineHandle();\n\n    if (!vs.handle || !fs.handle)\n      return VK_NULL_HANDLE;\n\n    std::array<VkPipeline, 4> libraries = {{\n      key.viLibrary->getHandle(), vs.handle, fs.handle,\n      key.foLibrary->getHandle(),\n    }};\n\n    VkPipelineCreateFlags2CreateInfo flags = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO };\n    flags.flags = vs.linkFlags | fs.linkFlags;\n\n    if (m_device->canUseDescriptorHeap())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (m_device->canUseDescriptorBuffer())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkPipelineLibraryCreateInfoKHR libInfo = { VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR };\n    libInfo.libraryCount    = libraries.size();\n    libInfo.pLibraries      = libraries.data();\n\n    VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo };\n    info.layout             = m_layout.getLayout(DxvkPipelineLayoutType::Independent)->getPipelineLayout();\n    info.basePipelineIndex  = -1;\n\n    if (flags.flags)\n      flags.pNext = std::exchange(info.pNext, &flags);\n\n    VkPipeline pipeline = VK_NULL_HANDLE;\n    VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);\n\n    if (vr && vr != VK_PIPELINE_COMPILE_REQUIRED_EXT)\n      Logger::err(str::format(\"DxvkGraphicsPipeline: Failed to create base pipeline: \", vr));\n\n    return pipeline;\n  }\n\n\n  VkPipeline DxvkGraphicsPipeline::getOptimizedPipeline(\n    const DxvkGraphicsPipelineStateInfo& state) {\n    DxvkGraphicsPipelineFastInstanceKey key(m_device,\n      m_shaders, state, m_flags, m_specConstantMask);\n\n    std::lock_guard lock(m_fastMutex);\n\n    auto entry = m_fastPipelines.find(key);\n    if (entry != m_fastPipelines.end())\n      return entry->second;\n\n    // Keep pipeline locked to prevent multiple threads from compiling\n    // identical Vulkan pipelines. This should be rare, but has been\n    // buggy on some drivers in the past, so just don't allow it.\n    VkPipeline handle = createOptimizedPipeline(key);\n\n    if (handle)\n      m_fastPipelines.insert({ key, handle });\n\n    return handle;\n  }\n\n\n  VkPipeline DxvkGraphicsPipeline::createOptimizedPipeline(\n    const DxvkGraphicsPipelineFastInstanceKey& key) const {\n    auto vk = m_device->vkd();\n    auto layout = m_layout.getLayout(DxvkPipelineLayoutType::Merged);\n\n    DxvkShaderStageInfo stageInfo(m_device, layout);\n    stageInfo.addStage(VK_SHADER_STAGE_VERTEX_BIT, getShaderCode(*m_shaders.vs, key.shState.vsInfo), &key.scState.scInfo);\n\n    if (m_shaders.tcs != nullptr)\n      stageInfo.addStage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, getShaderCode(*m_shaders.tcs, key.shState.tcsInfo), &key.scState.scInfo);\n    if (m_shaders.tes != nullptr)\n      stageInfo.addStage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, getShaderCode(*m_shaders.tes, key.shState.tesInfo), &key.scState.scInfo);\n    if (m_shaders.gs != nullptr)\n      stageInfo.addStage(VK_SHADER_STAGE_GEOMETRY_BIT, getShaderCode(*m_shaders.gs, key.shState.gsInfo), &key.scState.scInfo);\n    if (m_shaders.fs != nullptr)\n      stageInfo.addStage(VK_SHADER_STAGE_FRAGMENT_BIT, getShaderCode(*m_shaders.fs, key.shState.fsInfo), &key.scState.scInfo);\n\n    VkPipelineCreateFlags2CreateInfo flags = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO };\n\n    if (key.foState.feedbackLoop & VK_IMAGE_ASPECT_COLOR_BIT)\n      flags.flags |= VK_PIPELINE_CREATE_2_COLOR_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;\n\n    if (key.foState.feedbackLoop & VK_IMAGE_ASPECT_DEPTH_BIT)\n      flags.flags |= VK_PIPELINE_CREATE_2_DEPTH_STENCIL_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;\n\n    if (m_device->canUseDescriptorHeap())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (m_device->canUseDescriptorBuffer())\n      flags.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &key.foState.rtInfo };\n    info.stageCount               = stageInfo.getStageCount();\n    info.pStages                  = stageInfo.getStageInfos();\n    info.pVertexInputState        = &key.viState.viInfo;\n    info.pInputAssemblyState      = &key.viState.iaInfo;\n    info.pTessellationState       = &key.prState.tsInfo;\n    info.pViewportState           = &key.prState.vpInfo;\n    info.pRasterizationState      = &key.prState.rsInfo;\n    info.pMultisampleState        = &key.foState.msInfo;\n    info.pDepthStencilState       = &key.fsState.dsInfo;\n    info.pColorBlendState         = &key.foState.cbInfo;\n    info.pDynamicState            = &key.dyState.dyInfo;\n    info.layout                   = layout->getPipelineLayout();\n    info.basePipelineIndex        = -1;\n    \n    if (!key.prState.tsInfo.patchControlPoints)\n      info.pTessellationState = nullptr;\n\n    if (flags.flags)\n      flags.pNext = std::exchange(info.pNext, &flags);\n    \n    VkPipeline pipeline = VK_NULL_HANDLE;\n    VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);\n\n    if (vr != VK_SUCCESS) {\n      Logger::err(str::format(\"DxvkGraphicsPipeline: Failed to compile pipeline: \", vr));\n      return VK_NULL_HANDLE;\n    }\n\n    return pipeline;\n  }\n  \n  \n  void DxvkGraphicsPipeline::destroyBasePipelines() {\n    for (const auto& instance : m_basePipelines) {\n      this->destroyVulkanPipeline(instance.second);\n\n      m_vsLibrary->releasePipelineHandle();\n      m_fsLibrary->releasePipelineHandle();\n    }\n\n    m_basePipelines.clear();\n  }\n\n\n  void DxvkGraphicsPipeline::destroyOptimizedPipelines() {\n    for (const auto& instance : m_fastPipelines)\n      this->destroyVulkanPipeline(instance.second);\n\n    m_fastPipelines.clear();\n  }\n\n\n  void DxvkGraphicsPipeline::destroyVulkanPipeline(VkPipeline pipeline) const {\n    auto vk = m_device->vkd();\n\n    vk->vkDestroyPipeline(vk->device(), pipeline, nullptr);\n  }\n\n\n  SpirvCodeBuffer DxvkGraphicsPipeline::getShaderCode(\n          DxvkShader&         shader,\n    const DxvkShaderLinkage&  linkage) const {\n    return shader.getCode(m_layout.getBindingMap(DxvkPipelineLayoutType::Merged), &linkage);\n  }\n\n\n  uint32_t DxvkGraphicsPipeline::computeSpecConstantMask() const {\n    uint32_t mask = m_shaders.vs->metadata().specConstantMask;\n\n    if (m_shaders.tcs != nullptr)\n      mask |= m_shaders.tcs->metadata().specConstantMask;\n    if (m_shaders.tes != nullptr)\n      mask |= m_shaders.tes->metadata().specConstantMask;\n    if (m_shaders.gs != nullptr)\n      mask |= m_shaders.gs->metadata().specConstantMask;\n    if (m_shaders.fs != nullptr)\n      mask |= m_shaders.fs->metadata().specConstantMask;\n\n    return mask;\n  }\n\n\n  DxvkAttachmentMask DxvkGraphicsPipeline::computeAttachmentMask(\n    const DxvkGraphicsPipelineStateInfo& state) const {\n    // Scan color attachments first, we only need to check if any given\n    // attachment is accessed by the shader and has a non-zero write mask.\n    DxvkAttachmentMask result = { };\n\n    if (m_flags.test(DxvkGraphicsPipelineFlag::HasRasterizerDiscard))\n      return result;\n\n    if (m_shaders.fs) {\n      for (auto i : bit::BitMask(m_fsOut)) {\n        if (state.writesRenderTarget(i))\n          result.trackColorWrite(i);\n      }\n    }\n\n    // Depth buffer access is tracked by the command list\n    return result;\n  }\n\n\n  bool DxvkGraphicsPipeline::validatePipelineState(\n    const DxvkGraphicsPipelineStateInfo&  state,\n          bool                            trusted) const {\n    // Tessellation shaders and patches must be used together\n    bool hasPatches = state.ia.primitiveTopology() == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;\n\n    bool hasTcs = m_shaders.tcs != nullptr;\n    bool hasTes = m_shaders.tes != nullptr;\n\n    if (hasPatches != hasTcs || hasPatches != hasTes)\n      return false;\n    \n    // Filter out undefined primitive topologies\n    if (state.ia.primitiveTopology() == VK_PRIMITIVE_TOPOLOGY_MAX_ENUM)\n      return false;\n    \n    // Prevent unintended out-of-bounds access to the IL arrays\n    if (state.il.attributeCount() > DxvkLimits::MaxNumVertexAttributes\n     || state.il.bindingCount()   > DxvkLimits::MaxNumVertexBindings)\n      return false;\n\n    // Exit here on the fast path, perform more thorough validation if\n    // the state vector comes from an untrusted source (i.e. the cache)\n    if (trusted)\n      return true;\n\n    // Validate shaders\n    if (!m_shaders.validate())\n      return false;\n\n    // Validate vertex input layout\n    uint32_t ilLocationMask = 0;\n    uint32_t ilBindingMask = 0;\n\n    for (uint32_t i = 0; i < state.il.bindingCount(); i++)\n      ilBindingMask |= 1u << state.ilBindings[i].binding();\n\n    for (uint32_t i = 0; i < state.il.attributeCount(); i++) {\n      const DxvkIlAttribute& attribute = state.ilAttributes[i];\n\n      if (ilLocationMask & (1u << attribute.location()))\n        return false;\n\n      if (!(ilBindingMask & (1u << attribute.binding())))\n        return false;\n\n      DxvkFormatFeatures formatInfo = m_device->getFormatFeatures(attribute.format());\n\n      if (!(formatInfo.buffer & VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT))\n        return false;\n\n      ilLocationMask |= 1u << attribute.location();\n    }\n\n    // Validate rasterization state\n    if (state.rs.conservativeMode() != VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT) {\n      if (!m_device->features().extConservativeRasterization)\n        return false;\n\n      if (state.rs.conservativeMode() == VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT\n       && !m_device->properties().extConservativeRasterization.primitiveUnderestimation)\n        return false;\n    }\n\n    // Validate render target format support\n    VkFormat depthFormat = state.rt.getDepthStencilFormat();\n\n    if (depthFormat) {\n      DxvkFormatFeatures formatInfo = m_device->getFormatFeatures(depthFormat);\n\n      if (!(formatInfo.optimal & VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT))\n        return false;\n    }\n\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      VkFormat colorFormat = state.rt.getColorFormat(i);\n\n      if (colorFormat) {\n        DxvkFormatFeatures formatInfo = m_device->getFormatFeatures(colorFormat);\n\n        if (!(formatInfo.optimal & VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT))\n          return false;\n      }\n    }\n\n    // Validate spec constant state\n    for (uint32_t i = 0; i < MaxNumSpecConstants; i++) {\n      if (state.sc.specConstants[i] && !(m_specConstantMask & (1u << i)))\n        return false;\n    }\n\n    return true;\n  }\n\n\n  DxvkPipelineLayoutBuilder DxvkGraphicsPipeline::buildPipelineLayout() const {\n    DxvkPipelineLayoutBuilder builder(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);\n\n    if (m_shaders.vs)  builder.addLayout(m_shaders.vs->getLayout());\n    if (m_shaders.tcs) builder.addLayout(m_shaders.tcs->getLayout());\n    if (m_shaders.tes) builder.addLayout(m_shaders.tes->getLayout());\n    if (m_shaders.gs)  builder.addLayout(m_shaders.gs->getLayout());\n    if (m_shaders.fs)  builder.addLayout(m_shaders.fs->getLayout());\n\n    return builder;\n  }\n\n  \n  void DxvkGraphicsPipeline::logPipelineState(\n          LogLevel                       level,\n    const DxvkGraphicsPipelineStateInfo& state) const {\n    std::stringstream sstr;\n    sstr << \"Shader stages:\" << std::endl;\n    if (m_shaders.vs  != nullptr) sstr << \"  vs  : \" << m_shaders.vs ->debugName() << std::endl;\n    if (m_shaders.tcs != nullptr) sstr << \"  tcs : \" << m_shaders.tcs->debugName() << std::endl;\n    if (m_shaders.tes != nullptr) sstr << \"  tes : \" << m_shaders.tes->debugName() << std::endl;\n    if (m_shaders.gs  != nullptr) sstr << \"  gs  : \" << m_shaders.gs ->debugName() << std::endl;\n    if (m_shaders.fs  != nullptr) sstr << \"  fs  : \" << m_shaders.fs ->debugName() << std::endl;\n\n    // Log input assembly state\n    VkPrimitiveTopology topology = state.ia.primitiveTopology();\n    sstr << std::dec << \"Primitive topology: \" << topology;\n\n    if (topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)\n      sstr << \" [\" << state.ia.patchVertexCount() << \"]\" << std::endl;\n    else\n      sstr << \" [restart: \" << (state.ia.primitiveRestart() ? \"yes]\" : \"no]\") << std::endl;\n\n    // Log vertex input state\n    for (uint32_t i = 0; i < state.il.bindingCount(); i++) {\n      const auto& binding = state.ilBindings[i];\n      sstr << \"Vertex binding \" << binding.binding() << \" [\" << binding.stride() << \"]\" << std::endl;\n\n      for (uint32_t j = 0; j < state.il.attributeCount(); j++) {\n        const auto& attribute = state.ilAttributes[j];\n\n        if (attribute.binding() == binding.binding())\n          sstr << \"  \" << attribute.location() << \" [\" << attribute.offset() << \"]: \" << attribute.format() << std::endl;\n      }\n    }\n\n    // Log rasterizer state\n    sstr << \"Rasterizer state:\" << std::endl\n         << \"  depth clip:      \" << (state.rs.depthClipEnable() ? \"yes\" : \"no\") << std::endl\n         << \"  polygon mode:    \" << state.rs.polygonMode() << std::endl\n         << \"  conservative:    \" << (state.rs.conservativeMode() == VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT ? \"no\" : \"yes\") << std::endl;\n\n    // Log multisample state\n    VkSampleCountFlags sampleCount = VK_SAMPLE_COUNT_1_BIT;\n\n    if (state.ms.sampleCount())\n      sampleCount = state.ms.sampleCount();\n    else if (state.rs.sampleCount())\n      sampleCount = state.rs.sampleCount();\n\n    sstr << \"Sample count: \" << sampleCount << \" [0x\" << std::hex << state.ms.sampleMask() << std::dec << \"]\" << std::endl\n         << \"  alphaToCoverage: \" << (state.ms.enableAlphaToCoverage() ? \"yes\" : \"no\") << std::endl;\n\n    // Log logic op state\n    sstr << \"Logic op:          \";\n\n    if (state.om.enableLogicOp())\n      sstr << \"yes [\" << state.om.logicOp() << \"]\" << std::endl;\n    else\n      sstr << \"no\" << std::endl;\n\n    // Log render target and blend state\n    auto depthFormat = state.rt.getDepthStencilFormat();\n    auto depthFormatInfo = lookupFormatInfo(depthFormat);\n\n    VkImageAspectFlags writableAspects = depthFormat\n      ? (depthFormatInfo->aspectMask & ~state.rt.getDepthStencilReadOnlyAspects())\n      : 0u;\n\n    sstr << \"Depth attachment: \" << depthFormat;\n\n    if (depthFormat) {\n      sstr << \" [\"\n        << ((writableAspects & VK_IMAGE_ASPECT_DEPTH_BIT) ? \"d\" : \" \")\n        << ((writableAspects & VK_IMAGE_ASPECT_STENCIL_BIT) ? \"s\" : \" \")\n        << \"]\" << std::endl;\n    } else {\n      sstr << std::endl;\n    }\n\n    bool hasColorAttachments = false;\n\n    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {\n      auto format = state.rt.getColorFormat(i);\n\n      if (format) {\n        if (!hasColorAttachments) {\n          sstr << \"Color attachments:\" << std::endl;\n          hasColorAttachments = true;\n        }\n\n        const char* components = \"rgba\";\n        const auto& blend = state.omBlend[i];\n        const auto& swizzle = state.omSwizzle[i];\n\n        VkColorComponentFlags writeMask = blend.colorWriteMask();\n        char r = (writeMask & (1u << swizzle.rIndex())) ? components[swizzle.rIndex()] : ' ';\n        char g = (writeMask & (1u << swizzle.gIndex())) ? components[swizzle.gIndex()] : ' ';\n        char b = (writeMask & (1u << swizzle.bIndex())) ? components[swizzle.bIndex()] : ' ';\n        char a = (writeMask & (1u << swizzle.aIndex())) ? components[swizzle.aIndex()] : ' ';\n\n        sstr << \"  \" << i << \": \" << format << \" [\" << r << g << b << a << \"] blend: \";\n\n        if (blend.blendEnable())\n          sstr << \"yes (c:\" << blend.srcColorBlendFactor() << \",\" << blend.dstColorBlendFactor() << \",\" << blend.colorBlendOp()\n               <<     \";a:\" << blend.srcAlphaBlendFactor() << \",\" << blend.dstAlphaBlendFactor() << \",\" << blend.alphaBlendOp() << \")\" << std::endl;\n        else\n          sstr << \"no\" << std::endl;\n      }\n    }\n\n    // Log spec constants\n    bool hasSpecConstants = false;\n\n    for (uint32_t i = 0; i < MaxNumSpecConstants; i++) {\n      if (state.sc.specConstants[i]) {\n        if (!hasSpecConstants) {\n          sstr << \"Specialization constants:\" << std::endl;\n          hasSpecConstants = true;\n        }\n\n        sstr << \"  \" << i << \": 0x\" << std::hex << std::setw(8) << std::setfill('0') << state.sc.specConstants[i] << std::dec << std::endl;\n      }\n    }\n\n    Logger::log(level, sstr.str());\n  }\n\n\n  std::string DxvkGraphicsPipeline::createDebugName() const {\n    std::stringstream name;\n\n    std::array<Rc<DxvkShader>, 5> shaders = {{\n      m_shaders.vs,\n      m_shaders.tcs,\n      m_shaders.tes,\n      m_shaders.gs,\n      m_shaders.fs,\n    }};\n\n    for (const auto& shader : shaders) {\n      if (shader) {\n        std::string shaderName = shader->debugName();\n        size_t len = std::min(shaderName.size(), size_t(10));\n        name << \"[\" << shaderName.substr(0, len) << \"] \";\n      }\n    }\n\n    return name.str();\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_graphics.h",
    "content": "#pragma once\n\n#include <mutex>\n\n#include \"dxvk_bind_mask.h\"\n#include \"dxvk_constant_state.h\"\n#include \"dxvk_graphics_state.h\"\n#include \"dxvk_pipelayout.h\"\n#include \"dxvk_renderpass.h\"\n#include \"dxvk_shader.h\"\n#include \"dxvk_stats.h\"\n\nnamespace dxvk {\n  \n  class DxvkDevice;\n  class DxvkPipelineManager;\n  class DxvkPipelineWorkers;\n\n  struct DxvkGraphicsPipelineShaders;\n  struct DxvkPipelineStats;\n\n  /**\n   * \\brief Flags that describe pipeline properties\n   */\n  enum class DxvkGraphicsPipelineFlag {\n    HasRasterizerDiscard,\n    HasTransformFeedback,\n    HasStorageDescriptors,\n    HasSampleRateShading,\n    HasSampleMaskExport,\n    UnrollMergedDraws,\n  };\n\n  using DxvkGraphicsPipelineFlags = Flags<DxvkGraphicsPipelineFlag>;\n\n\n  /**\n   * \\brief Vertex input info for graphics pipelines\n   *\n   * Can be used to compile dedicated pipeline objects for use\n   * in a graphics pipeline library, or as part of the data\n   * required to compile a full graphics pipeline.\n   */\n  struct DxvkGraphicsPipelineVertexInputState {\n    DxvkGraphicsPipelineVertexInputState();\n\n    DxvkGraphicsPipelineVertexInputState(\n      const DxvkDevice*                     device,\n      const DxvkGraphicsPipelineStateInfo&  state,\n      const DxvkGraphicsPipelineShaders&    shaders);\n\n    VkPipelineInputAssemblyStateCreateInfo          iaInfo        = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };\n    VkPipelineVertexInputStateCreateInfo            viInfo        = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };\n    VkPipelineVertexInputDivisorStateCreateInfoEXT  viDivisorInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT };\n\n    std::array<VkVertexInputBindingDescription,           MaxNumVertexBindings>   viBindings    = { };\n    std::array<VkVertexInputBindingDivisorDescriptionEXT, MaxNumVertexBindings>   viDivisors    = { };\n    std::array<VkVertexInputAttributeDescription,         MaxNumVertexAttributes> viAttributes  = { };\n\n    VkBool32 viUseDynamicVertexStrides = VK_FALSE;\n\n    bool eq(const DxvkGraphicsPipelineVertexInputState& other) const;\n\n    size_t hash() const;\n  };\n\n\n  /**\n   * \\brief Vertex input pipeline library\n   *\n   * Creates a Vulkan pipeline object for a\n   * given vertex input state vector.\n   */\n  class DxvkGraphicsPipelineVertexInputLibrary {\n\n  public:\n\n    DxvkGraphicsPipelineVertexInputLibrary(\n            DxvkDevice*                           device,\n      const DxvkGraphicsPipelineVertexInputState& state);\n\n    ~DxvkGraphicsPipelineVertexInputLibrary();\n\n    VkPipeline getHandle() const {\n      return m_pipeline;\n    }\n\n  private:\n\n    DxvkDevice* m_device;\n    VkPipeline  m_pipeline;\n\n  };\n\n\n  /**\n   * \\brief Fragment output info for graphics pipelines\n   *\n   * Can be used to compile dedicated pipeline objects for use\n   * in a graphics pipeline library, or as part of the data\n   * required to compile a full graphics pipeline.\n   */\n  struct DxvkGraphicsPipelineFragmentOutputState {\n    DxvkGraphicsPipelineFragmentOutputState();\n\n    DxvkGraphicsPipelineFragmentOutputState(\n      const DxvkDevice*                     device,\n      const DxvkGraphicsPipelineStateInfo&  state,\n      const DxvkGraphicsPipelineShaders&    shaders);\n\n    VkPipelineRenderingCreateInfo                   rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };\n    VkPipelineColorBlendStateCreateInfo             cbInfo = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };\n    VkPipelineMultisampleStateCreateInfo            msInfo = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };\n\n    VkSampleMask                                    msSampleMask               = 0u;\n    VkBool32                                        cbUseDynamicBlendConstants = VK_FALSE;\n    VkBool32                                        cbUseDynamicAlphaToCoverage = VK_FALSE;\n\n    std::array<VkPipelineColorBlendAttachmentState, MaxNumRenderTargets> cbAttachments  = { };\n    std::array<VkFormat,                            MaxNumRenderTargets> rtColorFormats = { };\n\n    VkImageAspectFlags                              feedbackLoop = 0u;\n\n    bool eq(const DxvkGraphicsPipelineFragmentOutputState& other) const;\n\n    size_t hash() const;\n  };\n\n\n  /**\n   * \\brief Vertex input pipeline library\n   *\n   * Creates a Vulkan pipeline object for a\n   * given vertex input state vector.\n   */\n  class DxvkGraphicsPipelineFragmentOutputLibrary {\n\n  public:\n\n    DxvkGraphicsPipelineFragmentOutputLibrary(\n            DxvkDevice*                               device,\n      const DxvkGraphicsPipelineFragmentOutputState&  state);\n\n    ~DxvkGraphicsPipelineFragmentOutputLibrary();\n\n    VkPipeline getHandle() const {\n      return m_pipeline;\n    }\n\n  private:\n\n    DxvkDevice* m_device;\n    VkPipeline  m_pipeline;\n\n  };\n\n\n  /**\n   * \\brief Pre-rasterization info for graphics pipelines\n   *\n   * Can only be used when compiling full graphics pipelines\n   * when all pipeline state is known.\n   */\n  struct DxvkGraphicsPipelinePreRasterizationState {\n    DxvkGraphicsPipelinePreRasterizationState();\n\n    DxvkGraphicsPipelinePreRasterizationState(\n      const DxvkDevice*                     device,\n      const DxvkGraphicsPipelineStateInfo&  state,\n      const DxvkGraphicsPipelineShaders&    shaders);\n\n    VkPipelineViewportStateCreateInfo                     vpInfo              = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };\n    VkPipelineTessellationStateCreateInfo                 tsInfo              = { VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO };\n    VkPipelineRasterizationStateCreateInfo                rsInfo              = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };\n    VkPipelineRasterizationDepthClipStateCreateInfoEXT    rsDepthClipInfo     = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT };\n    VkPipelineRasterizationStateStreamCreateInfoEXT       rsXfbStreamInfo     = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT };\n    VkPipelineRasterizationConservativeStateCreateInfoEXT rsConservativeInfo  = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT };\n    VkPipelineRasterizationLineStateCreateInfoEXT         rsLineInfo          = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT };\n\n    bool eq(const DxvkGraphicsPipelinePreRasterizationState& other) const;\n\n    size_t hash() const;\n\n    static bool isLineRendering(\n      const DxvkGraphicsPipelineShaders&    shaders,\n      const DxvkGraphicsPipelineStateInfo&  state);\n\n  };\n\n\n  /**\n   * \\brief Fragment shader state info for graphics pipelines\n   *\n   * Can only be used when compiling full graphics pipelines\n   * when all pipeline state is known.\n   */\n  struct DxvkGraphicsPipelineFragmentShaderState {\n    DxvkGraphicsPipelineFragmentShaderState();\n\n    DxvkGraphicsPipelineFragmentShaderState(\n      const DxvkDevice*                     device,\n      const DxvkGraphicsPipelineStateInfo&  state);\n\n    VkPipelineDepthStencilStateCreateInfo           dsInfo = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };\n\n    bool eq(const DxvkGraphicsPipelineFragmentShaderState& other) const;\n\n    size_t hash() const;\n  };\n\n\n  /**\n   * \\brief Dynamic state for graphics pipelines\n   *\n   */\n  struct DxvkGraphicsPipelineDynamicState {\n    DxvkGraphicsPipelineDynamicState();\n\n    DxvkGraphicsPipelineDynamicState(\n      const DxvkDevice*                     device,\n      const DxvkGraphicsPipelineStateInfo&  state,\n            DxvkGraphicsPipelineFlags       flags);\n\n    VkPipelineDynamicStateCreateInfo  dyInfo    = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };\n    std::array<VkDynamicState, 20>    dyStates  = { };\n\n    bool eq(const DxvkGraphicsPipelineDynamicState& other) const;\n\n    size_t hash() const;\n  };\n\n\n  /**\n   * \\brief Shader create info state for graphics pipelines\n   *\n   * Can only be used when all pipeline state is known.\n   */\n  struct DxvkGraphicsPipelineShaderState {\n    DxvkGraphicsPipelineShaderState();\n\n    DxvkGraphicsPipelineShaderState(\n      const DxvkGraphicsPipelineShaders&    shaders,\n      const DxvkGraphicsPipelineStateInfo&  state);\n\n    DxvkShaderLinkage vsInfo;\n    DxvkShaderLinkage tcsInfo;\n    DxvkShaderLinkage tesInfo;\n    DxvkShaderLinkage gsInfo;\n    DxvkShaderLinkage fsInfo;\n\n    bool eq(const DxvkGraphicsPipelineShaderState& other) const;\n\n    size_t hash() const;\n\n  private:\n\n    DxvkShaderLinkage getLinkage(\n      const DxvkGraphicsPipelineShaders&    shaders,\n      const Rc<DxvkShader>&                 shader,\n      const DxvkGraphicsPipelineStateInfo&  state);\n\n    Rc<DxvkShader> getPrevStageShader(\n      const DxvkGraphicsPipelineShaders&    shaders,\n      const VkShaderStageFlagBits           stage);\n\n  };\n\n\n  /**\n   * \\brief Specialization constant state for pipelines\n   *\n   * Can only be used when all pipeline state is known.\n   */\n  struct DxvkPipelineSpecConstantState {\n    DxvkPipelineSpecConstantState();\n\n    DxvkPipelineSpecConstantState(\n            uint32_t                        mask,\n      const DxvkScInfo&                     state);\n\n    VkSpecializationInfo                                            scInfo         = { };\n    std::array<VkSpecializationMapEntry,  MaxNumSpecConstants + 1>  scConstantMap  = { };\n    std::array<uint32_t,                  MaxNumSpecConstants + 1>  scConstantData = { };\n\n    bool eq(const DxvkPipelineSpecConstantState& other) const;\n\n    size_t hash() const;\n\n  private:\n\n    void addConstant(uint32_t id, uint32_t value);\n\n  };\n\n\n  /**\n   * \\brief Shaders used in graphics pipelines\n   */\n  struct DxvkGraphicsPipelineShaders {\n    Rc<DxvkShader> vs;\n    Rc<DxvkShader> tcs;\n    Rc<DxvkShader> tes;\n    Rc<DxvkShader> gs;\n    Rc<DxvkShader> fs;\n\n    bool eq(const DxvkGraphicsPipelineShaders& other) const {\n      return vs == other.vs && tcs == other.tcs\n          && tes == other.tes && gs == other.gs\n          && fs == other.fs;\n    }\n\n    size_t hash() const {\n      DxvkHashState state;\n      state.add(DxvkShader::getCookie(vs));\n      state.add(DxvkShader::getCookie(tcs));\n      state.add(DxvkShader::getCookie(tes));\n      state.add(DxvkShader::getCookie(gs));\n      state.add(DxvkShader::getCookie(fs));\n      return state;\n    }\n\n    bool validate() const {\n      return validateShaderType(vs, VK_SHADER_STAGE_VERTEX_BIT)\n          && validateShaderType(tcs, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)\n          && validateShaderType(tes, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)\n          && validateShaderType(gs, VK_SHADER_STAGE_GEOMETRY_BIT)\n          && validateShaderType(fs, VK_SHADER_STAGE_FRAGMENT_BIT);\n    }\n\n    static bool validateShaderType(const Rc<DxvkShader>& shader, VkShaderStageFlagBits stage) {\n      return shader == nullptr || shader->metadata().stage == stage;\n    }\n  };\n\n\n  /**\n   * \\brief Graphics pipeline type\n   */\n  enum class DxvkGraphicsPipelineType : uint32_t {\n    BasePipeline = 0, ///< Unoptimized pipeline using graphics pipeline libraries\n    FastPipeline = 1, ///< Monolithic pipeline with less dynamic state\n  };\n\n\n  /**\n   * \\brief Graphics pipeline handle\n   */\n  struct DxvkGraphicsPipelineHandle {\n    VkPipeline                handle      = VK_NULL_HANDLE;\n    DxvkGraphicsPipelineType  type        = DxvkGraphicsPipelineType::FastPipeline;\n    DxvkAttachmentMask        attachments = { };\n  };\n\n\n  /**\n   * \\brief Graphics pipeline instance\n   * \n   * Stores a state vector and the\n   * corresponding pipeline handle.\n   */\n  struct DxvkGraphicsPipelineInstance {\n    DxvkGraphicsPipelineInstance() { }\n    DxvkGraphicsPipelineInstance(\n            VkPipeline                      baseHandle_,\n            VkPipeline                      fastHandle_,\n            DxvkAttachmentMask              attachments_)\n    : baseHandle  (baseHandle_),\n      fastHandle  (fastHandle_),\n      isCompiling (fastHandle_ != VK_NULL_HANDLE),\n      attachments (attachments_) { }\n\n    std::atomic<VkPipeline>       baseHandle  = { VK_NULL_HANDLE };\n    std::atomic<VkPipeline>       fastHandle  = { VK_NULL_HANDLE };\n    std::atomic<VkBool32>         isCompiling = { VK_FALSE };\n    DxvkAttachmentMask            attachments = { };\n\n    DxvkGraphicsPipelineHandle getHandle() const {\n      // Find a pipeline handle to use. If no optimized pipeline has\n      // been compiled yet, use the slower base pipeline instead.\n      DxvkGraphicsPipelineHandle result;\n      result.handle = fastHandle.load(std::memory_order_acquire);\n      result.type = DxvkGraphicsPipelineType::FastPipeline;\n      result.attachments = attachments;\n\n      if (likely(fastHandle))\n        return result;\n\n      result.handle = baseHandle.load(std::memory_order_acquire);\n      result.type = DxvkGraphicsPipelineType::BasePipeline;\n      return result;\n    }\n  };\n\n\n  /**\n   * \\brief Base instance key\n   *\n   * Stores the libraries and arguments used to\n   * compile a base pipeline.\n   */\n  struct DxvkGraphicsPipelineBaseInstanceKey {\n    const DxvkGraphicsPipelineVertexInputLibrary*     viLibrary = nullptr;\n    const DxvkGraphicsPipelineFragmentOutputLibrary*  foLibrary = nullptr;\n\n    bool eq(const DxvkGraphicsPipelineBaseInstanceKey& other) const {\n      return viLibrary == other.viLibrary\n          && foLibrary == other.foLibrary;\n    }\n\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(size_t(viLibrary));\n      hash.add(size_t(foLibrary));\n      return hash;\n    }\n  };\n\n\n  /**\n   * \\brief Fast instance key\n   *\n   * Stores pipeline state used to compile an\n   * optimized pipeline.\n   */\n  struct DxvkGraphicsPipelineFastInstanceKey {\n    DxvkGraphicsPipelineFastInstanceKey() { }\n\n    DxvkGraphicsPipelineFastInstanceKey(\n            DxvkDevice*                       device,\n      const DxvkGraphicsPipelineShaders&      shaders,\n      const DxvkGraphicsPipelineStateInfo&    state,\n            DxvkGraphicsPipelineFlags         flags,\n            uint32_t                          specConstantMask)\n    : shState(shaders, state),\n      dyState(device, state, flags),\n      viState(device, state, shaders),\n      prState(device, state, shaders),\n      fsState(device, state),\n      foState(device, state, shaders),\n      scState(specConstantMask, state.sc) { }\n\n    DxvkGraphicsPipelineShaderState           shState;\n    DxvkGraphicsPipelineDynamicState          dyState;\n    DxvkGraphicsPipelineVertexInputState      viState;\n    DxvkGraphicsPipelinePreRasterizationState prState;\n    DxvkGraphicsPipelineFragmentShaderState   fsState;\n    DxvkGraphicsPipelineFragmentOutputState   foState;\n    DxvkPipelineSpecConstantState             scState;\n\n    bool eq(const DxvkGraphicsPipelineFastInstanceKey& other) const {\n      return shState.eq(other.shState)\n          && dyState.eq(other.dyState)\n          && viState.eq(other.viState)\n          && prState.eq(other.prState)\n          && fsState.eq(other.fsState)\n          && foState.eq(other.foState)\n          && scState.eq(other.scState);\n    }\n\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(shState.hash());\n      hash.add(dyState.hash());\n      hash.add(viState.hash());\n      hash.add(prState.hash());\n      hash.add(fsState.hash());\n      hash.add(foState.hash());\n      hash.add(scState.hash());\n      return hash;\n    }\n  };\n\n\n  /**\n   * \\brief Graphics pipeline\n   * \n   * Stores the pipeline layout as well as methods to\n   * recompile the graphics pipeline against a given\n   * pipeline state vector.\n   */\n  class DxvkGraphicsPipeline {\n    \n  public:\n    \n    DxvkGraphicsPipeline(\n            DxvkDevice*                 device,\n            DxvkPipelineManager*        pipeMgr,\n            DxvkGraphicsPipelineShaders shaders,\n            DxvkShaderPipelineLibrary*  vsLibrary,\n            DxvkShaderPipelineLibrary*  fsLibrary);\n\n    ~DxvkGraphicsPipeline();\n\n    /**\n     * \\brief Shaders used by the pipeline\n     * \\returns Shaders used by the pipeline\n     */\n    const DxvkGraphicsPipelineShaders& shaders() const {\n      return m_shaders;\n    }\n    \n    /**\n     * \\brief Returns graphics pipeline flags\n     * \\returns Graphics pipeline property flags\n     */\n    DxvkGraphicsPipelineFlags flags() const {\n      return m_flags;\n    }\n    \n    /**\n     * \\brief Queries pipeline layout\n     * \\returns Pipeline layout\n     */\n    const DxvkPipelineBindings* getLayout() const {\n      return &m_layout;\n    }\n\n    /**\n     * \\brief Queries spec constant mask\n     *\n     * This only includes user spec constants.\n     * \\returns Bit mask of used spec constants\n     */\n    uint32_t getSpecConstantMask() const {\n      constexpr uint32_t globalMask = (1u << MaxNumSpecConstants) - 1;\n      return m_specConstantMask & globalMask;\n    }\n\n    /**\n     * \\brief Queries global resource barrier\n     *\n     * Returns the stages that can access resources in this\n     * pipeline with the given pipeline state, as well as\n     * the ways in which resources are accessed. This does\n     * not include render targets. The barrier is meant to\n     * be executed after the render pass.\n     * \\returns Global barrier\n     */\n    DxvkGlobalPipelineBarrier getGlobalBarrier(\n      const DxvkGraphicsPipelineStateInfo&    state) const;\n\n    /**\n     * \\brief Pipeline handle\n     * \n     * Retrieves a pipeline handle for the given pipeline\n     * state. If necessary, a new pipeline will be created.\n     * \\param [in] state Pipeline state vector\n     * \\returns Pipeline handle and handle type\n     */\n    DxvkGraphicsPipelineHandle getPipelineHandle(\n      const DxvkGraphicsPipelineStateInfo&    state);\n    \n    /**\n     * \\brief Compiles a pipeline\n     * \n     * Asynchronously compiles the given pipeline\n     * and stores the result for future use.\n     * \\param [in] state Pipeline state vector\n     */\n    void compilePipeline(\n      const DxvkGraphicsPipelineStateInfo&    state);\n\n    /**\n     * \\brief Acquires the pipeline\n     *\n     * Increments the use count by one and prevents any Vulkan\n     * pipelines from being destroyed. Must be called before\n     * \\ref getPipelineHandle or \\ref compilePipeline.\n     */\n    void acquirePipeline();\n\n    /**\n     * \\brief Releases the pipeline\n     *\n     * Decrements the use count by one. If the counter reaches\n     * zero, any Vulkan pipeline objects may be destroyed.\n     */\n    void releasePipeline();\n\n    /**\n     * \\brief Queries debug name for the pipeline\n     *\n     * The pipeline debug name contains the debug name of\n     * each shader included in the pipeline.\n     * \\returns Pipeline debug name\n     */\n    const char* debugName() const {\n      return m_debugName.c_str();\n    }\n\n  private:\n\n    DxvkDevice*                 m_device;    \n    DxvkPipelineManager*        m_manager;\n    DxvkPipelineWorkers*        m_workers;\n    DxvkPipelineStats*          m_stats;\n\n    DxvkGraphicsPipelineShaders m_shaders;\n    DxvkPipelineBindings        m_layout;\n    DxvkGlobalPipelineBarrier   m_barrier;\n    DxvkGraphicsPipelineFlags   m_flags;\n\n    DxvkShaderPipelineLibrary*  m_vsLibrary;\n    DxvkShaderPipelineLibrary*  m_fsLibrary;\n\n    uint32_t m_vsIn  = 0;\n    uint32_t m_fsOut = 0;\n\n    uint32_t m_specConstantMask = 0;\n\n    std::string m_debugName;\n\n    alignas(CACHE_LINE_SIZE)\n    dxvk::mutex                                   m_mutex;\n    DxvkPipelineVariantTable<\n      DxvkGraphicsPipelineStateInfo,\n      DxvkGraphicsPipelineInstance>               m_pipelines;\n    uint32_t                                      m_useCount = 0;\n\n    std::unordered_map<\n      DxvkGraphicsPipelineBaseInstanceKey,\n      VkPipeline, DxvkHash, DxvkEq>               m_basePipelines;\n\n    alignas(CACHE_LINE_SIZE)\n    dxvk::mutex                                   m_fastMutex;\n    std::unordered_map<\n      DxvkGraphicsPipelineFastInstanceKey,\n      VkPipeline, DxvkHash, DxvkEq>               m_fastPipelines;\n\n    DxvkGraphicsPipelineInstance* createInstance(\n      const DxvkGraphicsPipelineStateInfo& state,\n            bool                           doCreateBasePipeline);\n    \n    DxvkGraphicsPipelineInstance* findInstance(\n      const DxvkGraphicsPipelineStateInfo& state);\n\n    bool canCreateBasePipeline(\n      const DxvkGraphicsPipelineStateInfo& state) const;\n\n    VkPipeline getBasePipeline(\n      const DxvkGraphicsPipelineStateInfo& state);\n\n    VkPipeline createBasePipeline(\n      const DxvkGraphicsPipelineBaseInstanceKey& key) const;\n    \n    VkPipeline getOptimizedPipeline(\n      const DxvkGraphicsPipelineStateInfo& state);\n\n    VkPipeline createOptimizedPipeline(\n      const DxvkGraphicsPipelineFastInstanceKey& key) const;\n\n    void destroyBasePipelines();\n\n    void destroyOptimizedPipelines();\n\n    void destroyVulkanPipeline(\n            VkPipeline                    pipeline) const;\n\n    SpirvCodeBuffer getShaderCode(\n            DxvkShader&                   shader,\n      const DxvkShaderLinkage&            linkage) const;\n\n    uint32_t computeSpecConstantMask() const;\n\n    DxvkAttachmentMask computeAttachmentMask(\n      const DxvkGraphicsPipelineStateInfo& state) const;\n\n    bool validatePipelineState(\n      const DxvkGraphicsPipelineStateInfo& state,\n            bool                           trusted) const;\n\n    DxvkPipelineLayoutBuilder buildPipelineLayout() const;\n\n    void logPipelineState(\n            LogLevel                       level,\n      const DxvkGraphicsPipelineStateInfo& state) const;\n\n    std::string createDebugName() const;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_graphics_state.h",
    "content": "#pragma once\n\n#include \"dxvk_format.h\"\n#include \"dxvk_limits.h\"\n\n#include <atomic>\n#include <cstring>\n#include <optional>\n#include <utility>\n\nnamespace dxvk {\n\n  /**\n   * \\brief Packed input assembly state\n   *\n   * Stores the primitive topology\n   * and primitive restart info.\n   */\n  class DxvkIaInfo {\n\n  public:\n\n    DxvkIaInfo() = default;\n\n    DxvkIaInfo(\n            VkPrimitiveTopology primitiveTopology,\n            VkBool32            primitiveRestart,\n            uint32_t            patchVertexCount)\n    : m_primitiveTopology (uint16_t(primitiveTopology)),\n      m_primitiveRestart  (uint16_t(primitiveRestart)),\n      m_patchVertexCount  (uint16_t(patchVertexCount)),\n      m_reserved          (0) { }\n\n    VkPrimitiveTopology primitiveTopology() const {\n      return m_primitiveTopology <= VK_PRIMITIVE_TOPOLOGY_PATCH_LIST\n        ? VkPrimitiveTopology(m_primitiveTopology)\n        : VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;\n    }\n\n    VkBool32 primitiveRestart() const {\n      return VkBool32(m_primitiveRestart);\n    }\n\n    uint32_t patchVertexCount() const {\n      return m_patchVertexCount;\n    }\n\n  private:\n\n    uint16_t m_primitiveTopology      : 4;\n    uint16_t m_primitiveRestart       : 1;\n    uint16_t m_patchVertexCount       : 6;\n    uint16_t m_reserved               : 5;\n\n  };\n\n\n  /**\n   * \\brief Packed input layout metadata\n   * \n   * Stores the number of vertex attributes\n   * and bindings in one byte each.\n   */\n  class DxvkIlInfo {\n\n  public:\n\n    DxvkIlInfo() = default;\n\n    DxvkIlInfo(\n            uint32_t        attributeCount,\n            uint32_t        bindingCount)\n    : m_attributeCount(uint8_t(attributeCount)),\n      m_bindingCount  (uint8_t(bindingCount)) { }\n\n    uint32_t attributeCount() const {\n      return m_attributeCount;\n    }\n\n    uint32_t bindingCount() const {\n      return m_bindingCount;\n    }\n\n  private:\n\n    uint8_t m_attributeCount;\n    uint8_t m_bindingCount;\n\n  };\n\n\n  /**\n   * \\brief Packed vertex attribute\n   *\n   * Stores a vertex attribute description. Assumes\n   * that all vertex formats have numerical values\n   * of 127 or less (i.e. fit into 7 bits).\n   */\n  class DxvkIlAttribute {\n\n  public:\n\n    DxvkIlAttribute() = default;\n\n    DxvkIlAttribute(\n            uint32_t                        location,\n            uint32_t                        binding,\n            VkFormat                        format,\n            uint32_t                        offset)\n    : m_location(uint32_t(location)),\n      m_binding (uint32_t(binding)),\n      m_format  (uint32_t(format)),\n      m_offset  (uint32_t(offset)),\n      m_reserved(0) { }\n    \n    uint32_t location() const {\n      return m_location;\n    }\n    \n    uint32_t binding() const {\n      return m_binding;\n    }\n\n    VkFormat format() const {\n      return VkFormat(m_format);\n    }\n\n    uint32_t offset() const {\n      return m_offset;\n    }\n    \n    VkVertexInputAttributeDescription description() const {\n      VkVertexInputAttributeDescription result;\n      result.location = m_location;\n      result.binding  = m_binding;\n      result.format   = VkFormat(m_format);\n      result.offset   = m_offset;\n      return result;\n    }\n\n  private:\n\n    uint32_t m_location               : 5;\n    uint32_t m_binding                : 5;\n    uint32_t m_format                 : 7;\n    uint32_t m_offset                 : 11;\n    uint32_t m_reserved               : 4;\n  \n  };\n\n\n  /**\n   * \\brief Packed vertex binding\n   *\n   * Stores a vertex binding description,\n   * including the 32-bit divisor.\n   */\n  class DxvkIlBinding {\n\n  public:\n\n    DxvkIlBinding() = default;\n\n    DxvkIlBinding(\n            uint32_t                        binding,\n            uint32_t                        stride,\n            VkVertexInputRate               inputRate,\n            uint32_t                        divisor)\n    : m_binding   (uint32_t(binding)),\n      m_stride    (uint32_t(stride)),\n      m_inputRate (uint32_t(inputRate)),\n      m_divisor   (uint32_t(divisor < (1u << 14) ? divisor : 0u)) { }\n    \n    uint32_t binding() const {\n      return m_binding;\n    }\n    \n    uint32_t stride() const {\n      return m_stride;\n    }\n\n    VkVertexInputRate inputRate() const {\n      return VkVertexInputRate(m_inputRate);\n    }\n    \n    uint32_t divisor() const {\n      return m_divisor;\n    }\n\n    VkVertexInputBindingDescription description() const {\n      VkVertexInputBindingDescription result;\n      result.binding = m_binding;\n      result.stride  = m_stride;\n      result.inputRate = VkVertexInputRate(m_inputRate);\n      return result;\n    }\n\n    void setStride(uint32_t stride) {\n      m_stride = stride;\n    }\n\n  private:\n\n    uint32_t m_binding                : 5;\n    uint32_t m_stride                 : 12;\n    uint32_t m_inputRate              : 1;\n    uint32_t m_divisor                : 14;\n\n  };\n\n\n  /**\n   * \\brief Packed rasterizer state\n   *\n   * Stores a bunch of flags and parameters\n   * related to rasterization in four bytes.\n   */\n  class DxvkRsInfo {\n\n  public:\n\n    DxvkRsInfo() = default;\n\n    DxvkRsInfo(\n            VkBool32              depthClipEnable,\n            VkPolygonMode         polygonMode,\n            VkSampleCountFlags    sampleCount,\n            VkConservativeRasterizationModeEXT conservativeMode,\n            VkBool32              flatShading,\n            VkLineRasterizationModeEXT lineMode)\n    : m_depthClipEnable (uint16_t(depthClipEnable)),\n      m_polygonMode     (uint16_t(polygonMode)),\n      m_sampleCount     (uint16_t(sampleCount)),\n      m_conservativeMode(uint16_t(conservativeMode)),\n      m_flatShading     (uint16_t(flatShading)),\n      m_lineMode        (uint16_t(lineMode)),\n      m_reserved        (0) { }\n    \n    VkBool32 depthClipEnable() const {\n      return VkBool32(m_depthClipEnable);\n    }\n\n    VkPolygonMode polygonMode() const {\n      return VkPolygonMode(m_polygonMode);\n    }\n\n    VkSampleCountFlags sampleCount() const {\n      return VkSampleCountFlags(m_sampleCount);\n    }\n\n    VkConservativeRasterizationModeEXT conservativeMode() const {\n      return VkConservativeRasterizationModeEXT(m_conservativeMode);\n    }\n\n    VkBool32 flatShading() const {\n      return VkBool32(m_flatShading);\n    }\n\n    VkLineRasterizationModeEXT lineMode() const {\n      return VkLineRasterizationModeEXT(m_lineMode);\n    }\n\n    bool eq(const DxvkRsInfo& other) const {\n      return !std::memcmp(this, &other, sizeof(*this));\n    }\n\n  private:\n\n    uint16_t m_depthClipEnable        : 1;\n    uint16_t m_polygonMode            : 2;\n    uint16_t m_sampleCount            : 5;\n    uint16_t m_conservativeMode       : 2;\n    uint16_t m_flatShading            : 1;\n    uint16_t m_lineMode               : 2;\n    uint16_t m_reserved               : 3;\n  \n  };\n\n\n  /**\n   * \\brief Packed multisample info\n   *\n   * Stores the sample mask, sample count override\n   * and alpha-to-coverage state in four bytes.\n   */\n  class DxvkMsInfo {\n\n  public:\n\n    DxvkMsInfo() = default;\n\n    DxvkMsInfo(\n            VkSampleCountFlags      sampleCount,\n            uint32_t                sampleMask,\n            VkBool32                enableAlphaToCoverage)\n    : m_sampleCount           (uint16_t(sampleCount)),\n      m_enableAlphaToCoverage (uint16_t(enableAlphaToCoverage)),\n      m_reserved              (0),\n      m_sampleMask            (uint16_t(sampleMask)) { }\n    \n    VkSampleCountFlags sampleCount() const {\n      return VkSampleCountFlags(m_sampleCount);\n    }\n\n    uint32_t sampleMask() const {\n      return m_sampleMask;\n    }\n\n    VkBool32 enableAlphaToCoverage() const {\n      return VkBool32(m_enableAlphaToCoverage);\n    }\n\n    void setSampleCount(VkSampleCountFlags sampleCount) {\n      m_sampleCount = uint16_t(sampleCount);\n    }\n\n  private:\n\n    uint16_t m_sampleCount            : 5;\n    uint16_t m_enableAlphaToCoverage  : 1;\n    uint16_t m_reserved               : 10;\n    uint16_t m_sampleMask;\n\n  };\n\n\n  /**\n   * \\brief Packed output merger metadata\n   *\n   * Stores the logic op state in two bytes.\n   * Blend modes are stored separately.\n   */\n  class DxvkOmInfo {\n\n  public:\n\n    DxvkOmInfo() = default;\n\n    DxvkOmInfo(\n            VkBool32           enableLogicOp,\n            VkLogicOp          logicOp,\n            VkImageAspectFlags feedbackLoop)\n    : m_enableLogicOp (uint16_t(enableLogicOp)),\n      m_logicOp       (uint16_t(logicOp)),\n      m_feedbackLoop  (uint16_t(feedbackLoop)),\n      m_reserved      (0) { }\n    \n    VkBool32 enableLogicOp() const {\n      return VkBool32(m_enableLogicOp);\n    }\n\n    VkLogicOp logicOp() const {\n      return VkLogicOp(m_logicOp);\n    }\n\n    VkImageAspectFlags feedbackLoop() const {\n      return VkImageAspectFlags(m_feedbackLoop);\n    }\n\n    void setFeedbackLoop(VkImageAspectFlags feedbackLoop) {\n      m_feedbackLoop = uint16_t(feedbackLoop);\n    }\n\n  private:\n\n    uint16_t m_enableLogicOp          : 1;\n    uint16_t m_logicOp                : 4;\n    uint16_t m_feedbackLoop           : 2;\n    uint16_t m_reserved               : 9;\n\n  };\n\n\n  /**\n   * \\brief Packed render target formats\n   *\n   * Compact representation of depth-stencil and color attachments,\n   * as well as the read-only mask for the depth-stencil attachment,\n   * which needs to be known at pipeline compile time.\n   */\n  class DxvkRtInfo {\n\n  public:\n\n    DxvkRtInfo() = default;\n\n    DxvkRtInfo(\n            uint32_t            colorFormatCount,\n      const VkFormat*           colorFormats,\n            VkFormat            depthStencilFormat,\n            VkImageAspectFlags  depthStencilReadOnlyAspects)\n    : m_packedData(0ull) {\n      m_packedData |= encodeDepthStencilFormat(depthStencilFormat);\n      m_packedData |= encodeDepthStencilAspects(depthStencilReadOnlyAspects);\n\n      for (uint32_t i = 0; i < colorFormatCount; i++)\n        m_packedData |= encodeColorFormat(colorFormats[i], i);\n    }\n\n    VkFormat getColorFormat(uint32_t index) const {\n      return decodeColorFormat(m_packedData, index);\n    }\n\n    VkFormat getDepthStencilFormat() const {\n      return decodeDepthStencilFormat(m_packedData);\n    }\n\n    VkImageAspectFlags getDepthStencilReadOnlyAspects() const {\n      return decodeDepthStencilAspects(m_packedData);\n    }\n\n  private:\n\n    uint64_t m_packedData;\n\n    static uint64_t encodeDepthStencilAspects(VkImageAspectFlags aspects) {\n      return uint64_t(aspects) << 61;\n    }\n\n    static uint64_t encodeDepthStencilFormat(VkFormat format) {\n      return format\n        ? (uint64_t(format) - uint64_t(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32)) << 56\n        : (uint64_t(0));\n    }\n\n    static uint64_t encodeColorFormat(VkFormat format, uint32_t index) {\n      uint64_t value = 0u;\n\n      for (const auto& p : s_colorFormatRanges) {\n        if (format >= p.first && format <= p.second) {\n          value += uint32_t(format) - uint32_t(p.first);\n          break;\n        }\n\n        value += uint32_t(p.second) - uint32_t(p.first) + 1u;\n      }\n\n      return value << (7 * index);\n    }\n\n    static VkImageAspectFlags decodeDepthStencilAspects(uint64_t value) {\n      return VkImageAspectFlags((value >> 61) & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));\n    }\n\n    static VkFormat decodeDepthStencilFormat(uint64_t value) {\n      value = (value >> 56) & 0x1F;\n\n      return value\n        ? VkFormat(value + uint64_t(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32))\n        : VkFormat(VK_FORMAT_UNDEFINED);\n    }\n\n    static VkFormat decodeColorFormat(uint64_t value, uint32_t index) {\n      value = (value >> (7 * index)) & 0x7F;\n\n      for (const auto& p : s_colorFormatRanges) {\n        uint32_t rangeSize = uint32_t(p.second) - uint32_t(p.first) + 1u;\n\n        if (value < rangeSize)\n          return VkFormat(uint32_t(p.first) + uint32_t(value));\n\n        value -= rangeSize;\n      }\n\n      return VK_FORMAT_UNDEFINED;\n    }\n\n    static constexpr std::array<std::pair<VkFormat, VkFormat>, 3> s_colorFormatRanges = {{\n      { VK_FORMAT_UNDEFINED,                  VK_FORMAT_E5B9G9R9_UFLOAT_PACK32  },  /*   0 - 123 */\n      { VK_FORMAT_A4R4G4B4_UNORM_PACK16,      VK_FORMAT_A4B4G4R4_UNORM_PACK16   },  /* 124 - 125 */\n      { VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR,  VK_FORMAT_A8_UNORM_KHR            },  /* 126 - 127 */\n    }};\n\n  };\n\n\n  /**\n   * \\brief Packed attachment blend mode\n   *\n   * Stores blendig parameters for a single\n   * color attachment in four bytes.\n   */\n  class DxvkOmAttachmentBlend {\n\n  public:\n\n    DxvkOmAttachmentBlend() = default;\n\n    DxvkOmAttachmentBlend(\n            VkBool32                    blendEnable,\n            VkBlendFactor               srcColorBlendFactor,\n            VkBlendFactor               dstColorBlendFactor,\n            VkBlendOp                   colorBlendOp,\n            VkBlendFactor               srcAlphaBlendFactor,\n            VkBlendFactor               dstAlphaBlendFactor,\n            VkBlendOp                   alphaBlendOp,\n            VkColorComponentFlags       colorWriteMask)\n    : m_blendEnable         (uint32_t(blendEnable)),\n      m_srcColorBlendFactor (uint32_t(srcColorBlendFactor)),\n      m_dstColorBlendFactor (uint32_t(dstColorBlendFactor)),\n      m_colorBlendOp        (uint32_t(colorBlendOp)),\n      m_srcAlphaBlendFactor (uint32_t(srcAlphaBlendFactor)),\n      m_dstAlphaBlendFactor (uint32_t(dstAlphaBlendFactor)),\n      m_alphaBlendOp        (uint32_t(alphaBlendOp)),\n      m_colorWriteMask      (uint32_t(colorWriteMask)),\n      m_reserved            (0) { }\n    \n    VkBool32 blendEnable() const {\n      return m_blendEnable;\n    }\n\n    VkBlendFactor srcColorBlendFactor() const {\n      return VkBlendFactor(m_srcColorBlendFactor);\n    }\n\n    VkBlendFactor dstColorBlendFactor() const {\n      return VkBlendFactor(m_dstColorBlendFactor);\n    }\n\n    VkBlendOp colorBlendOp() const {\n      return VkBlendOp(m_colorBlendOp);\n    }\n\n    VkBlendFactor srcAlphaBlendFactor() const {\n      return VkBlendFactor(m_srcAlphaBlendFactor);\n    }\n\n    VkBlendFactor dstAlphaBlendFactor() const {\n      return VkBlendFactor(m_dstAlphaBlendFactor);\n    }\n\n    VkBlendOp alphaBlendOp() const {\n      return VkBlendOp(m_alphaBlendOp);\n    }\n\n    VkColorComponentFlags colorWriteMask() const {\n      return VkColorComponentFlags(m_colorWriteMask);\n    }\n\n    VkPipelineColorBlendAttachmentState state() const {\n      VkPipelineColorBlendAttachmentState result;\n      result.blendEnable         = VkBool32(m_blendEnable);\n      result.srcColorBlendFactor = VkBlendFactor(m_srcColorBlendFactor);\n      result.dstColorBlendFactor = VkBlendFactor(m_dstColorBlendFactor);\n      result.colorBlendOp        = VkBlendOp(m_colorBlendOp);\n      result.srcAlphaBlendFactor = VkBlendFactor(m_srcAlphaBlendFactor);\n      result.dstAlphaBlendFactor = VkBlendFactor(m_dstAlphaBlendFactor);\n      result.alphaBlendOp        = VkBlendOp(m_alphaBlendOp);\n      result.colorWriteMask      = VkColorComponentFlags(m_colorWriteMask);\n      return result;\n    }\n\n  private:\n\n    uint32_t m_blendEnable            : 1;\n    uint32_t m_srcColorBlendFactor    : 5;\n    uint32_t m_dstColorBlendFactor    : 5;\n    uint32_t m_colorBlendOp           : 3;\n    uint32_t m_srcAlphaBlendFactor    : 5;\n    uint32_t m_dstAlphaBlendFactor    : 5;\n    uint32_t m_alphaBlendOp           : 3;\n    uint32_t m_colorWriteMask         : 4;\n    uint32_t m_reserved               : 1;\n\n  };\n\n\n  /**\n   * \\brief Packed attachment swizzle\n   *\n   * Stores the component mapping for one\n   * single color attachment in one byte.\n   */\n  class DxvkOmAttachmentSwizzle {\n\n  public:\n\n    DxvkOmAttachmentSwizzle() = default;\n\n    DxvkOmAttachmentSwizzle(VkComponentMapping mapping)\n    : m_r(util::getComponentIndex(mapping.r, 0)),\n      m_g(util::getComponentIndex(mapping.g, 1)),\n      m_b(util::getComponentIndex(mapping.b, 2)),\n      m_a(util::getComponentIndex(mapping.a, 3)) { }\n    \n    uint32_t rIndex() const { return m_r; }\n    uint32_t gIndex() const { return m_g; }\n    uint32_t bIndex() const { return m_b; }\n    uint32_t aIndex() const { return m_a; }\n    \n    VkComponentMapping mapping() const {\n      VkComponentMapping result;\n      result.r = decodeSwizzle(m_r);\n      result.g = decodeSwizzle(m_g);\n      result.b = decodeSwizzle(m_b);\n      result.a = decodeSwizzle(m_a);\n      return result;\n    }\n\n  private:\n\n    uint8_t m_r : 2;\n    uint8_t m_g : 2;\n    uint8_t m_b : 2;\n    uint8_t m_a : 2;\n\n    static VkComponentSwizzle decodeSwizzle(uint8_t swizzle) {\n      return VkComponentSwizzle(uint32_t(swizzle) + uint32_t(VK_COMPONENT_SWIZZLE_R));\n    }\n\n  };\n\n\n  /**\n   * \\brief Specialization constant state\n   *\n   * Stores the raw 32-bit spec constant values.\n   */\n  struct DxvkScInfo {\n    uint32_t specConstants[DxvkLimits::MaxNumSpecConstants];\n  };\n\n\n  /**\n   * \\brief Packed graphics pipeline state\n   *\n   * Stores a compressed representation of the full\n   * graphics pipeline state which is optimized for\n   * lookup performance.\n   */\n  struct alignas(32) DxvkGraphicsPipelineStateInfo {\n    DxvkGraphicsPipelineStateInfo() {\n      std::memset(this, 0, sizeof(*this));\n    }\n\n    DxvkGraphicsPipelineStateInfo(const DxvkGraphicsPipelineStateInfo& other) {\n      std::memcpy(this, &other, sizeof(*this));\n    }\n    \n    DxvkGraphicsPipelineStateInfo& operator = (const DxvkGraphicsPipelineStateInfo& other) {\n      std::memcpy(this, &other, sizeof(*this));\n      return *this;\n    }\n    \n    bool eq(const DxvkGraphicsPipelineStateInfo& other) const {\n      return bit::bcmpeq(this, &other);\n    }\n\n    size_t hash() const {\n      auto src = reinterpret_cast<const unsigned char*>(this);\n      return size_t(bit::fnv1a_hash(src, sizeof(*this)));\n    }\n\n    bool useDynamicDepthTest() const {\n      return rt.getDepthStencilFormat();\n    }\n\n    bool useDynamicDepthBounds() const {\n      return rt.getDepthStencilFormat();\n    }\n\n    bool useDynamicStencilTest() const {\n      auto format = rt.getDepthStencilFormat();\n      return format && (lookupFormatInfo(format)->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT);\n    }\n\n    bool useDynamicVertexStrides() const {\n      if (!il.bindingCount())\n        return false;\n\n      bool result = true;\n\n      for (uint32_t i = 0; i < il.bindingCount() && result; i++)\n        result = !ilBindings[i].stride();\n\n      return result;\n    }\n\n    bool useDynamicBlendConstants() const {\n      bool result = false;\n      \n      for (uint32_t i = 0; i < MaxNumRenderTargets && !result; i++) {\n        result |= rt.getColorFormat(i) && omBlend[i].blendEnable()\n         && (util::isBlendConstantBlendFactor(omBlend[i].srcColorBlendFactor())\n          || util::isBlendConstantBlendFactor(omBlend[i].dstColorBlendFactor())\n          || util::isBlendConstantBlendFactor(omBlend[i].srcAlphaBlendFactor())\n          || util::isBlendConstantBlendFactor(omBlend[i].dstAlphaBlendFactor()));\n      }\n\n      return result;\n    }\n\n    bool useDualSourceBlending() const {\n      return omBlend[0].blendEnable() && (\n        util::isDualSourceBlendFactor(omBlend[0].srcColorBlendFactor()) ||\n        util::isDualSourceBlendFactor(omBlend[0].dstColorBlendFactor()) ||\n        util::isDualSourceBlendFactor(omBlend[0].srcAlphaBlendFactor()) ||\n        util::isDualSourceBlendFactor(omBlend[0].dstAlphaBlendFactor()));\n    }\n\n    bool useSampleLocations() const {\n      return ms.sampleCount() != VK_SAMPLE_COUNT_1_BIT\n          && rs.sampleCount() == VK_SAMPLE_COUNT_1_BIT;\n    }\n\n    bool writesRenderTarget(\n            uint32_t                        target) const {\n      if (!omBlend[target].colorWriteMask())\n        return false;\n\n      VkFormat rtFormat = rt.getColorFormat(target);\n      return rtFormat != VK_FORMAT_UNDEFINED;\n    }\n\n\n    DxvkIaInfo              ia;\n    DxvkIlInfo              il;\n    DxvkRsInfo              rs;\n    DxvkMsInfo              ms;\n    DxvkOmInfo              om;\n    DxvkRtInfo              rt;\n    DxvkScInfo              sc;\n    DxvkOmAttachmentSwizzle omSwizzle         [DxvkLimits::MaxNumRenderTargets];\n    DxvkOmAttachmentBlend   omBlend           [DxvkLimits::MaxNumRenderTargets];\n    DxvkIlAttribute         ilAttributes      [DxvkLimits::MaxNumVertexAttributes];\n    DxvkIlBinding           ilBindings        [DxvkLimits::MaxNumVertexBindings];\n  };\n\n\n  /**\n   * \\brief Compute pipeline state info\n   */\n  struct alignas(32) DxvkComputePipelineStateInfo {\n    DxvkComputePipelineStateInfo() {\n      std::memset(this, 0, sizeof(*this));\n    }\n\n    DxvkComputePipelineStateInfo(const DxvkComputePipelineStateInfo& other) {\n      std::memcpy(this, &other, sizeof(*this));\n    }\n    \n    DxvkComputePipelineStateInfo& operator = (const DxvkComputePipelineStateInfo& other) {\n      std::memcpy(this, &other, sizeof(*this));\n      return *this;\n    }\n    \n    bool eq(const DxvkComputePipelineStateInfo& other) const {\n      return bit::bcmpeq(this, &other);\n    }\n    \n    size_t hash() const {\n      auto src = reinterpret_cast<const unsigned char*>(this);\n      return size_t(bit::fnv1a_hash(src, sizeof(*this)));\n    }\n\n    DxvkScInfo              sc;\n  };\n\n\n  /**\n   * \\brief Pipeline state look-up table\n   *\n   * Provides a thread-safe, adaptive data structure for pipeline variants.\n   * Look-up and insertion are expected to be O(log n).\n   */\n  template<typename K, typename V>\n  class DxvkPipelineVariantTable {\n    static constexpr size_t LayerBits = 5u;\n    static constexpr size_t LayerSize = 1u << LayerBits;\n\n    static constexpr uint32_t HashThreshold = 4u;\n  public:\n\n    ~DxvkPipelineVariantTable() {\n      iter(m_table, [] (Entry* e) { delete e; });\n    }\n\n    V* find(const K& k) const {\n      // If the number of variants is small, avoid computing the\n      // state hash since that is somewhat expensive to do\n      uint32_t mask = m_table.mask.load(std::memory_order::memory_order_acquire);\n\n      bool useSimple = !(mask & (mask - 1u));\n\n      if (!useSimple)\n        useSimple = bit::popcnt(mask) < HashThreshold;\n\n      if (likely(useSimple)) {\n        for (auto index : bit::BitMask(mask)) {\n          // If more that one level is present, we need to consider\n          // those as well, but we can only do that on the hash path.\n          auto e = m_table.entries[index].load(std::memory_order_acquire);\n          useSimple = useSimple && !e->table.mask.load(std::memory_order_relaxed);\n\n          // Scan entries with the same hash\n          while (e) {\n            if (e->key.eq(k))\n              return &e->value;\n\n            e = e->next.load(std::memory_order_acquire);\n          }\n        }\n\n        if (likely(useSimple))\n          return nullptr;\n      }\n\n      // Compute hash and traverse entries\n      size_t hash = k.hash();\n      size_t shift = 0u;\n\n      const Table* table = &m_table;\n\n      while (true) {\n        size_t index = computeListIndex(hash, shift);\n        shift += LayerBits;\n\n        auto e = table->entries[index].load(std::memory_order_acquire);\n\n        if (!e)\n          break;\n\n        // Fetch next table from list head\n        // and ensure that the hash matches\n        table = &e->table;\n\n        if (e->hash != hash)\n          continue;\n\n        // Scan entries with the same hash\n        while (e) {\n          if (e->key.eq(k))\n            return &e->value;\n\n          e = e->next.load(std::memory_order_acquire);\n        }\n      }\n\n      // No pipeline found\n      return nullptr;\n    }\n\n    template<typename... Args>\n    V* add(const K& k, Args&&... args) {\n      size_t hash = k.hash();\n\n      // Try to insert the new entry into the top-level look-up table.\n      // If the given entry is already set, try the next level.\n      Entry* entry = new Entry(k, hash, std::forward<Args>(args)...);\n      Table* table = &m_table;\n      Entry* target = nullptr;\n\n      size_t index = -1;\n      size_t shift = 0u;\n\n      while (!target) {\n        index = computeListIndex(hash, shift);\n\n        // If this succeeds, this is the first entry at the given index\n        if (table->entries[index].compare_exchange_strong(target, entry,\n            std::memory_order_release, std::memory_order_acquire))\n          break;\n\n        // Check if there is a hash collision\n        if (target->hash == hash)\n          break;\n\n        table = &target->table;\n        target = nullptr;\n\n        shift += LayerBits;\n      }\n\n      if (target) {\n        // The new entry has the same hash as the target entry, so\n        // just append it to the linked list. This should be rare.\n        while (true) {\n          Entry* next = nullptr;\n\n          if (target->next.compare_exchange_strong(next, entry,\n              std::memory_order_release, std::memory_order_acquire))\n            break;\n\n          target = next;\n        }\n      } else {\n        // Update mask now that the corresponding entry is non-null\n        table->mask.fetch_or(1u << index, std::memory_order_release);\n      }\n\n      return &entry->value;\n    }\n\n    template<typename Fn>\n    void forEach(const Fn& fn) const {\n      iter(m_table, [&] (Entry* e) { fn(e->value); });\n    }\n\n  private:\n\n    struct Entry;\n\n    struct Table {\n      std::array<std::atomic<Entry*>, LayerSize> entries = { };\n      std::atomic<uint32_t> mask = { 0u };\n    };\n\n    struct Entry {\n      template<typename... Args>\n      Entry(const K& k, size_t h, Args&&... args)\n      : key(k), hash(h), value(std::forward<Args>(args)...) { }\n\n      K       key   = { };\n      size_t  hash  = 0u;\n      V       value = { };\n      Table   table = { };\n\n      std::atomic<Entry*> next = { nullptr };\n    };\n\n    Table m_table;\n\n    template<typename Fn>\n    static void iter(const Table& table, const Fn& fn) {\n      uint32_t mask = table.mask.load(std::memory_order_acquire);\n\n      for (auto index : bit::BitMask(mask)) {\n        Entry* e = table.entries[index].load(std::memory_order_relaxed);\n\n        // Recurse first so that the function can be used for destruction.\n        // Only the first entry in each list can have a sub-table.\n        if (e->table.mask.load(std::memory_order_relaxed))\n          iter(e->table, fn);\n\n        while (e) {\n          Entry* next = e->next.load(std::memory_order_acquire);\n          fn(e);\n          e = next;\n        }\n      }\n    }\n\n    static size_t computeListIndex(size_t hash, size_t shift) {\n      // Swap bytes to ensure that high bits of the hash contribute to the index.\n      // This is useful since hashes often only differ in the high 32 bits.\n      if constexpr (sizeof(size_t) == sizeof(uint64_t)) {\n        uint64_t index = hash;\n\n        index = ((index >> 56u) & 0x00000000000000ffull)\n              | ((index >> 40u) & 0x000000000000ff00ull)\n              | ((index >> 24u) & 0x0000000000ff0000ull)\n              | ((index >>  8u) & 0x00000000ff000000ull)\n              | ((index <<  8u) & 0x000000ff00000000ull)\n              | ((index << 24u) & 0x0000ff0000000000ull)\n              | ((index << 40u) & 0x00ff000000000000ull)\n              | ((index << 56u) & 0xff00000000000000ull);\n\n        return size_t((index + hash) >> shift) % LayerSize;\n      } else {\n        uint32_t index = hash;\n\n        index = ((index >> 24u) & 0x000000ffu)\n              | ((index >>  8u) & 0x0000ff00u)\n              | ((index <<  8u) & 0x00ff0000u)\n              | ((index << 24u) & 0xff000000u);\n\n        return size_t((index + hash) >> shift) % LayerSize;\n      }\n    }\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_hash.h",
    "content": "#pragma once\n\n#include <cstddef>\n\n#include \"../util/util_env.h\"\n\nnamespace dxvk {\n\n  struct DxvkEq {\n    template<typename T>\n    size_t operator () (const T& a, const T& b) const {\n      return a.eq(b);\n    }\n  };\n\n  struct DxvkHash {\n    template<typename T>\n    size_t operator () (const T& object) const {\n      return object.hash();\n    }\n  };\n  \n  class DxvkHashState {\n    static constexpr size_t Offset = env::is32BitHostPlatform()\n      ? size_t(0x811c9dc5u)\n      : size_t(0xcbf29ce484222325ull);\n\n    static constexpr size_t Prime = env::is32BitHostPlatform()\n      ? size_t(0x01000193u)\n      : size_t(0x00000100000001b3ull);\n  public:\n\n    void add(size_t hash) {\n      m_value ^= hash;\n      m_value *= Prime;\n    }\n\n    operator size_t () const {\n      return m_value;\n    }\n\n  private:\n\n    size_t m_value = Offset;\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_image.cpp",
    "content": "#include \"dxvk_image.h\"\n\n#include \"dxvk_device.h\"\n\nnamespace dxvk {\n  \n  DxvkKeyedMutex::DxvkKeyedMutex(\n      const Rc<DxvkDevice>& device,\n            uint64_t        initialValue,\n            bool            ntShared)\n  : m_vkd(device->vkd()) {\n    DxvkFenceCreateInfo fenceInfo;\n    fenceInfo.initialValue = 0;\n    fenceInfo.sharedType = ntShared\n      ? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT\n      : VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;\n    m_fence = device->createFence(fenceInfo);\n    if (!m_fence)\n      throw DxvkError(\"DxvkKeyedMutex: Failed to create fence\");\n\n    D3DKMT_CREATEKEYEDMUTEX2 create = { };\n    create.Flags.NtSecuritySharing = ntShared;\n    create.InitialValue = initialValue;\n    if (D3DKMTCreateKeyedMutex2(&create))\n      throw DxvkError(\"DxvkKeyedMutex: Failed to create mutex\");\n\n    m_kmtLocal = create.hKeyedMutex;\n    m_kmtGlobal = create.hSharedHandle;\n  }\n\n\n  DxvkKeyedMutex::DxvkKeyedMutex(\n      const Rc<DxvkDevice>& device,\n            Rc<DxvkFence>&& fence,\n            D3DKMT_HANDLE   kmtLocal,\n            D3DKMT_HANDLE   kmtGlobal)\n  : m_vkd(device->vkd()),\n    m_fence(fence),\n    m_kmtLocal(kmtLocal),\n    m_kmtGlobal(kmtGlobal) {\n    if (!fence)\n      Logger::err(\"DxvkKeyedMutex::DxvkKeyedMutex: No fence provided\");\n  }\n\n    \n  DxvkKeyedMutex::~DxvkKeyedMutex() {\n    if (m_kmtLocal) {\n      D3DKMT_DESTROYKEYEDMUTEX destroy = { };\n      destroy.hKeyedMutex = m_kmtLocal;\n      D3DKMTDestroyKeyedMutex(&destroy);\n    }\n  }\n\n\n  HRESULT DxvkKeyedMutex::AcquireSync(UINT64 key, DWORD  milliseconds) {\n    if (m_owned.load(std::memory_order_acquire))\n      return DXGI_ERROR_INVALID_CALL;\n\n    LARGE_INTEGER timeout = { };\n    D3DKMT_ACQUIREKEYEDMUTEX acquire = { };\n    acquire.hKeyedMutex = m_kmtLocal;\n    acquire.Key = key;\n    acquire.pTimeout = &timeout;\n    timeout.QuadPart = milliseconds * -10000;\n\n    NTSTATUS status = D3DKMTAcquireKeyedMutex(&acquire);\n    if (status == STATUS_TIMEOUT)\n      return WAIT_TIMEOUT;\n    if (status)\n      return DXGI_ERROR_INVALID_CALL;\n\n    VkSemaphore semaphore = m_fence->handle();\n    VkSemaphoreWaitInfo info = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };\n    info.semaphoreCount = 1;\n    info.pSemaphores = &semaphore;\n    info.pValues = &acquire.FenceValue;\n\n    if (m_vkd->vkWaitSemaphores(m_vkd->device(), &info, -1)) {\n      Logger::warn(\"DxvkKeyedMutex::AcquireSync: Failed to wait semaphore\");\n      return DXGI_ERROR_INVALID_CALL;\n    }\n\n    m_fenceValue = acquire.FenceValue;\n    m_owned.store(true, std::memory_order_release);\n    return S_OK;\n  }\n\n\n  HRESULT DxvkKeyedMutex::ReleaseSync(UINT64 key) {\n    if (!m_owned.load(std::memory_order_acquire))\n      return DXGI_ERROR_INVALID_CALL;\n\n    VkSemaphoreSignalInfo info = { VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO };\n    info.semaphore = m_fence->handle();\n    info.value = m_fenceValue + 1;\n\n    if (m_vkd->vkSignalSemaphore(m_vkd->device(), &info)) {\n      Logger::warn(\"D3D11DXGIKeyedMutex::ReleaseSync: Failed to signal semaphore\");\n      return DXGI_ERROR_INVALID_CALL;\n    }\n\n    D3DKMT_RELEASEKEYEDMUTEX release = { };\n    release.hKeyedMutex = m_kmtLocal;\n    release.Key = key;\n    release.FenceValue = m_fenceValue + 1;\n\n    if (D3DKMTReleaseKeyedMutex(&release)) {\n      Logger::warn(\"D3D11DXGIKeyedMutex::ReleaseSync: Failed to release mutex.\");\n      return DXGI_ERROR_INVALID_CALL;\n    }\n\n    m_owned.store(false, std::memory_order_release);\n    return S_OK;\n  }\n\n\n  DxvkImage::DxvkImage(\n          DxvkDevice*           device,\n    const DxvkImageCreateInfo&  createInfo,\n          DxvkMemoryAllocator&  allocator,\n          VkMemoryPropertyFlags memFlags)\n  : DxvkPagedResource(allocator),\n    m_vkd           (device->vkd()),\n    m_properties    (memFlags),\n    m_shaderStages  (util::shaderStages(createInfo.stages)),\n    m_info          (createInfo) {\n    m_allocator->registerResource(this);\n\n    copyFormatList(createInfo.viewFormatCount, createInfo.viewFormats);\n    m_unifiedLayoutAvailable = device->features().khrUnifiedImageLayouts.unifiedImageLayouts;\n\n    // Assign debug name to image\n    if (device->debugFlags().test(DxvkDebugFlag::Capture)) {\n      m_debugName = createDebugName(createInfo.debugName);\n      m_info.debugName = m_debugName.c_str();\n    } else {\n      m_info.debugName = nullptr;\n    }\n\n    // Always enable depth-stencil attachment usage for depth-stencil\n    // formats since some internal operations rely on it. Read-only\n    // versions of these make little sense to begin with.\n    if (lookupFormatInfo(createInfo.format)->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n      m_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n    // Check whether the unified layout path can be used once\n    // we know all image properties\n    m_unifiedLayoutEnabled = canUseUnifiedLayout(*device);\n\n    if (m_unifiedLayoutEnabled)\n      m_info.layout = VK_IMAGE_LAYOUT_GENERAL;\n\n    // Determine whether the image is shareable before creating the resource\n    VkImageCreateInfo imageInfo = getImageCreateInfo(DxvkImageUsageInfo());\n    m_shared = canShareImage(device, imageInfo, m_info.sharing);\n\n    m_globalLayout = (m_info.sharing.mode != DxvkSharedHandleMode::Import)\n      ? m_info.initialLayout : m_info.layout;\n\n    assignStorage(allocateStorage());\n  }\n\n\n  DxvkImage::DxvkImage(\n          DxvkDevice*           device,\n    const DxvkImageCreateInfo&  createInfo,\n          VkImage               imageHandle,\n          DxvkMemoryAllocator&  allocator,\n          VkMemoryPropertyFlags memFlags)\n  : DxvkPagedResource(allocator),\n    m_vkd           (device->vkd()),\n    m_properties    (memFlags),\n    m_shaderStages  (util::shaderStages(createInfo.stages)),\n    m_info          (createInfo),\n    m_stableAddress (true),\n    m_globalLayout  (createInfo.initialLayout) {\n    m_allocator->registerResource(this);\n\n    copyFormatList(createInfo.viewFormatCount, createInfo.viewFormats);\n\n    // Create backing storage for existing image resource\n    DxvkAllocationInfo allocationInfo = { };\n    allocationInfo.resourceCookie = cookie();\n\n    VkImageCreateInfo imageInfo = getImageCreateInfo(DxvkImageUsageInfo());\n    assignStorage(m_allocator->importImageResource(imageInfo, allocationInfo, imageHandle));\n  }\n\n\n  DxvkImage::~DxvkImage() {\n    m_allocator->unregisterResource(this);\n  }\n\n\n  bool DxvkImage::canRelocate() const {\n    return !m_imageInfo.mapPtr && !m_shared && !m_stableAddress\n        && !(m_info.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT);\n  }\n\n\n  HANDLE DxvkImage::sharedHandle() const {\n    HANDLE handle = INVALID_HANDLE_VALUE;\n\n    if (!m_shared)\n      return INVALID_HANDLE_VALUE;\n\n#ifdef _WIN32\n    DxvkResourceMemoryInfo memoryInfo = m_storage->getMemoryInfo();\n\n    VkMemoryGetWin32HandleInfoKHR handleInfo = { VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR };\n    handleInfo.handleType = m_info.sharing.type;\n    handleInfo.memory = memoryInfo.memory;\n\n    if (m_vkd->vkGetMemoryWin32HandleKHR(m_vkd->device(), &handleInfo, &handle) != VK_SUCCESS)\n      Logger::warn(\"DxvkImage::DxvkImage: Failed to get shared handle for image\");\n#endif\n\n    return handle;\n  }\n\n\n  DxvkSparsePageTable* DxvkImage::getSparsePageTable() {\n    return m_storage->getSparsePageTable();\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkImage::relocateStorage(\n          DxvkAllocationModes         mode) {\n    if (!canRelocate())\n      return nullptr;\n\n    return allocateStorageWithUsage(DxvkImageUsageInfo(), mode);\n  }\n\n\n  uint64_t DxvkImage::getSubresourceAddressAt(uint32_t mip, uint32_t layer, VkOffset3D coord) const {\n    // For 2D and 3D images, use morton codes to linearize the address ranges\n    // of pixel blocks. This helps reduce false positives in common use cases\n    // where the application copies aligned power-of-two blocks around.\n    uint64_t base = getSubresourceStartAddress(mip, layer);\n\n    if (likely(m_info.type == VK_IMAGE_TYPE_2D))\n      return base + bit::interleave(coord.x, coord.y);\n\n    // For 1D we can simply use the pixel coordinate as-is\n    if (m_info.type == VK_IMAGE_TYPE_1D)\n      return base + coord.x;\n\n    // 3D is uncommon, but there are different use cases. Assume that if the\n    // format is block-compressed, the app will access one layer at a time.\n    if (formatInfo()->flags.test(DxvkFormatFlag::BlockCompressed))\n      return base + bit::interleave(coord.x, coord.y) + (uint64_t(coord.z) << 32u);\n\n    // Otherwise, it may want to copy actual 3D blocks around.\n    return base + bit::interleave(coord.x, coord.y, coord.z);\n  }\n\n\n  Rc<DxvkImageView> DxvkImage::createView(\n    const DxvkImageViewKey& info) {\n    std::unique_lock lock(m_viewMutex);\n\n    auto entry = m_views.emplace(std::piecewise_construct,\n      std::make_tuple(info), std::make_tuple(this, info));\n\n    return &entry.first->second;\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkImage::allocateStorage() {\n    return allocateStorageWithUsage(DxvkImageUsageInfo(), 0u);\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkImage::allocateStorageWithUsage(\n    const DxvkImageUsageInfo&         usageInfo,\n          DxvkAllocationModes         mode) {\n    const DxvkFormatInfo* formatInfo = lookupFormatInfo(m_info.format);\n    small_vector<VkFormat, 4> localViewFormats;\n\n    VkImageCreateInfo imageInfo = getImageCreateInfo(usageInfo);\n\n    // Set up view format list so that drivers can better enable\n    // compression. Skip for planar formats due to validation errors.\n    VkImageFormatListCreateInfo formatList = { VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO };\n\n    if (!(formatInfo->aspectMask & VK_IMAGE_ASPECT_PLANE_0_BIT)) {\n      if (usageInfo.viewFormatCount) {\n        for (uint32_t i = 0; i < m_viewFormats.size(); i++)\n          localViewFormats.push_back(m_viewFormats[i]);\n\n        for (uint32_t i = 0; i < usageInfo.viewFormatCount; i++) {\n          if (!isViewCompatible(usageInfo.viewFormats[i]))\n            localViewFormats.push_back(usageInfo.viewFormats[i]);\n        }\n\n        formatList.viewFormatCount = localViewFormats.size();\n        formatList.pViewFormats = localViewFormats.data();\n      } else {\n        formatList.viewFormatCount = m_viewFormats.size();\n        formatList.pViewFormats = m_viewFormats.data();\n      }\n    }\n\n    if ((m_info.flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) && formatList.viewFormatCount)\n      formatList.pNext = std::exchange(imageInfo.pNext, &formatList);\n\n    // Set up external memory parameters for shared images\n    VkExternalMemoryImageCreateInfo externalInfo = { VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO };\n\n    if (m_shared) {\n      externalInfo.pNext = std::exchange(imageInfo.pNext, &externalInfo);\n      externalInfo.handleTypes = m_info.sharing.type;\n    }\n\n    // Set up shared memory properties\n    void* sharedMemoryInfo = nullptr;\n\n    VkExportMemoryAllocateInfo sharedExport = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO };\n    VkImportMemoryWin32HandleInfoKHR sharedImportWin32 = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR };\n\n    if (m_shared && m_info.sharing.mode == DxvkSharedHandleMode::Export) {\n      sharedExport.pNext = std::exchange(sharedMemoryInfo, &sharedExport);\n      sharedExport.handleTypes = m_info.sharing.type;\n    }\n\n    if (m_shared && m_info.sharing.mode == DxvkSharedHandleMode::Import) {\n      sharedImportWin32.pNext = std::exchange(sharedMemoryInfo, &sharedImportWin32);\n      sharedImportWin32.handleType = m_info.sharing.type;\n      sharedImportWin32.handle = m_info.sharing.handle;\n    }\n\n    DxvkAllocationInfo allocationInfo = { };\n    allocationInfo.resourceCookie = cookie();\n    allocationInfo.properties = m_properties;\n    allocationInfo.mode = mode;\n    allocationInfo.handleType = m_info.sharing.type;\n\n    if (m_info.transient)\n      allocationInfo.mode.set(DxvkAllocationMode::NoDedicated);\n\n    return m_allocator->createImageResource(imageInfo,\n      allocationInfo, sharedMemoryInfo);\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkImage::assignStorage(\n          Rc<DxvkResourceAllocation>&& resource) {\n    return assignStorageWithUsage(std::move(resource), DxvkImageUsageInfo());\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkImage::assignStorageWithUsage(\n          Rc<DxvkResourceAllocation>&& resource,\n    const DxvkImageUsageInfo&         usageInfo) {\n    Rc<DxvkResourceAllocation> old = std::move(m_storage);\n\n    // Self-assignment is possible here if we\n    // just update the image properties\n    bool invalidateViews = false;\n    m_storage = std::move(resource);\n\n    if (m_storage != old) {\n      m_imageInfo = m_storage->getImageInfo();\n\n      if (unlikely(m_info.debugName))\n        updateDebugName();\n\n      invalidateViews = true;\n    }\n\n    if ((m_info.access | usageInfo.access) != m_info.access)\n      invalidateViews = true;\n\n    m_info.flags |= usageInfo.flags;\n    m_info.usage |= usageInfo.usage;\n    m_info.stages |= usageInfo.stages;\n    m_info.access |= usageInfo.access;\n\n    if (usageInfo.colorSpace != VK_COLOR_SPACE_MAX_ENUM_KHR)\n      m_info.colorSpace = usageInfo.colorSpace;\n\n    for (uint32_t i = 0; i < usageInfo.viewFormatCount; i++) {\n      if (!isViewCompatible(usageInfo.viewFormats[i]))\n        m_viewFormats.push_back(usageInfo.viewFormats[i]);\n    }\n\n    if (!m_viewFormats.empty()) {\n      m_info.viewFormatCount = m_viewFormats.size();\n      m_info.viewFormats = m_viewFormats.data();\n    }\n\n    // If feedback loops are enabled and the unified layout extension is\n    // not natively supported, we need to disable the unified layout path\n    if (m_info.usage & VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT)\n      m_unifiedLayoutEnabled = m_unifiedLayoutEnabled && m_unifiedLayoutAvailable;\n\n    if (usageInfo.layout != VK_IMAGE_LAYOUT_UNDEFINED && !m_unifiedLayoutEnabled) {\n      m_info.layout = usageInfo.layout;\n      invalidateViews = true;\n    }\n\n    m_stableAddress |= usageInfo.stableGpuAddress;\n\n    if (invalidateViews)\n      m_version += 1u;\n\n    if (!(m_properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {\n      auto common = m_properties & m_storage->getMemoryProperties();\n\n      updateResidencyStatus((common & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)\n        ? DxvkResourceResidency::Resident\n        : DxvkResourceResidency::Evicted);\n    }\n\n    return old;\n  }\n\n\n  bool DxvkImage::isInitialized(const VkImageSubresource& subresource) const {\n    VkImageLayout layout = queryLayout(subresource);\n\n    return layout != VK_IMAGE_LAYOUT_UNDEFINED\n        && layout != VK_IMAGE_LAYOUT_PREINITIALIZED;\n  }\n\n\n  bool DxvkImage::isInitialized(const VkImageSubresourceRange& subresources) const {\n    if (m_globalLayout != VK_IMAGE_LAYOUT_MAX_ENUM) {\n      return m_globalLayout != VK_IMAGE_LAYOUT_UNDEFINED\n          && m_globalLayout != VK_IMAGE_LAYOUT_PREINITIALIZED;\n    } else {\n      // Check each individual subresource layout\n      VkImageAspectFlags aspects = subresources.aspectMask;\n\n      while (aspects) {\n        VkImageSubresource subresource = { };\n        subresource.aspectMask = vk::getNextAspect(aspects);\n        subresource.mipLevel = subresources.baseMipLevel;\n        subresource.arrayLayer = subresources.baseArrayLayer;\n\n        for (uint32_t m = 0u; m < subresources.levelCount; m++) {\n          uint32_t index = computeSubresourceIndex(subresource);\n\n          for (uint32_t l = 0u; l < subresources.layerCount; l++) {\n            VkImageLayout layout = m_localLayouts[index + l];\n\n            if (layout == VK_IMAGE_LAYOUT_UNDEFINED\n             || layout == VK_IMAGE_LAYOUT_PREINITIALIZED)\n              return false;\n          }\n\n          subresource.mipLevel += 1u;\n        }\n      }\n\n      return true;\n    }\n  }\n\n\n  VkImageLayout DxvkImage::queryLayout(const VkImageSubresourceRange& subresources) const {\n    // Check whether the entire resource is in the same layout\n    if (m_globalLayout != VK_IMAGE_LAYOUT_MAX_ENUM)\n      return m_globalLayout;\n\n    VkImageSubresource subresource = { };\n    subresource.aspectMask = subresources.aspectMask & (subresources.aspectMask - 1u);\n    subresource.mipLevel = subresources.baseMipLevel;\n    subresource.arrayLayer = subresources.baseArrayLayer;\n\n    VkImageLayout baseLayout = queryLayout(subresource);\n\n    // If only one subresource is included in the range, return its layout\n    VkImageAspectFlags nonplanarAspects = VK_IMAGE_ASPECT_COLOR_BIT\n                                        | VK_IMAGE_ASPECT_DEPTH_BIT\n                                        | VK_IMAGE_ASPECT_STENCIL_BIT;\n\n    if (subresources.levelCount == 1u\n     && subresources.layerCount == 1u\n     && (subresources.aspectMask & nonplanarAspects))\n      return baseLayout;\n\n    // Otherwise, check whether all subresources have the same layout\n    VkImageAspectFlags aspects = subresources.aspectMask;\n\n    while (aspects) {\n      subresource.aspectMask = vk::getNextAspect(aspects);\n      subresource.mipLevel = subresources.baseMipLevel;\n\n      for (uint32_t m = 0u; m < subresources.levelCount; m++) {\n        uint32_t index = computeSubresourceIndex(subresource);\n\n        for (uint32_t l = 0u; l < subresources.layerCount; l++) {\n          if (m_localLayouts[index + l] != baseLayout)\n            return VK_IMAGE_LAYOUT_MAX_ENUM;\n        }\n\n        subresource.mipLevel += 1u;\n      }\n    }\n\n    return baseLayout;\n  }\n\n\n  void DxvkImage::trackLayout(const VkImageSubresourceRange& subresources, VkImageLayout layout) {\n    // Nothing to do if the layout doesn't change\n    if (m_globalLayout == layout)\n      return;\n\n    if (subresources == getAvailableSubresources()) {\n      // Entire resource is in the same layout\n      m_globalLayout = layout;\n    } else {\n      if (m_globalLayout != VK_IMAGE_LAYOUT_MAX_ENUM) {\n        // If previously the entire resource was in the same layout,\n        // we need to update all subresource entries to that layout\n        if (m_localLayouts.empty())\n          m_localLayouts.resize(computeSubresourceCount());\n\n        for (size_t i = 0u; i < m_localLayouts.size(); i++)\n          m_localLayouts[i] = m_globalLayout;\n\n        m_globalLayout = VK_IMAGE_LAYOUT_MAX_ENUM;\n      }\n\n      // Update entries contained in the subresource range\n      VkImageAspectFlags aspects = subresources.aspectMask;\n\n      while (aspects) {\n        VkImageSubresource subresource;\n        subresource.aspectMask = vk::getNextAspect(aspects);\n        subresource.mipLevel = subresources.baseMipLevel;\n        subresource.arrayLayer = subresources.baseArrayLayer;\n\n        for (uint32_t m = 0u; m < subresources.levelCount; m++) {\n          uint32_t index = computeSubresourceIndex(subresource);\n\n          for (uint32_t l = 0u; l < subresources.layerCount; l++)\n            m_localLayouts[index + l] = layout;\n\n          subresource.mipLevel += 1u;\n        }\n      }\n    }\n  }\n\n\n  void DxvkImage::setDebugName(const char* name) {\n    if (likely(!m_info.debugName))\n      return;\n\n    m_debugName = createDebugName(name);\n    m_info.debugName = m_debugName.c_str();\n\n    updateDebugName();\n  }\n\n\n  void DxvkImage::updateDebugName() {\n    if (m_storage->flags().test(DxvkAllocationFlag::OwnsImage)) {\n      VkDebugUtilsObjectNameInfoEXT nameInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT };\n      nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;\n      nameInfo.objectHandle = vk::getObjectHandle(m_imageInfo.image);\n      nameInfo.pObjectName = m_info.debugName;\n\n      m_vkd->vkSetDebugUtilsObjectNameEXT(m_vkd->device(), &nameInfo);\n    }\n  }\n\n\n  std::string DxvkImage::createDebugName(const char* name) const {\n    return str::format(vk::isValidDebugName(name) ? name : \"Image\", \" (\", cookie(), \")\");\n  }\n\n\n  VkImageCreateInfo DxvkImage::getImageCreateInfo(\n    const DxvkImageUsageInfo&         usageInfo) const {\n    VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };\n    info.flags = m_info.flags | usageInfo.flags;\n    info.imageType = m_info.type;\n    info.format = m_info.format;\n    info.extent = m_info.extent;\n    info.mipLevels = m_info.mipLevels;\n    info.arrayLayers = m_info.numLayers;\n    info.samples = m_info.sampleCount;\n    info.tiling = m_info.tiling;\n    info.usage = m_info.usage | usageInfo.usage;\n    info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n    info.initialLayout = m_info.initialLayout;\n\n    return info;\n  }\n\n\n  void DxvkImage::copyFormatList(uint32_t formatCount, const VkFormat* formats) {\n    m_viewFormats.resize(formatCount);\n\n    for (uint32_t i = 0; i < formatCount; i++)\n      m_viewFormats[i] = formats[i];\n\n    m_info.viewFormats = m_viewFormats.data();\n  }\n\n\n  bool DxvkImage::canShareImage(DxvkDevice* device, const VkImageCreateInfo& createInfo, const DxvkSharedHandleInfo& sharingInfo) const {\n    if (sharingInfo.mode == DxvkSharedHandleMode::None)\n      return false;\n\n    if (!device->features().khrExternalMemoryWin32) {\n      Logger::err(\"Failed to create shared resource: VK_KHR_EXTERNAL_MEMORY_WIN32 not supported\");\n      return false;\n    }\n\n    if (createInfo.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) {\n      Logger::err(\"Failed to create shared resource: Sharing sparse resources not supported\");\n      return false;\n    }\n\n    DxvkFormatQuery formatQuery = { };\n    formatQuery.format = createInfo.format;\n    formatQuery.type = createInfo.imageType;\n    formatQuery.tiling = createInfo.tiling;\n    formatQuery.usage = createInfo.usage;\n    formatQuery.flags = createInfo.flags;\n    formatQuery.handleType = sharingInfo.type;\n\n    auto limits = device->getFormatLimits(formatQuery);\n\n    if (!limits)\n      return false;\n\n    VkExternalMemoryFeatureFlagBits requiredFeature = sharingInfo.mode == DxvkSharedHandleMode::Export\n      ? VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT\n      : VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;\n\n    if (!(limits->externalFeatures & requiredFeature)) {\n      Logger::err(\"Failed to create shared resource: Image cannot be shared\");\n      return false;\n    }\n\n    return true;\n  }\n\n\n  bool DxvkImage::canUseUnifiedLayout(const DxvkDevice& device) const {\n    if (m_unifiedLayoutAvailable)\n      return true;\n\n    // Always respect the config option if the extension is not supported\n    if (!device.config().enableUnifiedImageLayout)\n      return false;\n\n    // Speshul case\n    if (m_info.usage & VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT)\n      return false;\n\n    // On RDNA1/2 we can enable the unified path for everything\n    // that doesn't involve feedback loops or MSAA.\n    if (device.properties().vk12.driverID == VK_DRIVER_ID_MESA_RADV\n     && device.properties().vk13.minSubgroupSize == 32u)\n      return m_info.sampleCount == VK_SAMPLE_COUNT_1_BIT;\n\n    return false;\n  }\n\n\n\n\n\n  DxvkImageView::DxvkImageView(\n          DxvkImage*                image,\n    const DxvkImageViewKey&         key)\n  : m_image   (image),\n    m_key     (key) {\n    // If the view does not define a layout, figure out a suitable\n    // layout based on image view usage and image properties. This\n    // will be good enough in most situations.\n    if (!m_key.layout) {\n      switch (m_key.usage & ~VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT) {\n        case VK_IMAGE_USAGE_SAMPLED_BIT:\n          m_key.layout = (m_image->formatInfo()->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n            ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL\n            : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n          break;\n\n        case VK_IMAGE_USAGE_STORAGE_BIT:\n          m_key.layout = VK_IMAGE_LAYOUT_GENERAL;\n          break;\n\n        case VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT:\n          m_key.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n          break;\n\n        case VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT:\n          m_key.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n          break;\n\n        default:\n          break;\n      }\n    }\n\n    updateProperties();\n  }\n\n\n  DxvkImageView::~DxvkImageView() {\n\n  }\n\n\n  const DxvkDescriptor* DxvkImageView::createView(VkImageViewType type) const {\n    constexpr VkImageUsageFlags ViewUsage =\n      VK_IMAGE_USAGE_SAMPLED_BIT |\n      VK_IMAGE_USAGE_STORAGE_BIT |\n      VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |\n      VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n    // Legalize view usage. We allow creating transfer-only view\n    // objects so that some internal APIs can be more consistent.\n    DxvkImageViewKey key = m_key;\n    key.viewType = type;\n    key.layout = getLayout();\n\n    if (!(key.usage & ViewUsage))\n      return nullptr;\n\n    // If the image has feedback loops enabled, forward the required\n    // usage flags to the view as well.\n    if ((m_image->info().usage & VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT)\n     && (key.usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))) {\n      key.usage |= VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT\n                |  VK_IMAGE_USAGE_SAMPLED_BIT;\n    }\n\n    // Only use one layer for non-arrayed view types\n    if (type == VK_IMAGE_VIEW_TYPE_1D || type == VK_IMAGE_VIEW_TYPE_2D)\n      key.layerCount = 1u;\n\n    switch (m_image->info().type) {\n      case VK_IMAGE_TYPE_1D: {\n        // Trivial, just validate that view types are compatible\n        if (type != VK_IMAGE_VIEW_TYPE_1D && type != VK_IMAGE_VIEW_TYPE_1D_ARRAY)\n          return nullptr;\n      } break;\n\n      case VK_IMAGE_TYPE_2D: {\n        if (type == VK_IMAGE_VIEW_TYPE_CUBE || type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) {\n          // Ensure that the image is compatible with cube maps\n          if (key.layerCount < 6 || !(m_image->info().flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT))\n            return nullptr;\n\n          // Adjust layer count to make sure it's a multiple of 6\n          key.layerCount = type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY\n            ? key.layerCount - key.layerCount % 6u : 6u;\n        } else if (type != VK_IMAGE_VIEW_TYPE_2D && type != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {\n          return nullptr;\n        }\n      } break;\n\n      case VK_IMAGE_TYPE_3D: {\n        if (type == VK_IMAGE_VIEW_TYPE_2D || type == VK_IMAGE_VIEW_TYPE_2D_ARRAY) {\n          // Ensure that the image is actually compatible with 2D views\n          if (!(m_image->info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT))\n            return nullptr;\n\n          // In case the view's native type is 3D, we can only create 2D compat\n          // views if there is only one mip and with the full set of array layers.\n          if (m_key.viewType == VK_IMAGE_VIEW_TYPE_3D) {\n            if (m_key.mipCount != 1u)\n              return nullptr;\n\n            key.layerIndex = 0u;\n            key.layerCount = type == VK_IMAGE_VIEW_TYPE_2D_ARRAY\n              ? m_image->mipLevelExtent(key.mipIndex).depth : 1u;\n          }\n        } else if (type != VK_IMAGE_VIEW_TYPE_3D) {\n          return nullptr;\n        }\n      } break;\n\n      default:\n        return nullptr;\n    }\n\n    // We need to expose RT and UAV swizzles to the backend,\n    // but cannot legally pass them down to Vulkan\n    if ((key.usage & ~VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT) != VK_IMAGE_USAGE_SAMPLED_BIT)\n      key.packedSwizzle = 0u;\n\n    return m_image->m_storage->createImageView(key);\n  }\n\n\n  void DxvkImageView::updateViews() {\n    // Latch updated image properties\n    updateProperties();\n\n    // Update all views that are not currently null\n    for (uint32_t i = 0; i < m_views.size(); i++) {\n      if (m_views[i])\n        m_views[i] = createView(VkImageViewType(i));\n    }\n\n    m_version = m_image->m_version;\n  }\n\n\n  void DxvkImageView::updateProperties() {\n    m_properties.samples = m_image->info().sampleCount;\n    m_properties.access = m_image->info().access;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_image.h",
    "content": "#pragma once\n\n#include \"dxvk_descriptor_pool.h\"\n#include \"dxvk_fence.h\"\n#include \"dxvk_format.h\"\n#include \"dxvk_memory.h\"\n#include \"dxvk_sparse.h\"\n#include \"dxvk_util.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Image create info\n   * \n   * The properties of an image that are\n   * passed to \\ref DxvkDevice::createImage\n   */\n  struct DxvkImageCreateInfo {\n    /// Image dimension\n    VkImageType type = VK_IMAGE_TYPE_2D;\n    \n    /// Pixel format\n    VkFormat format = VK_FORMAT_UNDEFINED;\n    \n    /// Flags\n    VkImageCreateFlags flags = 0u;\n    \n    /// Sample count for MSAA\n    VkSampleCountFlagBits sampleCount = VkSampleCountFlagBits(0u);\n    \n    /// Image size, in texels\n    VkExtent3D extent = { };\n    \n    /// Number of image array layers\n    uint32_t numLayers = 0u;\n    \n    /// Number of mip levels\n    uint32_t mipLevels = 0u;\n    \n    /// Image usage flags\n    VkImageUsageFlags usage = 0u;\n    \n    /// Pipeline stages that can access\n    /// the contents of the image\n    VkPipelineStageFlags stages = 0u;\n    \n    /// Allowed access pattern\n    VkAccessFlags access = 0u;\n    \n    /// Image tiling mode\n    VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;\n    \n    /// Common image layout\n    VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n    // Initial image layout\n    VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n    // Color space to interpret image data with. This\n    // is only meaningful for swap chain back buffers.\n    VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_MAX_ENUM_KHR;\n\n    // Image is used by multiple contexts so it needs\n    // to be in its default layout after each submission\n    VkBool32 shared = VK_FALSE;\n\n    // Image is likely to have a short lifetime\n    VkBool32 transient = VK_FALSE;\n\n    // Image view formats that can\n    // be used with this image\n    uint32_t viewFormatCount = 0;\n    const VkFormat* viewFormats = nullptr;\n\n    // Shared handle info\n    DxvkSharedHandleInfo sharing = { };\n\n    // Debug name\n    const char* debugName = nullptr;\n  };\n  \n  \n  /**\n   * \\brief Extra image usage info\n   *\n   * Useful when recreating an image with different usage flags.\n   */\n  struct DxvkImageUsageInfo {\n    // New image flags to add.\n    VkImageCreateFlags flags = 0u;\n    // Usage flags to add to the image\n    VkImageUsageFlags usage = 0u;\n    // Stage flags to add to the image\n    VkPipelineStageFlags stages = 0u;\n    // Access flags to add to the image\n    VkAccessFlags access = 0u;\n    // New image layout. If undefined, the\n    // default layout will not be changed.\n    VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;\n    // Color space to interpret the image in\n    VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_MAX_ENUM_KHR;\n    // Number of new view formats to add\n    uint32_t viewFormatCount = 0u;\n    // View formats to add to the compatibility list\n    const VkFormat* viewFormats = nullptr;\n    // Requtes the image to not be relocated in the future\n    VkBool32 stableGpuAddress = VK_FALSE;\n  };\n\n\n  /**\n   * \\brief Image properties stored in the view\n   *\n   * Used to reduce some pointer chasing.\n   */\n  struct DxvkImageViewImageProperties {\n    VkImageLayout         layout  = VK_IMAGE_LAYOUT_UNDEFINED;\n    VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;\n    VkAccessFlags         access  = 0u;\n  };\n\n\n  /**\n   * \\brief Virtual image view\n   *\n   * Stores views for a number of different view types\n   * that the defined view is compatible with.\n   */\n  class DxvkImageView {\n    constexpr static uint32_t ViewCount = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY + 1;\n  public:\n\n    DxvkImageView(\n            DxvkImage*                image,\n      const DxvkImageViewKey&         key);\n\n    ~DxvkImageView();\n\n    void incRef();\n    void decRef();\n\n    /**\n     * \\brief Image view descriptor for the default type\n     *\n     * The default view type is guaranteed to be\n     * supported by the image view, and should be\n     * preferred over picking a different type.\n     * \\returns Image view handle\n     */\n    const DxvkDescriptor* getDescriptor() {\n      return getDescriptor(m_key.viewType);\n    }\n\n    /**\n     * \\brief Image view handle for a given view type\n     *\n     * If the view does not support the requested image\n     * view type, \\c VK_NULL_HANDLE will be returned.\n     * \\param [in] viewType The requested view type\n     * \\returns The image view handle\n     */\n    const DxvkDescriptor* getDescriptor(VkImageViewType viewType);\n\n    /**\n     * \\brief Image view handle for the default type\n     *\n     * The default view type is guaranteed to be\n     * supported by the image view, and should be\n     * preferred over picking a different type.\n     * \\returns Image view handle\n     */\n    VkImageView handle() {\n      return handle(m_key.viewType);\n    }\n\n    /**\n     * \\brief Image view handle for a given view type\n     *\n     * If the view does not support the requested image\n     * view type, \\c VK_NULL_HANDLE will be returned.\n     * \\param [in] viewType The requested view type\n     * \\returns The image view handle\n     */\n    VkImageView handle(VkImageViewType viewType) {\n      auto descriptor = getDescriptor(viewType);\n      return likely(descriptor) ? descriptor->legacy.image.imageView : VK_NULL_HANDLE;\n    }\n\n    /**\n     * \\brief Image view type\n     *\n     * Convenience method to query the view type\n     * in order to check for resource compatibility.\n     * \\returns Image view type\n     */\n    VkImageViewType type() const {\n      return m_key.viewType;\n    }\n\n    /**\n     * \\brief Image view properties\n     * \\returns Image view properties\n     */\n    DxvkImageViewKey info() const {\n      return m_key;\n    }\n\n    /**\n     * \\brief Image object\n     * \\returns Image object\n     */\n    DxvkImage* image() const {\n      return m_image;\n    }\n\n    /**\n     * \\brief View format info\n     * \\returns View format info\n     */\n    const DxvkFormatInfo* formatInfo() const {\n      return lookupFormatInfo(m_key.format);\n    }\n\n    /**\n     * \\brief Mip level size\n     *\n     * Computes the mip level size relative to\n     * the first mip level that the view includes.\n     * \\param [in] level Mip level\n     * \\returns Size of that level\n     */\n    VkExtent3D mipLevelExtent(uint32_t level) const;\n\n    /**\n     * \\brief View subresource range\n     *\n     * Returns the subresource range from the image\n     * description. For 2D views of 3D images, this\n     * will return the viewed 3D slices.\n     * \\returns View subresource range\n     */\n    VkImageSubresourceRange subresources() const {\n      VkImageSubresourceRange result;\n      result.aspectMask     = m_key.aspects;\n      result.baseMipLevel   = m_key.mipIndex;\n      result.levelCount     = m_key.mipCount;\n      result.baseArrayLayer = m_key.layerIndex;\n      result.layerCount     = m_key.layerCount;\n      return result;\n    }\n\n    /**\n     * \\brief Actual image subresource range\n     *\n     * Handles 3D images correctly in that it only\n     * returns one single array layer. Use this for\n     * barriers.\n     * \\returns Image subresource range\n     */\n    VkImageSubresourceRange imageSubresources() const;\n\n    /**\n     * \\brief Checks whether this view matches another\n     *\n     * \\param [in] view The other view to check\n     * \\returns \\c true if the two views have the same subresources\n     */\n    bool matchesView(const Rc<DxvkImageView>& view) const {\n      if (this == view.ptr())\n        return true;\n\n      return this->image()         == view->image()\n          && this->subresources()  == view->subresources()\n          && this->info().viewType == view->info().viewType\n          && this->info().format   == view->info().format;\n    }\n\n    /**\n     * \\brief Checks whether this view overlaps with another one\n     *\n     * Two views overlap if they were created for the same\n     * image and have at least one subresource in common.\n     * \\param [in] view The other view to check\n     * \\returns \\c true if the two views overlap\n     */\n    bool checkSubresourceOverlap(const Rc<DxvkImageView>& view) const {\n      if (likely(m_image != view->m_image))\n        return false;\n\n      return vk::checkSubresourceRangeOverlap(\n        this->imageSubresources(),\n        view->imageSubresources());\n    }\n\n    /**\n     * \\brief Queries the view layout\n     *\n     * If no layout was explicitly specified for the view, this\n     * will return a suitable layout for the given usage.\n     * \\returns Image view layout\n     */\n    VkImageLayout getLayout() const;\n\n    /**\n     * \\brief Checks whether the image is multisampled\n     * \\returns \\c true if the image is multisampled\n     */\n    bool isMultisampled() const {\n      return m_properties.samples > VK_SAMPLE_COUNT_1_BIT;\n    }\n\n    /**\n     * \\brief Checks whether the image has graphics stores\n     *\n     * This may include attachment access for render passes.\n     * \\returns \\c true if the image has graphics pipeline stores\n     */\n    bool hasGfxStores() const;\n\n  private:\n\n    DxvkImage*              m_image     = nullptr;\n    DxvkImageViewKey        m_key       = { };\n\n    uint32_t                m_version   = 0u;\n\n    DxvkImageViewImageProperties m_properties = { };\n\n    std::array<const DxvkDescriptor*, ViewCount> m_views = { };\n\n    const DxvkDescriptor* createView(VkImageViewType type) const;\n\n    void updateViews();\n\n    void updateProperties();\n\n  };\n\n  class DxvkKeyedMutex : public RcObject {\n  public:\n\n    /**\n     * \\brief Creates a new shared keyed mutex\n     */\n    DxvkKeyedMutex(\n      const Rc<DxvkDevice>& device,\n            uint64_t        initialValue,\n            bool            ntShared);\n\n    /**\n     * \\brief Opens a shared keyed mutex from its D3DKMT handles\n     */\n    DxvkKeyedMutex(\n      const Rc<DxvkDevice>& device,\n            Rc<DxvkFence>&& fence,\n            D3DKMT_HANDLE   kmtLocal,\n            D3DKMT_HANDLE   kmtGlobal);\n    \n    ~DxvkKeyedMutex();\n    \n    /**\n     * \\brief D3DKMT keyed mutex local handle\n     * \\returns The keyed mutex D3DKMT local handle\n     * \\returns \\c 0 if fence is not shared\n     */\n    D3DKMT_HANDLE kmtLocal() const {\n      return m_kmtLocal;\n    }\n\n    /**\n     * \\brief D3DKMT keyed mutex global handle\n     * \\returns The keyed mutex D3DKMT global handle\n     * \\returns \\c 0 if keyed mutex is not shared or shared with NT handle\n     */\n    D3DKMT_HANDLE kmtGlobal() const {\n      return m_kmtGlobal;\n    }\n\n    /**\n     * \\brief Retrieves current sync object\n     * \\returns sync object guarding this image\n     */\n    Rc<DxvkFence> getSyncObject() const {\n      return m_fence;\n    }\n\n    /**\n     * \\brief Try to acquire the keyed mutex\n     * \\returns DXGI_ERROR_INVALID_CALL if owned already or on error\n     * \\returns WAIT_TIMEOUT on timeout\n     * \\returns S_OK on success\n     */\n    HRESULT AcquireSync(UINT64 key, DWORD milliseconds);\n\n    /**\n     * \\brief Release the keyed mutex\n     * \\returns DXGI_ERROR_INVALID_CALL if not owned or on error\n     * \\returns S_OK on success\n     */\n    HRESULT ReleaseSync(UINT64 key);\n\n  private:\n\n    Rc<vk::DeviceFn>            m_vkd;\n    Rc<DxvkFence>               m_fence;\n    D3DKMT_HANDLE               m_kmtLocal  = 0;\n    D3DKMT_HANDLE               m_kmtGlobal = 0;\n\n    uint64_t                    m_fenceValue = 0;\n    std::atomic<bool>           m_owned = { false };\n\n  };\n\n  /**\n   * \\brief Virtual image resource\n   * \n   * An image resource consisting of various subresources.\n   * Can be accessed by the host if allocated on a suitable\n   * memory type and if created with the linear tiling option.\n   */\n  class DxvkImage : public DxvkPagedResource {\n    friend DxvkImageView;\n    friend class DxvkContext;\n  public:\n    \n    DxvkImage(\n            DxvkDevice*           device,\n      const DxvkImageCreateInfo&  createInfo,\n            DxvkMemoryAllocator&  allocator,\n            VkMemoryPropertyFlags memFlags);\n    \n    /**\n     * \\brief Creates image object from existing image\n     * \n     * This can be used to create an image object for\n     * an implementation-managed image. Make sure to\n     * provide the correct image properties, since\n     * otherwise some image operations may fail.\n     */\n    DxvkImage(\n            DxvkDevice*           device,\n      const DxvkImageCreateInfo&  createInfo,\n            VkImage               imageHandle,\n            DxvkMemoryAllocator&  allocator,\n            VkMemoryPropertyFlags memFlags);\n    \n    /**\n     * \\brief Destroys image\n     * \n     * If this is an implementation-managed image,\n     * this will not destroy the Vulkan image.\n     */\n    ~DxvkImage();\n    \n    /**\n     * \\brief Image handle\n     * \n     * Internal use only.\n     * \\returns Image handle\n     */\n    VkImage handle() const {\n      return m_imageInfo.image;\n    }\n\n    /**\n     * \\brief Image properties\n     * \n     * The image create info structure.\n     * \\returns Image properties\n     */\n    const DxvkImageCreateInfo& info() const {\n      return m_info;\n    }\n    \n    /**\n     * \\brief Memory type flags\n     * \n     * Use this to determine whether a\n     * buffer is mapped to host memory.\n     * \\returns Vulkan memory flags\n     */\n    VkMemoryPropertyFlags memFlags() const {\n      return m_properties;\n    }\n    \n    /**\n     * \\brief Queries shader stages that can access this image\n     *\n     * Derived from the pipeline stage mask passed in during creation.\n     * \\returns Shader stages that may access this image\n     */\n    VkShaderStageFlags getShaderStages() const {\n      return m_shaderStages;\n    }\n\n    /**\n     * \\brief Map pointer\n     * \n     * If the image has been created on a host-visible\n     * memory type, its memory is mapped and can be\n     * accessed by the host.\n     * \\param [in] offset Byte offset into mapped region\n     * \\returns Pointer to mapped memory region\n     */\n    void* mapPtr(VkDeviceSize offset) const {\n      return reinterpret_cast<char*>(m_imageInfo.mapPtr) + offset;\n    }\n    \n    /**\n     * \\brief Image format info\n     * \\returns Image format info\n     */\n    const DxvkFormatInfo* formatInfo() const {\n      return lookupFormatInfo(m_info.format);\n    }\n    \n    /**\n     * \\brief Size of a mipmap level\n     * \n     * \\param [in] level Mip level\n     * \\returns Size of that level\n     */\n    VkExtent3D mipLevelExtent(uint32_t level) const {\n      return util::computeMipLevelExtent(m_info.extent, level);\n    }\n    \n    /**\n     * \\brief Size of a mipmap level\n     * \n     * \\param [in] level Mip level\n     * \\returns Size of that level\n     */\n    VkExtent3D mipLevelExtent(uint32_t level, VkImageAspectFlags aspect) const {\n      return util::computeMipLevelExtent(m_info.extent, level, m_info.format, aspect);\n    }\n    \n    /**\n     * \\brief Picks a compatible layout\n     * \n     * Under some circumstances, we have to return\n     * a different layout than the one requested.\n     * \\param [in] layout The image layout\n     * \\returns A compatible image layout\n     */\n    VkImageLayout pickLayout(VkImageLayout layout) const {\n      if (m_unifiedLayoutEnabled)\n          return VK_IMAGE_LAYOUT_GENERAL;\n\n      if (unlikely(m_info.layout == VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT)) {\n        if (layout != VK_IMAGE_LAYOUT_GENERAL\n         && layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n         && layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)\n          return VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT;\n      }\n\n      return likely(m_info.tiling == VK_IMAGE_TILING_OPTIMAL)\n        ? layout : VK_IMAGE_LAYOUT_GENERAL;\n    }\n\n    /**\n     * \\brief Changes image layout\n     * \\param [in] layout New layout\n     */\n    void setLayout(VkImageLayout layout) {\n      if (!m_unifiedLayoutEnabled)\n        m_info.layout = layout;\n    }\n\n    /**\n     * \\brief Checks whether layout transitions are disabled for this image\n     * \\returns \\c true if the image will always remain in `GENERAL`.\n     */\n    bool hasUnifiedLayout() const {\n      return m_unifiedLayoutEnabled;\n    }\n\n    /**\n     * \\brief Checks whether a subresource is entirely covered\n     * \n     * This can be used to determine whether an image can or\n     * should be initialized with \\c VK_IMAGE_LAYOUT_UNDEFINED.\n     * \\param [in] subresource The image subresource\n     * \\param [in] extent Image extent to check\n     */\n    bool isFullSubresource(\n      const VkImageSubresourceLayers& subresource,\n            VkExtent3D                extent) const {\n      return subresource.aspectMask == this->formatInfo()->aspectMask\n          && extent == this->mipLevelExtent(subresource.mipLevel);\n    }\n\n    /**\n     * \\brief Checks view format compatibility\n     * \n     * If this returns true, a view with the given\n     * format can be safely created for this image.\n     * \\param [in] format The format to check\n     * \\returns \\c true if the format is vompatible\n     */\n    bool isViewCompatible(VkFormat format) const {\n      bool result = m_info.format == format;\n      for (uint32_t i = 0; i < m_viewFormats.size() && !result; i++)\n        result |= m_viewFormats[i] == format;\n      return result;\n    }\n\n    /**\n     * \\brief Memory object\n     * \\returns Backing memory\n     */\n    DxvkResourceMemoryInfo getMemoryInfo() const {\n      return m_storage->getMemoryInfo();\n    }\n\n    /**\n     * \\brief Get full subresource range of the image\n     * \n     * \\returns Resource range of the whole image\n     */\n    VkImageSubresourceRange getAvailableSubresources() const {\n      VkImageSubresourceRange result;\n      result.aspectMask     = formatInfo()->aspectMask;\n      result.baseMipLevel   = 0;\n      result.levelCount     = info().mipLevels;\n      result.baseArrayLayer = 0;\n      result.layerCount     = info().numLayers;\n      return result;\n    }\n\n    /**\n     * \\brief Checks whether the image can be relocated\n     *\n     * Images that are shared, imported from a different API\n     * or mapped to host address space cannot be relocated.\n     * \\returns \\c true if the image can be relocated\n     */\n    bool canRelocate() const;\n\n    /**\n     * \\brief Create a new shared handle to dedicated memory backing the image\n     * \\returns The shared handle with the type given by DxvkSharedHandleInfo::type\n     */\n    HANDLE sharedHandle() const;\n\n    /**\n     * \\brief Retrives sparse page table\n     * \\returns Page table\n     */\n    DxvkSparsePageTable* getSparsePageTable();\n\n    /**\n     * \\brief Allocates new backing storage with constraints\n     *\n     * \\param [in] mode Allocation mode flags\n     * \\returns Operation status and allocation\n     */\n    Rc<DxvkResourceAllocation> relocateStorage(\n            DxvkAllocationModes         mode);\n\n    /**\n     * \\brief Creates image resource\n     *\n     * The returned image can be used as backing storage.\n     * \\returns New underlying image resource\n     */\n    Rc<DxvkResourceAllocation> allocateStorage();\n\n    /**\n     * \\brief Creates image resource with extra usage\n     *\n     * Creates new backing storage with additional usage flags\n     * enabled. Useful to expand on usage flags after creation.\n     * \\param [in] usage Usage flags to add\n     * \\param [in] mode Allocation constraints\n     * \\returns New underlying image resource\n     */\n    Rc<DxvkResourceAllocation> allocateStorageWithUsage(\n      const DxvkImageUsageInfo&         usage,\n            DxvkAllocationModes         mode);\n\n    /**\n     * \\brief Assigns backing storage to the image\n     *\n     * Implicitly invalidates all image views.\n     * \\param [in] resource New backing storage\n     * \\returns Previous backing storage\n     */\n    Rc<DxvkResourceAllocation> assignStorage(\n            Rc<DxvkResourceAllocation>&& resource);\n\n    /**\n     * \\brief Assigns backing storage to the image with extra usage\n     *\n     * Implicitly invalidates all image views.\n     * \\param [in] resource New backing storage\n     * \\param [in] usageInfo Added usage info\n     * \\returns Previous backing storage\n     */\n    Rc<DxvkResourceAllocation> assignStorageWithUsage(\n            Rc<DxvkResourceAllocation>&& resource,\n      const DxvkImageUsageInfo&         usageInfo);\n\n    /**\n     * \\brief Retrieves current backing storage\n     * \\returns Backing storage for this image\n     */\n    Rc<DxvkResourceAllocation> storage() const {\n      return m_storage;\n    }\n\n    /**\n     * \\brief Retrieves current keyed mutex\n     * \\returns Keyed mutex guarding this image\n     */\n    Rc<DxvkKeyedMutex> getKeyedMutex() const {\n      return m_mutex;\n    }\n\n    /**\n     * \\brief Sets the image keyed mutex and sync object\n     */\n    void setKeyedMutex(Rc<DxvkKeyedMutex>&& mutex) {\n      m_mutex = mutex;\n    }\n\n    /**\n     * \\brief Retrieves resource ID for barrier tracking\n     * \\returns Unique resource ID\n     */\n    bit::uint48_t getResourceId() const {\n      constexpr static size_t Align = alignof(DxvkResourceAllocation);\n      return bit::uint48_t(reinterpret_cast<uintptr_t>(m_storage.ptr()) / (Align & -Align));\n    }\n\n    /**\n     * \\brief Computes virtual offset of a subresource\n     *\n     * Used for hazard tracking. Ignores the aspect mask and\n     * only takes the mip level and array layer into account.\n     * \\param [in] mip Mip level index\n     * \\param [in] layer Array layer index\n     */\n    uint64_t getSubresourceStartAddress(uint32_t mip, uint32_t layer) const {\n      // Put layers within the same mip into a contiguous range. This works well\n      // for not only transfer operations but also most image view use cases.\n      return uint64_t((m_info.numLayers * mip) + layer) << 48u;\n    }\n\n    /**\n     * \\brief Computes virtual offset of the end of a subresource\n     *\n     * \\param [in] mip Mip level index\n     * \\param [in] layer Array layer index\n     */\n    uint64_t getSubresourceEndAddress(uint32_t mip, uint32_t layer) const {\n      return getSubresourceStartAddress(mip, layer) + (1ull << 48u) - 1ull;\n    }\n\n    /**\n     * \\brief Computes virtual offset of a specific image region\n     *\n     * Used for more granular hazard tracking. This interleaves coordinate\n     * bits in order to compute a unique address for each pixel.\n     * \\param [in] mip Mip level index\n     * \\param [in] layer Array layer index\n     * \\param [in] coord Pixel coordinate within the subresource\n     */\n    uint64_t getSubresourceAddressAt(uint32_t mip, uint32_t layer, VkOffset3D coord) const;\n\n    /**\n     * \\brief Creates or retrieves an image view\n     *\n     * \\param [in] info Image view create info\n     * \\returns Newly created image view\n     */\n    Rc<DxvkImageView> createView(\n      const DxvkImageViewKey& info);\n\n    /**\n     * \\brief Checks whether an image subresource is initialized\n     *\n     * \\param [in] subresource The subresource to query\n     * \\returns \\c true if the given subresource is initialized\n     */\n    bool isInitialized(\n      const VkImageSubresource& subresource) const;\n\n    /**\n     * \\brief Checks whether subresources are initialized\n     *\n     * \\param [in] subresources Subresource range\n     * \\returns \\c true if the subresources are initialized\n     */\n    bool isInitialized(\n      const VkImageSubresourceRange& subresources) const;\n\n    /**\n     * \\brief Queries current layout of an image subresource\n     *\n     * \\param [in] subresource The subresource. Note that the aspect\n     *    mask must not have multiple planes set for planar images.\n     * \\returns Current layout of the given image subresource\n     */\n    VkImageLayout queryLayout(const VkImageSubresource& subresource) const {\n      if (m_globalLayout != VK_IMAGE_LAYOUT_MAX_ENUM)\n        return m_globalLayout;\n\n      uint32_t index = computeSubresourceIndex(subresource);\n      return m_localLayouts[index];\n    }\n\n    /**\n     * \\biref Queries current layout of a subresource range\n     *\n     * If layouts diverge, this returns \\c VK_IMAGE_LAYOUT_MAX_ENUM,\n     * and individual subresources must be queried manually.\n     * \\param [in] subresources Subresource range to query\n     * \\returns Current layout of the subresource range\n     */\n    VkImageLayout queryLayout(const VkImageSubresourceRange& subresources) const;\n\n    /**\n     * \\brief Updates per-subresource layout tracking\n     *\n     * \\param [in] subresources Subresource range to transition\n     * \\param [in] layout New layout for the subresource range\n     */\n    void trackLayout(const VkImageSubresourceRange& subresources, VkImageLayout layout);\n\n    /**\n     * \\brief Sets debug name for the backing resource\n     * \\param [in] name New debug name\n     */\n    void setDebugName(const char* name);\n\n    /**\n     * \\brief Retrieves debug name\n     * \\returns Debug name\n     */\n    const char* getDebugName() const {\n      return m_debugName.c_str();\n    }\n\n  private:\n\n    Rc<vk::DeviceFn>            m_vkd;\n    VkMemoryPropertyFlags       m_properties  = 0u;\n    VkShaderStageFlags          m_shaderStages = 0u;\n\n    DxvkImageCreateInfo         m_info        = { };\n\n    uint32_t                    m_version     = 0u;\n    bool                        m_shared      = false;\n    bool                        m_stableAddress = false;\n\n    bool                        m_unifiedLayoutEnabled = false;\n    bool                        m_unifiedLayoutAvailable = false;\n\n    Rc<DxvkKeyedMutex>          m_mutex       = nullptr;\n\n    DxvkResourceImageInfo       m_imageInfo   = { };\n\n    Rc<DxvkResourceAllocation>  m_storage     = nullptr;\n\n    small_vector<VkFormat, 4>   m_viewFormats;\n\n    VkImageLayout               m_globalLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    small_vector<VkImageLayout, 16> m_localLayouts;\n\n    dxvk::mutex                 m_viewMutex;\n    std::unordered_map<DxvkImageViewKey,\n      DxvkImageView, DxvkHash, DxvkEq> m_views;\n\n    std::string                 m_debugName;\n\n    void updateDebugName();\n\n    std::string createDebugName(const char* name) const;\n\n    VkImageCreateInfo getImageCreateInfo(\n      const DxvkImageUsageInfo&         usageInfo) const;\n\n    void copyFormatList(\n            uint32_t              formatCount,\n      const VkFormat*             formats);\n\n    bool canShareImage(\n            DxvkDevice*           device,\n      const VkImageCreateInfo&    createInfo,\n      const DxvkSharedHandleInfo& sharingInfo) const;\n\n    bool canUseUnifiedLayout(const DxvkDevice& device) const;\n\n    uint32_t computeSubresourceIndex(const VkImageSubresource& subresource) const {\n      return subresource.arrayLayer\n        + m_info.numLayers * (subresource.mipLevel\n        + m_info.mipLevels * vk::getPlaneIndex(subresource.aspectMask));\n    }\n\n    uint32_t computeSubresourceCount() const {\n      return m_info.numLayers * m_info.mipLevels * vk::getPlaneCount(formatInfo()->aspectMask);\n    }\n\n  };\n\n\n  /**\n   * \\brief Image relocation info\n   */\n  struct DxvkRelocateImageInfo {\n    /// Buffer object. Stores metadata.\n    Rc<DxvkImage> image;\n    /// Backing storage to copy to\n    Rc<DxvkResourceAllocation> storage;\n    /// Additional image usage\n    DxvkImageUsageInfo usageInfo;\n  };\n\n\n\n\n  force_inline void DxvkImageView::incRef() {\n    m_image->incRef();\n  }\n\n\n  force_inline void DxvkImageView::decRef() {\n    m_image->decRef();\n  }\n\n\n  inline VkImageSubresourceRange DxvkImageView::imageSubresources() const {\n    VkImageSubresourceRange result = { };\n    result.aspectMask     = m_key.aspects;\n    result.baseMipLevel   = m_key.mipIndex;\n    result.levelCount     = m_key.mipCount;\n\n    if (likely(m_image->info().type != VK_IMAGE_TYPE_3D)) {\n      result.baseArrayLayer = m_key.layerIndex;\n      result.layerCount     = m_key.layerCount;\n    } else {\n      result.baseArrayLayer = 0;\n      result.layerCount     = 1;\n    }\n\n    return result;\n  }\n\n\n  inline VkExtent3D DxvkImageView::mipLevelExtent(uint32_t level) const {\n    return m_image->mipLevelExtent(level + m_key.mipIndex, m_key.aspects);\n  }\n\n\n  inline const DxvkDescriptor* DxvkImageView::getDescriptor(VkImageViewType viewType) {\n    viewType = viewType != VK_IMAGE_VIEW_TYPE_MAX_ENUM ? viewType : m_key.viewType;\n\n    if (unlikely(m_version < m_image->m_version))\n      updateViews();\n\n    if (unlikely(!m_views[viewType]))\n      m_views[viewType] = createView(viewType);\n\n    return m_views[viewType];\n  }\n\n\n  inline VkImageLayout DxvkImageView::getLayout() const {\n    return m_image->pickLayout(m_key.layout);\n  }\n\n\n  inline bool DxvkImageView::hasGfxStores() const {\n    return (m_properties.access & (VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT))\n        && (m_image->hasGfxStores());\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_implicit_resolve.cpp",
    "content": "#include <algorithm>\n\n#include \"dxvk_device.h\"\n#include \"dxvk_implicit_resolve.h\"\n\nnamespace dxvk {\n\n  DxvkImplicitResolveTracker::DxvkImplicitResolveTracker(Rc<DxvkDevice> device)\n  : m_device(std::move(device)) {\n\n  }\n\n\n  DxvkImplicitResolveTracker::~DxvkImplicitResolveTracker() {\n\n  }\n\n\n  Rc<DxvkImageView> DxvkImplicitResolveTracker::getResolveView(\n            DxvkImageView&              view,\n            uint64_t                    trackingId) {\n    // We generally only expect to have one or two views at most in games\n    // that hit this path at all, so iterating over the arras is fine\n    for (auto& v : m_resolveViews) {\n      if (v.inputView == &view) {\n        addResolveOp(v);\n        return v.resolveView;\n      }\n    }\n\n    // Create a new resolve image with only the array layers covered by the\n    // input view. We expect resolve images to be somewhat short-lived.\n    DxvkImageCreateInfo imageInfo = view.image()->info();\n\n    DxvkImageCreateInfo resolveInfo = { };\n    resolveInfo.type = imageInfo.type;\n    resolveInfo.format = view.info().format;\n    resolveInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;\n    resolveInfo.extent = imageInfo.extent;\n    resolveInfo.numLayers = view.info().layerCount;\n    resolveInfo.mipLevels = 1u;\n    resolveInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    resolveInfo.stages = m_device->getShaderPipelineStages();\n    resolveInfo.access = VK_ACCESS_SHADER_READ_BIT;\n    resolveInfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n    resolveInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n    resolveInfo.transient = VK_TRUE;\n    resolveInfo.debugName = \"Resolve image\";\n\n    if (view.info().aspects & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n      resolveInfo.usage  |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n      resolveInfo.stages |= VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;\n      resolveInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n    } else {\n      resolveInfo.usage  |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n      resolveInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n      resolveInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n    }\n\n    Rc<DxvkImage> image = m_device->createImage(resolveInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n    cleanup(image->getMemoryInfo().size, trackingId);\n\n    DxvkImageViewKey viewKey = view.info();\n    viewKey.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    viewKey.layerIndex = 0u;\n\n    auto& resolveView = m_resolveViews.emplace_back();\n    resolveView.inputView = &view;\n    resolveView.resolveView = image->createView(viewKey);\n\n    addResolveOp(resolveView);\n\n    return resolveView.resolveView;\n  }\n\n\n  bool DxvkImplicitResolveTracker::extractResolve(\n          DxvkImplicitResolveOp&      resolve) {\n    if (m_resolveOps.empty()) {\n      resolve = DxvkImplicitResolveOp();\n      return false;\n    }\n\n    resolve = std::move(m_resolveOps.back());\n    m_resolveOps.pop_back();\n    return true;\n  }\n\n\n  void DxvkImplicitResolveTracker::invalidate(\n    const DxvkImage&                  image,\n    const VkImageSubresourceRange&    subresources) {\n    for (auto& v : m_resolveViews) {\n      if (v.resolveDone && v.inputView->image() == &image) {\n        auto viewSubresource = v.inputView->imageSubresources();\n\n        if ((subresources.aspectMask & viewSubresource.aspectMask)\n         && vk::checkSubresourceRangeOverlap(viewSubresource, subresources))\n          v.resolveDone = false;\n      }\n    }\n  }\n\n\n  void DxvkImplicitResolveTracker::cleanup(\n        uint64_t                    trackingId) {\n    cleanup(0u, trackingId);\n  }\n\n\n  void DxvkImplicitResolveTracker::addResolveOp(\n          DxvkImplicitResolveView&    view) {\n    if (view.resolveDone)\n      return;\n\n    // Determine resolve parameters based on the view format rather than the\n    // image format, since this will more likely represent what the app is\n    // trying to do\n    auto format = view.inputView->formatInfo();\n\n    auto& op = m_resolveOps.emplace_back();\n    op.inputImage = view.inputView->image();\n    op.resolveImage = view.resolveView->image();\n    op.resolveRegion.srcSubresource = vk::pickSubresourceLayers(view.inputView->imageSubresources(), 0u);\n    op.resolveRegion.srcSubresource.aspectMask = format->aspectMask;\n    op.resolveRegion.dstSubresource = vk::pickSubresourceLayers(view.resolveView->imageSubresources(), 0u);\n    op.resolveRegion.dstSubresource.aspectMask = format->aspectMask;\n    op.resolveRegion.dstSubresource.baseArrayLayer = 0u;\n    op.resolveRegion.extent = view.resolveView->mipLevelExtent(0u);\n    op.resolveFormat = view.inputView->info().format;\n\n    view.resolveDone = true;\n  }\n\n\n  void DxvkImplicitResolveTracker::cleanup(\n          VkDeviceSize                allocationSize,\n          uint64_t                    trackingId) {\n    constexpr VkDeviceSize MaxMemory = 64ull << 20u;\n\n    constexpr uint64_t MaxLifetime = 256u;\n    constexpr uint64_t MinLifetime =  16u;\n\n    // Eliminate images that haven't been used in a long time\n    for (auto i = m_resolveViews.begin(); i != m_resolveViews.end(); ) {\n      if (i->resolveView->image()->getTrackId() + MaxLifetime < trackingId) {\n        i = m_resolveViews.erase(i);\n      } else {\n        allocationSize += i->resolveView->image()->getMemoryInfo().size;\n        i++;\n      }\n    }\n\n    // If we're using a large amount of memory for resolve images, eliminate\n    // the least recently used resolve images until we drop below the size\n    // threshold again.\n    while (allocationSize > MaxMemory) {\n      auto lr = m_resolveViews.end();\n\n      for (auto i = m_resolveViews.begin(); i != m_resolveViews.end(); i++) {\n        if (i->resolveView->image()->getTrackId() + MinLifetime < trackingId) {\n          if (lr == m_resolveViews.end()\n           || lr->resolveView->image()->getTrackId() > i->resolveView->image()->getTrackId())\n            lr = i;\n        }\n      }\n\n      if (lr == m_resolveViews.end())\n        break;\n\n      allocationSize -= lr->resolveView->image()->getMemoryInfo().size;\n      m_resolveViews.erase(lr);\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_implicit_resolve.h",
    "content": "#pragma once\n\n#include <vector>\n\n#include \"dxvk_image.h\"\n\n#include \"../util/util_small_vector.h\"\n\nnamespace dxvk {\n\n  struct DxvkImplicitResolveView {\n    Rc<DxvkImageView> inputView   = nullptr;\n    Rc<DxvkImageView> resolveView = nullptr;\n    bool              resolveDone = false;\n  };\n\n\n  struct DxvkImplicitResolveOp {\n    Rc<DxvkImage>             inputImage    = nullptr;\n    Rc<DxvkImage>             resolveImage  = nullptr;\n    VkImageResolve            resolveRegion = { };\n    VkFormat                  resolveFormat = VK_FORMAT_UNDEFINED;\n  };\n\n\n  class DxvkDevice;\n\n  class DxvkImplicitResolveTracker {\n\n  public:\n\n    DxvkImplicitResolveTracker(Rc<DxvkDevice> device);\n\n    ~DxvkImplicitResolveTracker();\n\n    /**\n     * \\brief Checks whether there are pending resolves\n     *\n     * \\returns \\c true if any there are any resolves that must\n     *    be executed prior to submitting the current draw.\n     */\n    bool hasPendingResolves() const {\n      return !m_resolveOps.empty();\n    }\n\n    /**\n     * \\brief Retrieves resolve image view for a given input view\n     *\n     * \\param [in] view Multisampled view bound to the context\n     * \\returns Non-multisampled view to replace the bound view with\n     */\n    Rc<DxvkImageView> getResolveView(\n            DxvkImageView&              view,\n            uint64_t                    trackingId);\n\n    /**\n     * \\brief Extracts a resolve operation to execute\n     *\n     * \\param [out] resolve Extracted resolve parameters\n     * \\returns \\c true if a resolve was extracted, \\c false\n     *    if all resolves have already been processed.\n     */\n    bool extractResolve(\n            DxvkImplicitResolveOp&      resolve);\n\n    /**\n     * \\brief Invalidates resolve cache for a given set of image subresources\n     *\n     * Must be called any time the given set of subresources of this\n     * resource is written, so that the corresponding resolve image\n     * can get updated the next time it is read. Must not be called\n     * for any subresource that is only being read, since that may\n     * cause problems with read-only depth-stencil access.\n     * \\param [in] image The multisampled image\n     * \\param [in] subresources Image subresources written\n     */\n    void invalidate(\n      const DxvkImage&                  image,\n      const VkImageSubresourceRange&    subresources);\n\n    /**\n     * \\brief Cleans up resolve image cache\n     *\n     * Destroys resolve images that have not been used in a while\n     * in order to reduce memory wasted on unused images.\n     * \\param [in] trackingId Current context command list ID\n     */\n    void cleanup(\n            uint64_t                    trackingId);\n\n  private:\n\n    Rc<DxvkDevice> m_device;\n\n    std::vector<DxvkImplicitResolveView>  m_resolveViews;\n    std::vector<DxvkImplicitResolveOp>    m_resolveOps;\n\n    void addResolveOp(\n            DxvkImplicitResolveView&    view);\n\n    void cleanup(\n            VkDeviceSize                allocationSize,\n            uint64_t                    trackingId);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_include.h",
    "content": "#pragma once\n\n#include \"../util/log/log.h\"\n#include \"../util/log/log_debug.h\"\n\n#include \"../util/util_env.h\"\n#include \"../util/util_error.h\"\n#include \"../util/util_flags.h\"\n#include \"../util/util_likely.h\"\n#include \"../util/util_math.h\"\n#include \"../util/util_small_vector.h\"\n#include \"../util/util_string.h\"\n\n#include \"../util/rc/util_rc.h\"\n#include \"../util/rc/util_rc_ptr.h\"\n\n#include \"../util/sha1/sha1_util.h\"\n\n#include \"../util/sync/sync_signal.h\"\n#include \"../util/sync/sync_spinlock.h\"\n#include \"../util/sync/sync_ticketlock.h\"\n\n#include \"../vulkan/vulkan_loader.h\"\n#include \"../vulkan/vulkan_names.h\"\n#include \"../vulkan/vulkan_util.h\"\n"
  },
  {
    "path": "src/dxvk/dxvk_instance.cpp",
    "content": "#include <version.h>\n#include <buildenv.h>\n\n#include \"dxvk_instance.h\"\n#include \"dxvk_openvr.h\"\n#include \"dxvk_openxr.h\"\n#include \"dxvk_platform_exts.h\"\n\n#include \"../wsi/wsi_platform.h\"\n\n#include \"../vulkan/vulkan_util.h\"\n\n#include <algorithm>\n#include <set>\n#include <sstream>\n\nnamespace dxvk {\n\n  DxvkInstance::DxvkInstance(DxvkInstanceFlags flags)\n  : DxvkInstance(DxvkInstanceImportInfo(), flags) {\n\n  }\n\n\n  DxvkInstance::DxvkInstance(const DxvkInstanceImportInfo& args, DxvkInstanceFlags flags) {\n    Logger::info(str::format(\"Game: \", env::getExeName()));\n    Logger::info(str::format(\"DXVK: \", DXVK_VERSION));\n    Logger::info(str::format(\"Build: \", DXVK_TARGET, \" \", DXVK_COMPILER, \" \", DXVK_COMPILER_VERSION));\n\n    wsi::init();\n\n    m_config = Config::getUserConfig();\n    m_config.merge(Config::getAppConfig(env::getExePath()));\n    m_config.logOptions();\n\n    m_options = DxvkOptions(m_config);\n\n    // Load Vulkan library\n    if (!initVulkanLoader(args))\n      throw DxvkError(\"Failed to load vulkan-1 library.\");\n\n    // Initialize extension providers\n    m_extProviders.push_back(&DxvkPlatformExts::s_instance);\n#ifdef _WIN32\n    m_extProviders.push_back(&VrInstance::s_instance);\n    m_extProviders.push_back(&DxvkXrProvider::s_instance);\n#endif\n\n    Logger::info(\"Extension providers:\");\n\n    for (const auto& provider : m_extProviders) {\n      Logger::info(str::format(\"  \", provider->getName()));\n      provider->initInstanceExtensions();\n    }\n\n    if (!initVulkanInstance(args, flags))\n      throw DxvkError(\"Failed to initialize DXVK.\");\n\n    if (!initAdapters())\n      throw DxvkError(\"Failed to initialize DXVK.\");\n  }\n  \n  \n  DxvkInstance::~DxvkInstance() {\n    if (m_messenger)\n      m_vki->vkDestroyDebugUtilsMessengerEXT(m_vki->instance(), m_messenger, nullptr);\n\n    wsi::quit();\n  }\n  \n  \n  Rc<DxvkAdapter> DxvkInstance::enumAdapters(uint32_t index) const {\n    return index < m_adapters.size()\n      ? m_adapters[index]\n      : nullptr;\n  }\n\n\n  Rc<DxvkAdapter> DxvkInstance::findAdapterByLuid(const void* luid) const {\n    for (const auto& adapter : m_adapters) {\n      const auto& vk11 = adapter->deviceProperties().vk11;\n\n      if (vk11.deviceLUIDValid && !std::memcmp(luid, vk11.deviceLUID, VK_LUID_SIZE))\n        return adapter;\n    }\n\n    return nullptr;\n  }\n\n  \n  Rc<DxvkAdapter> DxvkInstance::findAdapterByDeviceId(uint16_t vendorId, uint16_t deviceId) const {\n    for (const auto& adapter : m_adapters) {\n      const auto& props = adapter->deviceProperties();\n\n      if (props.core.properties.vendorID == vendorId\n       && props.core.properties.deviceID == deviceId)\n        return adapter;\n    }\n\n    return nullptr;\n  }\n  \n  \n  bool DxvkInstance::initVulkanLoader(const DxvkInstanceImportInfo& args) {\n    m_vkl = args.loaderProc\n      ? new vk::LibraryFn(args.loaderProc)\n      : new vk::LibraryFn();\n\n    return m_vkl->getLoaderProc() != nullptr;\n  }\n\n\n  bool DxvkInstance::initVulkanInstance(const DxvkInstanceImportInfo& args, DxvkInstanceFlags flags) {\n    // Query supported instance layers\n    std::set<std::string> layersSupported;\n    std::set<std::string> layersEnabled;\n\n    uint32_t layerCount = 0u;\n    m_vkl->vkEnumerateInstanceLayerProperties(&layerCount, nullptr);\n\n    std::vector<VkLayerProperties> layers(layerCount);\n    m_vkl->vkEnumerateInstanceLayerProperties(&layerCount, layers.data());\n\n    for (const auto& layer : layers)\n      layersSupported.insert(layer.layerName);\n\n    // Query supported instance extensions\n    std::set<VkExtensionProperties, vk::SortExtension> extensionsSupported;\n    std::set<VkExtensionProperties, vk::SortExtension> extensionsEnabled;\n\n    uint32_t extensionNameCount = 0u;\n    m_vkl->vkEnumerateInstanceExtensionProperties(nullptr, &extensionNameCount, nullptr);\n\n    std::vector<VkExtensionProperties> extensionNamesSupported(extensionNameCount);\n    m_vkl->vkEnumerateInstanceExtensionProperties(nullptr, &extensionNameCount, extensionNamesSupported.data());\n\n    // When importing an instance, filter by enabled instance extensions\n    if (args.instance) {\n      for (uint32_t i = 0u; i < args.extensionCount; i++)\n        extensionsEnabled.insert(vk::makeExtension(args.extensionNames[i]));\n    }\n\n    for (const auto& ext : extensionNamesSupported) {\n      bool canEnable = true;\n\n      if (args.instance)\n        canEnable = extensionsEnabled.find(ext) != extensionsEnabled.end();\n\n      if (canEnable)\n        extensionsSupported.insert(ext);\n    }\n\n    // Check which known extensions are supported. We don't have spec\n    // version information for imported instances, but that's fine.\n    for (const auto& ext : getExtensionList(m_extensionInfo)) {\n      auto entry = extensionsSupported.find(*ext);\n\n      if (entry != extensionsSupported.end())\n        ext->specVersion = entry->specVersion;\n    }\n\n    // Only enable one of the surface maintenance extensions\n    if (m_extensionInfo.khrSurfaceMaintenance1.specVersion)\n      m_extensionInfo.extSurfaceMaintenance1.specVersion = 0u;\n\n    // Hide debug mode behind an environment variable since it adds\n    // significant overhead, and some games will not work with it enabled.\n    std::string debugEnv = env::getEnvVar(\"DXVK_DEBUG\");\n\n    bool capture = debugEnv.empty() && (\n      env::getEnvVar(\"ENABLE_VULKAN_RENDERDOC_CAPTURE\") == \"1\" ||\n      env::getEnvVar(\"MESA_VK_TRACE\") != \"\");\n\n    if (debugEnv == \"validation\")\n      m_debugFlags.set(DxvkDebugFlag::Validation);\n    else if (debugEnv == \"markers\")\n      m_debugFlags.set(DxvkDebugFlag::Capture, DxvkDebugFlag::Markers);\n    else if (debugEnv == \"capture\" || m_options.enableDebugUtils || capture)\n      m_debugFlags.set(DxvkDebugFlag::Capture);\n\n    if (m_debugFlags.isClear()) {\n      // Disable any usage of the extension altogether\n      m_extensionInfo.extDebugUtils.specVersion = 0u;\n    } else {\n      Logger::warn(\"Debug Utils are enabled. May affect performance.\");\n\n      if (m_debugFlags.test(DxvkDebugFlag::Validation)) {\n        const char* debugLayer = \"VK_LAYER_KHRONOS_validation\";\n\n        if (layersSupported.find(debugLayer) != layersSupported.end()) {\n          layersEnabled.insert(debugLayer);\n        } else {\n          // This can happen on winevulkan since it does not support layers\n          Logger::warn(str::format(\"Validation layers not found, set VK_INSTANCE_LAYERS=\", debugLayer));\n        }\n      }\n    }\n\n    // Log enabled layers, if any\n    if (!layersEnabled.empty()) {\n      Logger::info(\"Enabled instance layers:\");\n\n      for (const auto& layer : layersEnabled)\n        Logger::info(str::format(\"  \", layer));\n    }\n\n    // Generate list of extensions to actually enable\n    extensionsEnabled.clear();\n\n    for (const auto& ext : getExtensionList(m_extensionInfo)) {\n      if (ext->specVersion)\n        extensionsEnabled.insert(*ext);\n    }\n\n    for (const auto& provider : m_extProviders) {\n      for (const auto& ext : provider->getInstanceExtensions())\n        extensionsEnabled.insert(ext);\n    }\n\n    Logger::info(\"Enabled instance extensions:\");\n\n    for (const auto& ext : extensionsEnabled) {\n      Logger::info(str::format(\"  \", ext.extensionName));\n      m_extensionList.push_back(ext);\n    }\n\n    // If necessary, create a new Vulkan instance\n    VkInstance instance = args.instance;\n\n    if (!instance) {\n      std::string appName = env::getExeName();\n\n      std::vector<const char*> layerNames;\n      std::vector<const char*> extensionNames;\n\n      for (const auto& layer : layersEnabled)\n        layerNames.push_back(layer.c_str());\n\n      for (const auto& ext : extensionsEnabled)\n        extensionNames.push_back(ext.extensionName);\n\n      VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };\n      appInfo.pApplicationName      = appName.c_str();\n      appInfo.applicationVersion    = flags.raw();\n      appInfo.pEngineName           = \"DXVK\";\n      appInfo.engineVersion         = VK_MAKE_API_VERSION(0, 3, 0, 0);\n      appInfo.apiVersion            = DxvkVulkanApiVersion;\n\n      VkInstanceCreateInfo info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };\n      info.pApplicationInfo         = &appInfo;\n      info.enabledLayerCount        = layerNames.size();\n      info.ppEnabledLayerNames      = layerNames.data();\n      info.enabledExtensionCount    = extensionNames.size();\n      info.ppEnabledExtensionNames  = extensionNames.data();\n\n      VkResult status = m_vkl->vkCreateInstance(&info, nullptr, &instance);\n\n      if (status != VK_SUCCESS) {\n        Logger::err(\"DxvkInstance::createInstance: Failed to create Vulkan instance\");\n        return false;\n      }\n    }\n\n    // Create the Vulkan instance loader\n    m_vki = new vk::InstanceFn(m_vkl, !args.instance, instance);\n\n    if (m_debugFlags.test(DxvkDebugFlag::Validation)) {\n      VkDebugUtilsMessengerCreateInfoEXT messengerInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };\n      messengerInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT\n                                    | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT\n                                    | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;\n      messengerInfo.messageType     = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT\n                                    | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;\n      messengerInfo.pfnUserCallback = &debugCallback;\n\n      if (m_vki->vkCreateDebugUtilsMessengerEXT(m_vki->instance(), &messengerInfo, nullptr, &m_messenger))\n        Logger::err(\"DxvkInstance::createInstance: Failed to create debug messenger, proceeding without.\");\n    }\n\n    // Write back debug flags\n    return true;\n  }\n\n\n  bool DxvkInstance::initAdapters() {\n    uint32_t numAdapters = 0;\n    if (m_vki->vkEnumeratePhysicalDevices(m_vki->instance(), &numAdapters, nullptr) != VK_SUCCESS)\n      throw DxvkError(\"DxvkInstance::enumAdapters: Failed to enumerate adapters\");\n    \n    std::vector<VkPhysicalDevice> adapters(numAdapters);\n    if (m_vki->vkEnumeratePhysicalDevices(m_vki->instance(), &numAdapters, adapters.data()) != VK_SUCCESS)\n      throw DxvkError(\"DxvkInstance::enumAdapters: Failed to enumerate adapters\");\n\n    std::vector<VkPhysicalDeviceProperties> deviceProperties(numAdapters);\n    DxvkDeviceFilterFlags filterFlags = 0;\n\n    for (uint32_t i = 0; i < numAdapters; i++) {\n      m_vki->vkGetPhysicalDeviceProperties(adapters[i], &deviceProperties[i]);\n\n      if (deviceProperties[i].deviceType != VK_PHYSICAL_DEVICE_TYPE_CPU)\n        filterFlags.set(DxvkDeviceFilterFlag::SkipCpuDevices);\n    }\n\n    DxvkDeviceFilter filter(filterFlags, m_options);\n\n    uint32_t numDGPU = 0;\n    uint32_t numIGPU = 0;\n\n    for (uint32_t i = 0; i < numAdapters; i++) {\n      Rc<DxvkAdapter> adapter = new DxvkAdapter(*this, adapters[i]);\n\n      if (filter.testAdapter(*adapter)) {\n        if (deviceProperties[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)\n          numDGPU += 1;\n        else if (deviceProperties[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)\n          numIGPU += 1;\n\n        m_adapters.push_back(std::move(adapter));\n      }\n    }\n\n    std::stable_sort(m_adapters.begin(), m_adapters.end(),\n      [] (const Rc<DxvkAdapter>& a, const Rc<DxvkAdapter>& b) -> bool {\n        static const std::array<VkPhysicalDeviceType, 3> deviceTypes = {{\n          VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,\n          VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU,\n          VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU,\n        }};\n\n        uint32_t aRank = deviceTypes.size();\n        uint32_t bRank = deviceTypes.size();\n\n        for (uint32_t i = 0; i < std::min(aRank, bRank); i++) {\n          if (a->deviceProperties().core.properties.deviceType == deviceTypes[i]) aRank = i;\n          if (b->deviceProperties().core.properties.deviceType == deviceTypes[i]) bRank = i;\n        }\n\n        return aRank < bRank;\n      });\n\n    if (m_options.hideIntegratedGraphics && numDGPU > 0 && numIGPU > 0) {\n      m_adapters.resize(numDGPU);\n      numIGPU = 0;\n    }\n\n    if (m_adapters.empty()) {\n      Logger::warn(str::format(\n        \"DXVK: No adapters found. Please check your device filter settings\\n\"\n        \"and Vulkan drivers. A Vulkan \",\n        VK_API_VERSION_MAJOR(DxvkVulkanApiVersion), \".\",\n        VK_API_VERSION_MINOR(DxvkVulkanApiVersion), \" capable setup is required.\"));\n      return false;\n    }\n\n    for (const auto& provider : m_extProviders) {\n      provider->initDeviceExtensions(this);\n      for (uint32_t i = 0; enumAdapters(i) != nullptr; i++)\n        enumAdapters(i)->enableExtensions(provider->getDeviceExtensions(i));\n    }\n\n    if (numDGPU == 1u && numIGPU == 1u)\n      m_adapters[1u]->linkToDGPU(m_adapters[0u]);\n\n    return true;\n  }\n\n\n  std::vector<VkExtensionProperties*> DxvkInstance::getExtensionList(\n          DxvkInstanceExtensionInfo&              extensions) {\n    return {{\n      &extensions.extDebugUtils,\n      &extensions.extSurfaceMaintenance1,\n      &extensions.khrGetSurfaceCapabilities2,\n      &extensions.khrSurface,\n      &extensions.khrSurfaceMaintenance1,\n    }};\n  }\n\n\n  VkBool32 VKAPI_CALL DxvkInstance::debugCallback(\n          VkDebugUtilsMessageSeverityFlagBitsEXT  messageSeverity,\n          VkDebugUtilsMessageTypeFlagsEXT         messageTypes,\n    const VkDebugUtilsMessengerCallbackDataEXT*   pCallbackData,\n          void*                                   pUserData) {\n    LogLevel logLevel;\n\n    switch (messageSeverity) {\n      default:\n      case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:    logLevel = LogLevel::Info;  break;\n      case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: logLevel = LogLevel::Debug; break;\n      case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: logLevel = LogLevel::Warn;  break;\n      case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:   logLevel = LogLevel::Error; break;\n    }\n\n    static const std::array<uint32_t, 6> ignoredIds = {\n      // Ignore image format features for depth-compare instructions.\n      // These errors are expected in D3D9 and some D3D11 apps.\n      0x23259a0d,\n      0x4b9d1597,\n      0x534c50ad,\n      0x9750b479,\n      // Spammy perf warning about unused fragment shader outputs.\n      // This is expected and will be optimized by any sane driver.\n      0x46877e3e,\n      // Spammy warning about vertex format mismatches in many games.\n      // Quite common and we rely on hardware/drivers implementing\n      // D3D behaviour here since this is really just an app bug\n      // that we can't easily work around in most cases.\n      0x9367b2c1,\n    };\n\n    for (auto id : ignoredIds) {\n      if (uint32_t(pCallbackData->messageIdNumber) == id)\n        return VK_FALSE;\n    }\n\n    std::stringstream str;\n\n    if (pCallbackData->pMessageIdName)\n      str << pCallbackData->pMessageIdName << \" (0x\" << std::hex << pCallbackData->messageIdNumber << \")\" << std::endl;\n\n    str << pCallbackData->pMessage;\n\n    Logger::log(logLevel, str.str());\n    return VK_FALSE;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_instance.h",
    "content": "#pragma once\n\n#include \"../util/config/config.h\"\n\n#include \"dxvk_adapter.h\"\n#include \"dxvk_device_filter.h\"\n#include \"dxvk_extension_provider.h\"\n#include \"dxvk_options.h\"\n\nnamespace dxvk {\n\n  constexpr uint32_t DxvkVulkanApiVersion = VK_API_VERSION_1_3;\n\n  /**\n   * \\brief Vulkan instance creation parameters\n   */\n  struct DxvkInstanceImportInfo {\n    PFN_vkGetInstanceProcAddr loaderProc      = nullptr;\n    VkInstance                instance        = VK_NULL_HANDLE;\n    uint32_t                  extensionCount  = 0u;\n    const char**              extensionNames  = nullptr;\n  };\n\n\n  /**\n   * \\brief Instance extension properties\n   */\n  struct DxvkInstanceExtensionInfo {\n    VkExtensionProperties extDebugUtils               = vk::makeExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);\n    VkExtensionProperties extSurfaceMaintenance1      = vk::makeExtension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);\n    VkExtensionProperties khrGetSurfaceCapabilities2  = vk::makeExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME);\n    VkExtensionProperties khrSurface                  = vk::makeExtension(VK_KHR_SURFACE_EXTENSION_NAME);\n    VkExtensionProperties khrSurfaceMaintenance1      = vk::makeExtension(VK_KHR_SURFACE_MAINTENANCE_1_EXTENSION_NAME);\n  };\n\n\n  /**\n   * \\brief Debug flags\n   */\n  enum class DxvkDebugFlag : uint32_t {\n    Validation        = 0,\n    Capture           = 1,\n    Markers           = 2,\n  };\n\n  using DxvkDebugFlags = Flags<DxvkDebugFlag>;\n\n\n  /**\n   * \\brief Instance creation flags\n   *\n   * These flags will be passed to the app version field of the Vulkan\n   * instance, so that drivers can adjust behaviour for some edge cases\n   * that are not implementable with Vulkan itself.\n   */\n  enum class DxvkInstanceFlag : uint32_t {\n    /** Enforce D3D9 behaviour for texture coordinate snapping */\n    ClientApiIsD3D9,\n  };\n\n  using DxvkInstanceFlags = Flags<DxvkInstanceFlag>;\n\n\n  /**\n   * \\brief DXVK instance\n   * \n   * Manages a Vulkan instance and stores a list\n   * of adapters. This also provides methods for\n   * device creation.\n   */\n  class DxvkInstance : public RcObject {\n    \n  public:\n\n    /**\n     * \\brief Creates new Vulkan instance\n     * \\param [in] flags Instance flags\n     */\n    explicit DxvkInstance(DxvkInstanceFlags flags);\n\n    /**\n     * \\brief Imports existing Vulkan instance\n     * \\param [in] flags Instance flags\n     */\n    explicit DxvkInstance(\n      const DxvkInstanceImportInfo& args,\n            DxvkInstanceFlags       flags);\n\n    ~DxvkInstance();\n    \n    /**\n     * \\brief Vulkan instance functions\n     * \\returns Vulkan instance functions\n     */\n    Rc<vk::InstanceFn> vki() const {\n      return m_vki;\n    }\n    \n    /**\n     * \\brief Vulkan instance handle\n     * \\returns The instance handle\n     */\n    VkInstance handle() {\n      return m_vki->instance();\n    }\n\n     /**\n     * \\brief Number of adapters\n     * \n     * \\returns The number of adapters\n     */\n    size_t adapterCount() {\n      return m_adapters.size();\n    }\n    \n    /**\n     * \\brief Retrieves an adapter\n     * \n     * Note that the adapter does not hold\n     * a hard reference to the instance.\n     * \\param [in] index Adapter index\n     * \\returns The adapter, or \\c nullptr.\n     */\n    Rc<DxvkAdapter> enumAdapters(\n            uint32_t      index) const;\n    \n    /**\n     * \\brief Finds adapter by LUID\n     * \n     * \\param [in] luid Pointer to LUID\n     * \\returns Matching adapter, if any\n     */\n    Rc<DxvkAdapter> findAdapterByLuid(\n      const void*         luid) const;\n    \n    /**\n     * \\brief Finds adapter by device IDs\n     * \n     * \\param [in] vendorId Vendor ID\n     * \\param [in] deviceId Device ID\n     * \\returns Matching adapter, if any\n     */\n    Rc<DxvkAdapter> findAdapterByDeviceId(\n            uint16_t      vendorId,\n            uint16_t      deviceId) const;\n    \n    /**\n     * \\brief Retrieves configuration options\n     * \n     * The configuration set contains user-defined\n     * options as well as app-specific options.\n     * \\returns Configuration options\n     */\n    const Config& config() const {\n      return m_config;\n    }\n\n    /**\n     * \\brief DXVK options\n     * \\returns DXVK options\n     */\n    const DxvkOptions& options() const {\n      return m_options;\n    }\n\n    /**\n     * \\brief Queries extension support\n     */\n    const DxvkInstanceExtensionInfo& extensions() const {\n      return m_extensionInfo;\n    }\n\n    /**\n     * \\brief Instance extension list\n     *\n     * Returns the list of extensions that the\n     * instance was created with, provided by\n     * both DXVK and any extension providers.\n     * \\returns Instance extension name list\n     */\n    DxvkExtensionList getExtensionList() const {\n      return m_extensionList;\n    }\n\n    /**\n     * \\brief Debug flags\n     * \\returns Debug flags\n     */\n    DxvkDebugFlags debugFlags() const {\n      return m_debugFlags;\n    }\n    \n  private:\n\n    Config                    m_config;\n    DxvkOptions               m_options;\n\n    Rc<vk::LibraryFn>         m_vkl = nullptr;\n    Rc<vk::InstanceFn>        m_vki = nullptr;\n\n    DxvkInstanceExtensionInfo m_extensionInfo;\n    DxvkExtensionList         m_extensionList;\n\n    DxvkDebugFlags            m_debugFlags = 0u;\n\n    VkDebugUtilsMessengerEXT  m_messenger = VK_NULL_HANDLE;\n\n    std::vector<DxvkExtensionProvider*> m_extProviders;\n    std::vector<Rc<DxvkAdapter>> m_adapters;\n\n    bool initVulkanLoader(\n      const DxvkInstanceImportInfo& args);\n\n    bool initVulkanInstance(\n      const DxvkInstanceImportInfo& args,\n            DxvkInstanceFlags       flags);\n\n    bool initAdapters();\n\n    static std::vector<VkExtensionProperties*> getExtensionList(\n            DxvkInstanceExtensionInfo& extensions);\n\n    static VkBool32 VKAPI_CALL debugCallback(\n            VkDebugUtilsMessageSeverityFlagBitsEXT  messageSeverity,\n            VkDebugUtilsMessageTypeFlagsEXT         messageTypes,\n      const VkDebugUtilsMessengerCallbackDataEXT*   pCallbackData,\n            void*                                   pUserData);\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_latency.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <cstddef>\n#include <cstdint>\n\n#include \"../util/util_likely.h\"\n#include \"../util/util_time.h\"\n\n#include \"../util/rc/util_rc_ptr.h\"\n\n#include \"../vulkan/vulkan_loader.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Latency tracker statistics\n   */\n  struct DxvkLatencyStats {\n    std::chrono::microseconds frameLatency;\n    std::chrono::microseconds sleepDuration;\n  };\n\n\n  /**\n   * \\brief Timings for a single tracked frame\n   */\n  struct DxvkLatencyFrameData {\n    using time_point = dxvk::high_resolution_clock::time_point;\n    using duration = dxvk::high_resolution_clock::duration;\n\n    uint64_t    frameId         = 0u;\n    uint64_t    appFrameId      = 0u;\n    time_point  frameStart      = time_point();\n    time_point  frameEnd        = time_point();\n    time_point  cpuInputSample  = time_point();\n    time_point  cpuSimBegin     = time_point();\n    time_point  cpuSimEnd       = time_point();\n    time_point  cpuRenderBegin  = time_point();\n    time_point  cpuRenderEnd    = time_point();\n    time_point  cpuPresentBegin = time_point();\n    time_point  cpuPresentEnd   = time_point();\n    time_point  queueSubmit     = time_point();\n    time_point  queuePresent    = time_point();\n    time_point  gpuExecStart    = time_point();\n    time_point  gpuExecEnd      = time_point();\n    time_point  gpuIdleStart    = time_point();\n    time_point  gpuIdleEnd      = time_point();\n    duration    gpuIdleTime     = duration(0u);\n    duration    sleepDuration   = duration(0u);\n    VkResult    presentStatus   = VK_NOT_READY;\n  };\n\n\n  /**\n   * \\brief Latency tracker\n   *\n   * Accumulates time stamps of certain parts of a frame.\n   */\n  class DxvkLatencyTracker {\n\n  public:\n\n    virtual ~DxvkLatencyTracker() { }\n\n    /**\n     * \\brief Increments ref count\n     */\n    void incRef() {\n      m_refCount.fetch_add(1, std::memory_order_acquire);\n    }\n\n    /**\n     * \\brief Decrements ref count\n     *\n     * Destroys the object when there are no users left.\n     */\n    void decRef() {\n      if (m_refCount.fetch_sub(1, std::memory_order_release) == 1u)\n        delete this;\n    }\n\n    /**\n     * \\brief Checks whether automatic markers are needed\n     *\n     * Relevant for forwarding the latency tracker to the context.\n     * \\returns \\c true if automatic markers are necessary.\n     */\n    virtual bool needsAutoMarkers() = 0;\n\n    /**\n     * \\brief Called when presentation begins on the CPU timeline\n     *\n     * Must happen before acquiring an image from the presenter.\n     * \\param [in] frameId Current frame ID\n     */\n    virtual void notifyCpuPresentBegin(\n            uint64_t                  frameId) = 0;\n\n    /**\n     * \\brief Called when the CS thread reaches a given frame\n     *\n     * Should be recorded into the CS thread after completing\n     * the previous frame on the application's CPU timeline.\n     * \\param [in] frameId Current frame ID\n     */\n    virtual void notifyCsRenderBegin(\n            uint64_t                  frameId) = 0;\n\n    /**\n     * \\brief Called when the CS thread completes a frame\n     *\n     * Should be recorded into the CS thread after recording\n     * presentation commands for that frame.\n     * \\param [in] frameId Current frame ID\n     */\n    virtual void notifyCsRenderEnd(\n            uint64_t                  frameId) = 0;\n\n    /**\n     * \\brief Called when presentation ends on the CPU timeline\n     *\n     * Must happen after acquiring an image for presentation, but\n     * before synchronizing with previous frames or performing\n     * latency sleep. The intention is to measure acquire delays.\n     * \\param [in] frameId Current frame ID\n     */\n    virtual void notifyCpuPresentEnd(\n            uint64_t                  frameId) = 0;\n\n    /**\n     * \\brief Called when a command list is submitted to the GPU\n     *\n     * \\param [in] frameId Associated frame ID\n     */\n    virtual void notifyQueueSubmit(\n            uint64_t                  frameId) = 0;\n\n    /**\n     * \\brief Called when a frame is queued for presentation\n     *\n     * \\param [in] frameId Associated frame ID\n     */\n    virtual void notifyQueuePresentBegin(\n            uint64_t                  frameId) = 0;\n\n    /**\n     * \\brief Called after a frame has been queued for presentation\n     *\n     * \\param [in] frameId Associated frame ID\n     * \\param [in] status Result of the present operation\n     */\n    virtual void notifyQueuePresentEnd(\n            uint64_t                  frameId,\n            VkResult                  status) = 0;\n\n    /**\n     * \\brief Called when a submission begins execution on the GPU\n     *\n     * Any previous submissions will have completed by this time. This\n     * can be used to measure GPU idle time throughout a frame.\n     * \\param [in] frameId Associated frame ID\n     */\n    virtual void notifyGpuExecutionBegin(\n            uint64_t                  frameId) = 0;\n\n    /**\n     * \\brief Called when a submission completes execution on the GPU\n     *\n     * The previous submission will have completed by the time this\n     * gets called. This may be used to measure GPU idle time.\n     * \\param [in] frameId Associated frame ID\n     */\n    virtual void notifyGpuExecutionEnd(\n            uint64_t                  frameId) = 0;\n\n    /**\n     * \\brief Called when presentation of a given frame finishes on the GPU\n     *\n     * This is generally the last thing that happens within a frame.\n     * \\param [in] frameId Associated frame ID\n     */\n    virtual void notifyGpuPresentEnd(\n            uint64_t                  frameId) = 0;\n\n    /**\n     * \\brief Performs latency sleep and begins next frame\n     *\n     * Uses latency data from previous frames to estimate when to wake\n     * up the application thread in order to minimize input latency.\n     * \\param [in] frameId Frame ID of the upcoming frame\n     * \\param [in] maxFrameRate Maximum frame rate or refresh rate\n     */\n    virtual void sleepAndBeginFrame(\n            uint64_t                  frameId,\n            double                    maxFrameRate) = 0;\n\n    /**\n     * \\brief Discards all current timing data\n     *\n     * Should be called to reset latency tracking in case\n     * presentation failed for any given frame.\n     */\n    virtual void discardTimings() = 0;\n\n    /**\n     * \\brief Queries statistics for the given frame\n     *\n     * Returns statistics for the frame closest to \\c frameId for\n     * which data is available. If no such frame exists, the stat\n     * counters will return 0.\n     * \\param [in] frameId Frame to query\n     */\n    virtual DxvkLatencyStats getStatistics(\n            uint64_t                  frameId) = 0;\n\n  private:\n\n    std::atomic<uint64_t> m_refCount = { 0u };\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_latency_builtin.cpp",
    "content": "#include <cmath>\n\n#include \"dxvk_latency_builtin.h\"\n\n#include \"../util/log/log.h\"\n\n#include \"../util/util_fps_limiter.h\"\n#include \"../util/util_string.h\"\n\nnamespace dxvk {\n\n  DxvkBuiltInLatencyTracker::DxvkBuiltInLatencyTracker(\n          Rc<Presenter>             presenter,\n          int32_t                   toleranceUs,\n          bool                      useNvLowLatency2)\n  : m_presenter(std::move(presenter)),\n    m_tolerance(std::chrono::duration_cast<duration>(\n      std::chrono::microseconds(std::max(toleranceUs, 0)))),\n    m_useNvLowLatency2(useNvLowLatency2) {\n    Logger::info(str::format(\"Latency control enabled, using \",\n      useNvLowLatency2 ? \"VK_NV_low_latency2\" : \"built-in algorithm\"));\n  }\n\n\n  DxvkBuiltInLatencyTracker::~DxvkBuiltInLatencyTracker() {\n\n  }\n\n\n  bool DxvkBuiltInLatencyTracker::needsAutoMarkers() {\n    return true;\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyCpuPresentBegin(\n          uint64_t                  frameId) {\n    std::unique_lock lock(m_mutex);\n    auto frame = findFrame(frameId);\n\n    if (frame)\n      frame->cpuPresentBegin = dxvk::high_resolution_clock::now();\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyCpuPresentEnd(\n          uint64_t                  frameId) {\n    std::unique_lock lock(m_mutex);\n    auto frame = findFrame(frameId);\n\n    if (frame)\n      frame->cpuPresentEnd = dxvk::high_resolution_clock::now();\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyCsRenderBegin(\n          uint64_t                  frameId) {\n    if (forwardLatencyMarkerNv(frameId)) {\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_SIMULATION_END_NV);\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_RENDERSUBMIT_START_NV);\n    }\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyCsRenderEnd(\n          uint64_t                  frameId) {\n    if (forwardLatencyMarkerNv(frameId))\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_RENDERSUBMIT_END_NV);\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyQueueSubmit(\n          uint64_t                  frameId) {\n    std::unique_lock lock(m_mutex);\n    auto frame = findFrame(frameId);\n\n    if (frame && frame->queueSubmit == time_point())\n      frame->queueSubmit = dxvk::high_resolution_clock::now();\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyQueuePresentBegin(\n          uint64_t                  frameId) {\n    if (forwardLatencyMarkerNv(frameId))\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_PRESENT_START_NV);\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyQueuePresentEnd(\n          uint64_t                  frameId,\n          VkResult                  status) {\n    { std::unique_lock lock(m_mutex);\n      auto frame = findFrame(frameId);\n\n      if (frame) {\n        frame->presentStatus = status;\n        frame->queuePresent = dxvk::high_resolution_clock::now();\n      }\n\n      m_cond.notify_one();\n    }\n\n    if (forwardLatencyMarkerNv(frameId))\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_PRESENT_END_NV);\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyGpuExecutionBegin(\n          uint64_t                  frameId) {\n    std::unique_lock lock(m_mutex);\n    auto frame = findFrame(frameId);\n\n    if (frame) {\n      auto now = dxvk::high_resolution_clock::now();\n\n      if (frame->gpuExecStart == time_point())\n        frame->gpuExecStart = now;\n\n      if (frame->gpuIdleStart != time_point()) {\n        frame->gpuIdleTime += now - frame->gpuIdleStart;\n        frame->gpuIdleEnd = now;\n      }\n    }\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyGpuExecutionEnd(\n          uint64_t                  frameId) {\n    std::unique_lock lock(m_mutex);\n    auto frame = findFrame(frameId);\n\n    if (frame) {\n      auto now = dxvk::high_resolution_clock::now();\n\n      frame->gpuExecEnd = now;\n      frame->gpuIdleStart = now;\n    }\n  }\n\n\n  void DxvkBuiltInLatencyTracker::notifyGpuPresentEnd(\n          uint64_t                  frameId) {\n    std::unique_lock lock(m_mutex);\n    auto frame = findFrame(frameId);\n\n    if (frame)\n      frame->frameEnd = dxvk::high_resolution_clock::now();\n\n    m_cond.notify_one();\n  }\n\n\n  void DxvkBuiltInLatencyTracker::sleepAndBeginFrame(\n          uint64_t                  frameId,\n          double                    maxFrameRate) {\n    auto duration = m_useNvLowLatency2\n      ? sleepNv(frameId, maxFrameRate)\n      : sleepBuiltin(frameId, maxFrameRate);\n\n    { std::unique_lock lock(m_mutex);\n\n      auto next = initFrame(frameId);\n      next->frameStart = dxvk::high_resolution_clock::now();\n      next->sleepDuration = duration;\n    }\n\n    if (m_useNvLowLatency2) {\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_SIMULATION_START_NV);\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_INPUT_SAMPLE_NV);\n    }\n  }\n\n\n  void DxvkBuiltInLatencyTracker::discardTimings() {\n    std::unique_lock lock(m_mutex);\n    m_validRangeBegin = m_validRangeEnd + 1u;\n  }\n\n\n  DxvkLatencyStats DxvkBuiltInLatencyTracker::getStatistics(\n          uint64_t                  frameId) {\n    std::unique_lock lock(m_mutex);\n\n    DxvkLatencyStats stats = { };\n\n    while (frameId && frameId >= m_validRangeBegin) {\n      auto f = findFrame(frameId--);\n\n      if (f && f->frameEnd != time_point()) {\n        stats.frameLatency = std::chrono::duration_cast<std::chrono::microseconds>(f->frameEnd - f->frameStart);\n        stats.sleepDuration = std::chrono::duration_cast<std::chrono::microseconds>(f->sleepDuration);\n        break;\n      }\n    }\n\n    return stats;\n  }\n\n\n  DxvkBuiltInLatencyTracker::duration DxvkBuiltInLatencyTracker::sleepNv(\n          uint64_t                  frameId,\n          double                    maxFrameRate) {\n    // Set up low latency mode for subsequent frames. The presenter\n    // will figure out whether to reapply latency state or not.\n    VkLatencySleepModeInfoNV latencyMode = { VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV };\n    latencyMode.lowLatencyMode = VK_TRUE;\n    latencyMode.lowLatencyBoost = VK_TRUE;\n    latencyMode.minimumIntervalUs = 0;\n\n    if (m_envFpsLimit > 0.0)\n      maxFrameRate = m_envFpsLimit;\n\n    if (maxFrameRate > 0.0)\n      latencyMode.minimumIntervalUs = uint64_t(1'000'000.0 / maxFrameRate);\n\n    m_presenter->setLatencySleepModeNv(latencyMode);\n\n    // Wait for previous present call to complete in order to\n    // avoid potential issues with oscillating frame times\n    bool presentSuccessful = false;\n\n    { std::unique_lock lock(m_mutex);\n      auto curr = findFrame(frameId - 1u);\n\n      if (curr && curr->cpuPresentEnd != time_point()) {\n        m_cond.wait(lock, [curr] {\n          return curr->presentStatus != VK_NOT_READY;\n        });\n\n        presentSuccessful = curr->presentStatus >= 0;\n      }\n    }\n\n    if (!presentSuccessful)\n      return duration(0u);\n\n    return m_presenter->latencySleepNv();\n  }\n\n\n  DxvkBuiltInLatencyTracker::duration DxvkBuiltInLatencyTracker::sleepBuiltin(\n          uint64_t                  frameId,\n          double                    maxFrameRate) {\n    // Wait for all relevant timings to become available. This should\n    // generally not stall for very long if a maximum frame latency of\n    // 1 is enforced correctly by the swap chain.\n    std::unique_lock lock(m_mutex);\n\n    for (uint32_t i = 2; i <= FrameCount; i++) {\n      auto f = findFrame(frameId - i);\n\n      if (!f || f->cpuPresentEnd == time_point())\n        return duration(0u);\n\n      m_cond.wait(lock, [f] {\n        return f->frameEnd != time_point();\n      });\n    }\n\n    // Wait for the current frame's present call to be processed. Our\n    // algorithm will otherwise get confused if Present stalls or if\n    // any CPU work from previous frames delays GPU execution of the\n    // current frame.\n    auto curr = findFrame(frameId - 1u);\n\n    if (curr && curr->cpuPresentEnd != time_point()) {\n      m_cond.wait(lock, [curr] {\n        return curr->presentStatus != VK_NOT_READY;\n      });\n    }\n\n    // Frame entry of the last frame that fully completed\n    auto prev = findFrame(frameId - 2u);\n\n    // The way we want to align subsequent frames depends on whether\n    // we are limited by GPU performance or display refresh.\n    //\n    // In either case, we estimate the amount of CPU time the game requires\n    // before any GPU work can start to be the delay between frame start and\n    // first submission, plus any GPU idle time during the frame. This is not\n    // accurate if there are forced GPU sync points, but we can't work around\n    // that in a meaningful way.\n    constexpr size_t EntryCount = FrameCount - 1u;\n\n    std::array<duration, EntryCount> cpuTimes = { };\n    std::array<duration, EntryCount> gpuTimes = { };\n\n    for (uint32_t i = 0; i < EntryCount; i++) {\n      auto f = findFrame(frameId - (i + 2u));\n\n      cpuTimes[i] = (f->queueSubmit - f->frameStart) + f->gpuIdleTime;\n      gpuTimes[i] = (f->gpuExecEnd - f->gpuExecStart) - f->gpuIdleTime;\n    }\n\n    duration nextCpuTime = estimateTime(cpuTimes.data(), cpuTimes.size());\n    duration nextGpuTime = estimateTime(gpuTimes.data(), gpuTimes.size());\n\n    // Compute the initial deadline based on GPU execution times\n    time_point gpuDeadline = prev->gpuExecEnd + 2u * nextGpuTime;\n\n    // If we're rendering faster than refresh, use present_wait timings from\n    // previous frames as a starting point and compute an average in order to\n    // account for potentially erratic present_wait delays.\n    duration frameInterval = computeFrameInterval(maxFrameRate);\n\n    if (frameInterval.count()) {\n      duration nextPresentFromPrev = duration(0u);\n\n      for (uint32_t i = 2; i <= FrameCount; i++) {\n        auto f = findFrame(frameId - i);\n\n        time_point deadline = f->frameEnd + i * frameInterval - m_tolerance;\n        nextPresentFromPrev += deadline - prev->frameEnd;\n      }\n\n      time_point wsiDeadline = prev->frameEnd + (nextPresentFromPrev / int32_t(FrameCount - 1u));\n      gpuDeadline = std::max(gpuDeadline, wsiDeadline);\n    }\n\n    // Line up the next frame in such a way that the first GPU submission\n    // happens just before the current frame's final submission completes\n    time_point gpuStartTime = gpuDeadline - nextGpuTime;\n    time_point cpuStartTime = gpuStartTime - nextCpuTime - m_tolerance;\n\n    time_point now = dxvk::high_resolution_clock::now();\n\n    // Release lock before actually sleeping, or\n    // it will affect the time measurements.\n    lock.unlock();\n\n    Sleep::sleepUntil(now, cpuStartTime);\n    return std::max(duration(0u), cpuStartTime - now);\n  }\n\n\n  DxvkLatencyFrameData* DxvkBuiltInLatencyTracker::initFrame(\n          uint64_t                  frameId) {\n    if (m_validRangeEnd + 1u != frameId)\n      m_validRangeBegin = frameId;\n\n    if (m_validRangeBegin + FrameCount <= frameId)\n      m_validRangeBegin = frameId + 1u - FrameCount;\n\n    m_validRangeEnd = frameId;\n\n    auto& frame = m_frames[frameId % FrameCount];\n    frame = DxvkLatencyFrameData();\n    frame.frameId = frameId;\n    return &frame;\n  }\n\n\n  DxvkLatencyFrameData* DxvkBuiltInLatencyTracker::findFrame(\n          uint64_t                  frameId) {\n    return frameId >= m_validRangeBegin && frameId <= m_validRangeEnd\n      ? &m_frames[frameId % FrameCount]\n      : nullptr;\n  }\n\n\n  bool DxvkBuiltInLatencyTracker::forwardLatencyMarkerNv(\n          uint64_t                  frameId) {\n    if (!m_useNvLowLatency2)\n      return false;\n\n    std::unique_lock lock(m_mutex);\n    return findFrame(frameId) != nullptr;\n  }\n\n\n  DxvkBuiltInLatencyTracker::duration DxvkBuiltInLatencyTracker::computeFrameInterval(\n          double                    maxFrameRate) {\n    if (m_envFpsLimit > 0.0)\n      maxFrameRate = m_envFpsLimit;\n\n    return computeIntervalFromRate(maxFrameRate);\n  }\n\n\n  DxvkBuiltInLatencyTracker::duration DxvkBuiltInLatencyTracker::computeIntervalFromRate(\n          double                    frameRate) {\n    if (frameRate <= 0.0 || !std::isnormal(frameRate))\n      return duration(0u);\n\n    uint64_t ns = uint64_t(1'000'000'000.0 / frameRate);\n    return std::chrono::duration_cast<duration>(std::chrono::nanoseconds(ns));\n  }\n\n\n  DxvkBuiltInLatencyTracker::duration DxvkBuiltInLatencyTracker::estimateTime(\n    const duration*                 frames,\n          size_t                    frameCount) {\n    // For each frame, find the median of its neighbours, then\n    // use the maximum of those medians as our estimate.\n    duration result = duration(0u);\n\n    for (size_t i = 0u; i < frameCount - 2u; i++) {\n      duration a = frames[i];\n      duration b = frames[i + 1];\n      duration c = frames[i + 2];\n\n      duration min = std::min(std::min(a, b), c);\n      duration max = std::max(std::max(a, b), c);\n\n      result = std::max(result, a + b + c - min - max);\n    }\n\n    return result;\n  }\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_latency_builtin.h",
    "content": "#pragma once\n\n#include <array>\n\n#include \"dxvk_latency.h\"\n#include \"dxvk_presenter.h\"\n\n#include \"../util/thread.h\"\n\n#include \"../util/util_sleep.h\"\n#include \"../util/util_time.h\"\n\n#include \"../util/config/config.h\"\n\n#include \"../util/sync/sync_spinlock.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Built-in latency tracker\n   *\n   * Implements a simple latency reduction algorithm\n   * based on CPU timestamps received from the backend.\n   */\n  class DxvkBuiltInLatencyTracker : public DxvkLatencyTracker {\n    using time_point = typename DxvkLatencyFrameData::time_point;\n    using duration = typename DxvkLatencyFrameData::duration;\n\n    constexpr static size_t FrameCount = 8u;\n  public:\n\n    DxvkBuiltInLatencyTracker(\n            Rc<Presenter>             presenter,\n            int32_t                   toleranceUs,\n            bool                      useNvLowLatency2);\n\n    ~DxvkBuiltInLatencyTracker();\n\n    bool needsAutoMarkers();\n\n    void notifyCpuPresentBegin(\n            uint64_t                  frameId);\n\n    void notifyCpuPresentEnd(\n            uint64_t                  frameId);\n\n    void notifyCsRenderBegin(\n            uint64_t                  frameId);\n\n    void notifyCsRenderEnd(\n            uint64_t                  frameId);\n\n    void notifyQueueSubmit(\n            uint64_t                  frameId);\n\n    void notifyQueuePresentBegin(\n            uint64_t                  frameId);\n\n    void notifyQueuePresentEnd(\n            uint64_t                  frameId,\n            VkResult                  status);\n\n    void notifyGpuExecutionBegin(\n            uint64_t                  frameId);\n\n    void notifyGpuExecutionEnd(\n            uint64_t                  frameId);\n\n    void notifyGpuPresentEnd(\n            uint64_t                  frameId);\n\n    void sleepAndBeginFrame(\n            uint64_t                  frameId,\n            double                    maxFrameRate);\n\n    void discardTimings();\n\n    DxvkLatencyStats getStatistics(\n            uint64_t                  frameId);\n\n  private:\n\n    Rc<Presenter>             m_presenter;\n\n    dxvk::mutex               m_mutex;\n    dxvk::condition_variable  m_cond;\n\n    duration                  m_tolerance;\n\n    double                    m_envFpsLimit = 0.0;\n    bool                      m_useNvLowLatency2 = false;\n\n    std::array<DxvkLatencyFrameData, FrameCount> m_frames = { };\n\n    uint64_t m_validRangeBegin = 0u;\n    uint64_t m_validRangeEnd = 0u;\n\n    duration sleepNv(\n            uint64_t                  frameId,\n            double                    maxFrameRate);\n\n    duration sleepBuiltin(\n            uint64_t                  frameId,\n            double                    maxFrameRate);\n\n    DxvkLatencyFrameData* initFrame(\n            uint64_t                  frameId);\n\n    DxvkLatencyFrameData* findFrame(\n            uint64_t                  frameId);\n\n    bool forwardLatencyMarkerNv(\n            uint64_t                  frameId);\n\n    duration computeFrameInterval(\n            double                    maxFrameRate);\n\n    static duration computeIntervalFromRate(\n            double                    frameRate);\n\n    static duration estimateTime(\n      const duration*                 frames,\n            size_t                    frameCount);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_latency_reflex.cpp",
    "content": "#include \"dxvk_latency_reflex.h\"\n\nnamespace dxvk {\n\n  DxvkReflexLatencyTrackerNv::DxvkReflexLatencyTrackerNv(\n    const Rc<Presenter>&            presenter)\n  : m_presenter(presenter) {\n\n  }\n\n  DxvkReflexLatencyTrackerNv::~DxvkReflexLatencyTrackerNv() {\n\n  }\n\n\n  bool DxvkReflexLatencyTrackerNv::needsAutoMarkers() {\n    // In markerless mode we want to avoid submitting\n    // any markers at all and ignore the context\n    return false;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyCpuPresentBegin(\n          uint64_t                  frameId) {\n    std::lock_guard lock(m_mutex);\n\n    if (m_lastPresentAppFrameId) {\n      uint64_t expectedFrameId = lookupFrameId(m_lastPresentAppFrameId);\n\n      if (frameId != expectedFrameId) {\n        // This is a normal occurence after a swapchain recreation, or if\n        // tracking got reset for any reason. Remap the current app frame\n        // to the current internal frame, and map any app frames with a\n        // higher frame ID to subsequent frame IDs in order to fix the\n        // mapping; we should catch up within a few frames.\n        Logger::warn(str::format(\"Reflex: Expected internal frame ID \",\n          expectedFrameId, \" for \", m_lastPresentAppFrameId, \", got \", frameId));\n\n        uint64_t nextAppFrameId = m_lastPresentAppFrameId;\n        uint64_t nextDxvkFrameId = frameId;\n\n        auto entry = m_appToDxvkFrameIds.find(nextAppFrameId);\n\n        while (entry != m_appToDxvkFrameIds.end()) {\n          nextAppFrameId = entry->first;\n\n          mapFrameId(nextAppFrameId, nextDxvkFrameId++);\n\n          entry = m_appToDxvkFrameIds.upper_bound(nextAppFrameId);\n        }\n\n        m_nextAllocFrameId = nextDxvkFrameId;\n        m_nextValidFrameId = nextDxvkFrameId + 1u;\n      }\n\n      m_lowLatencyNoMarkers = false;\n    } else if (m_lowLatencyMode) {\n      // Game seemingly doesn't use markers?\n      if (!m_lowLatencyNoMarkers) {\n        Logger::warn(\"Reflex: No latency markers provided\");\n        m_lowLatencyNoMarkers = true;\n        reset();\n      }\n\n      // Update sleep duration since we haven't had the chance yet\n      auto& frame = getFrameData(frameId);\n      frame.sleepDuration = m_lastSleepDuration;\n\n      m_lastSleepDuration = duration(0u);\n    }\n\n    m_lastPresentAppFrameId = 0u;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyCpuPresentEnd(\n          uint64_t                  frameId) {\n    std::lock_guard lock(m_mutex);\n    m_lastPresentQueued = frameId;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyCsRenderBegin(\n          uint64_t                  frameId) {\n    bool setMarker = false;\n\n    { std::lock_guard lock(m_mutex);\n\n      auto& frame = getFrameData(frameId);\n      setMarker = frame.appFrameId && frameId >= m_nextValidFrameId;\n    }\n\n    if (setMarker)\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_RENDERSUBMIT_START_NV);\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyCsRenderEnd(\n          uint64_t                  frameId) {\n    bool setMarker = false;\n\n    { std::lock_guard lock(m_mutex);\n\n      auto& frame = getFrameData(frameId);\n      setMarker = frame.appFrameId && frameId >= m_nextValidFrameId;\n    }\n\n    if (setMarker)\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_RENDERSUBMIT_END_NV);\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyQueueSubmit(\n          uint64_t                  frameId) {\n    std::lock_guard lock(m_mutex);\n    auto& frame = getFrameData(frameId);\n\n    if (frame.queueSubmit == time_point())\n      frame.queueSubmit = dxvk::high_resolution_clock::now();\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyQueuePresentBegin(\n          uint64_t                  frameId) {\n    bool setMarker = false;\n\n    { std::lock_guard lock(m_mutex);\n\n      auto& frame = getFrameData(frameId);\n      setMarker = frame.appFrameId && frameId >= m_nextValidFrameId;\n    }\n\n    if (setMarker)\n      m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_PRESENT_START_NV);\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyQueuePresentEnd(\n          uint64_t                  frameId,\n          VkResult                  status) {\n    bool setMarker = false;\n\n    { std::lock_guard lock(m_mutex);\n\n      auto& frame = getFrameData(frameId);\n      setMarker = frame.appFrameId && frameId >= m_nextValidFrameId;\n    }\n\n    time_point cpuTime = time_point();\n\n    if (setMarker)\n      cpuTime = m_presenter->setLatencyMarkerNv(frameId, VK_LATENCY_MARKER_PRESENT_END_NV);\n\n    std::lock_guard lock(m_mutex);\n\n    if (setMarker) {\n      auto& frame = getFrameData(frameId);\n      frame.presentStatus = status;\n      frame.queuePresent = cpuTime;\n    }\n\n    // Ignore errors or we might never wake up a waiting thread\n    m_lastPresentComplete = frameId;\n    m_cond.notify_all();\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyGpuExecutionBegin(\n          uint64_t                  frameId) {\n    std::lock_guard lock(m_mutex);\n    auto now = dxvk::high_resolution_clock::now();\n\n    auto& frame = getFrameData(frameId);\n    frame.gpuIdleEnd = now;\n\n    if (frame.gpuExecStart == time_point())\n      frame.gpuExecStart = now;\n\n    if (frame.gpuIdleStart != time_point())\n      frame.gpuIdleTime += frame.gpuIdleEnd - frame.gpuIdleStart;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyGpuExecutionEnd(\n          uint64_t                  frameId) {\n    std::lock_guard lock(m_mutex);\n    auto now = dxvk::high_resolution_clock::now();\n\n    auto& frame = getFrameData(frameId);\n    frame.gpuExecEnd = now;\n    frame.gpuIdleStart = now;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::notifyGpuPresentEnd(\n          uint64_t                  frameId) {\n    std::lock_guard lock(m_mutex);\n\n    auto& frame = getFrameData(frameId);\n    frame.frameEnd = dxvk::high_resolution_clock::now();\n\n    m_lastCompletedFrameId = frameId;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::sleepAndBeginFrame(\n          uint64_t                  frameId,\n          double                    maxFrameRate) {\n    std::lock_guard lock(m_mutex);\n    m_lastNoMarkerFrameId = frameId;\n\n    if (m_lowLatencyMode) {\n      auto& frame = getFrameData(frameId);\n      frame.frameStart = dxvk::high_resolution_clock::now();\n    }\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::discardTimings() {\n    std::lock_guard lock(m_mutex);\n    reset();\n  }\n\n\n  DxvkLatencyStats DxvkReflexLatencyTrackerNv::getStatistics(\n          uint64_t                  frameId) {\n    std::lock_guard lock(m_mutex);\n\n    if (!m_lastCompletedFrameId)\n      return DxvkLatencyStats();\n\n    auto& frame = getFrameData(m_lastCompletedFrameId);\n\n    if (frame.frameEnd == time_point())\n      return DxvkLatencyStats();\n\n    time_point frameStart = frame.cpuSimBegin;\n\n    if (frame.cpuInputSample != time_point())\n      frameStart = frame.cpuInputSample;\n\n    if (frameStart == time_point())\n      frameStart = frame.frameStart;\n\n    if (frameStart == time_point())\n      return DxvkLatencyStats();\n\n    DxvkLatencyStats stats = { };\n    stats.frameLatency = std::chrono::duration_cast<std::chrono::microseconds>(frame.frameEnd - frameStart);\n    stats.sleepDuration = std::chrono::duration_cast<std::chrono::microseconds>(frame.sleepDuration);\n    return stats;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::setLatencySleepMode(\n          bool                      enableLowLatency,\n          bool                      enableBoost,\n          uint64_t                  minIntervalUs) {\n    if (m_lowLatencyMode != enableLowLatency)\n      Logger::info(str::format(\"Reflex: Low latency mode \", enableLowLatency ? \"enabled\" : \"disabled\"));\n\n    VkLatencySleepModeInfoNV modeInfo = { VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV };\n    modeInfo.lowLatencyMode = enableLowLatency;\n    modeInfo.lowLatencyBoost = enableBoost;\n    modeInfo.minimumIntervalUs = minIntervalUs;\n\n    m_presenter->setLatencySleepModeNv(modeInfo);\n\n    m_lowLatencyMode = enableLowLatency;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::setLatencyMarker(\n          uint64_t                  appFrameId,\n          VkLatencyMarkerNV         marker) {\n    std::unique_lock lock(m_mutex);\n\n    // Find frame ID. If this is the first marker in a new frame,\n    // try to map it to a new internal frame ID.\n    uint64_t frameId = lookupFrameId(appFrameId);\n\n    if (!frameId && (marker == VK_LATENCY_MARKER_SIMULATION_START_NV\n                  || marker == VK_LATENCY_MARKER_INPUT_SAMPLE_NV))\n      frameId = allocateFrameId(appFrameId);\n\n    // This can hapen if we reset tracking state and receive\n    // a stray present or render submit marker. Ignore these\n    // so that the next presents can recalibrate properly.\n    if (!frameId)\n      return;\n\n    // We use present markers to correlate app frame IDs\n    // with internal frame IDs, so always write this back.\n    if (marker == VK_LATENCY_MARKER_PRESENT_START_NV)\n      m_lastPresentAppFrameId = appFrameId;\n\n    // Don't submit markers for invalid frames since\n    // that could potentially confuse the algorithm\n    if (frameId < m_nextValidFrameId)\n      return;\n\n    // Need to unlock here so we don't deadlock with the presenter\n    auto cpuTime = dxvk::high_resolution_clock::now();\n\n    if (marker == VK_LATENCY_MARKER_INPUT_SAMPLE_NV\n     || marker == VK_LATENCY_MARKER_SIMULATION_START_NV\n     || marker == VK_LATENCY_MARKER_SIMULATION_END_NV) {\n      lock.unlock();\n\n      cpuTime = m_presenter->setLatencyMarkerNv(frameId, marker);\n\n      lock.lock();\n    }\n\n    // Store CPU timestamp to correlate times\n    auto& frame = getFrameData(frameId);\n\n    switch (marker) {\n      case VK_LATENCY_MARKER_INPUT_SAMPLE_NV:\n        frame.cpuInputSample = cpuTime;\n        break;\n\n      case VK_LATENCY_MARKER_SIMULATION_START_NV:\n        frame.cpuSimBegin = cpuTime;\n\n        if (m_lastSleepDuration != duration(0u))\n          frame.sleepDuration = std::exchange(m_lastSleepDuration, duration(0u));\n        break;\n\n      case VK_LATENCY_MARKER_SIMULATION_END_NV:\n        frame.cpuSimEnd = cpuTime;\n        break;\n\n      case VK_LATENCY_MARKER_RENDERSUBMIT_START_NV:\n        frame.cpuRenderBegin = cpuTime;\n        break;\n\n      case VK_LATENCY_MARKER_RENDERSUBMIT_END_NV:\n        frame.cpuRenderEnd = cpuTime;\n        break;\n\n      case VK_LATENCY_MARKER_PRESENT_START_NV:\n        frame.cpuPresentBegin = cpuTime;\n        break;\n\n      case VK_LATENCY_MARKER_PRESENT_END_NV:\n        frame.cpuPresentEnd = cpuTime;\n        break;\n\n      default:\n        Logger::warn(str::format(\"Reflex: Unknown marker \", marker));\n    }\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::latencySleep() {\n    { std::unique_lock lock(m_mutex);\n      // If the app doesn't use markers, wait for the previous present\n      // call to complete so that we don't confuse the algorithm by\n      // sleeping at random times relative to actual graphics work.\n      if (m_lowLatencyNoMarkers) {\n        m_cond.wait(lock, [this] {\n          return m_lastPresentComplete >= m_lastPresentQueued;\n        });\n      }\n    }\n\n    // Actually sleep and write back sleep duration for the next frame\n    auto sleepDuration = m_presenter->latencySleepNv();\n\n    std::lock_guard lock(m_mutex);\n    m_lastSleepAppFrameId = m_lastBeginAppFrameId;\n    m_lastSleepDuration = sleepDuration;\n\n    if (m_lowLatencyNoMarkers && m_lastNoMarkerFrameId > m_lastPresentQueued) {\n      // In markerless mode, assume that this gets called before any\n      // work is done for the next frame and update the frame start\n      // time accordingly.\n      auto& frame = getFrameData(m_lastNoMarkerFrameId);\n      frame.frameStart = dxvk::high_resolution_clock::now();\n    }\n  }\n\n\n  uint32_t DxvkReflexLatencyTrackerNv::getFrameReports(\n          uint32_t                  maxCount,\n          DxvkReflexFrameReport*    reports) {\n    small_vector<VkLatencyTimingsFrameReportNV, 64> nvReports(maxCount);\n\n    for (uint32_t i = 0; i < maxCount; i++)\n      nvReports[i] = { VK_STRUCTURE_TYPE_LATENCY_TIMINGS_FRAME_REPORT_NV };\n\n    // Adjust some statistics so that we actually return the\n    // correct timestamps for the application-defined markers\n    uint32_t count = m_presenter->getLatencyTimingsNv(maxCount, nvReports.data());\n\n    // Only lock after calling into the presenter to avoid deadlocks\n    std::lock_guard lock(m_mutex);\n\n    for (uint32_t i = 0; i < count; i++) {\n      auto& report = nvReports[i];\n      const auto& currFrame = m_frames[report.presentID % FrameCount];\n\n      if (report.presentID != currFrame.frameId || report.presentID < m_nextValidFrameId)\n        return 0;\n\n      report.presentID = currFrame.appFrameId;\n\n      // These represent when the CS thread starts processing the frame\n      report.driverStartTimeUs = report.renderSubmitStartTimeUs;\n      report.driverEndTimeUs = report.renderSubmitEndTimeUs;\n\n      // Return when the app set these markers rather than the time when\n      // we forward them to the driver\n      report.renderSubmitStartTimeUs = mapFrameTimestampToReportUs(currFrame, report, currFrame.cpuRenderBegin);\n      report.renderSubmitEndTimeUs = mapFrameTimestampToReportUs(currFrame, report, currFrame.cpuRenderEnd);\n      report.presentStartTimeUs = mapFrameTimestampToReportUs(currFrame, report, currFrame.cpuPresentBegin);\n      report.presentEndTimeUs = mapFrameTimestampToReportUs(currFrame, report, currFrame.cpuPresentEnd);\n\n      // Documentation for the OS timers seems nonsensical, but it seems to\n      // be the time from the the first submission to the end of the frame\n      report.osRenderQueueStartTimeUs = mapFrameTimestampToReportUs(currFrame, report, currFrame.queueSubmit);\n      report.osRenderQueueEndTimeUs = report.gpuRenderEndTimeUs;\n\n      // Apparently gpuRenderEndTime is when presentation completes rather\n      // than rendering, so we need to compute the active render time using\n      // our own timestamps\n      auto gpuActiveTime = currFrame.gpuExecEnd - currFrame.gpuExecStart - currFrame.gpuIdleTime;\n\n      reports[i].report = report;\n      reports[i].gpuActiveTimeUs = std::max<uint64_t>(0u,\n        std::chrono::duration_cast<std::chrono::microseconds>(gpuActiveTime).count());\n    }\n\n    return count;\n  }\n\n\n  uint64_t DxvkReflexLatencyTrackerNv::frameIdFromAppFrameId(\n          uint64_t                  appFrameId) {\n    std::lock_guard lock(m_mutex);\n    return lookupFrameId(appFrameId);\n  }\n\n\n  DxvkReflexLatencyFrameData& DxvkReflexLatencyTrackerNv::getFrameData(\n          uint64_t                  dxvkFrameId) {\n    auto& frameData = m_frames[dxvkFrameId % FrameCount];\n\n    if (frameData.frameId != dxvkFrameId) {\n      m_appToDxvkFrameIds.erase(frameData.appFrameId);\n\n      frameData = DxvkReflexLatencyFrameData();\n      frameData.frameId = dxvkFrameId;\n    }\n\n    return frameData;\n  }\n\n\n  uint64_t DxvkReflexLatencyTrackerNv::lookupFrameId(\n          uint64_t                  appFrameId) {\n    auto entry = m_appToDxvkFrameIds.find(appFrameId);\n\n    if (entry == m_appToDxvkFrameIds.end())\n      return 0u;\n\n    return entry->second;\n  }\n\n\n  uint64_t DxvkReflexLatencyTrackerNv::allocateFrameId(\n          uint64_t                  appFrameId) {\n    if (appFrameId <= m_lastBeginAppFrameId) {\n      Logger::warn(str::format(\"Reflex: Frame ID \", appFrameId, \" not monotonic, last was \", m_lastBeginAppFrameId));\n      reset();\n    }\n\n    uint64_t frameId = m_nextAllocFrameId++;\n    mapFrameId(appFrameId, frameId);\n\n    m_lastBeginAppFrameId = appFrameId;\n    return frameId;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::mapFrameId(\n          uint64_t                  appFrameId,\n          uint64_t                  dxvkFrameId) {\n    while (m_appToDxvkFrameIds.size() > FrameCount)\n      m_appToDxvkFrameIds.erase(m_appToDxvkFrameIds.begin());\n\n    m_appToDxvkFrameIds.insert_or_assign(appFrameId, dxvkFrameId);\n    getFrameData(dxvkFrameId).appFrameId = appFrameId;\n  }\n\n\n  void DxvkReflexLatencyTrackerNv::reset() {\n    m_nextValidFrameId = uint64_t(-1);\n\n    m_lastSleepDuration = duration(0u);\n\n    m_lastBeginAppFrameId = 0u;\n    m_lastPresentAppFrameId = 0u;\n\n    for (size_t i = 0; i < FrameCount; i++)\n      m_frames[i].appFrameId = 0u;\n\n    m_appToDxvkFrameIds.clear();\n  }\n\n\n  uint64_t DxvkReflexLatencyTrackerNv::mapFrameTimestampToReportUs(\n    const DxvkReflexLatencyFrameData&     frame,\n    const VkLatencyTimingsFrameReportNV&  report,\n          time_point                      timestamp) {\n    if (frame.cpuSimBegin == time_point() || !report.simStartTimeUs)\n      return 0u;\n\n    int64_t diffUs = std::chrono::duration_cast<std::chrono::microseconds>(timestamp - frame.cpuSimBegin).count();\n    return report.simStartTimeUs + diffUs;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_latency_reflex.h",
    "content": "#pragma once\n\n#include <array>\n#include <map>\n\n#include \"dxvk_latency.h\"\n#include \"dxvk_presenter.h\"\n\n#include \"../util/thread.h\"\n\n#include \"../util/util_sleep.h\"\n#include \"../util/util_time.h\"\n\n#include \"../util/config/config.h\"\n\n#include \"../util/sync/sync_spinlock.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Reflex frame info\n   *\n   * Stores frame ID mapping and all sorts of time stamps\n   * that are used for latency sleep or frame reports.\n   */\n  using DxvkReflexLatencyFrameData = DxvkLatencyFrameData;\n\n\n  /**\n   * \\brief Additional frame report info\n   */\n  struct DxvkReflexFrameReport {\n    VkLatencyTimingsFrameReportNV report;\n    uint64_t gpuActiveTimeUs;\n  };\n\n\n  /**\n   * \\brief Built-in latency tracker based on VK_NV_low_latency2\n   *\n   * Implements a simple latency reduction algorithm\n   * based on CPU timestamps received from the backend.\n   */\n  class DxvkReflexLatencyTrackerNv : public DxvkLatencyTracker {\n    using time_point = typename DxvkReflexLatencyFrameData::time_point;\n    using duration = typename DxvkReflexLatencyFrameData::duration;\n\n    // Keep data for a large number of frames around to support\n    // retrieving statistics from the driver properly.\n    constexpr static size_t FrameCount = 256u;\n  public:\n\n    DxvkReflexLatencyTrackerNv(\n      const Rc<Presenter>&            presenter);\n\n    ~DxvkReflexLatencyTrackerNv();\n\n    bool needsAutoMarkers();\n\n    void notifyCpuPresentBegin(\n            uint64_t                  frameId);\n\n    void notifyCpuPresentEnd(\n            uint64_t                  frameId);\n\n    void notifyCsRenderBegin(\n            uint64_t                  frameId);\n\n    void notifyCsRenderEnd(\n            uint64_t                  frameId);\n\n    void notifyQueueSubmit(\n            uint64_t                  frameId);\n\n    void notifyQueuePresentBegin(\n            uint64_t                  frameId);\n\n    void notifyQueuePresentEnd(\n            uint64_t                  frameId,\n            VkResult                  status);\n\n    void notifyGpuExecutionBegin(\n            uint64_t                  frameId);\n\n    void notifyGpuExecutionEnd(\n            uint64_t                  frameId);\n\n    void notifyGpuPresentEnd(\n            uint64_t                  frameId);\n\n    void sleepAndBeginFrame(\n            uint64_t                  frameId,\n            double                    maxFrameRate);\n\n    void discardTimings();\n\n    DxvkLatencyStats getStatistics(\n            uint64_t                  frameId);\n\n    /**\n     * \\brief Sets Reflex state\n     *\n     * \\param [in] enableLowLatency Whether to enable latency control\n     * \\param [in] enableBoost Whether to enable boost\n     * \\param [in] minIntervalUs Minimum frame interval\n     */\n    void setLatencySleepMode(\n            bool                      enableLowLatency,\n            bool                      enableBoost,\n            uint64_t                  minIntervalUs);\n\n    /**\n     * \\brief Sets latency marker from application\n     *\n     * \\param [in] appFrameId Application-provided frame ID\n     * \\param [in] marker Marker to set\n     */\n    void setLatencyMarker(\n            uint64_t                  appFrameId,\n            VkLatencyMarkerNV         marker);\n\n    /**\n     * \\brief Performs latency sleep\n     */\n    void latencySleep();\n\n    /**\n     * \\brief Retrieves frame reports\n     *\n     * \\param [in] maxCount Maximum number of reports\n     * \\param [out] reports Frame reports\n     * \\returns Number of reports retrieved\n     */\n    uint32_t getFrameReports(\n            uint32_t                  maxCount,\n            DxvkReflexFrameReport*    reports);\n\n    /**\n     * \\brief Looks up frame ID from application frame ID\n     *\n     * \\param [in] appFrameId Application-provided frame ID\n     * \\returns Internal frame ID, or 0 if none was found\n     */\n    uint64_t frameIdFromAppFrameId(\n            uint64_t                  appFrameId);\n\n  private:\n\n    Rc<Presenter>             m_presenter;\n\n    dxvk::mutex               m_mutex;\n    dxvk::condition_variable  m_cond;\n\n    uint64_t                  m_lastBeginAppFrameId = 0u;\n    uint64_t                  m_lastSleepAppFrameId = 0u;\n    uint64_t                  m_lastPresentAppFrameId = 0u;\n\n    uint64_t                  m_nextAllocFrameId = 1u;\n    uint64_t                  m_nextValidFrameId = uint64_t(-1);\n\n    uint64_t                  m_lastCompletedFrameId = 0u;\n\n    uint64_t                  m_lastPresentQueued   = 0u;\n    uint64_t                  m_lastPresentComplete = 0u;\n\n    uint64_t                  m_lastNoMarkerFrameId = 0u;\n\n    duration                  m_lastSleepDuration = duration(0u);\n\n    bool                      m_lowLatencyMode      = false;\n    bool                      m_lowLatencyNoMarkers = false;\n\n    std::array<DxvkReflexLatencyFrameData, FrameCount> m_frames = { };\n\n    std::map<uint64_t, uint64_t> m_appToDxvkFrameIds;\n\n    DxvkReflexLatencyFrameData& getFrameData(\n            uint64_t                  dxvkFrameId);\n\n    uint64_t lookupFrameId(\n            uint64_t                  appFrameId);\n\n    uint64_t allocateFrameId(\n            uint64_t                  appFrameId);\n\n    void mapFrameId(\n            uint64_t                  appFrameId,\n            uint64_t                  dxvkFrameId);\n\n    void reset();\n\n    static uint64_t mapFrameTimestampToReportUs(\n      const DxvkReflexLatencyFrameData&     frame,\n      const VkLatencyTimingsFrameReportNV&  report,\n            time_point                      timestamp);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_limits.h",
    "content": "#pragma once\n\n#include \"dxvk_include.h\"\n\nnamespace dxvk {\n  \n  enum DxvkLimits : size_t {\n    MaxNumRenderTargets         =     8,\n    MaxNumVertexAttributes      =    32,\n    MaxNumVertexBindings        =    32,\n    MaxNumXfbBuffers            =     4,\n    MaxNumXfbStreams            =     4,\n    MaxNumViewports             =    16,\n    MaxNumUniformBufferSlots    =   128,\n    MaxNumSamplerSlots          =   128,\n    MaxNumResourceSlots         =  1024,\n    MaxNumQueuedCommandBuffers  =    32,\n    MaxNumQueryCountPerPool     =   128,\n    MaxNumSpecConstants         =    20,\n    MaxUniformBufferSize        = 65536,\n    MaxVertexBindingStride      =  2048,\n    MaxTotalPushDataSize        =   256,\n    MaxSharedPushDataSize       =    64,\n    MaxPerStagePushDataSize     =    32,\n    MaxReservedPushDataSize     =    32,\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_memory.cpp",
    "content": "#include <algorithm>\n#include <iomanip>\n#include <sstream>\n\n#include \"../util/util_bit.h\"\n\n#include \"dxvk_device.h\"\n#include \"dxvk_memory.h\"\n#include \"dxvk_sparse.h\"\n\nnamespace dxvk {\n\n  void DxvkMemoryChunk::addAllocation(DxvkResourceAllocation* allocation) {\n    allocation->m_nextInChunk = allocationList;\n\n    if (allocationList)\n      allocationList->m_prevInChunk = allocation;\n\n    allocationList = allocation;\n  }\n\n\n  void DxvkMemoryChunk::removeAllocation(DxvkResourceAllocation* allocation) {\n    if (allocation->m_nextInChunk)\n      allocation->m_nextInChunk->m_prevInChunk = allocation->m_prevInChunk;\n\n    if (allocation->m_prevInChunk)\n      allocation->m_prevInChunk->m_nextInChunk = allocation->m_nextInChunk;\n    else if (allocationList == allocation)\n      allocationList = allocation->m_nextInChunk;\n\n    allocation->m_prevInChunk = nullptr;\n    allocation->m_nextInChunk = nullptr;\n  }\n\n\n\n\n  DxvkResourceBufferViewMap::DxvkResourceBufferViewMap(\n          DxvkMemoryAllocator*        allocator,\n          VkBuffer                    buffer,\n          VkDeviceAddress             va)\n  : m_device(allocator->device()), m_buffer(buffer), m_va(va) {\n\n  }\n\n\n  DxvkResourceBufferViewMap::~DxvkResourceBufferViewMap() {\n    auto vk = m_device->vkd();\n\n    for (const auto& view : m_views) {\n      if (view.first.format)\n        vk->vkDestroyBufferView(vk->device(), view.second.legacy.bufferView, nullptr);\n    }\n  }\n\n\n  const DxvkDescriptor* DxvkResourceBufferViewMap::createBufferView(\n    const DxvkBufferViewKey&          key,\n          VkDeviceSize                baseOffset) {\n    std::lock_guard lock(m_mutex);\n\n    auto entry = m_views.find(key);\n\n    if (entry != m_views.end())\n      return &entry->second;\n\n    auto vk = m_device->vkd();\n\n    auto& descriptor = m_views.emplace(std::piecewise_construct,\n      std::tuple(key), std::tuple()).first->second;\n\n    if (key.format) {\n      if (m_device->canUseDescriptorHeap()) {\n        VkHostAddressRangeEXT hostAddress = descriptor.getHostAddressRange();\n\n        VkTexelBufferDescriptorInfoEXT bufferInfo = { VK_STRUCTURE_TYPE_TEXEL_BUFFER_DESCRIPTOR_INFO_EXT };\n        bufferInfo.format = key.format;\n        bufferInfo.addressRange.address = m_va + key.offset;\n        bufferInfo.addressRange.size = key.size;\n\n        VkResourceDescriptorInfoEXT info = { VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT };\n        info.type = (key.usage == VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)\n          ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER\n          : VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;\n        info.data.pTexelBuffer = &bufferInfo;\n\n        VkResult vr = vk->vkWriteResourceDescriptorsEXT(vk->device(), 1u, &info, &hostAddress);\n\n        if (vr != VK_SUCCESS)\n          throw DxvkError(str::format(\"Failed to write Vulkan buffer view descriptor: \", vr));\n      } else if (m_device->canUseDescriptorBuffer()) {\n        VkDescriptorAddressInfoEXT bufferInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT };\n        bufferInfo.address = m_va + key.offset;\n        bufferInfo.range = key.size;\n        bufferInfo.format = key.format;\n\n        VkDescriptorGetInfoEXT info = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };\n\n        if (key.usage == VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT) {\n          info.type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;;\n          info.data.pStorageTexelBuffer = &bufferInfo;\n        } else {\n          info.type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;;\n          info.data.pUniformTexelBuffer = &bufferInfo;\n        }\n\n        vk->vkGetDescriptorEXT(vk->device(), &info,\n          m_device->getDescriptorProperties().getDescriptorTypeInfo(info.type).size,\n          descriptor.descriptor.data());\n      } else {\n        VkBufferUsageFlags2CreateInfoKHR flags = { VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO_KHR };\n        flags.usage = key.usage;\n\n        VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, &flags };\n        info.buffer = m_buffer;\n        info.format = key.format;\n        info.offset = key.offset + baseOffset;\n        info.range = key.size;\n\n        VkResult vr = vk->vkCreateBufferView(\n          vk->device(), &info, nullptr, &descriptor.legacy.bufferView);\n\n        if (vr != VK_SUCCESS) {\n          throw DxvkError(str::format(\"Failed to create Vulkan buffer view: \", vr,\n            \"\\n   usage:  0x\", std::hex, key.usage,\n            \"\\n   format: \", key.format,\n            \"\\n   offset: \", std::dec, key.offset,\n            \"\\n   size:   \", std::dec, key.size));\n        }\n      }\n    } else {\n      auto& bufferInfo = descriptor.legacy.buffer;\n      bufferInfo.buffer = m_buffer;\n      bufferInfo.offset = key.offset + baseOffset;\n      bufferInfo.range = key.size;\n\n      if (m_device->canUseDescriptorHeap()) {\n        VkHostAddressRangeEXT hostAddress = descriptor.getHostAddressRange();\n\n        VkDeviceAddressRangeEXT bufferRange = { };\n        bufferRange.address = m_va + key.offset;\n        bufferRange.size = key.size;\n\n        VkResourceDescriptorInfoEXT info = { VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT };\n        info.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n        info.data.pAddressRange = &bufferRange;\n\n        VkResult vr = vk->vkWriteResourceDescriptorsEXT(vk->device(), 1u, &info, &hostAddress);\n\n        if (vr != VK_SUCCESS)\n          throw DxvkError(str::format(\"Failed to write Vulkan buffer descriptor: \", vr));\n      } else if (m_device->canUseDescriptorBuffer()) {\n        VkDescriptorAddressInfoEXT bufferInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT };\n        bufferInfo.address = m_va + key.offset;\n        bufferInfo.range = key.size;\n\n        VkDescriptorGetInfoEXT info = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };\n        info.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n        info.data.pStorageBuffer = &bufferInfo;\n\n        vk->vkGetDescriptorEXT(vk->device(), &info,\n          m_device->getDescriptorProperties().getDescriptorTypeInfo(info.type).size,\n          descriptor.descriptor.data());\n      }\n    }\n\n    return &descriptor;\n  }\n\n\n\n\n  DxvkResourceImageViewMap::DxvkResourceImageViewMap(\n          DxvkMemoryAllocator*        allocator,\n          VkImage                     image)\n  : m_device(allocator->device()), m_image(image) {\n\n  }\n\n\n  DxvkResourceImageViewMap::~DxvkResourceImageViewMap() {\n    auto vk = m_device->vkd();\n\n    for (const auto& view : m_views)\n      vk->vkDestroyImageView(vk->device(), view.second.legacy.image.imageView, nullptr);\n  }\n\n\n  const DxvkDescriptor* DxvkResourceImageViewMap::createImageView(\n    const DxvkImageViewKey&           key) {\n    std::lock_guard lock(m_mutex);\n\n    auto entry = m_views.find(key);\n\n    if (entry != m_views.end())\n      return &entry->second;\n\n    auto vk = m_device->vkd();\n\n    auto& descriptor = m_views.emplace(std::piecewise_construct,\n      std::tuple(key), std::tuple()).first->second;\n\n    VkImageUsageFlags renderTargetUsage = key.usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);\n    VkImageUsageFlags shaderResourceUsage = key.usage & (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT);\n\n    VkImageViewUsageCreateInfo usage = { VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO };\n    usage.usage = key.usage;\n\n    VkImageViewCreateInfo viewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, &usage };\n    viewInfo.image = m_image;\n    viewInfo.viewType = key.viewType;\n    viewInfo.format = key.format;\n    viewInfo.components = key.unpackSwizzle();\n    viewInfo.subresourceRange.aspectMask = key.aspects;\n    viewInfo.subresourceRange.baseMipLevel = key.mipIndex;\n    viewInfo.subresourceRange.levelCount = key.mipCount;\n    viewInfo.subresourceRange.baseArrayLayer = key.layerIndex;\n    viewInfo.subresourceRange.layerCount = key.layerCount;\n\n    if (renderTargetUsage || !m_device->canUseDescriptorHeap() || m_device->hasCudaInterop()) {\n      descriptor.legacy.image.imageLayout = key.layout;\n\n      VkResult vr = vk->vkCreateImageView(\n        vk->device(), &viewInfo, nullptr, &descriptor.legacy.image.imageView);\n\n      if (vr != VK_SUCCESS)\n        throw DxvkError(str::format(\"Failed to create Vulkan image view: \", vr));\n    }\n\n    if (shaderResourceUsage && m_device->canUseDescriptorHeap()) {\n      VkHostAddressRangeEXT hostAddress = descriptor.getHostAddressRange();\n\n      VkImageDescriptorInfoEXT imageInfo = { VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT };\n      imageInfo.pView = &viewInfo;\n      imageInfo.layout = key.layout;\n\n      VkResourceDescriptorInfoEXT info = { VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT };\n      info.type = (key.usage == VK_IMAGE_USAGE_STORAGE_BIT)\n        ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE\n        : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n      info.data.pImage = &imageInfo;\n\n      VkResult vr = vk->vkWriteResourceDescriptorsEXT(vk->device(), 1u, &info, &hostAddress);\n\n      if (vr != VK_SUCCESS)\n        throw DxvkError(str::format(\"Failed to write Vulkan image view descriptor: \", vr));\n    } else if (m_device->canUseDescriptorBuffer() && shaderResourceUsage) {\n      VkDescriptorGetInfoEXT info = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };\n\n      if (shaderResourceUsage == VK_IMAGE_USAGE_STORAGE_BIT) {\n        info.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n        info.data.pStorageImage = &descriptor.legacy.image;\n      } else {\n        info.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n        info.data.pSampledImage = &descriptor.legacy.image;\n      }\n\n      vk->vkGetDescriptorEXT(vk->device(), &info,\n        m_device->getDescriptorProperties().getDescriptorTypeInfo(info.type).size,\n        descriptor.descriptor.data());\n    }\n\n    return &descriptor;\n  }\n\n\n\n\n  DxvkResourceAllocation::~DxvkResourceAllocation() {\n    if (unlikely(m_kmtLocal)) {\n      D3DKMT_DESTROYALLOCATION destroy = { };\n      destroy.hDevice = m_allocator->device()->kmtLocal();\n      destroy.hResource = m_kmtLocal;\n      D3DKMTDestroyAllocation(&destroy);\n    }\n\n    if (m_buffer) {\n      if (unlikely(m_bufferViews))\n        delete m_bufferViews;\n\n      if (unlikely(m_flags.test(DxvkAllocationFlag::OwnsBuffer))) {\n        auto vk = m_allocator->device()->vkd();\n        vk->vkDestroyBuffer(vk->device(), m_buffer, nullptr);\n      }\n    }\n\n    if (m_image) {\n      if (likely(m_imageViews))\n        delete m_imageViews;\n\n      if (likely(m_flags.test(DxvkAllocationFlag::OwnsImage))) {\n        auto vk = m_allocator->device()->vkd();\n        vk->vkDestroyImage(vk->device(), m_image, nullptr);\n      }\n    }\n\n    if (unlikely(m_flags.test(DxvkAllocationFlag::OwnsMemory))) {\n      auto vk = m_allocator->device()->vkd();\n      vk->vkFreeMemory(vk->device(), m_memory, nullptr);\n\n      if (unlikely(m_sparsePageTable))\n        delete m_sparsePageTable;\n    }\n  }\n\n\n  const DxvkDescriptor* DxvkResourceAllocation::createBufferView(\n    const DxvkBufferViewKey&          key) {\n    if (unlikely(!m_bufferViews))\n      m_bufferViews = new DxvkResourceBufferViewMap(m_allocator, m_buffer, m_bufferAddress);\n\n    return m_bufferViews->createBufferView(key, m_bufferOffset);\n  }\n\n\n  const DxvkDescriptor* DxvkResourceAllocation::createImageView(\n    const DxvkImageViewKey&           key) {\n    if (unlikely(!m_imageViews))\n      m_imageViews = new DxvkResourceImageViewMap(m_allocator, m_image);\n\n    return m_imageViews->createImageView(key);\n  }\n\n\n  void DxvkResourceAllocation::initKmtHandles(VkExternalMemoryHandleTypeFlagBits handleType) {\n#ifdef _WIN32\n    auto device = m_allocator->device();\n    auto vk = device->vkd();\n\n    VkMemoryGetWin32HandleInfoKHR handleInfo = { VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR };\n    handleInfo.handleType = handleType;\n    handleInfo.memory = m_memory;\n\n    D3DDDI_OPENALLOCATIONINFO2 alloc = { };\n    HANDLE sharedHandle = INVALID_HANDLE_VALUE;\n\n    if (vk->vkGetMemoryWin32HandleKHR(vk->device(), &handleInfo, &sharedHandle) != VK_SUCCESS) {\n      Logger::warn(\"DxvkMemoryAllocator::createImageResource: Failed to get shared handle for memory\");\n    } else if (handleInfo.handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) {\n      D3DKMT_OPENRESOURCE open = { };\n      open.hDevice = device->kmtLocal();\n      open.hGlobalShare = HandleToUlong(sharedHandle);\n      open.NumAllocations = 1;\n      open.pOpenAllocationInfo2 = &alloc;\n\n      if (D3DKMTOpenResource2(&open)) {\n        Logger::warn(\"DxvkMemoryAllocator::createImageResource: Failed to open shared D3DKMT handle\");\n      } else {\n        m_kmtLocal = open.hResource;\n        m_kmtGlobal = open.hGlobalShare;\n      }\n    } else {\n      D3DKMT_OPENRESOURCEFROMNTHANDLE open = { };\n      char dummy;\n\n      open.hDevice = device->kmtLocal();\n      open.hNtHandle = sharedHandle;\n      open.NumAllocations = 1;\n      open.pOpenAllocationInfo2 = &alloc;\n      open.pPrivateRuntimeData = &dummy;\n      open.PrivateRuntimeDataSize = 0;\n      open.pTotalPrivateDriverDataBuffer = &dummy;\n      open.TotalPrivateDriverDataBufferSize = 0;\n\n      if (D3DKMTOpenResourceFromNtHandle(&open)) {\n        Logger::warn(\"DxvkMemoryAllocator::createImageResource: Failed to open shared NT handle\");\n      } else {\n        m_kmtLocal = open.hResource;\n\n        if (open.hKeyedMutex) {\n          Logger::warn(str::format(\"DxvkMemoryAllocator::createImageResource: Unexpected bundled keyed mutex\"));\n          D3DKMT_DESTROYKEYEDMUTEX destroy_mutex = { };\n          destroy_mutex.hKeyedMutex = open.hKeyedMutex;\n          D3DKMTDestroyKeyedMutex(&destroy_mutex);\n        }\n        if (open.hSyncObject) {\n          Logger::warn(str::format(\"DxvkMemoryAllocator::createImageResource: Unexpected bundled sync object\"));\n          D3DKMT_DESTROYSYNCHRONIZATIONOBJECT destroy_sync = { };\n          destroy_sync.hSyncObject = open.hSyncObject;\n          D3DKMTDestroySynchronizationObject(&destroy_sync);\n        }\n      }\n      CloseHandle(sharedHandle);\n    }\n#else\n    Logger::warn(\"DxvkResourceAllocation::initKmtHandles: Not implemented on this platform\");\n#endif\n  }\n\n\n  void DxvkResourceAllocation::destroyBufferViews() {\n    if (m_bufferViews) {\n      delete m_bufferViews;\n      m_bufferViews = nullptr;\n    }\n  }\n\n\n\n\n  DxvkResourceAllocationPool::DxvkResourceAllocationPool() {\n\n  }\n\n\n  DxvkResourceAllocationPool::~DxvkResourceAllocationPool() {\n    auto list = m_next;\n\n    while (list) {\n      auto next = list->next;\n      list->~StorageList();\n      list = next;\n    }\n  }\n\n\n  void DxvkResourceAllocationPool::createPool() {\n    auto pool = std::make_unique<StoragePool>();\n    pool->next = std::move(m_pool);\n\n    for (size_t i = 0; i < pool->objects.size(); i++)\n      m_next = new (pool->objects[i].data) StorageList(m_next);\n\n    m_pool = std::move(pool);\n  }\n\n\n\n\n  DxvkResourceAllocation* DxvkLocalAllocationCache::allocateFromCache(\n          VkDeviceSize                size) {\n    uint32_t poolIndex = computePoolIndex(size);\n    DxvkResourceAllocation* allocation = m_pools[poolIndex];\n\n    if (!allocation)\n      return nullptr;\n\n    m_pools[poolIndex] = allocation->m_nextCached;\n    allocation->m_nextCached = nullptr;\n    return allocation;\n  }\n\n\n  DxvkResourceAllocation* DxvkLocalAllocationCache::assignCache(\n          VkDeviceSize                size,\n          DxvkResourceAllocation*     allocation) {\n    uint32_t poolIndex = computePoolIndex(size);\n    return std::exchange(m_pools[poolIndex], allocation);\n  }\n\n\n  void DxvkLocalAllocationCache::freeCache() {\n    if (m_allocator)\n      m_allocator->freeLocalCache(this);\n  }\n\n\n  uint32_t DxvkLocalAllocationCache::computePreferredAllocationCount(\n          VkDeviceSize                size) {\n    uint32_t poolIndex = computePoolIndex(size);\n    uint32_t count = (PoolCapacityInBytes / MinSize) >> poolIndex;\n\n    return std::max(count, 1u);\n  }\n\n\n  uint32_t DxvkLocalAllocationCache::computePoolIndex(\n          VkDeviceSize                size) {\n    return 64u - bit::lzcnt((std::max(size, MinSize) - 1u) / MinSize);\n  }\n\n\n  VkDeviceSize DxvkLocalAllocationCache::computeAllocationSize(\n          uint32_t                    index) {\n    return MinSize << index;\n  }\n\n\n\n\n  DxvkSharedAllocationCache::DxvkSharedAllocationCache(\n          DxvkMemoryAllocator*        allocator)\n  : m_allocator(allocator) {\n    for (uint32_t i = 0; i < m_pools.size(); i++) {\n      VkDeviceSize size = DxvkLocalAllocationCache::computeAllocationSize(i);\n      m_freeLists[i].capacity = DxvkLocalAllocationCache::computePreferredAllocationCount(size);\n    }\n\n    // Initialize unallocated list of lists\n    for (uint32_t i = 0u; i < m_lists.size() - 1u; i++)\n      m_lists[i].next = i + 1;\n\n    m_nextList = 0;\n  }\n\n\n  DxvkSharedAllocationCache::~DxvkSharedAllocationCache() {\n    for (const auto& freeList : m_freeLists)\n      m_allocator->freeCachedAllocations(freeList.head);\n\n    for (const auto& list : m_lists)\n      m_allocator->freeCachedAllocations(list.head);\n  }\n\n\n  DxvkResourceAllocation* DxvkSharedAllocationCache::getAllocationList(\n          VkDeviceSize                allocationSize) {\n    uint32_t poolIndex = DxvkLocalAllocationCache::computePoolIndex(allocationSize);\n\n    // If there's a list ready for us, take the whole thing\n    std::unique_lock poolLock(m_poolMutex);\n    m_numRequests += 1u;\n\n    auto& pool = m_pools[poolIndex];\n    int32_t listIndex = pool.listIndex;\n\n    if (listIndex < 0) {\n      m_numMisses += 1u;\n      return nullptr;\n    }\n\n    if (!(--pool.listCount))\n      pool.drainTime = high_resolution_clock::now();\n\n    // Extract allocations and mark list as free\n    DxvkResourceAllocation* allocation = m_lists[listIndex].head;\n    pool.listIndex = m_lists[listIndex].next;\n\n    m_lists[listIndex].head = nullptr;\n    m_lists[listIndex].next = m_nextList;\n\n    m_nextList = listIndex;\n\n    m_cacheSize -= PoolCapacityInBytes;\n    return allocation;\n  }\n\n\n  DxvkResourceAllocation* DxvkSharedAllocationCache::freeAllocation(\n          DxvkResourceAllocation*     allocation) {\n    uint32_t poolIndex = DxvkLocalAllocationCache::computePoolIndex(allocation->m_size);\n\n    { std::unique_lock freeLock(m_freeMutex);\n      auto& list = m_freeLists[poolIndex];\n\n      allocation->m_nextCached = list.head;\n      list.head = allocation;\n\n      if (++list.size < list.capacity)\n        return nullptr;\n\n      // Free list is full, try to add it to the list array\n      // so that subsequent allocations can use it.\n      list.head = nullptr;\n      list.size = 0u;\n    }\n\n    // Add free list to the pool if possible.\n    { std::unique_lock poolLock(m_poolMutex);\n      auto& pool = m_pools[poolIndex];\n\n      if (unlikely(m_nextList < 0)) {\n        // Cache is currently full, see if we can steal a list from\n        // the largest pool. This automatically balances pool sizes\n        // under cache pressure.\n        uint32_t largestPoolIndex = 0;\n\n        for (uint32_t i = 1; i < PoolCount; i++) {\n          if (m_pools[i].listCount > m_pools[largestPoolIndex].listCount)\n            largestPoolIndex = i;\n        }\n\n        // If the current pool is already (one of) the largest, give up\n        // and free the entire list to avoid pools playing ping-pong.\n        if (m_pools[largestPoolIndex].listCount == pool.listCount)\n          return allocation;\n\n        // Move first list of largest pool to current pool and free any\n        // allocations associated with it.\n        auto& largestPool = m_pools[largestPoolIndex];\n        int32_t listIndex = largestPool.listIndex;\n\n        DxvkResourceAllocation* result = m_lists[listIndex].head;\n        largestPool.listIndex = m_lists[listIndex].next;\n        largestPool.listCount -= 1u;\n\n        m_lists[listIndex].head = allocation;\n        m_lists[listIndex].next = pool.listIndex;\n\n        pool.listIndex = listIndex;\n        pool.listCount += 1u;\n        return result;\n      } else {\n        // Otherwise, allocate a fresh list and assign it to the pool\n        int32_t listIndex = m_nextList;\n        m_nextList = m_lists[listIndex].next;\n\n        m_lists[listIndex].head = allocation;\n        m_lists[listIndex].next = pool.listIndex;\n\n        pool.listIndex = listIndex;\n        pool.listCount += 1u;\n\n        if ((m_cacheSize += PoolCapacityInBytes) > m_maxCacheSize)\n          m_maxCacheSize = m_cacheSize;\n\n        return nullptr;\n      }\n    }\n  }\n\n\n  DxvkSharedAllocationCacheStats DxvkSharedAllocationCache::getStats() {\n    std::unique_lock poolLock(m_poolMutex);\n\n    DxvkSharedAllocationCacheStats result = { };\n    result.requestCount = m_numRequests;\n    result.missCount = m_numMisses;\n    result.size = m_maxCacheSize;\n\n    m_numRequests = 0u;\n    m_numMisses = 0u;\n    m_maxCacheSize = 0u;\n    return result;\n  }\n\n\n  void DxvkSharedAllocationCache::cleanupUnusedFromLockedAllocator(\n          high_resolution_clock::time_point time) {\n    std::unique_lock poolLock(m_poolMutex);\n\n    for (auto& pool : m_pools) {\n      int32_t listIndex = pool.listIndex;\n\n      if (listIndex < 0)\n        continue;\n\n      if (time - pool.drainTime >= std::chrono::seconds(1u)) {\n        m_allocator->freeCachedAllocationsLocked(m_lists[listIndex].head);\n\n        pool.listIndex = m_lists[listIndex].next;\n        pool.listCount -= 1u;\n        pool.drainTime = time;\n\n        m_lists[listIndex].head = nullptr;\n        m_lists[listIndex].next = m_nextList;\n\n        m_nextList = listIndex;\n\n        m_cacheSize -= PoolCapacityInBytes;\n      }\n    }\n  }\n\n\n\n\n  DxvkRelocationList::DxvkRelocationList() {\n\n  }\n\n\n  DxvkRelocationList::~DxvkRelocationList() {\n\n  }\n\n\n  std::vector<DxvkRelocationEntry> DxvkRelocationList::poll(\n          uint32_t                    count,\n          VkDeviceSize                size) {\n    std::lock_guard lock(m_mutex);\n\n    std::vector<DxvkRelocationEntry> result;\n    count = std::min(count, uint32_t(m_entries.size()));\n\n    if (!count)\n      return result;\n\n    result.reserve(count);\n\n    VkDeviceSize totalSize = 0u;\n\n    for (uint32_t i = 0; i < count; i++) {\n      auto iter = m_entries.begin();\n\n      if (totalSize && totalSize + iter->first.size > size)\n        break;\n\n      // Reduce number of resource evictions performed per request by\n      // overestimating the amount of memory moved. May reduce stutter\n      // in high-ish frame rate scenarios.\n      totalSize += iter->second.mode == DxvkAllocationMode::NoDeviceMemory\n        ? iter->first.size * 16u\n        : iter->first.size;\n\n      result.push_back(std::move(iter->second));\n      m_entries.erase(iter);\n    }\n\n    return result;\n  }\n\n\n  void DxvkRelocationList::addResource(\n          Rc<DxvkPagedResource>&&     resource,\n    const DxvkResourceAllocation*     allocation,\n          DxvkAllocationModes         mode) {\n    DxvkResourceMemoryInfo key = { };\n    key.offset = resource->cookie();\n\n    if (allocation)\n      key = allocation->getMemoryInfo();\n\n    std::lock_guard lock(m_mutex);\n    m_entries.emplace(std::piecewise_construct,\n      std::forward_as_tuple(key),\n      std::forward_as_tuple(std::move(resource), mode));\n  }\n\n\n  void DxvkRelocationList::clear() {\n    std::lock_guard lock(m_mutex);\n    m_entries.clear();\n  }\n\n\n\n\n  DxvkMemoryAllocator::DxvkMemoryAllocator(DxvkDevice* device)\n  : m_device(device), m_sharingModeInfo(m_device->getSharingMode()) {\n    VkPhysicalDeviceMemoryProperties memInfo = m_device->adapter()->memoryProperties();\n\n    m_memTypeCount = memInfo.memoryTypeCount;\n    m_memHeapCount = memInfo.memoryHeapCount;\n\n    for (uint32_t i = 0; i < m_memHeapCount; i++) {\n      auto& heap = m_memHeaps[i];\n\n      heap.index = i;\n      heap.memoryBudget = memInfo.memoryHeaps[i].size;\n      heap.properties = memInfo.memoryHeaps[i];\n      heap.enforceBudget = !m_device->isUnifiedMemoryArchitecture()\n        && m_device->properties().core.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;\n    }\n\n    for (uint32_t i = 0; i < m_memTypeCount; i++) {\n      auto& type = m_memTypes[i];\n\n      type.index = i;\n      type.properties = memInfo.memoryTypes[i];\n      type.heap = &m_memHeaps[type.properties.heapIndex];\n      type.heap->memoryTypes |= 1u << i;\n\n      type.devicePool.maxChunkSize = determineMaxChunkSize(type, false);\n      type.mappedPool.maxChunkSize = determineMaxChunkSize(type, true);\n\n      // Uncached system memory is going to be used for large temporary allocations\n      // during resource creation. Account for that by always using full-sized chunks.\n      if ((type.properties.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)\n       && !(type.properties.propertyFlags & (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT)))\n        type.mappedPool.nextChunkSize = type.mappedPool.maxChunkSize;\n    }\n\n    determineMemoryTypesWithPropertyFlags();\n\n    if (device->features().core.features.sparseBinding)\n      m_sparseMemoryTypes = determineSparseMemoryTypes(device);\n\n    determineBufferUsageFlagsPerMemoryType();\n\n    updateMemoryHeapBudgets();\n  }\n  \n  \n  DxvkMemoryAllocator::~DxvkMemoryAllocator() {\n    auto vk = m_device->vkd();\n\n    // Free all resources that are still queued up for relocation\n    // before destroying any allocator structures\n    m_relocations.clear();\n\n    // Destroy shared caches so that any allocations\n    // that are still alive get returned to the device\n    for (uint32_t i = 0; i < m_memTypeCount; i++) {\n      if (m_memTypes[i].sharedCache)\n        delete m_memTypes[i].sharedCache;\n    }\n\n    // Now that no allocations are alive, we can free chunks\n    for (uint32_t i = 0; i < m_memHeapCount; i++)\n      freeEmptyChunksInHeap(m_memHeaps[i], VkDeviceSize(-1), high_resolution_clock::time_point());\n\n    // Ensure adapter allocation statistics are consistent\n    // when the deivce is being destroyed\n    for (uint32_t i = 0; i < m_memHeapCount; i++) {\n      m_device->notifyMemoryStats(i,\n        -m_adapterHeapStats[i].memoryAllocated,\n        -m_adapterHeapStats[i].memoryUsed);\n    }\n  }\n  \n  \n  Rc<DxvkResourceAllocation> DxvkMemoryAllocator::allocateMemory(\n    const VkMemoryRequirements&             requirements,\n    const DxvkAllocationInfo&               allocationInfo) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    // If we're allocating device-local memory, only consider memory types from\n    // the first reported heap. This way, we avoid falling back to HVV on systems\n    // without resizeable BAR by accident.\n    uint32_t memoryTypeMask = requirements.memoryTypeBits & getMemoryTypeMask(allocationInfo.properties);\n\n    if (memoryTypeMask && (allocationInfo.properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT))\n      memoryTypeMask &= m_memTypes[bit::tzcnt(memoryTypeMask)].heap->memoryTypes;\n\n    // Ensure the allocation size is also aligned\n    VkDeviceSize size = align(requirements.size, requirements.alignment);\n\n    for (auto typeIndex : bit::BitMask(memoryTypeMask)) {\n      auto& type = m_memTypes[typeIndex];\n\n      // Use correct memory pool depending on property flags. This way we avoid\n      // wasting address space on fallback allocations, or on UMA devices that\n      // only expose one memory type.\n      auto& selectedPool = (allocationInfo.properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)\n        ? type.mappedPool\n        : type.devicePool;\n\n      // Always try to suballocate first, even if the allocation is\n      // very large. We will decide what to do if this fails.\n      int64_t address = selectedPool.alloc(size, requirements.alignment);\n\n      if (likely(address >= 0))\n        return createAllocation(type, selectedPool, address, size, allocationInfo);\n\n      // If we're not allowed to allocate additional device memory, move on.\n      // Also do not try to revive any chunks marked for defragmentation since\n      // that would defeat the purpose.\n      if (allocationInfo.mode.test(DxvkAllocationMode::NoAllocation))\n        continue;\n\n      // Otherwise, if there are any chunks marked for defragmentation, stop\n      // that process and use any available memory for new allocations.\n      if (selectedPool.pageAllocator.reviveChunks()) {\n        address = selectedPool.alloc(size, requirements.alignment);\n\n        if (address >= 0)\n          return createAllocation(type, selectedPool, address, size, allocationInfo);\n      }\n\n      // If the allocation is very large, use a dedicated allocation instead\n      // of creating a new chunk. This way we avoid excessive fragmentation,\n      // especially when a multiple such resources are created at once.\n      VkDeviceSize maxChunkSize = selectedPool.maxChunkSize;\n      uint32_t minResourcesPerChunk = 4u;\n\n      if (allocationInfo.properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {\n        if (allocationInfo.properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {\n          // For HVV allocations, it may be beneficial to ignore the chunk size\n          // limit if the resource is large. HVV is usually slow to allocate\n          // from and may be very limited in size, so we should avoid dedicated\n          // allocations as well as fragmentation as much as possible.\n          maxChunkSize = DxvkPageAllocator::MaxChunkSize / (env::is32BitHostPlatform() ? 4u : 1u);\n          maxChunkSize = std::min(maxChunkSize, type.heap->properties.size / MinAllocationsPerHeap);\n          maxChunkSize = std::max(maxChunkSize, selectedPool.maxChunkSize);\n\n          // Unlike on system memory heaps, we want to try and fit in multiple\n          // allocations into one chunk because these tend to get discarded often.\n          minResourcesPerChunk = std::clamp(uint32_t(maxChunkSize / size), 1u, 3u);\n        } else {\n          // System memory allocations tend to be more volatile, just be lenient\n          // here and allow a single resource to fill an entire chunk. We will\n          // generally keep multiple chunks of these types around anyway.\n          minResourcesPerChunk = 1u;\n        }\n      }\n\n      if (size * minResourcesPerChunk > maxChunkSize) {\n        DxvkDeviceMemory memory = allocateDeviceMemory(type, requirements.size, nullptr);\n\n        if (!memory.memory)\n          continue;\n\n        mapDeviceMemory(memory, allocationInfo.properties);\n        return createAllocation(type, memory, allocationInfo);\n      }\n\n      // Try to allocate a new chunk that is large enough to hold\n      // multiple resources of the type we're tying to allocate.\n      VkDeviceSize desiredSize = selectedPool.nextChunkSize;\n\n      while (desiredSize < size * minResourcesPerChunk)\n        desiredSize *= 2u;\n\n      if (allocateChunkInPool(type, selectedPool, allocationInfo.properties, size, desiredSize)) {\n        address = selectedPool.alloc(size, requirements.alignment);\n        return createAllocation(type, selectedPool, address, size, allocationInfo);\n      }\n    }\n\n    return nullptr;\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkMemoryAllocator::allocateDedicatedMemory(\n    const VkMemoryRequirements&             requirements,\n    const DxvkAllocationInfo&               allocationInfo,\n    const void*                             next) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    DxvkDeviceMemory memory = { };\n\n    for (auto typeIndex : bit::BitMask(requirements.memoryTypeBits & getMemoryTypeMask(allocationInfo.properties))) {\n      auto& type = m_memTypes[typeIndex];\n      memory = allocateDeviceMemory(type, requirements.size, next);\n\n      if (likely(memory.memory != VK_NULL_HANDLE)) {\n        mapDeviceMemory(memory, allocationInfo.properties);\n        return createAllocation(type, memory, allocationInfo);\n      }\n    }\n\n    return nullptr;\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkMemoryAllocator::createBufferResource(\n    const VkBufferCreateInfo&         createInfo,\n    const DxvkAllocationInfo&         allocationInfo,\n          DxvkLocalAllocationCache*   allocationCache) {\n    Rc<DxvkResourceAllocation> allocation;\n\n    if (likely(!createInfo.flags)) {\n      VkMemoryRequirements memoryRequirements = { };\n      memoryRequirements.size = createInfo.size;\n      memoryRequirements.alignment = GlobalBufferAlignment;\n      memoryRequirements.memoryTypeBits = m_globalBufferMemoryTypes;\n\n      if (unlikely(createInfo.usage & ~m_globalBufferUsageFlags))\n        memoryRequirements.memoryTypeBits = findGlobalBufferMemoryTypeMask(createInfo.usage);\n\n      if (unlikely(allocationInfo.mode.test(DxvkAllocationMode::NoDeviceMemory)))\n        memoryRequirements.memoryTypeBits &= ~getMemoryTypeMask(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      if (likely(memoryRequirements.memoryTypeBits)) {\n        bool allowSuballocation = true;\n\n        // If the given allocation cache supports the memory types and usage\n        // flags that we need, try to use it to service this allocation.\n        // Only use the allocation cache for mappable allocations since those\n        // are expected to happen frequently.\n        if (allocationCache && createInfo.size <= DxvkLocalAllocationCache::MaxSize\n         && allocationCache->m_memoryTypes && !(allocationCache->m_memoryTypes & ~memoryRequirements.memoryTypeBits)\n         && (allocationInfo.properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {\n          allocation = allocationCache->allocateFromCache(createInfo.size);\n\n          if (likely(allocation))\n            return allocation;\n\n          // If the cache is currently empty for the required allocation size,\n          // make sure it's not. This will also initialize the shared caches\n          // for any relevant memory pools as necessary.\n          if (refillAllocationCache(allocationCache, memoryRequirements, allocationInfo.properties))\n            return allocationCache->allocateFromCache(createInfo.size);\n        }\n\n        // If there is at least one memory type that supports the required\n        // buffer usage flags and requested memory properties, suballocate\n        // from a global buffer.\n        if (likely(allowSuballocation)) {\n          allocation = allocateMemory(memoryRequirements, allocationInfo);\n\n          if (likely(allocation && allocation->m_buffer))\n            return allocation;\n\n          if (!allocation && (allocationInfo.properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)\n          && !allocationInfo.mode.test(DxvkAllocationMode::NoFallback)) {\n            DxvkAllocationInfo fallbackInfo = allocationInfo;\n            fallbackInfo.properties &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n            allocation = allocateMemory(memoryRequirements, fallbackInfo);\n\n            if (likely(allocation && allocation->m_buffer))\n              return allocation;\n          }\n\n          // If we can't get an allocation for a global buffer, there's no\n          // real point in retrying with a dedicated buffer since the result\n          // will most likely be the same.\n          if (!allocation) {\n            if (allocationInfo.mode.isClear()) {\n              logMemoryError(memoryRequirements);\n              logMemoryStats();\n            }\n\n            return nullptr;\n          }\n\n          // If we end up here with an allocation but no buffer, something\n          // is weird, but we can keep the allocation around for now.\n          if (!allocation->m_buffer) {\n            Logger::err(str::format(\"Got allocation from memory type \",\n              allocation->m_type->index, \" without global buffer\"));\n          }\n        }\n      }\n    }\n\n    // If we can't suballocate from an existing global buffer\n    // for any reason, create a dedicated buffer resource.\n    auto vk = m_device->vkd();\n\n    VkBuffer buffer = VK_NULL_HANDLE;\n    VkResult vr = vk->vkCreateBuffer(vk->device(),\n      &createInfo, nullptr, &buffer);\n\n    if (vr != VK_SUCCESS) {\n      throw DxvkError(str::format(\"Failed to create buffer: \", vr,\n        \"\\n  size:    \", createInfo.size,\n        \"\\n  usage:   \", std::hex, createInfo.usage,\n        \"\\n  flags:   \", createInfo.flags));\n    }\n\n    if (!(createInfo.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT)) {\n      VkBufferMemoryRequirementsInfo2 requirementInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2 };\n      requirementInfo.buffer = buffer;\n\n      VkMemoryRequirements2 requirements = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 };\n      vk->vkGetBufferMemoryRequirements2(vk->device(), &requirementInfo, &requirements);\n\n      if (unlikely(allocationInfo.mode.test(DxvkAllocationMode::NoDeviceMemory)))\n        requirements.memoryRequirements.memoryTypeBits &= ~getMemoryTypeMask(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      // When allocating memory for a descriptor heap, use a dedicated allocation. We\n      // ca expect these to be long-lived and mapped, and potentially use a dedicated\n      // memory type that may have unexpected size restrictions. Also make sure not\n      // to ever relocate these buffers since they require a stable GPU address.\n      if (createInfo.usage & (DescriptorBufferUsage | DescriptorHeapUsage)) {\n        VkMemoryDedicatedAllocateInfo dedicatedInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO };\n        dedicatedInfo.buffer = buffer;\n\n        if ((allocation = allocateDedicatedMemory(requirements.memoryRequirements, allocationInfo, &dedicatedInfo)))\n          allocation->m_flags.clr(DxvkAllocationFlag::CanMove);\n      }\n\n      // If we have an existing global allocation from earlier, make sure it is suitable\n      if (!allocation || !(requirements.memoryRequirements.memoryTypeBits & (1u << allocation->m_type->index))\n       || (allocation->m_size < requirements.memoryRequirements.size)\n       || (allocation->m_address & requirements.memoryRequirements.alignment))\n        allocation = allocateMemory(requirements.memoryRequirements, allocationInfo);\n\n      if (!allocation && (allocationInfo.properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)\n       && !allocationInfo.mode.test(DxvkAllocationMode::NoFallback)) {\n        DxvkAllocationInfo fallbackInfo = allocationInfo;\n        fallbackInfo.properties &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n        allocation = allocateMemory(requirements.memoryRequirements, fallbackInfo);\n      }\n\n      if (!allocation && allocationInfo.mode.isClear()) {\n        logMemoryError(requirements.memoryRequirements);\n        logMemoryStats();\n      }\n    } else {\n      allocation = createAllocation(\n        new DxvkSparsePageTable(m_device, createInfo, buffer),\n        allocationInfo);\n    }\n\n    if (!allocation) {\n      vk->vkDestroyBuffer(vk->device(), buffer, nullptr);\n      return nullptr;\n    }\n\n    // Transfer ownership of te Vulkan buffer to the allocation\n    // and set up all remaining properties.\n    allocation->m_flags.set(DxvkAllocationFlag::OwnsBuffer);\n    allocation->m_buffer = buffer;\n    allocation->m_bufferOffset = 0u;\n    allocation->m_bufferAddress = 0u;\n\n    // Bind memory if the buffer is not sparse\n    if (allocation->m_memory) {\n      vr = vk->vkBindBufferMemory(vk->device(), allocation->m_buffer,\n        allocation->m_memory, allocation->m_address & DxvkPageAllocator::ChunkAddressMask);\n\n      if (vr != VK_SUCCESS) {\n        throw DxvkError(str::format(\"Failed to bind buffer memory: \", vr,\n          \"\\n  size:    \", createInfo.size,\n          \"\\n  usage:   \", std::hex, createInfo.usage,\n          \"\\n  flags:   \", createInfo.flags));\n      }\n    }\n\n    // Query device address after binding memory, or the address would be invalid\n    if (createInfo.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)\n      allocation->m_bufferAddress = getBufferDeviceAddress(buffer);\n\n    return allocation;\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkMemoryAllocator::createImageResource(\n    const VkImageCreateInfo&          createInfo,\n    const DxvkAllocationInfo&         allocationInfo,\n    const void*                       next) {\n    auto vk = m_device->vkd();\n\n    VkImage image = VK_NULL_HANDLE;\n    VkResult vr = vk->vkCreateImage(vk->device(), &createInfo, nullptr, &image);\n\n    if (vr != VK_SUCCESS) {\n      throw DxvkError(str::format(\"Failed to create image: \", vr,\n        \"\\n  type:    \", createInfo.imageType,\n        \"\\n  format:  \", createInfo.format,\n        \"\\n  extent:  \", createInfo.extent.width, \"x\", createInfo.extent.height, \"x\", createInfo.extent.depth,\n        \"\\n  layers:  \", createInfo.arrayLayers,\n        \"\\n  mips:    \", createInfo.mipLevels,\n        \"\\n  samples: \", createInfo.samples));\n    }\n\n    // Check memory requirements, including whether or not we need a dedicated allocation\n    VkMemoryDedicatedRequirements dedicatedRequirements = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS };\n\n    VkImageMemoryRequirementsInfo2 requirementInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2 };\n    requirementInfo.image = image;\n\n    VkMemoryRequirements2 requirements = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicatedRequirements };\n    vk->vkGetImageMemoryRequirements2(vk->device(), &requirementInfo, &requirements);\n\n    // For shared resources, we always require a dedicated allocation\n    if (next) {\n      dedicatedRequirements.requiresDedicatedAllocation = VK_TRUE;\n      dedicatedRequirements.prefersDedicatedAllocation = VK_TRUE;\n    }\n\n    if (!dedicatedRequirements.requiresDedicatedAllocation && allocationInfo.mode.test(DxvkAllocationMode::NoDedicated))\n      dedicatedRequirements.prefersDedicatedAllocation = VK_FALSE;\n\n    Rc<DxvkResourceAllocation> allocation;\n\n    if (!(createInfo.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT)) {\n      // Force the resource to go into a system memory type if requested\n      if (unlikely(allocationInfo.mode.test(DxvkAllocationMode::NoDeviceMemory)))\n        requirements.memoryRequirements.memoryTypeBits &= ~getMemoryTypeMask(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      // If a dedicated allocation is at least preferred for this resource, try this first\n      if (!allocation && dedicatedRequirements.prefersDedicatedAllocation\n       && !allocationInfo.mode.test(DxvkAllocationMode::NoAllocation)) {\n        VkMemoryDedicatedAllocateInfo dedicatedInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, next };\n        dedicatedInfo.image = image;\n\n        allocation = allocateDedicatedMemory(requirements.memoryRequirements,\n          allocationInfo, &dedicatedInfo);\n\n        // Only retry with a dedicated sysmem allocation if a dedicated allocation\n        // is required. Otherwise, we should try to suballocate in device memory.\n        if (!allocation && dedicatedRequirements.requiresDedicatedAllocation\n         && !allocationInfo.mode.test(DxvkAllocationMode::NoFallback)\n         && (allocationInfo.properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {\n          DxvkAllocationInfo fallbackInfo = allocationInfo;\n          fallbackInfo.properties &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n          allocation = allocateDedicatedMemory(requirements.memoryRequirements,\n            fallbackInfo, &dedicatedInfo);\n        }\n      }\n\n      if (!allocation && !dedicatedRequirements.requiresDedicatedAllocation) {\n        // Pad alignment as necessary to not overlap tiled and linear memory.\n        if (createInfo.tiling == VK_IMAGE_TILING_OPTIMAL) {\n          requirements.memoryRequirements.alignment = std::max(\n            requirements.memoryRequirements.alignment,\n            m_device->properties().core.properties.limits.bufferImageGranularity);\n        }\n\n        // Try to suballocate memory and fall back to system memory on error.\n        allocation = allocateMemory(requirements.memoryRequirements, allocationInfo);\n\n        if (!allocation && (allocationInfo.properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)\n         && !allocationInfo.mode.test(DxvkAllocationMode::NoFallback)) {\n          DxvkAllocationInfo fallbackInfo = allocationInfo;\n          fallbackInfo.properties &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n          allocation = allocateMemory(requirements.memoryRequirements, fallbackInfo);\n        }\n      }\n    } else {\n      // Create a sparse page table and determine whether we need to allocate\n      // actual memory for the metadata aspect of the image or not.\n      auto pageTable = std::make_unique<DxvkSparsePageTable>(m_device, createInfo, image);\n      auto pageProperties = pageTable->getProperties();\n\n      if (pageProperties.metadataPageCount) {\n        VkMemoryRequirements metadataRequirements = { };\n        metadataRequirements.size = SparseMemoryPageSize * pageProperties.metadataPageCount;\n        metadataRequirements.alignment = SparseMemoryPageSize;\n        metadataRequirements.memoryTypeBits = requirements.memoryRequirements.memoryTypeBits;\n\n        DxvkAllocationInfo metadataInfo = { };\n        metadataInfo.properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n        allocation = allocateMemory(metadataRequirements, metadataInfo);\n\n        if (!allocation) {\n          metadataInfo.properties = 0u;\n          allocation = allocateMemory(metadataRequirements, metadataInfo);\n        }\n\n        if (allocation)\n          allocation->m_sparsePageTable = pageTable.release();\n      } else {\n        // Just need a page table, but no memory\n        allocation = createAllocation(pageTable.release(), allocationInfo);\n      }\n    }\n\n    if (!allocation) {\n      vk->vkDestroyImage(vk->device(), image, nullptr);\n\n      if (allocationInfo.mode.isClear()) {\n        logMemoryError(requirements.memoryRequirements);\n        logMemoryStats();\n      }\n\n      return nullptr;\n    }\n\n    // Set up allocation object and bind memory\n    allocation->m_flags.set(DxvkAllocationFlag::OwnsImage);\n    allocation->m_image = image;\n\n    if (allocation->m_memory) {\n      vr = vk->vkBindImageMemory(vk->device(), image, allocation->m_memory,\n        allocation->m_address & DxvkPageAllocator::ChunkAddressMask);\n\n      if (vr != VK_SUCCESS) {\n        throw DxvkError(str::format(\"Failed to bind image memory: \", vr,\n          \"\\n  type:    \", createInfo.imageType,\n          \"\\n  format:  \", createInfo.format,\n          \"\\n  extent:  \", createInfo.extent.width, \"x\", createInfo.extent.height, \"x\", createInfo.extent.depth,\n          \"\\n  layers:  \", createInfo.arrayLayers,\n          \"\\n  mips:    \", createInfo.mipLevels,\n          \"\\n  samples: \", createInfo.samples));\n      }\n    }\n\n    if (allocationInfo.handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM)\n      allocation->initKmtHandles(allocationInfo.handleType);\n\n    return allocation;\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkMemoryAllocator::createSparsePage() {\n    VkMemoryRequirements requirements = { };\n    requirements.size = SparseMemoryPageSize;\n    requirements.alignment = SparseMemoryPageSize;\n    requirements.memoryTypeBits = m_sparseMemoryTypes;\n\n    // Try device memory first, fall back to system memory if that fails.\n    // We might get an allocation with a global buffer, just ignore that.\n    DxvkAllocationInfo allocationInfo = { };\n    allocationInfo.properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\n    auto allocation = allocateMemory(requirements, allocationInfo);\n\n    if (!allocation) {\n      allocationInfo.properties = 0u;\n      allocation = allocateMemory(requirements, allocationInfo);\n    }\n\n    if (!allocation)\n      return nullptr;\n\n    return allocation;\n  }\n\n\n  DxvkLocalAllocationCache DxvkMemoryAllocator::createAllocationCache(\n          VkBufferUsageFlags          bufferUsage,\n          VkMemoryPropertyFlags       properties) {\n    uint32_t memoryTypeMask = m_globalBufferMemoryTypes;\n\n    if (bufferUsage & ~m_globalBufferUsageFlags)\n      memoryTypeMask = findGlobalBufferMemoryTypeMask(bufferUsage);\n\n    memoryTypeMask &= getMemoryTypeMask(properties);\n    return DxvkLocalAllocationCache(this, memoryTypeMask);\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkMemoryAllocator::importBufferResource(\n    const VkBufferCreateInfo&         createInfo,\n    const DxvkAllocationInfo&         allocationInfo,\n    const DxvkBufferImportInfo&       importInfo) {\n    Rc<DxvkResourceAllocation> allocation = m_allocationPool.create(this, nullptr);\n    allocation->m_flags.set(DxvkAllocationFlag::Imported);\n    allocation->m_resourceCookie = allocation->m_resourceCookie;\n    allocation->m_size = createInfo.size;\n    allocation->m_mapPtr = importInfo.mapPtr;\n    allocation->m_buffer = importInfo.buffer;\n    allocation->m_bufferOffset = importInfo.offset;\n\n    if (createInfo.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)\n      allocation->m_bufferAddress = getBufferDeviceAddress(importInfo.buffer) + importInfo.offset;\n\n    return allocation;\n  }\n\n\n  Rc<DxvkResourceAllocation> DxvkMemoryAllocator::importImageResource(\n    const VkImageCreateInfo&          createInfo,\n    const DxvkAllocationInfo&         allocationInfo,\n          VkImage                     imageHandle) {\n    Rc<DxvkResourceAllocation> allocation = m_allocationPool.create(this, nullptr);\n    allocation->m_flags.set(DxvkAllocationFlag::Imported);\n    allocation->m_resourceCookie = allocation->m_resourceCookie;\n    allocation->m_image = imageHandle;\n\n    return allocation;\n  }\n\n\n  DxvkDeviceMemory DxvkMemoryAllocator::allocateDeviceMemory(\n          DxvkMemoryType&       type,\n          VkDeviceSize          size,\n    const void*                 next) {\n    auto vk = m_device->vkd();\n\n    // If global buffers are enabled for this allocation, pad the allocation size\n    // to a multiple of the global buffer alignment. This can happen when we create\n    // a dedicated allocation for a large resource.\n    if (type.bufferUsage && !next)\n      size = align(size, GlobalBufferAlignment);\n\n    // Preemptively free some unused allocations to reduce memory waste\n    freeEmptyChunksInHeap(*type.heap, size, high_resolution_clock::now());\n\n    // If we're exceeding vram budget on a dedicated GPU, fall back to system memory.\n    if (!next && type.heap->enforceBudget && (getMemoryStats(type.heap->index).memoryAllocated + size > type.heap->memoryBudget)) {\n      type.heap->enableEviction = true;\n      return DxvkDeviceMemory();\n    }\n\n    VkMemoryAllocateInfo memoryInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, next };\n    memoryInfo.allocationSize = size;\n    memoryInfo.memoryTypeIndex = type.index;\n\n    // Decide on a memory priority based on the memory type and allocation properties\n    VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };\n\n    if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {\n      if (next) {\n        // Dedicated allocation, may or may not be a shared resource. Assign this the\n        // highest priority since this is expected to be a high-bandwidth resource,\n        // such as a render target or a descriptor heap. The latter may be host-visible.\n        priorityInfo.priority = 1.0f;\n      } else if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {\n        // Regular HVV allocation. Give this a low priority since these are typically\n        // useful when when placed in system memory.\n        priorityInfo.priority = 0.0f;\n      } else {\n        // Standard priority for resource allocations\n        priorityInfo.priority = 0.5f;\n      }\n\n      if (m_device->features().extMemoryPriority.memoryPriority)\n        priorityInfo.pNext = std::exchange(memoryInfo.pNext, &priorityInfo);\n    }\n\n    // If buffers can be created on this memory type, also enable the device address bit\n    VkMemoryAllocateFlagsInfo memoryFlags = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO };\n\n    if (type.bufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) {\n      memoryFlags.pNext = std::exchange(memoryInfo.pNext, &memoryFlags);\n      memoryFlags.flags |= VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;\n    }\n\n    // Try to allocate memory. If this fails, free any remaining\n    // unused memory from the heap and try again.\n    DxvkDeviceMemory result = { };\n    result.size = size;\n\n    if (vk->vkAllocateMemory(vk->device(), &memoryInfo, nullptr, &result.memory)) {\n      freeEmptyChunksInHeap(*type.heap, VkDeviceSize(-1), high_resolution_clock::time_point());\n\n      if (vk->vkAllocateMemory(vk->device(), &memoryInfo, nullptr, &result.memory))\n        return DxvkDeviceMemory();\n    }\n\n    // Technically redundant if EXT_memory_priority is also supported, but this shouldn't hurt\n    if (m_device->features().extPageableDeviceLocalMemory.pageableDeviceLocalMemory)\n      vk->vkSetDeviceMemoryPriorityEXT(vk->device(), result.memory, priorityInfo.priority);\n\n    // Create global buffer if the allocation supports it\n    if (type.bufferUsage && !next) {\n      VkBuffer buffer = VK_NULL_HANDLE;\n\n      VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };\n      bufferInfo.size = size;\n      bufferInfo.usage = type.bufferUsage;\n      m_sharingModeInfo.fill(bufferInfo);\n\n      VkResult status = vk->vkCreateBuffer(vk->device(), &bufferInfo, nullptr, &buffer);\n\n      if (status == VK_SUCCESS) {\n        VkBufferMemoryRequirementsInfo2 memInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2 };\n        memInfo.buffer = buffer;\n\n        VkMemoryRequirements2 requirements = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 };\n        vk->vkGetBufferMemoryRequirements2(vk->device(), &memInfo, &requirements);\n\n        if ((requirements.memoryRequirements.size == size)\n         && (requirements.memoryRequirements.memoryTypeBits & (1u << type.index))) {\n          status = vk->vkBindBufferMemory(vk->device(), buffer, result.memory, 0);\n\n          if (status == VK_SUCCESS) {\n            result.buffer = buffer;\n\n            if (type.bufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)\n              result.gpuVa = getBufferDeviceAddress(buffer);\n          }\n        }\n\n        if (!result.buffer)\n          vk->vkDestroyBuffer(vk->device(), buffer, nullptr);\n      }\n\n      if (!result.buffer) {\n        Logger::warn(str::format(\"Failed to create global buffer:\",\n          \"\\n  size:  \", std::dec, size,\n          \"\\n  usage: \", std::hex, type.bufferUsage,\n          \"\\n  type:  \", std::dec, type.index));\n      }\n    }\n\n    result.cookie = ++m_nextCookie;\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      assignMemoryDebugName(result, type);\n\n    type.stats.memoryAllocated += size;\n    return result;\n  }\n\n\n  void DxvkMemoryAllocator::assignMemoryDebugName(\n    const DxvkDeviceMemory&     memory,\n    const DxvkMemoryType&       type) {\n    auto vk = m_device->vkd();\n\n    const char* memoryType = \"Unspecified memory\";\n\n    if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {\n      if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)\n        memoryType = \"Cached system memory\";\n      else if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)\n        memoryType = \"Mapped video memory\";\n      else\n        memoryType = \"Write-combined system memory\";\n    } else if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {\n      memoryType = \"Video memory\";\n    }\n\n    std::string memoryName = str::format(memoryType, \" (\", memory.cookie, \")\");\n\n    VkDebugUtilsObjectNameInfoEXT nameInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT };\n    nameInfo.objectType = VK_OBJECT_TYPE_DEVICE_MEMORY;\n    nameInfo.objectHandle = vk::getObjectHandle(memory.memory);\n    nameInfo.pObjectName = memoryName.c_str();\n\n    vk->vkSetDebugUtilsObjectNameEXT(vk->device(), &nameInfo);\n\n    if (memory.buffer) {\n      std::string bufferName = str::format(\"Global buffer (\", memory.cookie, \")\");\n\n      nameInfo.objectType = VK_OBJECT_TYPE_BUFFER;\n      nameInfo.objectHandle = vk::getObjectHandle(memory.buffer);\n      nameInfo.pObjectName = bufferName.c_str();\n\n      vk->vkSetDebugUtilsObjectNameEXT(vk->device(), &nameInfo);\n    }\n  }\n\n\n  bool DxvkMemoryAllocator::allocateChunkInPool(\n          DxvkMemoryType&       type,\n          DxvkMemoryPool&       pool,\n          VkMemoryPropertyFlags properties,\n          VkDeviceSize          requiredSize,\n          VkDeviceSize          desiredSize) {\n    // Try to allocate device memory. If the allocation fails, retry with\n    // a smaller size until we reach a point where we cannot service the\n    // allocation.\n    DxvkDeviceMemory chunk = { };\n\n    while (!chunk.memory && desiredSize >= std::max(requiredSize, DxvkMemoryPool::MinChunkSize)) {\n      chunk = allocateDeviceMemory(type, desiredSize, nullptr);\n      desiredSize /= 2u;\n    }\n\n    if (!chunk.memory)\n      return false;\n\n    mapDeviceMemory(chunk, properties);\n\n    // If we expect the application to require more memory in the\n    // future, increase the chunk size for subsequent allocations.\n    if (pool.nextChunkSize < pool.maxChunkSize\n     && pool.nextChunkSize <= type.stats.memoryAllocated / 2u)\n      pool.nextChunkSize *= 2u;\n\n    // Add the newly created chunk to the pool\n    uint32_t chunkIndex = pool.pageAllocator.addChunk(chunk.size);\n\n    pool.chunks.resize(std::max<size_t>(pool.chunks.size(), chunkIndex + 1u));\n    pool.chunks[chunkIndex].memory = chunk;\n    pool.chunks[chunkIndex].unusedTime = high_resolution_clock::time_point();\n    pool.chunks[chunkIndex].canMove = true;\n    return true;\n  }\n\n\n  DxvkResourceAllocation* DxvkMemoryAllocator::createAllocation(\n          DxvkMemoryType&       type,\n          DxvkMemoryPool&       pool,\n          VkDeviceSize          address,\n          VkDeviceSize          size,\n    const DxvkAllocationInfo&   allocationInfo) {\n    type.stats.memoryUsed += size;\n\n    uint32_t chunkIndex = address >> DxvkPageAllocator::ChunkAddressBits;\n    VkDeviceSize offset = address & DxvkPageAllocator::ChunkAddressMask;\n\n    auto& chunk = pool.chunks[chunkIndex];\n    chunk.unusedTime = high_resolution_clock::time_point();\n\n    auto allocation = m_allocationPool.create(this, &type);\n\n    if (!(allocationInfo.properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && allocationInfo.resourceCookie)\n      allocation->m_flags.set(DxvkAllocationFlag::CanMove);\n\n    allocation->m_resourceCookie = allocationInfo.resourceCookie;\n    allocation->m_memory = chunk.memory.memory;\n    allocation->m_address = address;\n    allocation->m_size = size;\n\n    if (chunk.memory.mapPtr) {\n      allocation->m_mapPtr = reinterpret_cast<char*>(chunk.memory.mapPtr) + offset;\n\n      if (unlikely(m_device->config().zeroMappedMemory)) {\n        // Some games will not write mapped buffers and will break if\n        // there is any stale data stored within. Clear when the allocation is\n        // freed, so that subsequent allocations will receive cleared buffers.\n        allocation->m_flags.set(DxvkAllocationFlag::ClearOnFree);\n      }\n    }\n\n    if (chunk.memory.buffer) {\n      allocation->m_buffer = chunk.memory.buffer;\n      allocation->m_bufferOffset = offset;\n      allocation->m_bufferAddress = chunk.memory.gpuVa\n        ? chunk.memory.gpuVa + offset : 0u;\n    }\n\n    if (&pool == &type.devicePool)\n      chunk.addAllocation(allocation);\n\n    return allocation;\n  }\n\n\n  DxvkResourceAllocation* DxvkMemoryAllocator::createAllocation(\n          DxvkSparsePageTable*  sparsePageTable,\n    const DxvkAllocationInfo&   allocationInfo) {\n    auto allocation = m_allocationPool.create(this, nullptr);\n    allocation->m_resourceCookie = allocationInfo.resourceCookie;\n    allocation->m_sparsePageTable = sparsePageTable;\n\n    return allocation;\n  }\n\n\n  DxvkResourceAllocation* DxvkMemoryAllocator::createAllocation(\n          DxvkMemoryType&       type,\n    const DxvkDeviceMemory&     memory,\n    const DxvkAllocationInfo&   allocationInfo) {\n    type.stats.memoryUsed += memory.size;\n\n    auto allocation = m_allocationPool.create(this, &type);\n    allocation->m_flags.set(DxvkAllocationFlag::OwnsMemory);\n\n    if (memory.buffer)\n      allocation->m_flags.set(DxvkAllocationFlag::OwnsBuffer);\n\n    if (!(allocationInfo.properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && allocationInfo.resourceCookie)\n      allocation->m_flags.set(DxvkAllocationFlag::CanMove);\n\n    allocation->m_resourceCookie = allocationInfo.resourceCookie;\n    allocation->m_memory = memory.memory;\n    allocation->m_address = DedicatedChunkAddress;\n    allocation->m_size = memory.size;\n    allocation->m_mapPtr = memory.mapPtr;\n\n    allocation->m_buffer = memory.buffer;\n    allocation->m_bufferAddress = memory.gpuVa;\n    return allocation;\n  }\n\n\n  void DxvkMemoryAllocator::freeDeviceMemory(\n          DxvkMemoryType&       type,\n          DxvkDeviceMemory      memory) {\n    auto vk = m_device->vkd();\n    vk->vkDestroyBuffer(vk->device(), memory.buffer, nullptr);\n    vk->vkFreeMemory(vk->device(), memory.memory, nullptr);\n\n    type.stats.memoryAllocated -= memory.size;\n  }\n\n\n  void DxvkMemoryAllocator::freeAllocation(\n          DxvkResourceAllocation* allocation) {\n    if (allocation->m_flags.test(DxvkAllocationFlag::ClearOnFree)) {\n      if (allocation->m_mapPtr)\n        bit::bclear(allocation->m_mapPtr, allocation->m_size);\n    }\n\n    if (allocation->m_flags.test(DxvkAllocationFlag::CanCache)) {\n      // Return cacheable allocations to the shared cache\n      allocation->destroyBufferViews();\n\n      if (allocation->m_type->sharedCache)\n        allocation = allocation->m_type->sharedCache->freeAllocation(allocation);\n\n      // If we get a list of allocations back from the\n      // shared cache, free all of them in one go\n      freeCachedAllocations(allocation);\n    } else {\n      std::unique_lock lock(m_mutex);\n\n      if (likely(allocation->m_type)) {\n        allocation->m_type->stats.memoryUsed -= allocation->m_size;\n\n        if (unlikely(allocation->m_flags.test(DxvkAllocationFlag::OwnsMemory))) {\n          // We free the actual allocation later, just update stats here.\n          allocation->m_type->stats.memoryAllocated -= allocation->m_size;\n        } else {\n          DxvkMemoryPool& pool = allocation->m_mapPtr\n            ? allocation->m_type->mappedPool\n            : allocation->m_type->devicePool;\n\n          if (!allocation->m_mapPtr) {\n            uint32_t chunkIndex = allocation->m_address >> DxvkPageAllocator::ChunkAddressBits;\n            pool.chunks[chunkIndex].removeAllocation(allocation);\n          }\n\n          if (unlikely(pool.free(allocation->m_address, allocation->m_size))) {\n            uint32_t chunkIndex = allocation->m_address >> DxvkPageAllocator::ChunkAddressBits;\n            pool.chunks[chunkIndex].canMove = true;\n\n            if (freeEmptyChunksInPool(*allocation->m_type, pool, 0, high_resolution_clock::now()))\n              updateMemoryHeapStats(allocation->m_type->properties.heapIndex);\n          }\n        }\n      }\n\n      m_allocationPool.free(allocation);\n    }\n  }\n\n\n  void DxvkMemoryAllocator::freeLocalCache(\n          DxvkLocalAllocationCache* cache) {\n    std::unique_lock lock(m_mutex);\n\n    for (size_t i = 0; i < cache->m_pools.size(); i++)\n      freeCachedAllocationsLocked(std::exchange(cache->m_pools[i], nullptr));\n  }\n\n\n  void DxvkMemoryAllocator::freeCachedAllocations(\n          DxvkResourceAllocation* allocation) {\n    if (allocation) {\n      std::unique_lock lock(m_mutex);\n      freeCachedAllocationsLocked(allocation);\n    }\n  }\n\n\n  void DxvkMemoryAllocator::freeCachedAllocationsLocked(\n          DxvkResourceAllocation* allocation) {\n    while (allocation) {\n      auto& pool = allocation->m_mapPtr\n        ? allocation->m_type->mappedPool\n        : allocation->m_type->devicePool;\n\n      // Cached allocations may have a reference count of 0, but they\n      // still own the memory, so make sure to release it here.\n      allocation->m_type->stats.memoryUsed -= allocation->m_size;\n\n      if (unlikely(pool.free(allocation->m_address, allocation->m_size))) {\n        if (freeEmptyChunksInPool(*allocation->m_type, pool, 0, high_resolution_clock::now()))\n          updateMemoryHeapStats(allocation->m_type->properties.heapIndex);\n      }\n\n      m_allocationPool.free(std::exchange(allocation, allocation->m_nextCached));\n    }\n  }\n\n\n  void DxvkMemoryAllocator::freeEmptyChunksInHeap(\n    const DxvkMemoryHeap&       heap,\n          VkDeviceSize          allocationSize,\n          high_resolution_clock::time_point time) {\n    bool freed = false;\n\n    for (auto typeIndex : bit::BitMask(heap.memoryTypes)) {\n      auto& type = m_memTypes[typeIndex];\n\n      freed |= freeEmptyChunksInPool(type, type.devicePool, allocationSize, time);\n      freed |= freeEmptyChunksInPool(type, type.mappedPool, allocationSize, time);\n    }\n\n    if (freed)\n      updateMemoryHeapStats(heap.index);\n  }\n\n\n  bool DxvkMemoryAllocator::freeEmptyChunksInPool(\n          DxvkMemoryType&       type,\n          DxvkMemoryPool&       pool,\n          VkDeviceSize          allocationSize,\n          high_resolution_clock::time_point time) {\n    // Allow for one unused max-size chunk on device-local memory types.\n    // For mapped memory allocations, we need to be more lenient since\n    // applications will frequently allocate staging buffers or dynamic\n    // resources.\n    VkDeviceSize maxUnusedMemory = pool.maxChunkSize;\n\n    if (&pool == &type.mappedPool)\n      maxUnusedMemory *= 4u;\n\n    // Factor current memory allocation into the decision to free chunks\n    VkDeviceSize heapBudget = type.heap->memoryBudget;\n    VkDeviceSize heapAllocated = getMemoryStats(type.heap->index).memoryAllocated;\n\n    VkDeviceSize unusedMemory = 0u;\n\n    bool chunkFreed = false;\n\n    for (uint32_t i = 0; i < pool.chunks.size(); i++) {\n      DxvkMemoryChunk& chunk = pool.chunks[i];\n\n      if (!chunk.memory.memory || pool.pageAllocator.pagesUsed(i))\n        continue;\n\n      // Free the chunk if it is smaller than the current chunk size of\n      // the pool, since it is unlikely to be useful for future allocations.\n      // Also free if the pending allocation would exceed the heap budget.\n      bool shouldFree = chunk.memory.size < pool.nextChunkSize\n        || allocationSize + heapAllocated > heapBudget\n        || allocationSize > heapBudget;\n\n      // If we still don't free the chunk under these conditions, count it\n      // towards unused memory in the current memory pool. Once we exceed\n      // the limit, free any empty chunk we encounter.\n      if (!shouldFree) {\n        unusedMemory += chunk.memory.size;\n        shouldFree = unusedMemory > maxUnusedMemory;\n      }\n\n      // Free chunks that have not been used in some time, but only free\n      // one chunk per iteration. Reset the timer if we already freed one.\n      if (!shouldFree && time != high_resolution_clock::time_point()) {\n        if (chunk.unusedTime == high_resolution_clock::time_point() || chunkFreed)\n          chunk.unusedTime = time;\n        else\n          shouldFree = time - chunk.unusedTime >= std::chrono::seconds(20);\n      }\n\n      if (shouldFree) {\n        freeDeviceMemory(type, chunk.memory);\n        heapAllocated -= chunk.memory.size;\n\n        chunk = DxvkMemoryChunk();\n        pool.pageAllocator.removeChunk(i);\n\n        chunkFreed = true;\n      }\n    }\n\n    return chunkFreed;\n  }\n\n\n  int32_t DxvkMemoryAllocator::findEmptyChunkInPool(\n    const DxvkMemoryPool&       pool,\n          VkDeviceSize          minSize,\n          VkDeviceSize          maxSize) const {\n    for (uint32_t i = 0; i < pool.chunks.size(); i++) {\n      const auto& chunk = pool.chunks[i].memory;\n\n      if (chunk.memory && chunk.size >= minSize && chunk.size <= maxSize\n       && !pool.pageAllocator.pagesUsed(i))\n        return int32_t(i);\n    }\n\n    return -1;\n  }\n\n\n  void DxvkMemoryAllocator::mapDeviceMemory(\n          DxvkDeviceMemory&     memory,\n          VkMemoryPropertyFlags properties) {\n    if (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {\n      if (memory.mapPtr)\n        return;\n\n      auto vk = m_device->vkd();\n\n      VkResult vr = vk->vkMapMemory(vk->device(),\n        memory.memory, 0, memory.size, 0, &memory.mapPtr);\n\n      if (vr != VK_SUCCESS) {\n        throw DxvkError(str::format(\"Failed to map Vulkan memory: \", vr,\n          \"\\n  size: \", memory.size, \" bytes\"));\n      }\n\n      if (m_device->config().zeroMappedMemory)\n        bit::bclear(memory.mapPtr, memory.size);\n\n      Logger::debug(str::format(\"Mapped memory region 0x\", std::hex,\n        reinterpret_cast<uintptr_t>(memory.mapPtr), \" - 0x\",\n        reinterpret_cast<uintptr_t>(memory.mapPtr) + memory.size - 1u));\n    } else {\n      if (!memory.mapPtr)\n        return;\n\n      auto vk = m_device->vkd();\n      vk->vkUnmapMemory(vk->device(), memory.memory);\n\n      Logger::debug(str::format(\"Unmapped memory region 0x\", std::hex,\n        reinterpret_cast<uintptr_t>(memory.mapPtr), \" - 0x\",\n        reinterpret_cast<uintptr_t>(memory.mapPtr) + memory.size - 1u));\n\n      memory.mapPtr = nullptr;\n    }\n  }\n\n\n  bool DxvkMemoryAllocator::refillAllocationCache(\n          DxvkLocalAllocationCache*   cache,\n    const VkMemoryRequirements&       requirements,\n          VkMemoryPropertyFlags       properties) {\n    // Ensure that all cached allocations report a power-of-two size.\n    // The shared cache implementation currently relies on this.\n    VkDeviceSize allocationSize = (VkDeviceSize(-1) >> bit::lzcnt(requirements.size - 1u)) + 1u;\n    allocationSize = std::max(allocationSize, DxvkLocalAllocationCache::MinSize);\n\n    // Maximum number of allocations when we miss in the shared cache\n    uint32_t allocationCount = DxvkLocalAllocationCache::computePreferredAllocationCount(allocationSize);\n\n    for (auto typeIndex : bit::BitMask(cache->m_memoryTypes)) {\n      auto& memoryType = m_memTypes[typeIndex];\n\n      // Initialize shared cache on demand only\n      if (unlikely(!memoryType.sharedCache)) {\n        std::unique_lock lock(m_mutex);\n\n        if (!memoryType.sharedCache)\n          memoryType.sharedCache = new DxvkSharedAllocationCache(this);\n      }\n\n      // Try to grab a list of allocations from the shared cache first. If\n      // this succeeds, allocating several pages of memory is near instant.\n      DxvkResourceAllocation* allocation = memoryType.sharedCache->getAllocationList(allocationSize);\n\n      if (likely(allocation)) {\n        allocation = cache->assignCache(allocationSize, allocation);\n        freeCachedAllocations(allocation);\n        return true;\n      }\n\n      // Fill cache with the preferred allocation count of this size category so\n      // that subsequent allocations can be handled without locking the allocator.\n      DxvkResourceAllocation* head = nullptr;\n      DxvkResourceAllocation* tail = nullptr;\n\n      std::unique_lock lock(m_mutex);\n      auto& memoryPool = memoryType.mappedPool;\n\n      while (allocationCount) {\n        // Try to suballocate from existing chunks, but do not create\n        // any new chunks. Let the regular code path handle that case\n        // as necessary.\n        int64_t address = memoryPool.alloc(allocationSize, requirements.alignment);\n\n        if (address < 0)\n          break;\n\n        // Add allocation to the list and mark it as cacheable,\n        // so it will get recycled as-is after use.\n        allocation = createAllocation(memoryType, memoryPool,\n          address, allocationSize, DxvkAllocationInfo());\n        allocation->m_flags.set(DxvkAllocationFlag::CanCache);\n\n        if (tail) {\n          tail->m_nextCached = allocation;\n          tail = allocation;\n        } else {\n          head = allocation;\n          tail = allocation;\n        }\n\n        allocationCount--;\n      }\n\n      if (tail) {\n        tail->m_nextCached = cache->assignCache(allocationSize, head);\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n\n  void DxvkMemoryAllocator::getAllocationStatsForPool(\n    const DxvkMemoryType&       type,\n    const DxvkMemoryPool&       pool,\n          DxvkMemoryAllocationStats& stats) {\n    auto& typeStats = stats.memoryTypes[type.index];\n\n    size_t first = stats.chunks.size();\n\n    for (uint32_t i = 0; i < pool.chunks.size(); i++) {\n      if (!pool.chunks[i].memory.memory)\n        continue;\n\n      typeStats.chunkCount += 1u;\n\n      auto& chunkStats = stats.chunks.emplace_back();\n      chunkStats.capacity = pool.chunks[i].memory.size;\n      chunkStats.used = pool.pageAllocator.pagesUsed(i) * DxvkPageAllocator::PageSize;\n      chunkStats.pageMaskOffset = stats.pageMasks.size();\n      chunkStats.pageCount = pool.pageAllocator.pageCount(i);\n      chunkStats.mapped = &pool == &type.mappedPool;\n      chunkStats.active = pool.pageAllocator.chunkIsAvailable(i);\n      chunkStats.cookie = pool.chunks[i].memory.cookie;\n\n      size_t maskCount = (chunkStats.pageCount + 31u) / 32u;\n      stats.pageMasks.resize(chunkStats.pageMaskOffset + maskCount);\n\n      pool.pageAllocator.getPageAllocationMask(i, &stats.pageMasks[chunkStats.pageMaskOffset]);\n    }\n\n    std::sort(stats.chunks.begin() + first, stats.chunks.end(),\n      [] (const DxvkMemoryChunkStats& a, const DxvkMemoryChunkStats& b) {\n        return a.cookie < b.cookie;\n      });\n  }\n\n\n  VkDeviceSize DxvkMemoryAllocator::determineMaxChunkSize(\n    const DxvkMemoryType&       type,\n          bool                  mappable) const {\n    VkDeviceSize size = DxvkMemoryPool::MaxChunkSize;\n\n    // Prefer smaller chunks for host-visible allocations in order to\n    // reduce the amount of address space required. We compensate for\n    // the smaller size by allowing more unused memory on these heaps.\n    if (mappable)\n      size /= env::is32BitHostPlatform() ? 16u : 4u;\n\n    // Ensure that we can at least do 7  allocations to fill\n    // the heap. Might be useful on systems with small BAR.\n    while (MinAllocationsPerHeap * size > type.heap->properties.size)\n      size /= 2u;\n\n    // Always use at least the minimum chunk size\n    return std::max(size, DxvkMemoryPool::MinChunkSize);\n  }\n\n\n  uint32_t DxvkMemoryAllocator::determineSparseMemoryTypes(\n          DxvkDevice*           device) const {\n    auto vk = device->vkd();\n\n    VkMemoryRequirements2 requirements = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 };\n    uint32_t typeMask = ~0u;\n\n    // Create sparse dummy buffer to find available memory types\n    VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };\n    bufferInfo.flags        = VK_BUFFER_CREATE_SPARSE_BINDING_BIT\n                            | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT\n                            | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT;\n    bufferInfo.size         = 65536;\n    bufferInfo.usage        = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT\n                            | VK_BUFFER_USAGE_INDEX_BUFFER_BIT\n                            | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT\n                            | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                            | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                            | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT\n                            | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT\n                            | VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                            | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n    m_sharingModeInfo.fill(bufferInfo);\n\n    if (getBufferMemoryRequirements(bufferInfo, requirements))\n      typeMask &= requirements.memoryRequirements.memoryTypeBits;\n\n    // Create sparse dummy image to find available memory types\n    VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };\n    imageInfo.flags         = VK_IMAGE_CREATE_SPARSE_BINDING_BIT\n                            | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT\n                            | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;\n    imageInfo.imageType     = VK_IMAGE_TYPE_2D;\n    imageInfo.format        = VK_FORMAT_R8G8B8A8_UNORM;\n    imageInfo.extent        = { 256, 256, 1 };\n    imageInfo.mipLevels     = 1;\n    imageInfo.arrayLayers   = 1;\n    imageInfo.samples       = VK_SAMPLE_COUNT_1_BIT;\n    imageInfo.tiling        = VK_IMAGE_TILING_OPTIMAL;\n    imageInfo.usage         = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n                            | VK_IMAGE_USAGE_SAMPLED_BIT\n                            | VK_IMAGE_USAGE_STORAGE_BIT\n                            | VK_IMAGE_USAGE_TRANSFER_DST_BIT\n                            | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;\n    imageInfo.sharingMode   = VK_SHARING_MODE_EXCLUSIVE;\n\n    if (getImageMemoryRequirements(imageInfo, requirements))\n      typeMask &= requirements.memoryRequirements.memoryTypeBits;\n\n    Logger::log(typeMask ? LogLevel::Info : LogLevel::Error,\n      str::format(\"Memory type mask for sparse resources: 0x\", std::hex, typeMask));\n    return typeMask;\n  }\n\n\n  void DxvkMemoryAllocator::determineBufferUsageFlagsPerMemoryType() {\n    VkBufferUsageFlags flags = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT\n                             | VK_BUFFER_USAGE_INDEX_BUFFER_BIT\n                             | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT\n                             | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                             | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT\n                             | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT\n                             | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT\n                             | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;\n\n    if (m_device->features().extTransformFeedback.transformFeedback) {\n      flags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT\n            |  VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT;\n    }\n\n    // Check which individual flags are supported on each memory type. This is a\n    // bit dodgy since the spec technically does not require a combination of flags\n    // to be supported, but we need to be robust around buffer creation anyway.\n    VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };\n    bufferInfo.size = 65536;\n    m_sharingModeInfo.fill(bufferInfo);\n\n    VkMemoryRequirements2 requirements = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 };\n\n    while (flags) {\n      VkBufferCreateFlags flag = flags & -flags;\n\n      bufferInfo.usage = flag\n        | VK_BUFFER_USAGE_TRANSFER_DST_BIT\n        | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n\n      if (getBufferMemoryRequirements(bufferInfo, requirements)) {\n        uint32_t typeMask = requirements.memoryRequirements.memoryTypeBits;\n\n        for (auto type : bit::BitMask(typeMask)) {\n          if (type < m_memTypeCount)\n            m_memTypes.at(type).bufferUsage |= bufferInfo.usage;\n        }\n      }\n\n      flags &= ~flag;\n    }\n\n    // Figure out which memory types support descriptor heaps and set the device\n    // address flag so that memory allocations correctly enable it too. Do not\n    // set the descriptor heap usage flags themselves since we do not want any\n    // non-descriptor allocation to enable those bits.\n    VkBufferUsageFlags descriptorHeapUsage = 0u;\n\n    if (m_device->canUseDescriptorHeap())\n      descriptorHeapUsage |= DescriptorHeapUsage;\n\n    if (m_device->canUseDescriptorBuffer())\n      descriptorHeapUsage |= DescriptorBufferUsage;\n\n    while (descriptorHeapUsage) {\n      VkBufferCreateFlags flag = descriptorHeapUsage & -descriptorHeapUsage;\n\n      bufferInfo.usage = flag\n        | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT\n        | VK_BUFFER_USAGE_TRANSFER_DST_BIT;\n\n      if (getBufferMemoryRequirements(bufferInfo, requirements)) {\n        uint32_t typeMask = requirements.memoryRequirements.memoryTypeBits;\n\n        for (auto type : bit::BitMask(typeMask)) {\n          if (type < m_memTypeCount)\n            m_memTypes.at(type).bufferUsage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;\n        }\n      }\n\n      descriptorHeapUsage &= ~flag;\n    }\n\n    // Only use a minimal set of usage flags for the global buffer if the\n    // full combination of flags is not supported for whatever reason.\n    m_globalBufferUsageFlags = -1;\n    m_globalBufferMemoryTypes = 0u;\n\n    for (uint32_t i = 0; i < m_memTypeCount; i++) {\n      bufferInfo.usage = m_memTypes[i].bufferUsage;\n\n      if (MinGlobalBufferUsage & ~bufferInfo.usage)\n        continue;\n\n      if (!getBufferMemoryRequirements(bufferInfo, requirements)\n       || !(requirements.memoryRequirements.memoryTypeBits & (1u << i)))\n        m_memTypes[i].bufferUsage &= MinGlobalBufferUsage;\n\n      if (m_memTypes[i].bufferUsage) {\n        m_globalBufferUsageFlags &= m_memTypes[i].bufferUsage;\n        m_globalBufferMemoryTypes |= 1u << i;\n      }\n    }\n\n    Logger::info(str::format(\"Memory type mask for buffer resources: \"\n      \"0x\", std::hex, m_globalBufferMemoryTypes, \", usage: 0x\", m_globalBufferUsageFlags));\n  }\n\n\n  void DxvkMemoryAllocator::determineMemoryTypesWithPropertyFlags() {\n    // Initialize look-up table for memory type masks based on required property\n    // flags. This lets us avoid iterating over unsupported memory types\n    for (uint32_t i = 0; i < m_memTypesByPropertyFlags.size(); i++) {\n      VkMemoryPropertyFlags flags = VkMemoryPropertyFlags(i);\n\n      uint32_t vidmemMask = 0u;\n      uint32_t sysmemMask = 0u;\n\n      for (uint32_t j = 0; j < m_memTypeCount; j++) {\n        VkMemoryPropertyFlags typeFlags = m_memTypes[j].properties.propertyFlags;\n\n        if ((typeFlags & flags) != flags)\n          continue;\n\n        if (typeFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)\n          vidmemMask |= 1u << j;\n        else\n          sysmemMask |= 1u << j;\n      }\n\n      // If a system memory type exists with the given properties, do not\n      // include any device-local memory types. This way we won't ever pick\n      // host-visible vram when explicitly trying to allocate system memory.\n      m_memTypesByPropertyFlags[i] = sysmemMask ? sysmemMask : vidmemMask;\n    }\n\n    // If there is no cached coherent memory type, reuse the uncached\n    // one. This is likely slow, but API front-ends are relying on it.\n    uint32_t hostCachedIndex = uint32_t(\n      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n      VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |\n      VK_MEMORY_PROPERTY_HOST_CACHED_BIT);\n\n    uint32_t hostCoherentIndex = uint32_t(\n      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n      VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n\n    if (!m_memTypesByPropertyFlags[hostCachedIndex])\n      m_memTypesByPropertyFlags[hostCachedIndex] = m_memTypesByPropertyFlags[hostCoherentIndex];\n  }\n\n\n  DxvkMemoryStats DxvkMemoryAllocator::getMemoryStats(uint32_t heap) const {\n    DxvkMemoryStats result = { };\n\n    for (auto typeIndex : bit::BitMask(m_memHeaps[heap].memoryTypes)) {\n      const auto& type = m_memTypes[typeIndex];\n\n      result.memoryAllocated += type.stats.memoryAllocated;\n      result.memoryUsed += type.stats.memoryUsed;\n    }\n\n    result.memoryBudget = m_memHeaps[heap].memoryBudget;\n    return result;\n  }\n\n\n  void DxvkMemoryAllocator::getAllocationStats(DxvkMemoryAllocationStats& stats) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    stats.chunks.clear();\n    stats.pageMasks.clear();\n\n    for (uint32_t i = 0; i < m_memTypeCount; i++) {\n      const auto& typeInfo = m_memTypes[i];\n      auto& typeStats = stats.memoryTypes[i];\n\n      typeStats.properties = typeInfo.properties;\n      typeStats.allocated = typeInfo.stats.memoryAllocated;\n      typeStats.used = typeInfo.stats.memoryUsed;\n      typeStats.chunkIndex = stats.chunks.size();\n      typeStats.chunkCount = 0u;\n\n      getAllocationStatsForPool(typeInfo, typeInfo.devicePool, stats);\n      getAllocationStatsForPool(typeInfo, typeInfo.mappedPool, stats);\n    }\n  }\n\n\n  DxvkSharedAllocationCacheStats DxvkMemoryAllocator::getAllocationCacheStats() const {\n    DxvkSharedAllocationCacheStats result = { };\n\n    for (uint32_t i = 0; i < m_memTypeCount; i++) {\n      const auto& type = m_memTypes[i];\n\n      if (type.sharedCache) {\n        DxvkSharedAllocationCacheStats stats = type.sharedCache->getStats();\n        result.requestCount += stats.requestCount;\n        result.missCount += stats.missCount;\n        result.size += stats.size;\n      }\n    }\n\n    return result;\n  }\n\n\n  bool DxvkMemoryAllocator::getBufferMemoryRequirements(\n    const VkBufferCreateInfo&     createInfo,\n          VkMemoryRequirements2&  memoryRequirements) const {\n    auto vk = m_device->vkd();\n\n    VkDeviceBufferMemoryRequirements info = { VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS };\n    info.pCreateInfo = &createInfo;\n\n    vk->vkGetDeviceBufferMemoryRequirements(vk->device(), &info, &memoryRequirements);\n    return true;\n  }\n\n\n  bool DxvkMemoryAllocator::getImageMemoryRequirements(\n    const VkImageCreateInfo&      createInfo,\n          VkMemoryRequirements2&  memoryRequirements) const {\n    auto vk = m_device->vkd();\n\n    VkDeviceImageMemoryRequirements info = { VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS };\n    info.pCreateInfo = &createInfo;\n\n    vk->vkGetDeviceImageMemoryRequirements(vk->device(), &info, &memoryRequirements);\n    return true;\n  }\n\n\n  void DxvkMemoryAllocator::registerResource(\n          DxvkPagedResource*          resource) {\n    std::lock_guard lock(m_resourceMutex);\n    m_resourceMap.emplace(resource->cookie(), resource);\n  }\n\n\n  void DxvkMemoryAllocator::unregisterResource(\n          DxvkPagedResource*          resource) {\n    std::lock_guard lock(m_resourceMutex);\n    m_resourceMap.erase(resource->cookie());\n  }\n\n\n  void DxvkMemoryAllocator::requestMakeResident(\n          DxvkPagedResource*          resource) {\n    std::lock_guard lock(m_resourceMutex);\n\n    m_relocations.addResource(resource, nullptr,\n      DxvkAllocationMode::NoFallback);\n  }\n\n\n  void DxvkMemoryAllocator::lockResourceGpuAddress(\n    const Rc<DxvkResourceAllocation>& allocation) {\n    if (allocation->m_flags.test(DxvkAllocationFlag::CanMove)) {\n      std::lock_guard lock(m_resourceMutex);\n      allocation->m_flags.clr(DxvkAllocationFlag::CanMove);\n\n      if (!allocation->m_flags.test(DxvkAllocationFlag::OwnsMemory) && !allocation->m_mapPtr) {\n        uint32_t chunkIndex = allocation->m_address >> DxvkPageAllocator::ChunkAddressBits;\n        allocation->m_type->devicePool.chunks[chunkIndex].canMove = false;\n      }\n    }\n  }\n\n\n  VkDeviceAddress DxvkMemoryAllocator::getBufferDeviceAddress(VkBuffer buffer) const {\n    auto vk = m_device->vkd();\n\n    VkBufferDeviceAddressInfo bdaInfo = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO };\n    bdaInfo.buffer = buffer;\n\n    return vk->vkGetBufferDeviceAddress(vk->device(), &bdaInfo);\n  }\n\n\n  void DxvkMemoryAllocator::logMemoryError(const VkMemoryRequirements& req) const {\n    std::stringstream sstr;\n    sstr << \"DxvkMemoryAllocator: Memory allocation failed\" << std::endl\n         << \"  Size:      \" << req.size << std::endl\n         << \"  Alignment: \" << req.alignment << std::endl\n         << \"  Mem types: \";\n\n    uint32_t memTypes = req.memoryTypeBits;\n\n    while (memTypes) {\n      uint32_t index = bit::tzcnt(memTypes);\n      sstr << index;\n\n      if ((memTypes &= memTypes - 1))\n        sstr << \",\";\n      else\n        sstr << std::endl;\n    }\n\n    Logger::err(sstr.str());\n  }\n\n\n  void DxvkMemoryAllocator::logMemoryStats() const {\n    DxvkAdapterMemoryInfo memHeapInfo = m_device->adapter()->getMemoryHeapInfo();\n\n    std::stringstream sstr;\n    sstr << \"Heap  Size (MiB)  Allocated   Used        Reserved    Budget\" << std::endl;\n\n    for (uint32_t i = 0; i < m_memHeapCount; i++) {\n      DxvkMemoryStats stats = getMemoryStats(i);\n\n      sstr << std::setw(2) << i << \":   \"\n           << std::setw(6) << (m_memHeaps[i].properties.size >> 20) << \"      \"\n           << std::setw(6) << (stats.memoryAllocated >> 20) << \"      \"\n           << std::setw(6) << (stats.memoryUsed >> 20) << \"      \";\n\n      if (m_device->features().extMemoryBudget) {\n        sstr << std::setw(6) << (memHeapInfo.heaps[i].memoryAllocated >> 20) << \"      \"\n             << std::setw(6) << (memHeapInfo.heaps[i].memoryBudget >> 20) << \"      \" << std::endl;\n      } else {\n        sstr << \" n/a         n/a\" << std::endl;\n      }\n    }\n\n    Logger::err(sstr.str());\n  }\n\n\n  uint32_t DxvkMemoryAllocator::getMemoryTypeMask(\n          VkMemoryPropertyFlags properties) const {\n    return m_memTypesByPropertyFlags[uint32_t(properties) % uint32_t(m_memTypesByPropertyFlags.size())];\n  }\n\n\n  uint32_t DxvkMemoryAllocator::findGlobalBufferMemoryTypeMask(\n          VkBufferUsageFlags    usage) const {\n    // Iterate over all candidate memory types as a fallback in case\n    // the device has memory types with limited buffer support.\n    uint32_t mask = m_globalBufferMemoryTypes;\n\n    for (auto typeIndex : bit::BitMask(mask)) {\n      if (usage & ~m_memTypes[typeIndex].bufferUsage)\n        mask ^= 1u << typeIndex;\n    }\n\n    return mask;\n  }\n\n\n  void DxvkMemoryAllocator::updateMemoryHeapBudgets() {\n    if (!m_device->features().extMemoryBudget)\n      return;\n\n    VkDeviceSize maxBudget = m_device->config().maxMemoryBudget;\n\n    VkPhysicalDeviceMemoryBudgetPropertiesEXT memBudget = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };\n    VkPhysicalDeviceMemoryProperties2 memInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2, &memBudget };\n\n    auto vki = m_device->adapter()->vki();\n    vki->vkGetPhysicalDeviceMemoryProperties2(m_device->adapter()->handle(), &memInfo);\n\n    for (uint32_t i = 0; i < m_memHeapCount; i++) {\n      if (memBudget.heapBudget[i]) {\n        // Deduct driver-internal allocations from the resource budget\n        VkDeviceSize allocated = getMemoryStats(i).memoryAllocated;\n\n        VkDeviceSize internal = std::max(memBudget.heapUsage[i], allocated) - allocated;\n                     internal = std::min(memBudget.heapBudget[i], internal);\n\n        m_memHeaps[i].memoryBudget = std::min(memBudget.heapBudget[i] - internal, m_memHeaps[i].properties.size);\n\n        if (m_memHeaps[i].properties.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) {\n          // Keep a small amount of the budget unused. This avoids problematic behaviour\n          // in some drivers when maxing out the budget, and allows small driver-internal\n          // allocations to succeed while giving us time to evict more resources.\n          VkDeviceSize reservedSize = std::clamp<VkDeviceSize>(m_memHeaps[i].properties.size / 100u, MinChunkSize, MaxChunkSize);\n          m_memHeaps[i].memoryBudget -= std::min(reservedSize, m_memHeaps[i].memoryBudget);\n\n          if (maxBudget)\n            m_memHeaps[i].memoryBudget = std::min(m_memHeaps[i].memoryBudget, maxBudget);\n        }\n      }\n    }\n  }\n\n\n  void DxvkMemoryAllocator::updateMemoryHeapStats(uint32_t heapIndex) {\n    DxvkMemoryStats stats = getMemoryStats(heapIndex);\n\n    m_device->notifyMemoryStats(heapIndex,\n      stats.memoryAllocated - m_adapterHeapStats[heapIndex].memoryAllocated,\n      stats.memoryUsed - m_adapterHeapStats[heapIndex].memoryUsed);\n\n    m_adapterHeapStats[heapIndex] = stats;\n  }\n\n\n  void DxvkMemoryAllocator::moveDefragChunk(\n          DxvkMemoryType&       type) {\n    auto& pool = type.devicePool;\n\n    // Ensure that we only process each chunk once\n    uint32_t chunkIndex = std::exchange(pool.nextDefragChunk, ~0u);\n\n    if (chunkIndex >= pool.chunks.size())\n      return;\n\n    // If the chunk has been revived in the meantime, we need\n    // the memory and should not relocate any resources\n    if (pool.pageAllocator.chunkIsAvailable(chunkIndex))\n      return;\n\n    // Set allocation modes depending pn whether the memory type\n    // is a device-local type or a fallback system memory type.\n    DxvkAllocationModes mode(DxvkAllocationMode::NoAllocation);\n\n    if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)\n      mode.set(DxvkAllocationMode::NoFallback);\n    else\n      mode.set(DxvkAllocationMode::NoDeviceMemory);\n\n    // Iterate over the chunk's allocation list and look up resources\n    std::unique_lock lock(m_resourceMutex);\n\n    for (auto a = pool.chunks[chunkIndex].allocationList; a; a = a->m_nextInChunk) {\n      // If we can't find the resource by its cookie, it has probably\n      // already been destroyed. This is fine since the allocation will\n      // likely get freed soon anyway.\n      auto entry = m_resourceMap.find(a->m_resourceCookie);\n\n      if (entry == m_resourceMap.end())\n        continue;\n\n      // Same if there are no external references. There is a small chance\n      // that we pick up a newly created resource here that has no public\n      // references yet, but skipping that will not affect correctness.\n      auto resource = entry->second->tryAcquire();\n\n      if (!resource)\n        continue;\n\n      // Acquired the resource, add it to the relocation list.\n      m_relocations.addResource(std::move(resource), a, mode);\n    }\n  }\n\n\n  void DxvkMemoryAllocator::pickDefragChunk(\n          DxvkMemoryType&       type) {\n    auto& pool = type.devicePool;\n\n    if (pool.chunks.empty())\n      return;\n\n    // Only engage defragmentation at all if we have a significant\n    // amount of memory wasted, or if we're under memory pressure.\n    auto heapStats = getMemoryStats(type.heap->index);\n\n    if (heapStats.memoryAllocated <= heapStats.memoryBudget) {\n      uint32_t pagesTotal = 0u;\n      uint32_t pagesUsed = 0u;\n\n      for (uint32_t i = 0; i < pool.chunks.size(); i++) {\n        uint32_t used = pool.pageAllocator.pagesUsed(i);\n\n        if (used) {\n          pagesUsed += used;\n          pagesTotal += pool.pageAllocator.pageCount(i);\n        }\n      }\n\n      uint32_t pagesPerChunk = pool.nextChunkSize / DxvkPageAllocator::PageSize;\n\n      // Allow for more \"wasted\" system memory since it's usually less of an issue\n      uint32_t tolerance = (type.heap->properties.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)\n        ? pagesUsed / 8u\n        : pagesUsed / 3u;\n\n      if (pagesUsed + tolerance + pagesPerChunk >= pagesTotal)\n        return;\n    }\n\n    // Find live chunk with the lowest number of pages used. Skip\n    // empty chunks since the goal here is to turn a used chunk\n    // into an empty one.\n    uint32_t chunkIndex = 0u;\n    uint32_t chunkPages = 0u;\n\n    for (uint32_t i = 0; i < pool.chunks.size(); i++) {\n      // Mark any empty chunk as dead for now as well so that we don't\n      // keep moving resources between multiple otherwise unused chunks\n      uint32_t pagesUsed = pool.pageAllocator.pagesUsed(i);\n\n      if (!pagesUsed) {\n        pool.pageAllocator.killChunk(i);\n        continue;\n      }\n\n      // Move on if the chunk cannot be relocated anyway\n      if (!pool.chunks[i].canMove)\n        continue;\n\n      // If there's a non-empty chunk already marked as dead and we haven't\n      // finished moving resources around yet, killing another chunk would\n      // do more harm than good so wait for that to finish first.\n      if (!pool.pageAllocator.chunkIsAvailable(i)) {\n        if (!m_relocations.empty())\n          return;\n        continue;\n      }\n\n      if (!chunkPages || pagesUsed < chunkPages) {\n        chunkIndex = i;\n        chunkPages = pagesUsed;\n      }\n    }\n\n    if (!chunkPages)\n      return;\n\n    // Check if the remaining chunks in the pool have sufficient free space.\n    // This is not a strong guarantee that relocation will succeed, but the\n    // chance is reasonably high.\n    uint32_t freePages = 0u;\n\n    for (uint32_t i = 0; i < pool.chunks.size(); i++) {\n      uint32_t pagesUsed = pool.pageAllocator.pagesUsed(i);\n      uint32_t pageCount = pool.pageAllocator.pageCount(i);\n\n      if (pagesUsed && pool.pageAllocator.chunkIsAvailable(i) && i != chunkIndex)\n        freePages += pageCount - pagesUsed;\n    }\n\n    if (2u * freePages < 3u * chunkPages)\n      return;\n\n    // We only want one non-empty dead chunk at a time in order to prevent\n    // situations where defragmation locks itself up in a suboptimal state.\n    // If we already have a dead chunk with a resource that cannot be moved,\n    // revive it and mark the newly selected one instead so that it can be\n    // moved into the previously dead chunk.\n    for (uint32_t i = 0; i < pool.chunks.size(); i++) {\n      if (!pool.pageAllocator.chunkIsAvailable(i) && pool.pageAllocator.pagesUsed(i))\n        pool.pageAllocator.reviveChunk(i);\n    }\n\n    // Mark the chunk as dead. If it does not subsequently get reactivated\n    // because the game is loading more resources, the next worker iteration\n    // will queue all live resources for relocation.\n    pool.pageAllocator.killChunk(chunkIndex);\n    pool.nextDefragChunk = chunkIndex;\n  }\n\n\n  void DxvkMemoryAllocator::evictResources(\n          DxvkMemoryType&       type) {\n    auto& pool = type.devicePool;\n\n    // Doing this on integrated graphics would be harmful, so don't'\n    if (pool.chunks.empty() || !type.heap->enableEviction)\n      return;\n\n    // Work out how much memory we should ideally leave unused\n    VkDeviceSize minUnusedMemory = 2u * pool.maxChunkSize;\n\n    // Account for mapped memory types on the same heap where we may not be\n    // able to move any resources, and count their total allocated amount.\n    VkDeviceSize heapUsage = type.stats.memoryUsed;\n\n    for (auto i : bit::BitMask(type.heap->memoryTypes & ~(1u << type.index)))\n      heapUsage += m_memTypes[i].stats.memoryAllocated;\n\n    // If we're within budget with some headroom already, don't evict anything.\n    VkDeviceSize heapBudget = getMemoryStats(type.heap->index).memoryBudget;\n\n    if (heapUsage + minUnusedMemory <= heapBudget)\n      return;\n\n    // Check the old previous chunk to evict unused resources right\n    // away, and then advance to the next available chunk if possible\n    std::array<uint32_t, 2u> chunkIndices = { pool.nextEvictChunk, ~0u };\n\n    for (uint32_t i = 0u; i < pool.chunks.size(); i++) {\n      pool.nextEvictChunk += 1u;\n      pool.nextEvictChunk %= pool.chunks.size();\n\n      if (pool.pageAllocator.chunkIsAvailable(pool.nextEvictChunk))\n        break;\n    }\n\n    chunkIndices[1u] = pool.nextEvictChunk;\n\n    for (auto chunkIndex : chunkIndices) {\n      // Ensure we actually have a valid, live chunk to work with\n      if (chunkIndex >= pool.chunks.size() || !pool.pageAllocator.chunkIsAvailable(chunkIndex))\n        continue;\n\n      auto& chunk = pool.chunks[chunkIndex];\n\n      // Scan resources and mark everything for eviction. If a demoted\n      // resource hasn't been reactivated, evict it to system memory.\n      VkDeviceSize memoryEvicted = 0u;\n\n      for (auto a = chunk.allocationList; a; a = a->m_nextInChunk) {\n        if (!a->flags().test(DxvkAllocationFlag::CanMove))\n          continue;\n\n        // Look up resource by its cookie\n        auto entry = m_resourceMap.find(a->m_resourceCookie);\n\n        if (entry == m_resourceMap.end())\n          continue;\n\n        // Try to acquire resource and request its eviction\n        auto resource = entry->second->tryAcquire();\n\n        if (!resource)\n          continue;\n\n        bool evicted = resource->requestEviction();\n\n        if (evicted && (heapUsage + minUnusedMemory > heapBudget + memoryEvicted)) {\n          m_relocations.addResource(std::move(resource), a, DxvkAllocationMode::NoDeviceMemory);\n          memoryEvicted += a->getMemoryInfo().size;\n        }\n\n        if (!evicted && memoryEvicted) {\n          // Relocate other resources within the chunk to reduce fragmentation\n          m_relocations.addResource(std::move(resource), a, DxvkAllocationModes(\n            DxvkAllocationMode::NoFallback, DxvkAllocationMode::NoAllocation));\n        }\n      }\n\n      // Relocate resource in the chunk we evicted from, and override any\n      // chunk that defragmentation may have picked. This greatly reduces\n      // fragmentation caused evicting a subset of resources from the chunk.\n      if (memoryEvicted) {\n        pool.pageAllocator.killChunk(chunkIndex);\n\n        for (uint32_t i = 0u; i < pool.chunks.size(); i++) {\n          if (i != chunkIndex && pool.pageAllocator.pagesUsed(i))\n            pool.pageAllocator.reviveChunk(i);\n        }\n      }\n    }\n  }\n\n\n  void DxvkMemoryAllocator::performTimedTasks() {\n    static constexpr auto Interval = std::chrono::milliseconds(500u);\n\n    // This function shouldn't be called concurrently, so checking and\n    // updating the deadline is fine without taking the global lock\n    auto currentTime = high_resolution_clock::now();\n\n    if (m_taskDeadline != high_resolution_clock::time_point()\n     && m_taskDeadline > currentTime)\n      return;\n\n    if (m_taskDeadline == high_resolution_clock::time_point()\n     || m_taskDeadline + Interval <= currentTime)\n      m_taskDeadline = currentTime + Interval;\n    else\n      m_taskDeadline = m_taskDeadline + Interval;\n\n    std::unique_lock lock(m_mutex);\n    performTimedTasksLocked(currentTime);\n  }\n\n\n  void DxvkMemoryAllocator::performTimedTasksLocked(high_resolution_clock::time_point currentTime) {\n    // Re-query current memory budgets\n    updateMemoryHeapBudgets();\n\n    // Periodically free unused memory chunks and update\n    // memory allocation statistics for the adapter.\n    for (uint32_t i = 0; i < m_memHeapCount; i++)\n      freeEmptyChunksInHeap(m_memHeaps[i], 0, currentTime);\n\n    // Periodically clean up unused cached allocations\n    for (uint32_t i = 0; i < m_memTypeCount; i++) {\n      if (m_memTypes[i].sharedCache)\n        m_memTypes[i].sharedCache->cleanupUnusedFromLockedAllocator(currentTime);\n    }\n\n    if (enableDefrag()) {\n      // Periodically defragment device-local memory types. We cannot\n      // do anything about mapped allocations since we rely on pointer\n      // stability there.\n      for (uint32_t i = 0; i < m_memTypeCount; i++) {\n        moveDefragChunk(m_memTypes[i]);\n        pickDefragChunk(m_memTypes[i]);\n\n        if (m_memTypes[i].properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)\n          evictResources(m_memTypes[i]);\n      }\n    }\n  }\n\n\n  bool DxvkMemoryAllocator::enableDefrag() const {\n    auto option = m_device->config().enableMemoryDefrag;\n\n    if (option == Tristate::Auto) {\n      // For unknown reasons, defragmentation seems to break Genshin Impact and\n      // possibly other games on ANV while working fine on other drivers even in\n      // a stress-test scenario, see https://github.com/doitsujin/dxvk/issues/4395.\n      // This issue does not seem to affect Battlemage GPUs, which have a minimum\n      // reported subgroup size of 16 as opposed to 8.\n      if (m_device->adapter()->matchesDriver(VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA))\n        return m_device->properties().vk13.minSubgroupSize >= 16u;\n\n      return true;\n    } else {\n      return option == Tristate::True;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_memory.h",
    "content": "#pragma once\n\n#include <map>\n#include <memory>\n\n#include \"dxvk_access.h\"\n#include \"dxvk_adapter.h\"\n#include \"dxvk_allocator.h\"\n#include \"dxvk_descriptor.h\"\n#include \"dxvk_hash.h\"\n\n#include \"../util/util_time.h\"\n\nnamespace dxvk {\n  \n  class DxvkMemoryAllocator;\n  class DxvkSparsePageTable;\n  class DxvkSharedAllocationCache;\n  class DxvkResourceAllocation;\n  class DxvkPagedResource;\n  struct DxvkMemoryChunk;\n\n  /**\n   * \\brief Memory stats\n   * \n   * Reports the amount of device memory\n   * allocated and used by the application.\n   */\n  struct DxvkMemoryStats {\n    VkDeviceSize memoryAllocated = 0;\n    VkDeviceSize memoryUsed      = 0;\n    VkDeviceSize memoryBudget    = 0;\n  };\n\n\n  enum class DxvkSharedHandleMode {\n      None,\n      Import,\n      Export,\n  };\n\n  /**\n   * \\brief Shared handle info\n   *\n   * The shared resource information for a given resource.\n   */\n  struct DxvkSharedHandleInfo {\n    DxvkSharedHandleMode mode = DxvkSharedHandleMode::None;\n    VkExternalMemoryHandleTypeFlagBits type   = VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;\n    union {\n      // When we want to implement this on non-Windows platforms,\n      // we could add a `int fd` here, etc.\n      HANDLE handle = INVALID_HANDLE_VALUE;\n    };\n  };\n\n\n  /**\n   * \\brief Device memory object\n   * \n   * Stores a Vulkan memory object. If the object\n   * was allocated on host-visible memory, it will\n   * be persistently mapped.\n   */\n  struct DxvkDeviceMemory {\n    VkBuffer              buffer  = VK_NULL_HANDLE;\n    VkDeviceMemory        memory  = VK_NULL_HANDLE;\n    VkDeviceSize          size    = 0u;\n    void*                 mapPtr  = nullptr;\n    VkDeviceAddress       gpuVa   = 0u;\n    uint64_t              cookie  = 0u;\n  };\n\n\n  /**\n   * \\brief Memory chunk\n   *\n   * Stores a device memory object with some metadata.\n   */\n  struct DxvkMemoryChunk {\n    /// Backing storage for this chunk\n    DxvkDeviceMemory memory;\n    /// Time when the chunk has been marked as unused. Must\n    /// be set to 0 when allocating memory from the chunk\n    high_resolution_clock::time_point unusedTime = { };\n    /// Unordered list of resources suballocated from this chunk.\n    DxvkResourceAllocation* allocationList = nullptr;\n    /// Whether defragmentation can be performed on this chunk.\n    /// Only relevant for chunks in non-mappable device memory.\n    VkBool32 canMove = true;\n\n    void addAllocation(DxvkResourceAllocation* allocation);\n    void removeAllocation(DxvkResourceAllocation* allocation);\n  };\n\n  \n  /**\n   * \\brief Memory pool\n   *\n   * Stores a list of memory chunks, as well as an allocator\n   * over the entire pool.\n   */\n  struct DxvkMemoryPool {\n    constexpr static VkDeviceSize MaxChunkSize = DxvkPageAllocator::MaxChunkSize;\n    constexpr static VkDeviceSize MinChunkSize = MaxChunkSize / 64u;\n\n    /// Backing storage for allocated memory chunks\n    std::vector<DxvkMemoryChunk> chunks;\n    /// Memory allocator covering the entire memory pool\n    DxvkPageAllocator pageAllocator;\n    /// Pool allocator that sits on top of the page allocator\n    DxvkPoolAllocator poolAllocator = { pageAllocator };\n    /// Minimum desired allocation size for the next chunk.\n    /// Always a power of two.\n    VkDeviceSize nextChunkSize = MinChunkSize;\n    /// Maximum chunk size for the memory pool. Hard limit.\n    VkDeviceSize maxChunkSize = MaxChunkSize;\n    /// Next chunk to relocate for defragmentation\n    uint32_t nextDefragChunk = ~0u;\n    /// Next chunk to evict resources from\n    uint32_t nextEvictChunk = ~0u;\n\n    force_inline int64_t alloc(uint64_t size, uint64_t align) {\n      if (size <= DxvkPoolAllocator::MaxSize)\n        return poolAllocator.alloc(size);\n      else\n        return pageAllocator.alloc(size, align);\n    }\n\n    force_inline bool free(uint64_t address, uint64_t size) {\n      if (size <= DxvkPoolAllocator::MaxSize)\n        return poolAllocator.free(address, size);\n      else\n        return pageAllocator.free(address, size);\n    }\n  };\n\n\n  /**\n   * \\brief Memory heap\n   * \n   * Corresponds to a Vulkan memory heap and stores\n   * its properties as well as allocation statistics.\n   */\n  struct DxvkMemoryHeap {\n    uint32_t          index         = 0u;\n    uint32_t          memoryTypes   = 0u;\n    VkDeviceSize      memoryBudget  = 0u;\n    VkMemoryHeap      properties    = { };\n    bool              enforceBudget = false;\n    bool              enableEviction = false;\n  };\n\n\n  /**\n   * \\brief Memory type\n   * \n   * Corresponds to a Vulkan memory type and stores\n   * memory chunks used to sub-allocate memory on\n   * this memory type.\n   */\n  struct DxvkMemoryType {\n    uint32_t          index         = 0u;\n    VkMemoryType      properties    = { };\n\n    DxvkMemoryHeap*   heap          = nullptr;\n\n    DxvkMemoryStats   stats         = { };\n\n    VkBufferUsageFlags bufferUsage  = 0u;\n\n    DxvkMemoryPool    devicePool;\n    DxvkMemoryPool    mappedPool;\n\n    DxvkSharedAllocationCache* sharedCache = nullptr;\n  };\n\n\n  /**\n   * \\brief Memory type statistics\n   */\n  struct DxvkMemoryTypeStats {\n    /// Memory type properties\n    VkMemoryType properties = { };\n    /// Amount of memory allocated\n    VkDeviceSize allocated = 0u;\n    /// Amount of memory used\n    VkDeviceSize used = 0u;\n    /// First chunk in the array\n    size_t chunkIndex = 0u;\n    /// Number of chunks allocated\n    size_t chunkCount = 0u;\n  };\n\n\n  /**\n   * \\brief Chunk statistics\n   */\n  struct DxvkMemoryChunkStats {\n    /// Chunk size, in bytes\n    VkDeviceSize capacity = 0u;\n    /// Used memory, in bytes\n    VkDeviceSize used = 0u;\n    /// Index of first page mask belonging to this\n    /// chunk in the page mask array\n    uint32_t pageMaskOffset = 0u;\n    /// Number of pages in this chunk.\n    uint16_t pageCount = 0u;\n    /// Whether this chunk is mapped\n    bool mapped = false;\n    /// Whether this chunk is active\n    bool active = false;\n    /// Chunk cookie\n    uint32_t cookie = 0u;\n  };\n\n\n  /**\n   * \\brief Detailed memory allocation statistics\n   */\n  struct DxvkMemoryAllocationStats {\n    std::array<DxvkMemoryTypeStats, VK_MAX_MEMORY_TYPES> memoryTypes = { };\n    std::vector<DxvkMemoryChunkStats> chunks;\n    std::vector<uint32_t> pageMasks;\n  };\n\n\n  /**\n   * \\brief Sharing mode info\n   *\n   * Stores available queue families and provides methods\n   * to fill in sharing mode infos for resource creation.\n   */\n  struct DxvkSharingModeInfo {\n    std::array<uint32_t, 2u> queueFamilies = { };\n\n    VkSharingMode sharingMode() const {\n      return queueFamilies[0] != queueFamilies[1]\n        ? VK_SHARING_MODE_CONCURRENT\n        : VK_SHARING_MODE_EXCLUSIVE;\n    }\n\n    template<typename CreateInfo>\n    void fill(CreateInfo& info) const {\n      info.sharingMode = sharingMode();\n\n      if (info.sharingMode == VK_SHARING_MODE_CONCURRENT) {\n        info.queueFamilyIndexCount = queueFamilies.size();\n        info.pQueueFamilyIndices = queueFamilies.data();\n      }\n    }\n  };\n\n\n  /**\n   * \\brief Buffer view key\n   *\n   * Stores buffer view properties.\n   */\n  struct DxvkBufferViewKey {\n    /// Buffer view format\n    VkFormat format = VK_FORMAT_UNDEFINED;\n    /// View usage. Must include one or both texel buffer flags for\n    /// formatted views, or the storage buffer bit for raw views.\n    VkBufferUsageFlagBits usage = VkBufferUsageFlagBits(0u);\n    /// Buffer offset, in bytes\n    VkDeviceSize offset = 0u;\n    /// Buffer view size, in bytes\n    VkDeviceSize size = 0u;\n\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(uint32_t(format));\n      hash.add(uint32_t(usage));\n      hash.add(offset);\n      hash.add(size);\n      return hash;\n    }\n\n    bool eq(const DxvkBufferViewKey& other) const {\n      return format == other.format\n          && usage  == other.usage\n          && offset == other.offset\n          && size   == other.size;\n    }\n  };\n\n\n  /**\n   * \\brief Image view map\n   */\n  class DxvkResourceBufferViewMap {\n\n  public:\n\n    DxvkResourceBufferViewMap(\n            DxvkMemoryAllocator*        allocator,\n            VkBuffer                    buffer,\n            VkDeviceAddress             va);\n\n    ~DxvkResourceBufferViewMap();\n\n    /**\n     * \\brief Creates a buffer view\n     *\n     * \\param [in] key View properties\n     * \\param [in] baseOffset Buffer offset\n     * \\returns Buffer view handle\n     */\n    const DxvkDescriptor* createBufferView(\n      const DxvkBufferViewKey&          key,\n            VkDeviceSize                baseOffset);\n\n  private:\n\n    DxvkDevice*       m_device          = nullptr;\n    VkBuffer          m_buffer          = VK_NULL_HANDLE;\n    VkDeviceAddress   m_va              = 0u;\n\n    dxvk::mutex       m_mutex;\n    std::unordered_map<DxvkBufferViewKey,\n      DxvkDescriptor, DxvkHash, DxvkEq> m_views;\n\n  };\n\n\n  /**\n   * \\brief Image view key\n   *\n   * Stores a somewhat compressed representation\n   * of image view properties.\n   */\n  struct DxvkImageViewKey {\n    /// View type\n    VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n    /// View usage flags\n    VkImageUsageFlags usage = VkImageUsageFlags(0u);\n    /// View format\n    VkFormat format = VK_FORMAT_UNDEFINED;\n    /// Image layout that the view will be used as\n    VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;\n    /// Aspect flags to include in this view\n    VkImageAspectFlags aspects = 0u;\n    /// First mip\n    uint8_t mipIndex = 0u;\n    /// Number of mips\n    uint8_t mipCount = 0u;\n    /// First array layer\n    uint16_t layerIndex = 0u;\n    /// Number of array layers\n    uint16_t layerCount = 0u;\n    /// Packed component swizzle, with four bits per component\n    uint16_t packedSwizzle = 0u;\n\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(uint32_t(viewType));\n      hash.add(uint32_t(usage));\n      hash.add(uint32_t(format));\n      hash.add(uint32_t(layout));\n      hash.add(uint32_t(aspects));\n      hash.add(uint32_t(mipIndex) | (uint32_t(mipCount) << 16));\n      hash.add(uint32_t(layerIndex) | (uint32_t(layerCount) << 16));\n      hash.add(uint32_t(packedSwizzle));\n      return hash;\n    }\n\n    bool eq(const DxvkImageViewKey& other) const {\n      return viewType == other.viewType\n          && usage == other.usage\n          && format == other.format\n          && layout == other.layout\n          && aspects == other.aspects\n          && mipIndex == other.mipIndex\n          && mipCount == other.mipCount\n          && layerIndex == other.layerIndex\n          && layerCount == other.layerCount\n          && packedSwizzle == other.packedSwizzle;\n    }\n\n    VkComponentMapping unpackSwizzle() const {\n      return VkComponentMapping {\n        VkComponentSwizzle((packedSwizzle >>  0) & 0xf),\n        VkComponentSwizzle((packedSwizzle >>  4) & 0xf),\n        VkComponentSwizzle((packedSwizzle >>  8) & 0xf),\n        VkComponentSwizzle((packedSwizzle >> 12) & 0xf) };\n    }\n\n    static uint16_t packSwizzle(VkComponentMapping mapping) {\n      return (uint16_t(mapping.r) <<  0)\n           | (uint16_t(mapping.g) <<  4)\n           | (uint16_t(mapping.b) <<  8)\n           | (uint16_t(mapping.a) << 12);\n    }\n  };\n\n\n  /**\n   * \\brief Image view map\n   */\n  class DxvkResourceImageViewMap {\n\n  public:\n\n    DxvkResourceImageViewMap(\n            DxvkMemoryAllocator*        allocator,\n            VkImage                     image);\n\n    ~DxvkResourceImageViewMap();\n\n    /**\n     * \\brief Creates an image view\n     *\n     * \\param [in] key View properties\n     * \\returns Pointer to descriptor info\n     */\n    const DxvkDescriptor* createImageView(\n      const DxvkImageViewKey&           key);\n\n  private:\n\n    DxvkDevice*       m_device = nullptr;\n    VkImage           m_image = VK_NULL_HANDLE;\n\n    dxvk::mutex       m_mutex;\n    std::unordered_map<DxvkImageViewKey,\n      DxvkDescriptor, DxvkHash, DxvkEq> m_views;\n\n  };\n\n\n  /**\n   * \\brief Memory properties\n   */\n  struct DxvkResourceMemoryInfo {\n    /// Vulkan memory handle\n    VkDeviceMemory memory = VK_NULL_HANDLE;\n    /// Offset into memory object\n    VkDeviceSize offset = 0u;\n    /// Size of memory range\n    VkDeviceSize size = 0u;\n  };\n\n\n  /**\n   * \\brief Buffer properties\n   */\n  struct DxvkResourceBufferInfo {\n    /// Buffer handle\n    VkBuffer buffer = VK_NULL_HANDLE;\n    /// Buffer offset, in bytes\n    VkDeviceSize offset = 0u;\n    /// Buffer size, in bytes\n    VkDeviceSize size = 0u;\n    /// Pointer to mapped memory region\n    void* mapPtr = nullptr;\n    /// GPU address of the buffer\n    VkDeviceSize gpuAddress = 0u;\n  };\n\n\n  /**\n   * \\brief Image properties\n   */\n  struct DxvkResourceImageInfo {\n    /// Image handle\n    VkImage image = VK_NULL_HANDLE;\n    /// Pointer to mapped memory region\n    void* mapPtr = nullptr;\n  };\n\n\n  /**\n   * \\brief Resource allocation flags\n   */\n  enum class DxvkAllocationFlag : uint32_t {\n    /// Allocation owns the given VkDeviceMemory allocation\n    /// and is not suballocated from an existing chunk.\n    OwnsMemory  = 0,\n    /// Allocation owns a dedicated VkBuffer object rather\n    /// than the global buffer for the parent chunk, if any.\n    OwnsBuffer  = 1,\n    /// Allocation owns a VkImage object.\n    OwnsImage   = 2,\n    /// Allocation can use an allocation cache.\n    CanCache    = 3,\n    /// Allocation can be relocated for defragmentation.\n    CanMove     = 4,\n    /// Allocation is imported from an external API.\n    Imported    = 5,\n    /// Memory must be cleared to zero when the allocation\n    /// is freed. Only used to work around app bugs.\n    ClearOnFree = 6,\n  };\n\n  using DxvkAllocationFlags = Flags<DxvkAllocationFlag>;\n\n\n  /**\n   * \\brief Vulkan resource with memory allocation\n   *\n   * Reference-counted object that stores a Vulkan resource together\n   * with the memory allocation backing the resource, as well as views\n   * created from that resource.\n   */\n  class alignas(CACHE_LINE_SIZE) DxvkResourceAllocation {\n    friend DxvkMemoryAllocator;\n\n    friend struct DxvkMemoryChunk;\n    friend class DxvkLocalAllocationCache;\n    friend class DxvkSharedAllocationCache;\n  public:\n\n    DxvkResourceAllocation(\n            DxvkMemoryAllocator*        allocator,\n            DxvkMemoryType*             type)\n    : m_allocator(allocator), m_type(type) { }\n\n    ~DxvkResourceAllocation();\n\n    /**\n     * \\brief Increments reference count\n     */\n    force_inline void incRef() {\n      m_useCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    /**\n     * \\brief Decrements reference count\n     * Frees allocation if necessary\n     */\n    force_inline void decRef() {\n      if (unlikely(m_useCount.fetch_sub(1u, std::memory_order_acquire) == 1u))\n        free();\n    }\n\n    /**\n     * \\brief Queries allocation flags\n     * \\returns Allocation flags\n     */\n    DxvkAllocationFlags flags() const {\n      return m_flags;\n    }\n\n    /**\n     * \\brief Queries mapped memory region\n     * \\returns Mapped memory region\n     */\n    void* mapPtr() const {\n      return m_mapPtr;\n    }\n\n    /**\n     * \\brief D3DKMT resource local handle\n     * \\returns The resource D3DKMT local handle\n     * \\returns \\c 0 if resource is not shared\n     */\n    D3DKMT_HANDLE kmtLocal() const {\n      return m_kmtLocal;\n    }\n\n    /**\n     * \\brief D3DKMT resource global handle\n     * \\returns The resource D3DKMT global handle\n     * \\returns \\c 0 if resource is not shared or shared with NT handle\n     */\n    D3DKMT_HANDLE kmtGlobal() const {\n      return m_kmtGlobal;\n    }\n\n    /**\n     * \\brief Queries memory info\n     * \\returns Memory info\n     */\n    DxvkResourceMemoryInfo getMemoryInfo() const {\n      DxvkResourceMemoryInfo result = { };\n      result.memory = m_memory;\n      result.offset = m_address & DxvkPageAllocator::ChunkAddressMask;\n      result.size = m_size;\n      return result;\n    }\n\n    /**\n     * \\brief Queries buffer info\n     * \\returns Buffer info\n     */\n    DxvkResourceBufferInfo getBufferInfo() const {\n      DxvkResourceBufferInfo result = { };\n      result.buffer = m_buffer;\n      result.offset = m_bufferOffset;\n      result.size = m_size;\n      result.mapPtr = m_mapPtr;\n      result.gpuAddress = m_bufferAddress;\n      return result;\n    }\n\n    /**\n     * \\brief Queries image info\n     * \\returns Image info\n     */\n    DxvkResourceImageInfo getImageInfo() const {\n      DxvkResourceImageInfo result = { };\n      result.image = m_image;\n      result.mapPtr = m_mapPtr;\n      return result;\n    }\n\n    /**\n     * \\brief Queries sparse page table\n     *\n     * Only applies to sparse resources.\n     * \\returns Pointer to sparse page table\n     */\n    DxvkSparsePageTable* getSparsePageTable() const {\n      return m_sparsePageTable;\n    }\n\n    /**\n     * \\brief Queries memory property flags\n     *\n     * May be 0 for imported or foreign resources.\n     * \\returns Memory property flags\n     */\n    VkMemoryPropertyFlags getMemoryProperties() const {\n      return m_type ? m_type->properties.propertyFlags : 0u;\n    }\n\n    /**\n     * \\brief Creates buffer view\n     *\n     * \\param [in] key View properties\n     * \\returns Buffer view handle\n     */\n    const DxvkDescriptor* createBufferView(\n      const DxvkBufferViewKey&          key);\n\n    /**\n     * \\brief Creates image view\n     *\n     * \\param [in] key View properties\n     * \\returns Image view handle\n     */\n    const DxvkDescriptor* createImageView(\n      const DxvkImageViewKey&           key);\n\n  private:\n\n    std::atomic<uint32_t>       m_useCount = { 0u };\n    DxvkAllocationFlags         m_flags = 0u;\n\n    uint64_t                    m_resourceCookie = 0u;\n\n    VkDeviceMemory              m_memory = VK_NULL_HANDLE;\n    VkDeviceSize                m_address = 0u;\n    VkDeviceSize                m_size = 0u;\n    void*                       m_mapPtr = nullptr;\n\n    VkBuffer                    m_buffer = VK_NULL_HANDLE;\n    VkDeviceSize                m_bufferOffset = 0u;\n    VkDeviceAddress             m_bufferAddress = 0u;\n    DxvkResourceBufferViewMap*  m_bufferViews = nullptr;\n\n    VkImage                     m_image = VK_NULL_HANDLE;\n    DxvkResourceImageViewMap*   m_imageViews = nullptr;\n    D3DKMT_HANDLE               m_kmtLocal = 0;\n    D3DKMT_HANDLE               m_kmtGlobal = 0;\n\n    DxvkSparsePageTable*        m_sparsePageTable = nullptr;\n\n    DxvkMemoryAllocator*        m_allocator = nullptr;\n    DxvkMemoryType*             m_type = nullptr;\n\n    DxvkResourceAllocation*     m_nextCached = nullptr;\n\n    DxvkResourceAllocation*     m_prevInChunk = nullptr;\n    DxvkResourceAllocation*     m_nextInChunk = nullptr;\n\n    void initKmtHandles(VkExternalMemoryHandleTypeFlagBits handleType);\n\n    void destroyBufferViews();\n\n    void free();\n\n    static force_inline uint64_t getIncrement(DxvkAccess access) {\n      return uint64_t(1u) << (20u * uint32_t(access));\n    }\n\n  };\n\n\n  /**\n   * \\brief Resource allocation pool\n   *\n   * Creates and recycles resource allocation objects.\n   */\n  class DxvkResourceAllocationPool {\n\n  public:\n\n    DxvkResourceAllocationPool();\n\n    ~DxvkResourceAllocationPool();\n\n    template<typename... Args>\n    DxvkResourceAllocation* create(Args&&... args) {\n      return new (alloc()) DxvkResourceAllocation(std::forward<Args>(args)...);\n    }\n\n    void free(DxvkResourceAllocation* allocation) {\n      allocation->~DxvkResourceAllocation();\n      recycle(allocation);\n    }\n\n  private:\n\n    struct Storage {\n      alignas(DxvkResourceAllocation)\n      char data[sizeof(DxvkResourceAllocation)];\n    };\n\n    struct StorageList {\n      StorageList(StorageList* next_)\n      : next(next_) { }\n\n      StorageList* next = nullptr;\n    };\n\n    struct StoragePool {\n      std::array<Storage, 1023> objects;\n      std::unique_ptr<StoragePool> next;\n    };\n\n    std::unique_ptr<StoragePool>  m_pool;\n    StorageList*                  m_next = nullptr;\n\n    void* alloc() {\n      if (unlikely(!m_next))\n        createPool();\n\n      StorageList* list = m_next;\n      m_next = list->next;\n      list->~StorageList();\n\n      auto storage = std::launder(reinterpret_cast<Storage*>(list));\n      return storage->data;\n    }\n\n    void recycle(void* allocation) {\n      auto storage = std::launder(reinterpret_cast<Storage*>(allocation));\n      m_next = new (storage->data) StorageList(m_next);\n    }\n\n    void createPool();\n\n  };\n\n\n  /**\n   * \\brief Local allocation cache\n   *\n   * Provides pre-allocated memory of supported power-of-two sizes\n   * in a non-thread safe manner. This is intended to be used for\n   * context classes in order to reduce lock contention.\n   */\n  class DxvkLocalAllocationCache {\n    friend DxvkMemoryAllocator;\n  public:\n    // Cache allocations up to 128 kiB\n    constexpr static uint32_t PoolCount = 10u;\n\n    constexpr static VkDeviceSize MinSize = DxvkPoolAllocator::MinSize;\n    constexpr static VkDeviceSize MaxSize = MinSize << (PoolCount - 1u);\n\n    constexpr static VkDeviceSize PoolCapacityInBytes = 4u * DxvkPageAllocator::PageSize;\n\n    DxvkLocalAllocationCache() = default;\n\n    DxvkLocalAllocationCache(\n            DxvkMemoryAllocator*        allocator,\n            uint32_t                    memoryTypes)\n    : m_allocator(allocator), m_memoryTypes(memoryTypes) { }\n\n    DxvkLocalAllocationCache(DxvkLocalAllocationCache&& other)\n    : m_allocator(other.m_allocator), m_memoryTypes(other.m_memoryTypes),\n      m_pools(other.m_pools) {\n      other.m_allocator = nullptr;\n      other.m_memoryTypes = 0u;\n      other.m_pools = { };\n    }\n\n    DxvkLocalAllocationCache& operator = (DxvkLocalAllocationCache&& other) {\n      freeCache();\n\n      m_allocator = other.m_allocator;\n      m_memoryTypes = other.m_memoryTypes;\n      m_pools = other.m_pools;\n\n      other.m_allocator = nullptr;\n      other.m_memoryTypes = 0u;\n      other.m_pools = { };\n      return *this;\n    }\n\n    ~DxvkLocalAllocationCache() {\n      freeCache();\n    }\n\n    /**\n     * \\brief Computes preferred number of cached allocations\n     *\n     * Depends on size so that a large enough number of consecutive\n     * allocations can be handled by the local cache without wasting\n     * too much memory on larger allocations.\n     * \\param [in] size Allocation size\n     */\n    static uint32_t computePreferredAllocationCount(\n            VkDeviceSize                size);\n\n    /**\n     * \\brief Computes pool index for a given allocation size\n     *\n     * \\param [in] size Allocation size\n     * \\returns Pool index\n     */\n    static uint32_t computePoolIndex(\n            VkDeviceSize                size);\n\n    /**\n     * \\brief Computes allocation size for a given index\n     *\n     * \\param [in] poolIndex Pool index\n     * \\returns Allocation size for the pool\n     */\n    static VkDeviceSize computeAllocationSize(\n            uint32_t                    index);\n\n  private:\n\n    DxvkMemoryAllocator*  m_allocator   = nullptr;\n    uint32_t              m_memoryTypes = 0u;\n\n    std::array<DxvkResourceAllocation*, PoolCount> m_pools = { };\n\n    DxvkResourceAllocation* allocateFromCache(\n            VkDeviceSize                size);\n\n    DxvkResourceAllocation* assignCache(\n            VkDeviceSize                size,\n            DxvkResourceAllocation*     allocation);\n\n    void freeCache();\n\n  };\n\n\n  /**\n   * \\brief Allocation cache stats\n   *\n   * Keeps track of the number of requests as\n   * well as the total size of the cache.\n   */\n  struct DxvkSharedAllocationCacheStats {\n    /// Total number of requests\n    uint32_t requestCount = 0u;\n    /// Number of failed requests\n    uint32_t missCount = 0u;\n    /// Cache size, in bytes\n    VkDeviceSize size = 0u;\n  };\n\n\n  /**\n   * \\brief Shared allocation cache\n   *\n   * Accumulates small allocations in free lists\n   * that can be allocated in their entirety.\n   */\n  class DxvkSharedAllocationCache {\n    constexpr static uint32_t PoolCount = DxvkLocalAllocationCache::PoolCount;\n    constexpr static uint32_t PoolSize = PoolCount * (env::is32BitHostPlatform() ? 6u : 12u);\n\n    constexpr static VkDeviceSize PoolCapacityInBytes = DxvkLocalAllocationCache::PoolCapacityInBytes;\n\n    friend DxvkMemoryAllocator;\n  public:\n\n    DxvkSharedAllocationCache(\n            DxvkMemoryAllocator*        allocator);\n\n    ~DxvkSharedAllocationCache();\n\n    /**\n     * \\brief Retrieves list of cached allocations\n     *\n     * \\param [in] allocationSize Required allocation size\n     * \\returns Pointer to head of allocation list,\n     *    or \\c nullptr if the cache is empty.\n     */\n    DxvkResourceAllocation* getAllocationList(\n            VkDeviceSize                allocationSize);\n\n    /**\n     * \\brief Frees cacheable allocation\n     *\n     * \\param [in] allocation Allocation to free\n     * \\returns List to destroy if the cache is full. Usually,\n     *    \\c nullptr if the allocation was successfully added.\n     */\n    DxvkResourceAllocation* freeAllocation(\n            DxvkResourceAllocation*     allocation);\n\n    /**\n     * \\brief Queries statistics\n     * \\returns Cache statistics\n     */\n    DxvkSharedAllocationCacheStats getStats();\n\n    /**\n     * \\brief Frees unused memory\n     *\n     * Periodically called from the worker to free some\n     * memory that has not been used in some time.\n     * \\param [in] time Current time\n     */\n    void cleanupUnusedFromLockedAllocator(\n            high_resolution_clock::time_point time);\n\n  private:\n\n    struct FreeList {\n      uint16_t size = 0u;\n      uint16_t capacity = 0u;\n\n      DxvkResourceAllocation* head = nullptr;\n    };\n\n    struct List {\n      DxvkResourceAllocation* head = nullptr;\n      int32_t                 next = -1;\n    };\n\n    struct Pool {\n      int32_t   listIndex = -1;\n      uint32_t  listCount = 0u;\n      high_resolution_clock::time_point drainTime = { };\n    };\n\n    alignas(CACHE_LINE_SIZE)\n    DxvkMemoryAllocator*        m_allocator = nullptr;\n\n    dxvk::mutex                 m_freeMutex;\n    std::array<FreeList, PoolCount> m_freeLists = { };\n\n    alignas(CACHE_LINE_SIZE)\n    dxvk::mutex                 m_poolMutex;\n    std::array<Pool, PoolCount> m_pools = { };\n    std::array<List, PoolSize>  m_lists = { };\n    int32_t                     m_nextList = -1;\n\n    uint32_t                    m_numRequests = 0u;\n    uint32_t                    m_numMisses = 0u;\n\n    VkDeviceSize                m_cacheSize = 0u;\n    VkDeviceSize                m_maxCacheSize = 0u;\n\n  };\n\n\n  /**\n   * \\brief Buffer import info\n   *\n   * Used to import an existing Vulkan buffer. Note\n   * that imported buffers must not be renamed.\n   */\n  struct DxvkBufferImportInfo {\n    /// Buffer handle\n    VkBuffer buffer = VK_NULL_HANDLE;\n    /// Buffer offset\n    VkDeviceSize offset = 0;\n    /// Pointer to mapped memory region\n    void* mapPtr = nullptr;\n  };\n\n\n  /**\n   * \\brief Allocation modes\n   */\n  enum class DxvkAllocationMode : uint32_t {\n    /// If set, the allocation will fail if video memory is\n    /// full rather than falling back to system memory.\n    NoFallback      = 0,\n    /// If set, the allocation will only succeed if it\n    /// can be suballocated from an existing chunk.\n    NoAllocation    = 1,\n    /// Avoid using a dedicated allocation for this resource\n    NoDedicated     = 2,\n    /// Do not use device memory. Used to evict resources.\n    NoDeviceMemory  = 3,\n\n    eFlagEnum\n  };\n\n  using DxvkAllocationModes = Flags<DxvkAllocationMode>;\n\n\n  /**\n   * \\brief Allocation properties\n   */\n  struct DxvkAllocationInfo {\n    /// Virtual resource cookie for the allocation\n    uint64_t resourceCookie = 0u;\n    /// Desired memory property flags\n    VkMemoryPropertyFlags properties = 0u;\n    /// Allocation mode flags\n    DxvkAllocationModes mode = 0u;\n    /// Shared handle type\n    VkExternalMemoryHandleTypeFlagBits handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;\n  };\n\n\n  /**\n   * \\brief Relocation entry\n   */\n  struct DxvkRelocationEntry {\n    DxvkRelocationEntry() = default;\n    DxvkRelocationEntry(Rc<DxvkPagedResource>&& r, DxvkAllocationModes m)\n    : resource(std::move(r)), mode(m) { }\n\n    /// Resource to relocate\n    Rc<DxvkPagedResource> resource;\n    /// Resource to relocate\n    DxvkAllocationModes mode = 0u;\n  };\n\n\n  /**\n   * \\brief Resource relocation helper\n   *\n   * Simple thread-safe data structure used to pass a list of\n   * resources to move from the allocator to the CS thread.\n   */\n  class DxvkRelocationList {\n\n  public:\n\n    DxvkRelocationList();\n\n    ~DxvkRelocationList();\n\n    /**\n     * \\brief Retrieves list of resources to move\n     *\n     * Removes items from the internally stored list.\n     * Any duplicate entries will be removed.\n     * \\param [in] count Number of entries to return\n     * \\param [in] size Maximum total resource size\n     * \\returns List of resources to move\n     */\n    std::vector<DxvkRelocationEntry> poll(\n            uint32_t                    count,\n            VkDeviceSize                size);\n\n    /**\n     * \\brief Adds relocation entry to the list\n     *\n     * \\param [in] resource Resource to add\n     * \\param [in] allocation Resource storage\n     * \\param [in] mode Allocation mode\n     */\n    void addResource(\n            Rc<DxvkPagedResource>&&     resource,\n      const DxvkResourceAllocation*     allocation,\n            DxvkAllocationModes         mode);\n\n    /**\n     * \\brief Clears list\n     */\n    void clear();\n\n    /**\n     * \\brief Checks whether resource list is empty\n     * \\returns \\c true if the list is empty\n     */\n    bool empty() {\n      return m_entries.empty();\n    }\n\n  private:\n\n    struct RelocationOrdering {\n      bool operator () (const DxvkResourceMemoryInfo& a, const DxvkResourceMemoryInfo& b) const {\n        // Keep chunks together, then order by offset in order to increase\n        // the likelihood of freeing up a contiguous block of memory.\n        if (a.memory < b.memory) return true;\n        if (a.memory > b.memory) return false;\n        return a.offset > b.offset;\n      }\n    };\n\n    dxvk::mutex                 m_mutex;\n\n    std::map<\n      DxvkResourceMemoryInfo,\n      DxvkRelocationEntry,\n      RelocationOrdering>       m_entries;\n\n  };\n\n\n  /**\n   * \\brief Memory allocator\n   * \n   * Allocates device memory for Vulkan resources.\n   * Memory objects will be destroyed automatically.\n   */\n  class DxvkMemoryAllocator {\n    friend DxvkResourceAllocation;\n    friend DxvkLocalAllocationCache;\n    friend DxvkSharedAllocationCache;\n\n    constexpr static uint64_t DedicatedChunkAddress = 1ull << 63u;\n\n    constexpr static VkDeviceSize MinChunkSize =   4ull << 20;\n    constexpr static VkDeviceSize MaxChunkSize = 256ull << 20;\n\n    // Assume an alignment of 256 bytes. This is enough to satisfy all\n    // buffer use cases, and matches our minimum allocation size.\n    constexpr static VkDeviceSize GlobalBufferAlignment = 256u;\n\n    // Minimum number of allocations we want to be able to fit into a heap\n    constexpr static uint32_t MinAllocationsPerHeap = 7u;\n\n    // Minimal set of buffer usage flags to consider for global buffers\n    constexpr static VkBufferUsageFlags MinGlobalBufferUsage =\n      VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |\n      VK_BUFFER_USAGE_TRANSFER_DST_BIT |\n      VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n\n    // Possible buffer usage flags for descriptor buffers\n    constexpr static VkBufferUsageFlags DescriptorBufferUsage =\n      VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT |\n      VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    // Buffer usage flag for descriptor heaps\n    constexpr static VkBufferUsageFlags DescriptorHeapUsage =\n      VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT;\n  public:\n    \n    DxvkMemoryAllocator(DxvkDevice* device);\n    ~DxvkMemoryAllocator();\n    \n    DxvkDevice* device() const {\n      return m_device;\n    }\n\n    /**\n     * \\brief Allocates memory for a regular resource\n     *\n     * This method should be used when a dedicated allocation is\n     * not required. Very large resources may still be placed in\n     * a dedicated allocation.\n     * \\param [in] requirements Memory requirements\n     * \\param [in] allocationInfo Allocation info\n     * \\param [in] properties Memory property flags. Some of\n     *    these may be ignored in case of memory pressure.\n     * \\returns Allocated memory\n     */\n    Rc<DxvkResourceAllocation> allocateMemory(\n      const VkMemoryRequirements&             requirements,\n      const DxvkAllocationInfo&               allocationInfo);\n\n    /**\n     * \\brief Allocates memory for a resource\n     *\n     * Will always create a dedicated allocation.\n     * \\param [in] requirements Memory requirements\n     * \\param [in] allocationInfo Allocation info\n     * \\param [in] next Further memory properties\n     * \\returns Allocated memory\n     */\n    Rc<DxvkResourceAllocation> allocateDedicatedMemory(\n      const VkMemoryRequirements&             requirements,\n      const DxvkAllocationInfo&               allocationInfo,\n      const void*                             next);\n\n    /**\n     * \\brief Creates buffer resource\n     *\n     * Will make use of global buffers whenever possible, but\n     * may fall back to creating a dedicated Vulkan buffer.\n     * \\param [in] createInfo Buffer create info\n     * \\param [in] allocationInfo Allocation properties\n     * \\param [in] allocationCache Optional allocation cache\n     * \\returns Buffer resource\n     */\n    Rc<DxvkResourceAllocation> createBufferResource(\n      const VkBufferCreateInfo&         createInfo,\n      const DxvkAllocationInfo&         allocationInfo,\n            DxvkLocalAllocationCache*   allocationCache);\n\n    /**\n     * \\brief Creates image resource\n     *\n     * \\param [in] createInfo Image create info\n     * \\param [in] allocationInfo Allocation properties\n     * \\param [in] next External memory properties\n     * \\returns Image resource\n     */\n    Rc<DxvkResourceAllocation> createImageResource(\n      const VkImageCreateInfo&          createInfo,\n      const DxvkAllocationInfo&         allocationInfo,\n      const void*                       next);\n\n    /**\n     * \\brief Creates allocation for sparse binding\n     *\n     * Allocates a single page of memory for sparse binding.\n     * \\returns Allocated memory region\n     */\n    Rc<DxvkResourceAllocation> createSparsePage();\n\n    /**\n     * \\brief Creates local allocation cache for buffer resources\n     *\n     * \\param [in] bufferUsage Required buffer usage flags\n     * \\param [in] properties Required memory properties\n     * \\returns Local allocation cache\n     */\n    DxvkLocalAllocationCache createAllocationCache(\n            VkBufferUsageFlags          bufferUsage,\n            VkMemoryPropertyFlags       properties);\n\n    /**\n     * \\brief Imports existing buffer resource\n     *\n     * \\param [in] createInfo Buffer create info\n     * \\param [in] importInfo Buffer import info\n     * \\returns Buffer resource\n     */\n    Rc<DxvkResourceAllocation> importBufferResource(\n      const VkBufferCreateInfo&         createInfo,\n      const DxvkAllocationInfo&         allocationInfo,\n      const DxvkBufferImportInfo&       importInfo);\n\n    /**\n     * \\brief Imports existing image resource\n     *\n     * \\param [in] createInfo Image create info\n     * \\param [in] imageHandle Image handle\n     * \\returns Image resource\n     */\n    Rc<DxvkResourceAllocation> importImageResource(\n      const VkImageCreateInfo&          createInfo,\n      const DxvkAllocationInfo&         allocationInfo,\n            VkImage                     imageHandle);\n\n    /**\n     * \\brief Queries memory stats\n     * \n     * Returns the total amount of memory\n     * allocated and used for a given heap.\n     * \\param [in] heap Heap index\n     * \\returns Memory stats for this heap\n     */\n    DxvkMemoryStats getMemoryStats(uint32_t heap) const;\n\n    /**\n     * \\brief Retrieves detailed memory statistics\n     *\n     * Queries statistics for each memory type and each allocated chunk.\n     * Can be useful to determine the degree of memory fragmentation.\n     * \\param [out] stats Memory statistics\n     */\n    void getAllocationStats(DxvkMemoryAllocationStats& stats);\n\n    /**\n     * \\brief Queries shared cache stats\n     *\n     * Returns statistics for all shared caches.\n     * \\returns Shared cache stats\n     */\n    DxvkSharedAllocationCacheStats getAllocationCacheStats() const;\n\n    /**\n     * \\brief Queries buffer memory requirements\n     *\n     * Can be used to get memory requirements without having\n     * to create a buffer object first.\n     * \\param [in] createInfo Buffer create info\n     * \\param [in,out] memoryRequirements Memory requirements\n     */\n    bool getBufferMemoryRequirements(\n      const VkBufferCreateInfo&     createInfo,\n            VkMemoryRequirements2&  memoryRequirements) const;\n\n    /**\n     * \\brief Queries image memory requirements\n     *\n     * Can be used to get memory requirements without having\n     * to create an image object first.\n     * \\param [in] createInfo Image create info\n     * \\param [in,out] memoryRequirements Memory requirements\n     */\n    bool getImageMemoryRequirements(\n      const VkImageCreateInfo&      createInfo,\n            VkMemoryRequirements2&  memoryRequirements) const;\n\n    /**\n     * \\brief Registers a paged resource with cookie\n     *\n     * Useful when the allocator needs to track resources.\n     * \\param [in] resource Resource to add\n     */\n    void registerResource(\n            DxvkPagedResource*          resource);\n\n    /**\n     * \\brief Unregisters a paged resource\n     * \\param [in] resource Resource to remove\n     */\n    void unregisterResource(\n            DxvkPagedResource*          resource);\n\n    /**\n     * \\brief Requests to make a resource resident\n     *\n     * Attempts to move an evicted resource back to VRAM.\n     * \\param [in] resource Resource to relocate\n     */\n    void requestMakeResident(\n            DxvkPagedResource*          resource);\n\n    /**\n     * \\brief Locks an allocation in place\n     *\n     * Ensures that the resource is marked as immovable so\n     * that defragmentation won't attempt to relocate it.\n     */\n    void lockResourceGpuAddress(\n      const Rc<DxvkResourceAllocation>& allocation);\n\n    /**\n     * \\brief Performs clean-up tasks\n     *\n     * Intended to be called periodically by a worker thread in order\n     * to initiate defragmentation, clean up the allocation cache and\n     * free unused memory.\n     */\n    void performTimedTasks();\n\n    /**\n     * \\brief Polls relocation list\n     *\n     * \\param [in] count Maximum resource count\n     * \\param [in] size Maximum total size\n     * \\returns Relocation entries\n     */\n    auto pollRelocationList(uint32_t count, VkDeviceSize size) {\n      return m_relocations.poll(count, size);\n    }\n\n  private:\n\n    DxvkDevice* m_device;\n\n    DxvkSharingModeInfo       m_sharingModeInfo;\n\n    dxvk::mutex               m_mutex;\n\n    uint32_t m_memTypeCount = 0u;\n    uint32_t m_memHeapCount = 0u;\n\n    std::array<DxvkMemoryType, VK_MAX_MEMORY_TYPES> m_memTypes = { };\n    std::array<DxvkMemoryHeap, VK_MAX_MEMORY_HEAPS> m_memHeaps = { };\n\n    VkBufferUsageFlags  m_globalBufferUsageFlags = 0u;\n    uint32_t            m_globalBufferMemoryTypes = 0u;\n\n    uint32_t            m_sparseMemoryTypes = 0u;\n\n    std::array<uint32_t, 16> m_memTypesByPropertyFlags = { };\n\n    DxvkResourceAllocationPool  m_allocationPool;\n\n    uint64_t            m_nextCookie = 0u;\n\n    alignas(CACHE_LINE_SIZE)\n    high_resolution_clock::time_point m_taskDeadline = { };\n    std::array<DxvkMemoryStats, VK_MAX_MEMORY_HEAPS> m_adapterHeapStats = { };\n\n    alignas(CACHE_LINE_SIZE)\n    dxvk::mutex               m_resourceMutex;\n    std::unordered_map<uint64_t, DxvkPagedResource*> m_resourceMap;\n\n    alignas(CACHE_LINE_SIZE)\n    DxvkRelocationList        m_relocations;\n\n    DxvkDeviceMemory allocateDeviceMemory(\n            DxvkMemoryType&       type,\n            VkDeviceSize          size,\n      const void*                 next);\n\n    void assignMemoryDebugName(\n      const DxvkDeviceMemory&     memory,\n      const DxvkMemoryType&       type);\n\n    bool allocateChunkInPool(\n            DxvkMemoryType&       type,\n            DxvkMemoryPool&       pool,\n            VkMemoryPropertyFlags properties,\n            VkDeviceSize          requiredSize,\n            VkDeviceSize          desiredSize);\n\n    void freeDeviceMemory(\n            DxvkMemoryType&       type,\n            DxvkDeviceMemory      memory);\n\n    void freeAllocation(\n            DxvkResourceAllocation* allocation);\n\n    void freeLocalCache(\n            DxvkLocalAllocationCache* cache);\n\n    void freeCachedAllocations(\n            DxvkResourceAllocation* allocation);\n\n    void freeCachedAllocationsLocked(\n            DxvkResourceAllocation* allocation);\n\n    uint32_t countEmptyChunksInPool(\n      const DxvkMemoryPool&       pool) const;\n\n    void freeEmptyChunksInHeap(\n      const DxvkMemoryHeap&       heap,\n            VkDeviceSize          allocationSize,\n            high_resolution_clock::time_point time);\n\n    bool freeEmptyChunksInPool(\n            DxvkMemoryType&       type,\n            DxvkMemoryPool&       pool,\n            VkDeviceSize          allocationSize,\n            high_resolution_clock::time_point time);\n\n    int32_t findEmptyChunkInPool(\n      const DxvkMemoryPool&       pool,\n            VkDeviceSize          minSize,\n            VkDeviceSize          maxSize) const;\n\n    void mapDeviceMemory(\n            DxvkDeviceMemory&     memory,\n            VkMemoryPropertyFlags properties);\n\n    DxvkResourceAllocation* createAllocation(\n            DxvkMemoryType&       type,\n            DxvkMemoryPool&       pool,\n            VkDeviceSize          address,\n            VkDeviceSize          size,\n      const DxvkAllocationInfo&   allocationInfo);\n\n    DxvkResourceAllocation* createAllocation(\n            DxvkMemoryType&       type,\n      const DxvkDeviceMemory&     memory,\n      const DxvkAllocationInfo&   allocationInfo);\n\n    DxvkResourceAllocation* createAllocation(\n            DxvkSparsePageTable*  sparsePageTable,\n      const DxvkAllocationInfo&   allocationInfo);\n\n    bool refillAllocationCache(\n            DxvkLocalAllocationCache* cache,\n      const VkMemoryRequirements& requirements,\n            VkMemoryPropertyFlags properties);\n\n    void getAllocationStatsForPool(\n      const DxvkMemoryType&       type,\n      const DxvkMemoryPool&       pool,\n            DxvkMemoryAllocationStats& stats);\n\n    VkDeviceSize determineMaxChunkSize(\n      const DxvkMemoryType&       type,\n            bool                  mappable) const;\n\n    uint32_t determineSparseMemoryTypes(\n            DxvkDevice*           device) const;\n\n    void determineBufferUsageFlagsPerMemoryType();\n\n    void determineMemoryTypesWithPropertyFlags();\n\n    VkDeviceAddress getBufferDeviceAddress(\n            VkBuffer              buffer) const;\n\n    void logMemoryError(\n      const VkMemoryRequirements& req) const;\n\n    void logMemoryStats() const;\n\n    uint32_t getMemoryTypeMask(\n            VkMemoryPropertyFlags properties) const;\n\n    uint32_t findGlobalBufferMemoryTypeMask(\n            VkBufferUsageFlags    usage) const;\n\n    void updateMemoryHeapBudgets();\n\n    void updateMemoryHeapStats(\n            uint32_t              heapIndex);\n\n    void moveDefragChunk(\n            DxvkMemoryType&       type);\n\n    void pickDefragChunk(\n            DxvkMemoryType&       type);\n\n    void evictResources(\n            DxvkMemoryType&       type);\n\n    void performTimedTasksLocked(\n            high_resolution_clock::time_point currentTime);\n\n    bool enableDefrag() const;\n\n  };\n  \n\n\n  inline void DxvkResourceAllocation::free() {\n    m_allocator->freeAllocation(this);\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_blit.cpp",
    "content": "#include \"dxvk_device.h\"\n#include \"dxvk_meta_blit.h\"\n#include \"dxvk_util.h\"\n\n#include <dxvk_fullscreen_geom.h>\n#include <dxvk_fullscreen_vert.h>\n#include <dxvk_fullscreen_layer_vert.h>\n\n#include <dxvk_blit_frag_1d.h>\n#include <dxvk_blit_frag_2d.h>\n#include <dxvk_blit_frag_3d.h>\n#include <dxvk_blit_frag_2d_ms.h>\n\nnamespace dxvk {\n  \n  DxvkMetaBlitObjects::DxvkMetaBlitObjects(DxvkDevice* device)\n  : m_device(device),\n    m_layout(createPipelineLayout()){\n\n  }\n\n\n  DxvkMetaBlitObjects::~DxvkMetaBlitObjects() {\n    auto vk = m_device->vkd();\n\n    for (const auto& pair : m_pipelines)\n      vk->vkDestroyPipeline(vk->device(), pair.second.pipeline, nullptr);\n  }\n  \n  \n  DxvkMetaBlitPipeline DxvkMetaBlitObjects::getPipeline(\n          VkImageViewType       viewType,\n          VkFormat              viewFormat,\n          VkSampleCountFlagBits srcSamples,\n          VkSampleCountFlagBits dstSamples,\n          DxvkMetaBlitResolveMode resolveMode) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    DxvkMetaBlitPipelineKey key;\n    key.viewType   = viewType;\n    key.viewFormat = viewFormat;\n    key.srcSamples = srcSamples;\n    key.dstSamples = dstSamples;\n\n    if (srcSamples != VK_SAMPLE_COUNT_1_BIT)\n      key.resolveMode = resolveMode;\n\n    auto entry = m_pipelines.find(key);\n    if (entry != m_pipelines.end())\n      return entry->second;\n\n    DxvkMetaBlitPipeline pipeline = this->createPipeline(key);\n    m_pipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n\n  const DxvkPipelineLayout* DxvkMetaBlitObjects::createPipelineLayout() const {\n    DxvkDescriptorSetLayoutBinding binding = { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT };\n\n    return m_device->createBuiltInPipelineLayout(DxvkPipelineLayoutFlag::UsesSamplerHeap,\n      VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(DxvkMetaBlitPushConstants), 1, &binding);\n  }\n\n\n  DxvkMetaBlitPipeline DxvkMetaBlitObjects::createPipeline(\n    const DxvkMetaBlitPipelineKey&    key) const {\n    util::DxvkBuiltInGraphicsState state = { };\n\n    std::array<VkSpecializationMapEntry, 3u> specMap = {{\n      { 0u, offsetof(DxvkMetaBlitPipelineKey, srcSamples),  sizeof(VkSampleCountFlagBits) },\n      { 1u, offsetof(DxvkMetaBlitPipelineKey, dstSamples),  sizeof(VkSampleCountFlagBits) },\n      { 2u, offsetof(DxvkMetaBlitPipelineKey, resolveMode), sizeof(DxvkMetaBlitResolveMode) },\n    }};\n\n    VkSpecializationInfo specInfo = { };\n    specInfo.mapEntryCount = specMap.size();\n    specInfo.pMapEntries = specMap.data();\n    specInfo.dataSize = sizeof(key);\n    specInfo.pData = &key;\n\n    if (m_device->features().vk12.shaderOutputLayer) {\n      state.vs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_layer_vert, nullptr);\n    } else {\n      state.vs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_vert, nullptr);\n      state.gs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_geom, nullptr);\n    }\n\n    if (key.srcSamples != VK_SAMPLE_COUNT_1_BIT) {\n      if (key.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) {\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_blit_frag_2d_ms, &specInfo);\n      } else {\n        throw DxvkError(\"DxvkMetaBlitObjects: Invalid view type for multisampled image\");\n      }\n    } else {\n      switch (key.viewType) {\n        case VK_IMAGE_VIEW_TYPE_1D_ARRAY: state.fs = util::DxvkBuiltInShaderStage(dxvk_blit_frag_1d, nullptr); break;\n        case VK_IMAGE_VIEW_TYPE_2D_ARRAY: state.fs = util::DxvkBuiltInShaderStage(dxvk_blit_frag_2d, nullptr); break;\n        case VK_IMAGE_VIEW_TYPE_3D:       state.fs = util::DxvkBuiltInShaderStage(dxvk_blit_frag_3d, nullptr); break;\n        default: throw DxvkError(\"DxvkMetaBlitObjects: Invalid view type\");\n      }\n    }\n\n    state.colorFormat = key.viewFormat;\n    state.sampleCount = key.dstSamples;\n\n    return { m_layout, m_device->createBuiltInGraphicsPipeline(m_layout, state) };\n  }\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_blit.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <unordered_map>\n\n#include \"../spirv/spirv_code_buffer.h\"\n\n#include \"dxvk_hash.h\"\n#include \"dxvk_image.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Texture coordinates\n   */\n  struct DxvkMetaBlitOffset {\n    float x, y, z;\n  };\n  \n  /**\n   * \\brief Push constant data\n   */\n  struct DxvkMetaBlitPushConstants {\n    DxvkMetaBlitOffset srcCoord0;\n    DxvkMetaBlitOffset srcCoord1;\n    uint32_t           layerCount;\n    uint32_t           sampler;\n  };\n\n  /**\n   * \\brief Resolve mode for multisampled blits\n   */\n  enum class DxvkMetaBlitResolveMode : uint32_t {\n    FilterNearest     = 0u,\n    FilterLinear      = 1u,\n    ResolveAverage    = 2u,\n  };\n  \n  /**\n   * \\brief Blit pipeline key\n   * \n   * We have to create pipelines for each\n   * combination of source image view type\n   * and image format.\n   */\n  struct DxvkMetaBlitPipelineKey {\n    VkImageViewType       viewType;\n    VkFormat              viewFormat;\n    VkSampleCountFlagBits srcSamples;\n    VkSampleCountFlagBits dstSamples;\n    DxvkMetaBlitResolveMode resolveMode;\n    \n    bool eq(const DxvkMetaBlitPipelineKey& other) const {\n      return this->viewType     == other.viewType\n          && this->viewFormat   == other.viewFormat\n          && this->srcSamples   == other.srcSamples\n          && this->dstSamples   == other.dstSamples\n          && this->resolveMode  == other.resolveMode;\n    }\n    \n    size_t hash() const {\n      DxvkHashState result;\n      result.add(uint32_t(this->viewType));\n      result.add(uint32_t(this->viewFormat));\n      result.add(uint32_t(this->srcSamples));\n      result.add(uint32_t(this->dstSamples));\n      result.add(uint32_t(this->resolveMode));\n      return result;\n    }\n  };\n\n  \n  /**\n   * \\brief Blit pipeline\n   * \n   * Stores the objects for a single pipeline\n   * that is used for blitting.\n   */\n  struct DxvkMetaBlitPipeline {\n    const DxvkPipelineLayout* layout    = nullptr;\n    VkPipeline                pipeline  = VK_NULL_HANDLE;;\n  };\n  \n\n  /**\n   * \\brief Blitter objects\n   * \n   * Stores render pass objects and pipelines used\n   * to generate mip maps. Due to Vulkan API design\n   * decisions, we have to create one render pass\n   * and pipeline object per image format used.\n   */\n  class DxvkMetaBlitObjects {\n    \n  public:\n    \n    DxvkMetaBlitObjects(DxvkDevice* device);\n    ~DxvkMetaBlitObjects();\n    \n    /**\n     * \\brief Creates a blit pipeline\n     * \n     * \\param [in] viewType Source image view type\n     * \\param [in] viewFormat Image view format\n     * \\param [in] srcSamples Source sample count\n     * \\param [in] dstSamples Target sample count\n     * \\param [in] resolveMode The resolve mode to use\n     * \\returns The blit pipeline\n     */\n    DxvkMetaBlitPipeline getPipeline(\n            VkImageViewType       viewType,\n            VkFormat              viewFormat,\n            VkSampleCountFlagBits srcSamples,\n            VkSampleCountFlagBits dstSamples,\n            DxvkMetaBlitResolveMode resolveMode);\n    \n  private:\n\n    DxvkDevice* m_device = nullptr;\n\n    const DxvkPipelineLayout* m_layout = nullptr;\n    \n    dxvk::mutex m_mutex;\n    \n    std::unordered_map<\n      DxvkMetaBlitPipelineKey,\n      DxvkMetaBlitPipeline,\n      DxvkHash, DxvkEq> m_pipelines;\n    \n    const DxvkPipelineLayout* createPipelineLayout() const;\n    \n    DxvkMetaBlitPipeline createPipeline(\n      const DxvkMetaBlitPipelineKey& key) const;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_clear.cpp",
    "content": "#include \"dxvk_meta_clear.h\"\n#include \"dxvk_device.h\"\n\n#include <dxvk_clear_buffer_f.h>\n#include <dxvk_clear_buffer_u.h>\n#include <dxvk_clear_image1d_f.h>\n#include <dxvk_clear_image1d_u.h>\n#include <dxvk_clear_image1darr_f.h>\n#include <dxvk_clear_image1darr_u.h>\n#include <dxvk_clear_image2d_f.h>\n#include <dxvk_clear_image2d_u.h>\n#include <dxvk_clear_image2darr_f.h>\n#include <dxvk_clear_image2darr_u.h>\n#include <dxvk_clear_image3d_f.h>\n#include <dxvk_clear_image3d_u.h>\n\nnamespace dxvk {\n  \n  DxvkMetaClearObjects::DxvkMetaClearObjects(DxvkDevice* device)\n  : m_device(device) {\n    // Create pipeline layouts using those descriptor set layouts\n    m_clearBufPipeLayout = createPipelineLayout(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);\n    m_clearImgPipeLayout = createPipelineLayout(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);\n\n    // Create the actual compute pipelines\n    m_clearPipesF32.clearBuf = createPipeline(dxvk_clear_buffer_f, m_clearBufPipeLayout);\n    m_clearPipesU32.clearBuf = createPipeline(dxvk_clear_buffer_u, m_clearBufPipeLayout);\n\n    m_clearPipesF32.clearImg1D = createPipeline(dxvk_clear_image1d_f, m_clearImgPipeLayout);\n    m_clearPipesU32.clearImg1D = createPipeline(dxvk_clear_image1d_u, m_clearImgPipeLayout);\n    m_clearPipesF32.clearImg2D = createPipeline(dxvk_clear_image2d_f, m_clearImgPipeLayout);\n    m_clearPipesU32.clearImg2D = createPipeline(dxvk_clear_image2d_u, m_clearImgPipeLayout);\n    m_clearPipesF32.clearImg3D = createPipeline(dxvk_clear_image3d_f, m_clearImgPipeLayout);\n    m_clearPipesU32.clearImg3D = createPipeline(dxvk_clear_image3d_u, m_clearImgPipeLayout);\n\n    m_clearPipesF32.clearImg1DArray = createPipeline(dxvk_clear_image1darr_f, m_clearImgPipeLayout);\n    m_clearPipesU32.clearImg1DArray = createPipeline(dxvk_clear_image1darr_u, m_clearImgPipeLayout);\n    m_clearPipesF32.clearImg2DArray = createPipeline(dxvk_clear_image2darr_f, m_clearImgPipeLayout);\n    m_clearPipesU32.clearImg2DArray = createPipeline(dxvk_clear_image2darr_u, m_clearImgPipeLayout);\n  }\n  \n  \n  DxvkMetaClearObjects::~DxvkMetaClearObjects() {\n    auto vk = m_device->vkd();\n\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesF32.clearBuf, nullptr);\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesU32.clearBuf, nullptr);\n    \n    vk->vkDestroyPipeline(vk->device(), m_clearPipesF32.clearImg1D, nullptr);\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesU32.clearImg1D, nullptr);\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesF32.clearImg2D, nullptr);\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesU32.clearImg2D, nullptr);\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesF32.clearImg3D, nullptr);\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesU32.clearImg3D, nullptr);\n    \n    vk->vkDestroyPipeline(vk->device(), m_clearPipesF32.clearImg1DArray, nullptr);\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesU32.clearImg1DArray, nullptr);\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesF32.clearImg2DArray, nullptr);\n    vk->vkDestroyPipeline(vk->device(), m_clearPipesU32.clearImg2DArray, nullptr);\n  }\n  \n  \n  DxvkMetaClearPipeline DxvkMetaClearObjects::getClearBufferPipeline(\n          DxvkFormatFlags       formatFlags) const {\n    DxvkMetaClearPipeline result = { };\n    result.layout     = m_clearBufPipeLayout;\n    result.pipeline   = m_clearPipesF32.clearBuf;\n    \n    if (formatFlags.any(DxvkFormatFlag::SampledUInt, DxvkFormatFlag::SampledSInt))\n      result.pipeline = m_clearPipesU32.clearBuf;\n\n    result.workgroupSize = VkExtent3D { 128, 1, 1 };\n    return result;\n  }\n  \n  \n  DxvkMetaClearPipeline DxvkMetaClearObjects::getClearImagePipeline(\n          VkImageViewType       viewType,\n          DxvkFormatFlags       formatFlags) const {\n    const auto& pipelines = formatFlags.any(DxvkFormatFlag::SampledUInt, DxvkFormatFlag::SampledSInt)\n      ? m_clearPipesU32\n      : m_clearPipesF32;\n\n    auto pipeInfo = [&pipelines, viewType] () -> std::pair<VkPipeline, VkExtent3D> {\n      switch (viewType) {\n        case VK_IMAGE_VIEW_TYPE_1D:       return { pipelines.clearImg1D,      VkExtent3D { 64, 1, 1 } };\n        case VK_IMAGE_VIEW_TYPE_2D:       return { pipelines.clearImg2D,      VkExtent3D {  8, 8, 1 } };\n        case VK_IMAGE_VIEW_TYPE_3D:       return { pipelines.clearImg3D,      VkExtent3D {  4, 4, 4 } };\n        case VK_IMAGE_VIEW_TYPE_1D_ARRAY: return { pipelines.clearImg1DArray, VkExtent3D { 64, 1, 1 } };\n        case VK_IMAGE_VIEW_TYPE_2D_ARRAY: return { pipelines.clearImg2DArray, VkExtent3D {  8, 8, 1 } };\n        default:                          return { VkPipeline(VK_NULL_HANDLE), VkExtent3D { 0, 0, 0, } };\n      }\n    }();\n\n    DxvkMetaClearPipeline result = { };\n    result.layout        = m_clearImgPipeLayout;\n    result.pipeline      = pipeInfo.first;\n    result.workgroupSize = pipeInfo.second;\n    return result;\n  }\n  \n  \n  const DxvkPipelineLayout* DxvkMetaClearObjects::createPipelineLayout(\n          VkDescriptorType        descriptorType) {\n    DxvkDescriptorSetLayoutBinding bindInfo = { descriptorType, 1, VK_SHADER_STAGE_COMPUTE_BIT };\n\n    return m_device->createBuiltInPipelineLayout(0u, VK_SHADER_STAGE_COMPUTE_BIT,\n      sizeof(DxvkMetaClearArgs), 1, &bindInfo);\n  }\n\n\n  VkPipeline DxvkMetaClearObjects::createPipeline(\n          size_t                  size,\n    const uint32_t*               code,\n    const DxvkPipelineLayout*     layout) {\n    util::DxvkBuiltInShaderStage stage = { };\n    stage.code = code;\n    stage.size = size;\n\n    return m_device->createBuiltInComputePipeline(layout, stage);\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_clear.h",
    "content": "#pragma once\n\n#include \"dxvk_format.h\"\n#include \"dxvk_include.h\"\n#include \"dxvk_pipelayout.h\"\n\n#include \"../spirv/spirv_code_buffer.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n  \n  /**\n   * \\brief Clear args\n   * \n   * The data structure that can be passed\n   * to the clear shaders as push constants.\n   */\n  struct DxvkMetaClearArgs {\n    VkClearColorValue clearValue;\n    VkOffset3D offset; uint32_t pad1;\n    VkExtent3D extent; uint32_t pad2;\n  };\n  \n  \n  /**\n   * \\brief Pipeline-related objects\n   * \n   * Use this to bind the pipeline\n   * and allocate a descriptor set.\n   */\n  struct DxvkMetaClearPipeline {\n    const DxvkPipelineLayout* layout = nullptr;\n    VkPipeline pipeline = VK_NULL_HANDLE;\n    VkExtent3D workgroupSize = { };\n  };\n  \n  \n  /**\n   * \\brief Clear shaders and related objects\n   * \n   * Creates the shaders, pipeline layouts, and\n   * compute pipelines that are going to be used\n   * for clear operations.\n   */\n  class DxvkMetaClearObjects {\n    \n  public:\n    \n    DxvkMetaClearObjects(DxvkDevice* device);\n    ~DxvkMetaClearObjects();\n    \n    /**\n     * \\brief Retrieves objects to use for buffers\n     * \n     * Returns the pipeline, pipeline layout and descriptor\n     * set layout which are required to perform a meta clear\n     * operation on a buffer resource with the given format.\n     * \\param [in] viewType The image virw type\n     */\n    DxvkMetaClearPipeline getClearBufferPipeline(\n            DxvkFormatFlags       formatFlags) const;\n    \n    /**\n     * \\brief Retrieves objects for a given image view type\n     * \n     * Returns the pipeline, pipeline layout and descriptor\n     * set layout which are required to perform a meta clear\n     * operation on a resource with the given view type.\n     * \\param [in] viewType The image virw type\n     * \\returns The pipeline-related objects to use\n     */\n    DxvkMetaClearPipeline getClearImagePipeline(\n            VkImageViewType       viewType,\n            DxvkFormatFlags       formatFlags) const;\n    \n  private:\n    \n    struct DxvkMetaClearPipelines {\n      VkPipeline clearBuf        = VK_NULL_HANDLE;\n      VkPipeline clearImg1D      = VK_NULL_HANDLE;\n      VkPipeline clearImg2D      = VK_NULL_HANDLE;\n      VkPipeline clearImg3D      = VK_NULL_HANDLE;\n      VkPipeline clearImg1DArray = VK_NULL_HANDLE;\n      VkPipeline clearImg2DArray = VK_NULL_HANDLE;\n    };\n    \n    DxvkDevice* m_device = nullptr;\n    \n    const DxvkPipelineLayout* m_clearBufPipeLayout = VK_NULL_HANDLE;\n    const DxvkPipelineLayout* m_clearImgPipeLayout = VK_NULL_HANDLE;\n    \n    DxvkMetaClearPipelines m_clearPipesF32;\n    DxvkMetaClearPipelines m_clearPipesU32;\n    \n    const DxvkPipelineLayout* createPipelineLayout(\n            VkDescriptorType        descriptorType);\n    \n    VkPipeline createPipeline(\n            size_t                  size,\n      const uint32_t*               code,\n      const DxvkPipelineLayout*     layout);\n\n    template<size_t N>\n    VkPipeline createPipeline(\n      const uint32_t                (&code)[N],\n      const DxvkPipelineLayout*     layout) {\n      return createPipeline(sizeof(uint32_t) * N, &code[0], layout);\n    }\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_copy.cpp",
    "content": "#include \"dxvk_device.h\"\n#include \"dxvk_meta_copy.h\"\n#include \"dxvk_util.h\"\n\n#include <dxvk_fullscreen_geom.h>\n#include <dxvk_fullscreen_vert.h>\n#include <dxvk_fullscreen_layer_vert.h>\n\n#include <dxvk_buffer_to_image_d.h>\n#include <dxvk_buffer_to_image_ds_export.h>\n#include <dxvk_buffer_to_image_f.h>\n#include <dxvk_buffer_to_image_s_discard.h>\n#include <dxvk_buffer_to_image_u.h>\n\n#include <dxvk_image_to_buffer_ds.h>\n#include <dxvk_image_to_buffer_f.h>\n\n#include <dxvk_copy_buffer_image.h>\n#include <dxvk_copy_color_1d.h>\n#include <dxvk_copy_color_2d.h>\n#include <dxvk_copy_color_ms.h>\n#include <dxvk_copy_depth_stencil_1d.h>\n#include <dxvk_copy_depth_stencil_2d.h>\n#include <dxvk_copy_depth_stencil_ms.h>\n\nnamespace dxvk {\n\n  DxvkMetaCopyViews::DxvkMetaCopyViews(\n    const Rc<DxvkImage>&            dstImage,\n    const VkImageSubresourceLayers& dstSubresources,\n          VkFormat                  dstFormat,\n    const Rc<DxvkImage>&            srcImage,\n    const VkImageSubresourceLayers& srcSubresources,\n          VkFormat                  srcFormat) {\n    VkImageAspectFlags dstAspects = dstImage->formatInfo()->aspectMask;\n    VkImageAspectFlags srcAspects = srcImage->formatInfo()->aspectMask;\n\n    // We don't support 3D here, so we can safely ignore that case\n    VkImageViewType dstViewType = dstImage->info().type == VK_IMAGE_TYPE_1D\n      ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n    VkImageViewType srcViewType = srcImage->info().type == VK_IMAGE_TYPE_1D\n      ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n\n    DxvkImageViewKey dstViewInfo;\n    dstViewInfo.viewType = dstViewType;\n    dstViewInfo.format = dstFormat;\n    dstViewInfo.aspects = dstSubresources.aspectMask;\n    dstViewInfo.mipIndex = dstSubresources.mipLevel;\n    dstViewInfo.mipCount = 1u;\n    dstViewInfo.layerIndex = dstSubresources.baseArrayLayer;\n    dstViewInfo.layerCount = dstSubresources.layerCount;\n    dstViewInfo.usage = (dstAspects & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n      ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT\n      : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\n    dstImageView = dstImage->createView(dstViewInfo);\n\n    // Create source image views\n    DxvkImageViewKey srcViewInfo;\n    srcViewInfo.viewType = srcViewType;\n    srcViewInfo.format = srcFormat;\n    srcViewInfo.aspects = srcSubresources.aspectMask & ~VK_IMAGE_ASPECT_STENCIL_BIT;\n    srcViewInfo.mipIndex = srcSubresources.mipLevel;\n    srcViewInfo.mipCount = 1u;\n    srcViewInfo.layerIndex = srcSubresources.baseArrayLayer;\n    srcViewInfo.layerCount = srcSubresources.layerCount;\n    srcViewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n\n    srcImageView = srcImage->createView(srcViewInfo);\n\n    if (srcAspects & VK_IMAGE_ASPECT_STENCIL_BIT) {\n      srcViewInfo.aspects = VK_IMAGE_ASPECT_STENCIL_BIT;\n      srcStencilView = srcImage->createView(srcViewInfo);\n    }\n  }\n  \n\n  DxvkMetaCopyViews::~DxvkMetaCopyViews() {\n\n  }\n\n  \n  DxvkMetaCopyObjects::DxvkMetaCopyObjects(DxvkDevice* device)\n  : m_device(device) {\n\n  }\n\n\n  DxvkMetaCopyObjects::~DxvkMetaCopyObjects() {\n    auto vk = m_device->vkd();\n\n    for (const auto& p : m_copyImagePipelines)\n      vk->vkDestroyPipeline(vk->device(), p.second.pipeline, nullptr);\n\n    for (const auto& p : m_bufferToImagePipelines)\n      vk->vkDestroyPipeline(vk->device(), p.second.pipeline, nullptr);\n\n    for (const auto& p : m_imageToBufferPipelines)\n      vk->vkDestroyPipeline(vk->device(), p.second.pipeline, nullptr);\n\n    vk->vkDestroyPipeline(vk->device(), m_copyBufferImagePipeline.pipeline, nullptr);\n  }\n\n\n  DxvkMetaCopyFormats DxvkMetaCopyObjects::getCopyImageFormats(\n          VkFormat              dstFormat,\n          VkImageAspectFlags    dstAspect,\n          VkFormat              srcFormat,\n          VkImageAspectFlags    srcAspect) const {\n    if (dstAspect == srcAspect)\n      return { dstFormat, srcFormat };\n\n    if (dstAspect == VK_IMAGE_ASPECT_COLOR_BIT && srcAspect == VK_IMAGE_ASPECT_DEPTH_BIT) {\n      switch (srcFormat) {\n        case VK_FORMAT_D16_UNORM:  return { VK_FORMAT_R16_UNORM,  VK_FORMAT_D16_UNORM  };\n        case VK_FORMAT_D32_SFLOAT: return { VK_FORMAT_R32_SFLOAT, VK_FORMAT_D32_SFLOAT };\n        default:                   return { VK_FORMAT_UNDEFINED,  VK_FORMAT_UNDEFINED  };\n      }\n    } else if (dstAspect == VK_IMAGE_ASPECT_DEPTH_BIT && srcAspect == VK_IMAGE_ASPECT_COLOR_BIT) {\n      switch (dstFormat) {\n        case VK_FORMAT_D16_UNORM:  return { VK_FORMAT_D16_UNORM,  VK_FORMAT_R16_UNORM  };\n        case VK_FORMAT_D32_SFLOAT: return { VK_FORMAT_D32_SFLOAT, VK_FORMAT_R32_SFLOAT };\n        default:                   return { VK_FORMAT_UNDEFINED,  VK_FORMAT_UNDEFINED  };\n      }\n    }\n\n    return { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED };\n  }\n\n\n  DxvkMetaCopyPipeline DxvkMetaCopyObjects::getCopyBufferToImagePipeline(\n          VkFormat              dstFormat,\n          VkFormat              srcFormat,\n          VkImageAspectFlags    aspects,\n          VkSampleCountFlags    samples) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    DxvkMetaBufferImageCopyPipelineKey key;\n    key.imageFormat = dstFormat;\n    key.bufferFormat = srcFormat;\n    key.imageAspects = aspects;\n    key.sampleCount = VkSampleCountFlagBits(samples);\n\n    auto entry = m_bufferToImagePipelines.find(key);\n    if (entry != m_bufferToImagePipelines.end())\n      return entry->second;\n\n    DxvkMetaCopyPipeline pipeline = createCopyBufferToImagePipeline(key);\n    m_bufferToImagePipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n\n  DxvkMetaCopyPipeline DxvkMetaCopyObjects::getCopyImageToBufferPipeline(\n          VkImageViewType       viewType,\n          VkFormat              dstFormat) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    DxvkMetaBufferImageCopyPipelineKey key;\n    key.imageViewType = viewType;\n    key.imageFormat = VK_FORMAT_UNDEFINED;\n    key.bufferFormat = dstFormat;\n    key.imageAspects = lookupFormatInfo(dstFormat)->aspectMask;\n\n    auto entry = m_bufferToImagePipelines.find(key);\n    if (entry != m_bufferToImagePipelines.end())\n      return entry->second;\n\n    DxvkMetaCopyPipeline pipeline = createCopyImageToBufferPipeline(key);\n    m_bufferToImagePipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n\n  DxvkMetaCopyPipeline DxvkMetaCopyObjects::getCopyImagePipeline(\n          VkImageViewType       viewType,\n          VkFormat              dstFormat,\n          VkSampleCountFlagBits dstSamples) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    DxvkMetaImageCopyPipelineKey key;\n    key.viewType = viewType;\n    key.format   = dstFormat;\n    key.samples  = dstSamples;\n    \n    auto entry = m_copyImagePipelines.find(key);\n    if (entry != m_copyImagePipelines.end())\n      return entry->second;\n\n    DxvkMetaCopyPipeline pipeline = createCopyImagePipeline(key);\n    m_copyImagePipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n\n  DxvkMetaCopyPipeline DxvkMetaCopyObjects::getCopyFormattedBufferPipeline() {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    if (!m_copyBufferImagePipeline.pipeline)\n      m_copyBufferImagePipeline = createCopyFormattedBufferPipeline();\n\n    return m_copyBufferImagePipeline;\n  }\n  \n  \n  DxvkMetaCopyPipeline DxvkMetaCopyObjects::createCopyFormattedBufferPipeline() {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 2> bindings = {{\n      { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },\n      { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },\n    }};\n\n    DxvkMetaCopyPipeline pipeline;\n    pipeline.layout = m_device->createBuiltInPipelineLayout(0u, VK_SHADER_STAGE_COMPUTE_BIT,\n      sizeof(DxvkFormattedBufferCopyArgs), bindings.size(), bindings.data());\n    pipeline.pipeline = m_device->createBuiltInComputePipeline(pipeline.layout,\n      util::DxvkBuiltInShaderStage(dxvk_copy_buffer_image, nullptr));\n    return pipeline;\n  }\n\n\n  DxvkMetaCopyPipeline DxvkMetaCopyObjects::createCopyImagePipeline(\n    const DxvkMetaImageCopyPipelineKey& key) {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 2> bindings = {{\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n    }};\n\n    DxvkMetaCopyPipeline pipeline = { };\n    pipeline.layout = m_device->createBuiltInPipelineLayout(0u, VK_SHADER_STAGE_FRAGMENT_BIT,\n      sizeof(VkOffset2D), bindings.size(), bindings.data());\n\n    VkImageAspectFlags aspect = lookupFormatInfo(key.format)->aspectMask;\n\n    util::DxvkBuiltInGraphicsState state = { };\n\n    if (m_device->features().vk12.shaderOutputLayer) {\n      state.vs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_layer_vert, nullptr);\n    } else {\n      state.vs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_vert, nullptr);\n      state.gs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_geom, nullptr);\n    }\n\n    bool useDepthStencil = m_device->features().extShaderStencilExport\n      && (aspect == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));\n\n    if (useDepthStencil) {\n      if (key.viewType == VK_IMAGE_VIEW_TYPE_1D)\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_copy_depth_stencil_1d, nullptr);\n      else if (key.samples == VK_SAMPLE_COUNT_1_BIT)\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_copy_depth_stencil_2d, nullptr);\n      else\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_copy_depth_stencil_ms, nullptr);\n    } else {\n      if (key.viewType == VK_IMAGE_VIEW_TYPE_1D)\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_copy_color_1d, nullptr);\n      else if (key.samples == VK_SAMPLE_COUNT_1_BIT)\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_copy_color_2d, nullptr);\n      else\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_copy_color_ms, nullptr);\n    }\n\n    state.sampleCount = key.samples;\n\n    if (aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))\n      state.depthFormat = key.format;\n    else\n      state.colorFormat = key.format;\n\n    pipeline.pipeline = m_device->createBuiltInGraphicsPipeline(pipeline.layout, state);\n    return pipeline;\n  }\n\n\n  DxvkMetaCopyPipeline DxvkMetaCopyObjects::createCopyBufferToImagePipeline(\n    const DxvkMetaBufferImageCopyPipelineKey& key) {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 1> bindings = {{\n      { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT }\n    }};\n\n    DxvkMetaCopyPipeline pipeline;\n    pipeline.layout = m_device->createBuiltInPipelineLayout(0u, VK_SHADER_STAGE_FRAGMENT_BIT,\n      sizeof(DxvkBufferImageCopyArgs), bindings.size(), bindings.data());\n\n    VkStencilOpState stencilOp = { };\n    stencilOp.failOp      = VK_STENCIL_OP_REPLACE;\n    stencilOp.passOp      = VK_STENCIL_OP_REPLACE;\n    stencilOp.depthFailOp = VK_STENCIL_OP_REPLACE;\n    stencilOp.compareOp   = VK_COMPARE_OP_ALWAYS;\n    stencilOp.compareMask = 0xff;\n    stencilOp.writeMask   = 0xff;\n    stencilOp.reference   = 0xff;\n\n    // Clear stencil when writing depth aspect\n    if (!m_device->features().extShaderStencilExport && key.imageAspects != VK_IMAGE_ASPECT_STENCIL_BIT)\n      stencilOp.reference = 0x00;\n\n    VkPipelineDepthStencilStateCreateInfo dsState = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };\n    dsState.depthTestEnable   = !!(key.imageAspects & VK_IMAGE_ASPECT_DEPTH_BIT);\n    dsState.depthWriteEnable  = dsState.depthTestEnable;\n    dsState.depthCompareOp    = VK_COMPARE_OP_ALWAYS;\n    dsState.stencilTestEnable = !!(key.imageAspects & VK_IMAGE_ASPECT_STENCIL_BIT);\n    dsState.front             = stencilOp;\n    dsState.back              = stencilOp;\n\n    // Set up dynamic states. Stencil write mask is\n    // only required for the stencil discard shader.\n    VkDynamicState dynamicStencilWriteMask = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;\n\n    // Determine fragment shader to use. Always use the DS export shader\n    // if possible, it can support writing to one aspect exclusively.\n    VkSpecializationMapEntry specMap = { };\n    specMap.size = sizeof(VkFormat);\n\n    VkSpecializationInfo specInfo = { };\n    specInfo.mapEntryCount = 1;\n    specInfo.pMapEntries = &specMap;\n    specInfo.dataSize = sizeof(VkFormat);\n    specInfo.pData = &key.bufferFormat;\n\n    // Set up final pipeline state\n    util::DxvkBuiltInGraphicsState state = { };\n    state.sampleCount = key.sampleCount;\n\n    if (m_device->features().vk12.shaderOutputLayer) {\n      state.vs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_layer_vert, nullptr);\n    } else {\n      state.vs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_vert, nullptr);\n      state.gs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_geom, nullptr);\n    }\n\n    if (key.imageAspects & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n      if (m_device->features().extShaderStencilExport) {\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_buffer_to_image_ds_export, &specInfo);\n      } else if (key.imageAspects == VK_IMAGE_ASPECT_STENCIL_BIT) {\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_buffer_to_image_s_discard, &specInfo);\n\n        state.dynamicStateCount = 1u;\n        state.dynamicStates = &dynamicStencilWriteMask;\n      } else {\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_buffer_to_image_d, &specInfo);\n      }\n\n      state.depthFormat = key.imageFormat;\n      state.dsState = &dsState;\n    } else {\n      const auto* formatInfo = lookupFormatInfo(key.imageFormat);\n\n      state.fs = formatInfo->flags.any(DxvkFormatFlag::SampledUInt, DxvkFormatFlag::SampledSInt)\n        ? util::DxvkBuiltInShaderStage(dxvk_buffer_to_image_u, &specInfo)\n        : util::DxvkBuiltInShaderStage(dxvk_buffer_to_image_f, &specInfo);\n\n      state.colorFormat = key.imageFormat;\n    }\n\n    pipeline.pipeline = m_device->createBuiltInGraphicsPipeline(pipeline.layout, state);\n    return pipeline;\n  }\n\n\n  DxvkMetaCopyPipeline DxvkMetaCopyObjects::createCopyImageToBufferPipeline(\n    const DxvkMetaBufferImageCopyPipelineKey& key) {\n    DxvkMetaCopyPipeline pipeline = { };\n\n    static const std::array<DxvkDescriptorSetLayoutBinding, 3> bindings = {{\n      { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,        1, VK_SHADER_STAGE_COMPUTE_BIT },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,        1, VK_SHADER_STAGE_COMPUTE_BIT },\n    }};\n\n    pipeline.layout = m_device->createBuiltInPipelineLayout(0u, VK_SHADER_STAGE_COMPUTE_BIT,\n      sizeof(DxvkBufferImageCopyArgs), bindings.size(), bindings.data());\n\n    if (key.imageViewType != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {\n      Logger::err(str::format(\"DxvkMetaCopyObjects: Unsupported view type: \", key.imageViewType));\n      return DxvkMetaCopyPipeline();\n    }\n\n    VkSpecializationMapEntry specMap = { };\n    specMap.size = sizeof(VkFormat);\n\n    VkSpecializationInfo specInfo = { };\n    specInfo.mapEntryCount = 1;\n    specInfo.pMapEntries = &specMap;\n    specInfo.dataSize = sizeof(VkFormat);\n    specInfo.pData = &key.bufferFormat;\n\n    util::DxvkBuiltInShaderStage stage = (key.imageAspects & VK_IMAGE_ASPECT_STENCIL_BIT)\n      ? util::DxvkBuiltInShaderStage(dxvk_image_to_buffer_ds, &specInfo)\n      : util::DxvkBuiltInShaderStage(dxvk_image_to_buffer_f, &specInfo);\n\n    pipeline.pipeline = m_device->createBuiltInComputePipeline(pipeline.layout, stage);\n    return pipeline;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_copy.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <unordered_map>\n\n#include \"../spirv/spirv_code_buffer.h\"\n\n#include \"dxvk_barrier.h\"\n#include \"dxvk_cmdlist.h\"\n#include \"dxvk_hash.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n\n  /**\n   * \\brief Push constants for formatted buffer copies\n   */\n  struct DxvkFormattedBufferCopyArgs {\n    VkOffset3D dstOffset; uint32_t pad0;\n    VkOffset3D srcOffset; uint32_t pad1;\n    VkExtent3D extent;    uint32_t pad2;\n    VkExtent2D dstSize;\n    VkExtent2D srcSize;\n  };\n\n  /**\n   * \\brief Pair of view formats for copy operation\n   */\n  struct DxvkMetaCopyFormats {\n    VkFormat dstFormat = VK_FORMAT_UNDEFINED;\n    VkFormat srcFormat = VK_FORMAT_UNDEFINED;\n  };\n\n  /**\n   * \\brief Copy pipeline\n   * \n   * Stores the objects for a single pipeline\n   * that is used for fragment shader copies.\n   */\n  struct DxvkMetaCopyPipeline {\n    const DxvkPipelineLayout* layout   = nullptr;\n    VkPipeline                pipeline = VK_NULL_HANDLE;\n  };\n\n\n  /**\n   * \\brief Push constants for buffer <-> image copies\n   */\n  struct DxvkBufferImageCopyArgs {\n    VkOffset3D imageOffset;\n    uint32_t bufferOffset;\n    VkExtent3D imageExtent;\n    uint32_t bufferImageWidth;\n    uint32_t bufferImageHeight;\n    uint32_t stencilBitIndex;\n  };\n\n  /**\n   * \\brief Copy pipeline key\n   * \n   * Used to look up copy pipelines based\n   * on the copy operation they support.\n   */\n  struct DxvkMetaImageCopyPipelineKey {\n    VkImageViewType       viewType  = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n    VkFormat              format    = VK_FORMAT_UNDEFINED;\n    VkSampleCountFlagBits samples   = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;\n\n    bool eq(const DxvkMetaImageCopyPipelineKey& other) const {\n      return this->viewType == other.viewType\n          && this->format   == other.format\n          && this->samples  == other.samples;\n    }\n\n    size_t hash() const {\n      return (uint32_t(format)  << 8)\n           ^ (uint32_t(samples) << 4)\n           ^ (uint32_t(viewType));\n    }\n  };\n\n  /**\n   * \\brief Buffer to image copy pipeline key\n   */\n  struct DxvkMetaBufferImageCopyPipelineKey {\n    VkImageViewType imageViewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n    VkFormat imageFormat = VK_FORMAT_UNDEFINED;\n    VkFormat bufferFormat = VK_FORMAT_UNDEFINED;\n    VkImageAspectFlags imageAspects = 0u;\n    VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;\n\n    bool eq(const DxvkMetaBufferImageCopyPipelineKey& other) const {\n      return this->imageViewType == other.imageViewType\n          && this->imageFormat   == other.imageFormat\n          && this->imageAspects  == other.imageAspects\n          && this->bufferFormat  == other.bufferFormat\n          && this->sampleCount   == other.sampleCount;\n    }\n\n    size_t hash() const {\n      return (uint32_t(imageViewType))\n           ^ (uint32_t(imageAspects) << 4)\n           ^ (uint32_t(imageFormat) << 8)\n           ^ (uint32_t(bufferFormat) << 16)\n           ^ (uint32_t(sampleCount) << 28);\n    }\n  };\n\n\n  /**\n   * \\brief Copy view objects\n   * \n   * Creates and manages views used in a\n   * framebuffer-based copy operations.\n   */\n  class DxvkMetaCopyViews {\n\n  public:\n\n    DxvkMetaCopyViews(\n      const Rc<DxvkImage>&            dstImage,\n      const VkImageSubresourceLayers& dstSubresources,\n            VkFormat                  dstFormat,\n      const Rc<DxvkImage>&            srcImage,\n      const VkImageSubresourceLayers& srcSubresources,\n            VkFormat                  srcFormat);\n    \n    ~DxvkMetaCopyViews();\n\n    Rc<DxvkImageView> dstImageView;\n    Rc<DxvkImageView> srcImageView;\n    Rc<DxvkImageView> srcStencilView;\n\n  };\n\n  /**\n   * \\brief Meta copy objects\n   * \n   * Meta copy operations are necessary in order\n   * to copy data between color and depth images.\n   */\n  class DxvkMetaCopyObjects {\n\n  public:\n\n    DxvkMetaCopyObjects(DxvkDevice* device);\n    ~DxvkMetaCopyObjects();\n\n    /**\n     * \\brief Queries color format for d->c copies\n     * \n     * Returns the color format that we need to use\n     * as the destination image view format in case\n     * of depth to color image copies.\n     * \\param [in] dstFormat Destination image format\n     * \\param [in] dstAspect Destination aspect mask\n     * \\param [in] srcFormat Source image format\n     * \\param [in] srcAspect Source aspect mask\n     * \\returns Corresponding color format\n     */\n    DxvkMetaCopyFormats getCopyImageFormats(\n            VkFormat              dstFormat,\n            VkImageAspectFlags    dstAspect,\n            VkFormat              srcFormat,\n            VkImageAspectFlags    srcAspect) const;\n\n    /**\n     * \\brief Creates pipeline for buffer to image copy\n     *\n     * Note that setting both depth and stencil aspects\n     * requires device support for depth-stencil export.\n     * For multisampled images, all samples for a pixel\n     * will receive the same value.\n     * \\param [in] dstFormat Destionation image format\n     * \\param [in] srcFormat Source buffer data format\n     * \\param [in] aspects Aspect mask to copy\n     * \\param [in] samples Sample count\n     */\n    DxvkMetaCopyPipeline getCopyBufferToImagePipeline(\n            VkFormat              dstFormat,\n            VkFormat              srcFormat,\n            VkImageAspectFlags    aspects,\n            VkSampleCountFlags    samples);\n\n    /**\n     * \\brief Creates pipeline for image to buffer copy\n     *\n     * This method always returns a compute pipeline.\n     * \\param [in] viewType Image view type\n     * \\param [in] dstFormat Destionation buffer format\n     */\n    DxvkMetaCopyPipeline getCopyImageToBufferPipeline(\n            VkImageViewType       viewType,\n            VkFormat              dstFormat);\n\n    /**\n     * \\brief Creates pipeline for meta copy operation\n     * \n     * \\param [in] viewType Image view type\n     * \\param [in] dstFormat Destination image format\n     * \\param [in] dstSamples Destination sample count\n     * \\returns Compatible pipeline for the operation\n     */\n    DxvkMetaCopyPipeline getCopyImagePipeline(\n            VkImageViewType       viewType,\n            VkFormat              dstFormat,\n            VkSampleCountFlagBits dstSamples);\n\n    /**\n     * \\brief Creates pipeline for buffer image copy\n     * \\returns Compute pipeline for buffer image copies\n     */\n    DxvkMetaCopyPipeline getCopyFormattedBufferPipeline();\n\n  private:\n\n    DxvkDevice* m_device = nullptr;\n\n    dxvk::mutex m_mutex;\n\n    std::unordered_map<DxvkMetaImageCopyPipelineKey,\n      DxvkMetaCopyPipeline, DxvkHash, DxvkEq> m_copyImagePipelines;\n\n    std::unordered_map<DxvkMetaBufferImageCopyPipelineKey,\n      DxvkMetaCopyPipeline, DxvkHash, DxvkEq> m_bufferToImagePipelines;\n\n    std::unordered_map<DxvkMetaBufferImageCopyPipelineKey,\n      DxvkMetaCopyPipeline, DxvkHash, DxvkEq> m_imageToBufferPipelines;\n\n    DxvkMetaCopyPipeline m_copyBufferImagePipeline = { };\n\n    DxvkMetaCopyPipeline createCopyFormattedBufferPipeline();\n\n    DxvkMetaCopyPipeline createCopyImagePipeline(\n      const DxvkMetaImageCopyPipelineKey& key);\n\n    DxvkMetaCopyPipeline createCopyBufferToImagePipeline(\n      const DxvkMetaBufferImageCopyPipelineKey& key);\n\n    DxvkMetaCopyPipeline createCopyImageToBufferPipeline(\n      const DxvkMetaBufferImageCopyPipelineKey& key);\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_mipgen.cpp",
    "content": "#include \"dxvk_device.h\"\n#include \"dxvk_meta_mipgen.h\"\n\n#include <dxvk_mipgen.h>\n\nnamespace dxvk {\n\n  DxvkMetaMipGenViews::DxvkMetaMipGenViews(\n    const Rc<DxvkImageView>&  view,\n          VkPipelineBindPoint bindPoint)\n  : m_view(view), m_bindPoint(bindPoint) {\n    // Determine view type based on image type\n    const std::array<std::pair<VkImageViewType, VkImageViewType>, 3> viewTypes = {{\n      { VK_IMAGE_VIEW_TYPE_1D_ARRAY, VK_IMAGE_VIEW_TYPE_1D_ARRAY },\n      { VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_VIEW_TYPE_2D_ARRAY },\n      { VK_IMAGE_VIEW_TYPE_3D,       VK_IMAGE_VIEW_TYPE_2D_ARRAY },\n    }};\n    \n    m_srcViewType = viewTypes.at(uint32_t(view->image()->info().type)).first;\n    m_dstViewType = viewTypes.at(uint32_t(view->image()->info().type)).second;\n    \n    // Create image views and framebuffers\n    m_passes.resize(view->info().mipCount - 1);\n    \n    for (uint32_t i = 0; i < m_passes.size(); i++)\n      m_passes[i] = createViews(i);\n  }\n  \n  \n  DxvkMetaMipGenViews::~DxvkMetaMipGenViews() {\n\n  }\n  \n  \n  VkExtent3D DxvkMetaMipGenViews::computePassExtent(uint32_t passId) const {\n    VkExtent3D extent = m_view->mipLevelExtent(passId + 1);\n    \n    if (m_view->image()->info().type != VK_IMAGE_TYPE_3D)\n      extent.depth = m_view->info().layerCount;\n    \n    return extent;\n  }\n  \n  \n  DxvkMetaMipGenViews::PassViews DxvkMetaMipGenViews::createViews(uint32_t pass) const {\n    PassViews result = { };\n\n    // Source image view\n    DxvkImageViewKey srcViewInfo;\n    srcViewInfo.viewType = m_srcViewType;\n    srcViewInfo.format = m_view->info().format;\n    srcViewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    srcViewInfo.aspects = m_view->info().aspects;\n    srcViewInfo.mipIndex = m_view->info().mipIndex + pass;\n    srcViewInfo.mipCount = 1;\n    srcViewInfo.layerIndex = m_view->info().layerIndex;\n    srcViewInfo.layerCount = m_view->info().layerCount;\n\n    result.src = m_view->image()->createView(srcViewInfo);\n    \n    // Create destination image view, which points\n    // to the mip level we're going to render to.\n    VkExtent3D dstExtent = m_view->mipLevelExtent(pass + 1);\n    \n    DxvkImageViewKey dstViewInfo;\n    dstViewInfo.viewType = m_dstViewType;\n    dstViewInfo.format = m_view->info().format;\n    dstViewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n    dstViewInfo.aspects = m_view->info().aspects;\n    dstViewInfo.mipIndex = m_view->info().mipIndex + pass + 1;\n    dstViewInfo.mipCount = 1u;\n    \n    if (m_view->image()->info().type != VK_IMAGE_TYPE_3D) {\n      dstViewInfo.layerIndex = m_view->info().layerIndex;\n      dstViewInfo.layerCount = m_view->info().layerCount;\n    } else {\n      dstViewInfo.layerIndex = 0;\n      dstViewInfo.layerCount = dstExtent.depth;\n    }\n\n    if (m_bindPoint == VK_PIPELINE_BIND_POINT_COMPUTE) {\n      dstViewInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT;\n      dstViewInfo.layout = VK_IMAGE_LAYOUT_GENERAL;\n    }\n\n    result.dst = m_view->image()->createView(dstViewInfo);\n\n    return result;\n  }\n  \n\n  DxvkMetaMipGenObjects::DxvkMetaMipGenObjects(DxvkDevice* device)\n  : m_device(device), m_layout(createPipelineLayout()) {\n\n  }\n\n\n  DxvkMetaMipGenObjects::~DxvkMetaMipGenObjects() {\n\n  }\n\n\n  bool DxvkMetaMipGenObjects::checkFormatSupport(\n          VkFormat              viewFormat) {\n    std::lock_guard lock(m_mutex);\n    auto entry = m_formatSupport.find(viewFormat);\n\n    if (entry != m_formatSupport.end())\n      return entry->second;\n\n    bool support = queryFormatSupport(viewFormat);\n    m_formatSupport.insert({ viewFormat, support });\n\n    return support;\n  }\n\n\n  DxvkMetaMipGenPipeline DxvkMetaMipGenObjects::getPipeline(\n          VkFormat              viewFormat) {\n    std::lock_guard lock(m_mutex);\n\n    auto entry = m_pipelines.find(viewFormat);\n\n    if (entry != m_pipelines.end())\n      return entry->second;\n\n    DxvkMetaMipGenPipeline pipeline = createPipeline(viewFormat);\n    m_pipelines.insert({ viewFormat, pipeline });\n    return pipeline;\n  }\n\n\n  const DxvkPipelineLayout* DxvkMetaMipGenObjects::createPipelineLayout() const {\n    std::array<DxvkDescriptorSetLayoutBinding, 2> bindings = {{\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1u,                  VK_SHADER_STAGE_COMPUTE_BIT },\n      { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, MipCount + MipCount, VK_SHADER_STAGE_COMPUTE_BIT },\n    }};\n\n    return m_device->createBuiltInPipelineLayout(DxvkPipelineLayoutFlag::UsesSamplerHeap,\n      VK_SHADER_STAGE_COMPUTE_BIT, sizeof(DxvkMetaMipGenPushConstants), bindings.size(), bindings.data());\n  }\n\n\n  DxvkMetaMipGenPipeline DxvkMetaMipGenObjects::createPipeline(VkFormat format) const {\n    auto formatInfo = lookupFormatInfo(format);\n\n    const std::array<VkSpecializationMapEntry, 2u> specMap = {{\n      { 0u, offsetof(DxvkMetaMipGenSpecConstants, format),          sizeof(VkFormat) },\n      { 1u, offsetof(DxvkMetaMipGenSpecConstants, formatDwords),    sizeof(uint32_t) },\n    }};\n\n    DxvkMetaMipGenSpecConstants specConstants = { };\n    specConstants.format = format;\n    specConstants.formatDwords = std::max<uint32_t>(1u,\n      formatInfo->elementSize / sizeof(uint32_t));\n\n    VkSpecializationInfo specInfo = { };\n    specInfo.mapEntryCount = specMap.size();\n    specInfo.pMapEntries = specMap.data();\n    specInfo.dataSize = sizeof(specConstants);\n    specInfo.pData = &specConstants;\n\n    util::DxvkBuiltInShaderStage shader(dxvk_mipgen, &specInfo);\n\n    DxvkMetaMipGenPipeline pipeline = { };\n    pipeline.layout = m_layout;\n    pipeline.mipsPerStep = MipCount;\n    pipeline.pipeline = m_device->createBuiltInComputePipeline(m_layout, shader);\n\n    return pipeline;\n  }\n\n\n  bool DxvkMetaMipGenObjects::queryFormatSupport(\n          VkFormat              viewFormat) const {\n    // Fixed list of formats that the shader understands\n    static const std::array<VkFormat, 26> s_formats = {{\n      VK_FORMAT_R8_UNORM,\n      VK_FORMAT_R8_SNORM,\n      VK_FORMAT_R8G8_UNORM,\n      VK_FORMAT_R8G8_SNORM,\n      VK_FORMAT_R16_SFLOAT,\n      VK_FORMAT_R16G16_SFLOAT,\n      VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_A8B8G8R8_UNORM_PACK32,\n      VK_FORMAT_R8G8B8A8_SNORM,\n      VK_FORMAT_B8G8R8A8_SNORM,\n      VK_FORMAT_A8B8G8R8_SNORM_PACK32,\n      VK_FORMAT_A2R10G10B10_UNORM_PACK32,\n      VK_FORMAT_A2B10G10R10_UNORM_PACK32,\n      VK_FORMAT_A2R10G10B10_SNORM_PACK32,\n      VK_FORMAT_A2B10G10R10_SNORM_PACK32,\n      VK_FORMAT_B10G11R11_UFLOAT_PACK32,\n      VK_FORMAT_R16G16B16A16_SFLOAT,\n      VK_FORMAT_R16_UNORM,\n      VK_FORMAT_R16_SNORM,\n      VK_FORMAT_R32_SFLOAT,\n      VK_FORMAT_R16G16_UNORM,\n      VK_FORMAT_R16G16_SNORM,\n      VK_FORMAT_R32G32_SFLOAT,\n      VK_FORMAT_R16G16B16A16_UNORM,\n      VK_FORMAT_R16G16B16A16_SNORM,\n    }};\n\n    if (!m_device->perfHints().preferComputeMipGen)\n      return false;\n\n    // Check whether the shader actually supports the format in question\n    if (std::find(s_formats.begin(), s_formats.end(), viewFormat) == s_formats.end())\n      return false;\n\n    // The shader has some feature requirements that aren't otherwise\n    // needed to run DXVK, make sure everything is supported.\n    if (!m_device->features().vk12.shaderInt8\n     || !m_device->features().vk12.shaderFloat16\n     || !m_device->features().khrShaderSubgroupUniformControlFlow.shaderSubgroupUniformControlFlow)\n      return false;\n\n    // Ensure that the format can support the required usage patterns\n    auto formatFeatures = m_device->adapter()->getFormatFeatures(viewFormat);\n\n    if (!(formatFeatures.optimal & VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT)\n     || !(formatFeatures.optimal & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT))\n      return false;\n\n    return true;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_mipgen.h",
    "content": "#pragma once\n\n#include <vector>\n\n#include \"../util/util_small_vector.h\"\n\n#include \"dxvk_meta_blit.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Mip map generation render pass\n   * \n   * Stores image views, framebuffer objects and\n   * a render pass object for mip map generation.\n   * This must be created per image view.\n   */\n  class DxvkMetaMipGenViews {\n    \n  public:\n    \n    DxvkMetaMipGenViews(\n      const Rc<DxvkImageView>&  view,\n            VkPipelineBindPoint bindPoint);\n    \n    ~DxvkMetaMipGenViews();\n    \n    /**\n     * \\brief Source image view type\n     * \n     * Use this to figure out which type the\n     * resource descriptor needs to have.\n     * \\returns Source image view type\n     */\n    VkImageViewType getSrcViewType() const {\n      return m_srcViewType;\n    }\n    \n    /**\n     * \\brief Render pass count\n     * \n     * Number of mip levels to generate.\n     * \\returns Render pass count\n     */\n    uint32_t getPassCount() const {\n      return m_passes.size();\n    }\n    \n    /**\n     * \\brief Source image view\n     * \n     * \\param [in] pass Render pass index\n     * \\returns Source image view handle for the given pass\n     */\n    Rc<DxvkImageView> getSrcView(uint32_t passId) const {\n      return m_passes[passId].src;\n    }\n\n    /**\n     * \\brief Destination image view\n     * \n     * \\param [in] pass Render pass index\n     * \\returns Destination image view handle for the given pass\n     */\n    Rc<DxvkImageView> getDstView(uint32_t passId) const {\n      return m_passes[passId].dst;\n    }\n\n    /**\n     * \\brief Returns subresource that will only be read\n     * \\returns Top level of the image view\n     */\n    VkImageSubresourceRange getTopSubresource() const {\n      VkImageSubresourceRange sr = m_view->imageSubresources();\n      sr.levelCount = 1;\n      return sr;\n    }\n\n    /**\n     * \\brief Returns subresource that will only be written\n     * \\returns Top level of the image view\n     */\n    VkImageSubresourceRange getBottomSubresource() const {\n      VkImageSubresourceRange sr = m_view->imageSubresources();\n      sr.baseMipLevel += sr.levelCount - 1;\n      sr.levelCount = 1;\n      return sr;\n    }\n\n    /**\n     * \\brief Returns all subresources that will be written\n     * \\returns All mip levels except the top level\n     */\n    VkImageSubresourceRange getAllTargetSubresources() const {\n      VkImageSubresourceRange sr = m_view->imageSubresources();\n      sr.baseMipLevel += 1;\n      sr.levelCount -= 1;\n      return sr;\n    }\n\n    /**\n     * \\brief Returns all subresources that will be read\n     * \\returns All mip levels except the bottom level\n     */\n    VkImageSubresourceRange getAllSourceSubresources() const {\n      VkImageSubresourceRange sr = m_view->imageSubresources();\n      sr.levelCount -= 1;\n      return sr;\n    }\n\n    /**\n     * \\brief Returns subresource read in a given pass\n     *\n     * \\param [in] pass Pass index\n     * \\returns The source subresource\n     */\n    VkImageSubresourceRange getSourceSubresource(uint32_t pass) const {\n      VkImageSubresourceRange sr = m_view->imageSubresources();\n      sr.baseMipLevel += pass;\n      sr.levelCount = 1;\n      return sr;\n    }\n\n    /**\n     * \\brief Framebuffer size for a given pass\n     * \n     * Stores the width, height, and layer count\n     * of the framebuffer for the given pass ID.\n     */\n    VkExtent3D computePassExtent(uint32_t passId) const;\n    \n  private:\n\n    struct PassViews {\n      Rc<DxvkImageView> src;\n      Rc<DxvkImageView> dst;\n    };\n\n    Rc<DxvkImageView> m_view;\n\n    VkPipelineBindPoint m_bindPoint;\n    \n    VkImageViewType m_srcViewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n    VkImageViewType m_dstViewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n    \n    small_vector<PassViews, 16> m_passes;\n    \n    PassViews createViews(uint32_t pass) const;\n    \n  };\n\n\n  /**\n   * \\brief Push data layout for mip gen pass\n   */\n  struct DxvkMetaMipGenPushConstants {\n    VkDeviceAddress atomicCounterVa = 0u;\n    uint32_t samplerIndex = 0u;\n    uint32_t mipCount = 0u;\n  };\n\n\n  /**\n   * \\brief Mip gen pipeline info\n   */\n  struct DxvkMetaMipGenPipeline {\n    const DxvkPipelineLayout* layout = nullptr;\n    VkPipeline pipeline = VK_NULL_HANDLE;\n    uint32_t mipsPerStep = 0u;\n  };\n\n\n  /**\n   * \\brief Spec constants for mip gen pipeline\n   */\n  struct DxvkMetaMipGenSpecConstants {\n    VkFormat format = VK_FORMAT_UNDEFINED;\n    uint32_t formatDwords = 0u;\n  };\n\n\n  /**\n   * \\brief Mip gen pipeline objects\n   */\n  class DxvkMetaMipGenObjects {\n\n  public:\n\n    constexpr static uint32_t MipCount = 6u;\n\n    DxvkMetaMipGenObjects(DxvkDevice* device);\n    ~DxvkMetaMipGenObjects();\n\n    /**\n     * \\brief Checks format-specific support\n     *\n     * \\param [in] format Format to query\n     * \\returns \\c true if compute mip-gen can be used\n     *    for the given format on the given device.\n     */\n    bool checkFormatSupport(\n            VkFormat              viewFormat);\n\n    /**\n     * \\brief Queries pipeline and properties of that pipeline\n     *\n     * Must only be called for supported formats.\n     * \\param [in] format Format to create the pipeline for\n     * \\returns Pipeline properties\n     */\n    DxvkMetaMipGenPipeline getPipeline(\n            VkFormat              viewFormat);\n\n  private:\n\n    DxvkDevice* m_device = nullptr;\n\n    dxvk::mutex m_mutex;\n\n    const DxvkPipelineLayout* m_layout;\n\n    std::unordered_map<VkFormat, bool> m_formatSupport;\n    std::unordered_map<VkFormat, DxvkMetaMipGenPipeline> m_pipelines;\n\n    const DxvkPipelineLayout* createPipelineLayout() const;\n\n    DxvkMetaMipGenPipeline createPipeline(VkFormat format) const;\n\n    bool queryFormatSupport(VkFormat viewFormat) const;\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_resolve.cpp",
    "content": "#include \"dxvk_device.h\"\n#include \"dxvk_meta_resolve.h\"\n#include \"dxvk_util.h\"\n\n#include <dxvk_fullscreen_geom.h>\n#include <dxvk_fullscreen_vert.h>\n#include <dxvk_fullscreen_layer_vert.h>\n\n#include <dxvk_resolve_frag_d.h>\n#include <dxvk_resolve_frag_ds.h>\n#include <dxvk_resolve_frag_f.h>\n#include <dxvk_resolve_frag_u.h>\n#include <dxvk_resolve_frag_i.h>\n\nnamespace dxvk {\n  \n  DxvkMetaResolveViews::DxvkMetaResolveViews(\n    const Rc<DxvkImage>&            dstImage,\n    const VkImageSubresourceLayers& dstSubresources,\n    const Rc<DxvkImage>&            srcImage,\n    const VkImageSubresourceLayers& srcSubresources,\n          VkFormat                  format) {\n    DxvkImageViewKey viewInfo;\n    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n    viewInfo.format = format;\n    viewInfo.aspects = dstSubresources.aspectMask;\n    viewInfo.mipIndex = dstSubresources.mipLevel;\n    viewInfo.mipCount = 1u;\n    viewInfo.layerIndex = dstSubresources.baseArrayLayer;\n    viewInfo.layerCount = dstSubresources.layerCount;\n    viewInfo.usage = (lookupFormatInfo(format)->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT)\n      ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n      : VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\n    dstView = dstImage->createView(viewInfo);\n\n    viewInfo.aspects = srcSubresources.aspectMask;\n    viewInfo.mipIndex = srcSubresources.mipLevel;\n    viewInfo.layerIndex = srcSubresources.baseArrayLayer;\n    viewInfo.layerCount = srcSubresources.layerCount;\n\n    srcView = srcImage->createView(viewInfo);\n  }\n\n\n  DxvkMetaResolveViews::~DxvkMetaResolveViews() {\n\n  }\n\n\n\n\n  DxvkMetaResolveObjects::DxvkMetaResolveObjects(DxvkDevice* device)\n  : m_device(device) {\n\n  }\n\n\n  DxvkMetaResolveObjects::~DxvkMetaResolveObjects() {\n    auto vk = m_device->vkd();\n\n    for (const auto& pair : m_pipelines)\n      vk->vkDestroyPipeline(vk->device(), pair.second.pipeline, nullptr);\n  }\n\n\n  DxvkMetaResolvePipeline DxvkMetaResolveObjects::getPipeline(\n          VkFormat                  format,\n          VkSampleCountFlagBits     samples,\n          VkResolveModeFlagBits     depthResolveMode,\n          VkResolveModeFlagBits     stencilResolveMode) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    DxvkMetaResolvePipelineKey key = { };\n    key.format  = format;\n    key.samples = samples;\n    key.modeD   = depthResolveMode;\n    key.modeS   = stencilResolveMode;\n\n    auto entry = m_pipelines.find(key);\n    if (entry != m_pipelines.end())\n      return entry->second;\n\n    DxvkMetaResolvePipeline pipeline = createPipeline(key);\n    m_pipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n\n  DxvkMetaResolvePipeline DxvkMetaResolveObjects::createPipeline(\n    const DxvkMetaResolvePipelineKey& key) {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 2> bindings = {{\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n    }};\n\n    DxvkMetaResolvePipeline pipeline = { };\n    pipeline.layout = m_device->createBuiltInPipelineLayout(0u, VK_SHADER_STAGE_FRAGMENT_BIT,\n      sizeof(VkOffset2D), bindings.size(), bindings.data());\n\n    auto formatInfo = lookupFormatInfo(key.format);\n\n    VkStencilOpState stencilOp = { };\n    stencilOp.failOp            = VK_STENCIL_OP_REPLACE;\n    stencilOp.passOp            = VK_STENCIL_OP_REPLACE;\n    stencilOp.depthFailOp       = VK_STENCIL_OP_REPLACE;\n    stencilOp.compareOp         = VK_COMPARE_OP_ALWAYS;\n    stencilOp.compareMask       = 0xffu;\n    stencilOp.writeMask         = 0xffu;\n\n    VkPipelineDepthStencilStateCreateInfo dsState = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };\n    dsState.depthTestEnable     = key.modeD != VK_RESOLVE_MODE_NONE;\n    dsState.depthWriteEnable    = key.modeD != VK_RESOLVE_MODE_NONE;\n    dsState.depthCompareOp      = VK_COMPARE_OP_ALWAYS;\n    dsState.stencilTestEnable   = key.modeS != VK_RESOLVE_MODE_NONE;\n    dsState.front               = stencilOp;\n    dsState.back                = stencilOp;\n\n    static const std::array<VkSpecializationMapEntry, 3> specEntries = {{\n      { 0, offsetof(DxvkMetaResolvePipelineKey, samples), sizeof(VkSampleCountFlagBits) },\n      { 1, offsetof(DxvkMetaResolvePipelineKey, modeD),   sizeof(VkResolveModeFlagBits) },\n      { 2, offsetof(DxvkMetaResolvePipelineKey, modeS),   sizeof(VkResolveModeFlagBits) },\n    }};\n\n    VkSpecializationInfo specInfo = { };\n    specInfo.mapEntryCount      = specEntries.size();\n    specInfo.pMapEntries        = specEntries.data();\n    specInfo.dataSize           = sizeof(key);\n    specInfo.pData              = &key;\n\n    util::DxvkBuiltInGraphicsState state = { };\n\n    if (m_device->features().vk12.shaderOutputLayer) {\n      state.vs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_layer_vert, nullptr);\n    } else {\n      state.vs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_vert, nullptr);\n      state.gs = util::DxvkBuiltInShaderStage(dxvk_fullscreen_geom, nullptr);\n    }\n\n    if (key.modeS && (formatInfo->aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)) {\n      if (m_device->features().extShaderStencilExport) {\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_resolve_frag_ds, &specInfo);\n      } else {\n        state.fs = util::DxvkBuiltInShaderStage(dxvk_resolve_frag_d, &specInfo);\n        Logger::warn(\"DXVK: Stencil export not supported by device, skipping stencil resolve\");\n      }\n    } else if (formatInfo->aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) {\n      state.fs = util::DxvkBuiltInShaderStage(dxvk_resolve_frag_d, &specInfo);\n    } else if (formatInfo->flags.test(DxvkFormatFlag::SampledUInt)) {\n      state.fs = util::DxvkBuiltInShaderStage(dxvk_resolve_frag_u, &specInfo);\n    } else if (formatInfo->flags.test(DxvkFormatFlag::SampledSInt)) {\n      state.fs = util::DxvkBuiltInShaderStage(dxvk_resolve_frag_i, &specInfo);\n    } else {\n      state.fs = util::DxvkBuiltInShaderStage(dxvk_resolve_frag_f, &specInfo);\n    }\n\n    if (formatInfo->aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {\n      state.depthFormat = key.format;\n      state.dsState = &dsState;\n    } else {\n      state.colorFormat = key.format;\n    }\n\n    pipeline.pipeline = m_device->createBuiltInGraphicsPipeline(pipeline.layout, state);\n    return pipeline;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_meta_resolve.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <unordered_map>\n\n#include \"../spirv/spirv_code_buffer.h\"\n\n#include \"dxvk_barrier.h\"\n#include \"dxvk_cmdlist.h\"\n#include \"dxvk_image.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Resolve pipeline\n   * \n   * Stores the objects for a single pipeline\n   * that is used for fragment shader resolve.\n   */\n  struct DxvkMetaResolvePipeline {\n    const DxvkPipelineLayout* layout   = nullptr;\n    VkPipeline                pipeline = VK_NULL_HANDLE;\n  };\n\n  /**\n   * \\brief Copy pipeline key\n   * \n   * Used to look up copy pipelines based\n   * on the copy operation they support.\n   */\n  struct DxvkMetaResolvePipelineKey {\n    VkFormat                  format  = VK_FORMAT_UNDEFINED;\n    VkSampleCountFlagBits     samples = VK_SAMPLE_COUNT_1_BIT;\n    VkResolveModeFlagBits     modeD   = VK_RESOLVE_MODE_NONE;\n    VkResolveModeFlagBits     modeS   = VK_RESOLVE_MODE_NONE;\n\n    bool eq(const DxvkMetaResolvePipelineKey& other) const {\n      return this->format  == other.format\n          && this->samples == other.samples\n          && this->modeD   == other.modeD\n          && this->modeS   == other.modeS;\n    }\n\n    size_t hash() const {\n      return (uint32_t(format)  << 4)\n           ^ (uint32_t(samples) << 0)\n           ^ (uint32_t(modeD)   << 12)\n           ^ (uint32_t(modeS)   << 16);\n    }\n  };\n\n  /**\n   * \\brief Meta resolve views for attachment-based resolves\n   */\n  class DxvkMetaResolveViews {\n\n  public:\n\n    DxvkMetaResolveViews(\n      const Rc<DxvkImage>&            dstImage,\n      const VkImageSubresourceLayers& dstSubresources,\n      const Rc<DxvkImage>&            srcImage,\n      const VkImageSubresourceLayers& srcSubresources,\n            VkFormat                  format);\n\n    ~DxvkMetaResolveViews();\n\n    Rc<DxvkImageView> dstView;\n    Rc<DxvkImageView> srcView;\n\n  };\n\n\n  /**\n   * \\brief Meta resolve objects\n   * \n   * Implements resolve operations in fragment\n   * shaders when using different formats.\n   */\n  class DxvkMetaResolveObjects {\n\n  public:\n\n    DxvkMetaResolveObjects(DxvkDevice* device);\n    ~DxvkMetaResolveObjects();\n\n    /**\n     * \\brief Creates pipeline for meta copy operation\n     * \n     * \\param [in] format Destination image format\n     * \\param [in] samples Destination sample count\n     * \\param [in] depthResolveMode Depth resolve mode\n     * \\param [in] stencilResolveMode Stencil resolve mode\n     * \\returns Compatible pipeline for the operation\n     */\n    DxvkMetaResolvePipeline getPipeline(\n            VkFormat                  format,\n            VkSampleCountFlagBits     samples,\n            VkResolveModeFlagBits     depthResolveMode,\n            VkResolveModeFlagBits     stencilResolveMode);\n\n  private:\n\n    DxvkDevice* m_device = nullptr;\n\n    dxvk::mutex m_mutex;\n\n    std::unordered_map<\n      DxvkMetaResolvePipelineKey,\n      DxvkMetaResolvePipeline,\n      DxvkHash, DxvkEq> m_pipelines;\n\n    DxvkMetaResolvePipeline createPipeline(\n      const DxvkMetaResolvePipelineKey& key);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_objects.h",
    "content": "#pragma once\n\n#include \"dxvk_descriptor_info.h\"\n#include \"dxvk_gpu_event.h\"\n#include \"dxvk_gpu_query.h\"\n#include \"dxvk_memory.h\"\n#include \"dxvk_meta_blit.h\"\n#include \"dxvk_meta_clear.h\"\n#include \"dxvk_meta_copy.h\"\n#include \"dxvk_meta_mipgen.h\"\n#include \"dxvk_meta_resolve.h\"\n#include \"dxvk_pipemanager.h\"\n#include \"dxvk_renderpass.h\"\n#include \"dxvk_sampler.h\"\n#include \"dxvk_unbound.h\"\n\n#include \"../util/util_lazy.h\"\n\nnamespace dxvk {\n\n  class DxvkObjects {\n\n  public:\n\n    DxvkObjects(DxvkDevice* device)\n    : m_device          (device),\n      m_descriptorInfo  (device),\n      m_memoryManager   (device),\n      m_samplerPool     (device),\n      m_pipelineManager (device),\n      m_eventPool       (device),\n      m_queryPool       (device),\n      m_dummyResources  (device) {\n\n    }\n\n    DxvkDescriptorProperties& descriptors() {\n      return m_descriptorInfo;\n    }\n\n    DxvkMemoryAllocator& memoryManager() {\n      return m_memoryManager;\n    }\n\n    DxvkPipelineManager& pipelineManager() {\n      return m_pipelineManager;\n    }\n\n    DxvkSamplerPool& samplerPool() {\n      return m_samplerPool;\n    }\n\n    DxvkGpuEventPool& eventPool() {\n      return m_eventPool;\n    }\n\n    DxvkGpuQueryPool& queryPool() {\n      return m_queryPool;\n    }\n\n    DxvkUnboundResources& dummyResources() {\n      return m_dummyResources;\n    }\n\n    DxvkMetaBlitObjects& metaBlit() {\n      return m_metaBlit.get(m_device);\n    }\n\n    DxvkMetaMipGenObjects& metaMipGen() {\n      return m_metaMipGen.get(m_device);\n    }\n\n    DxvkMetaClearObjects& metaClear() {\n      return m_metaClear.get(m_device);\n    }\n\n    DxvkMetaCopyObjects& metaCopy() {\n      return m_metaCopy.get(m_device);\n    }\n\n    DxvkMetaResolveObjects& metaResolve() {\n      return m_metaResolve.get(m_device);\n    }\n    \n  private:\n\n    DxvkDevice*                   m_device;\n\n    DxvkDescriptorProperties      m_descriptorInfo;\n\n    DxvkMemoryAllocator           m_memoryManager;\n    DxvkSamplerPool               m_samplerPool;\n    DxvkPipelineManager           m_pipelineManager;\n\n    DxvkGpuEventPool              m_eventPool;\n    DxvkGpuQueryPool              m_queryPool;\n\n    DxvkUnboundResources          m_dummyResources;\n\n    Lazy<DxvkMetaBlitObjects>     m_metaBlit;\n    Lazy<DxvkMetaMipGenObjects>   m_metaMipGen;\n    Lazy<DxvkMetaClearObjects>    m_metaClear;\n    Lazy<DxvkMetaCopyObjects>     m_metaCopy;\n    Lazy<DxvkMetaResolveObjects>  m_metaResolve;\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_openvr.cpp",
    "content": "#include \"dxvk_instance.h\"\n#include \"dxvk_openvr.h\"\n\n#ifdef __GNUC__\n#pragma GCC diagnostic ignored \"-Wnon-virtual-dtor\"\n#endif\n\n#include <openvr/openvr.hpp>\n\nusing VR_InitInternalProc        = vr::IVRSystem* (VR_CALLTYPE *)(vr::EVRInitError*, vr::EVRApplicationType);\nusing VR_ShutdownInternalProc    = void  (VR_CALLTYPE *)();\nusing VR_GetGenericInterfaceProc = void* (VR_CALLTYPE *)(const char*, vr::EVRInitError*);\n\nnamespace dxvk {\n  \n  struct VrFunctions {\n    VR_InitInternalProc        initInternal        = nullptr;\n    VR_ShutdownInternalProc    shutdownInternal    = nullptr;\n    VR_GetGenericInterfaceProc getGenericInterface = nullptr;\n  };\n  \n  VrFunctions g_vrFunctions;\n  VrInstance VrInstance::s_instance;\n\n  VrInstance:: VrInstance() {\n    m_no_vr = env::getEnvVar(\"DXVK_NO_VR\") == \"1\";\n  }\n  VrInstance::~VrInstance() { }\n\n\n  std::string_view VrInstance::getName() {\n    return \"OpenVR\";\n  }\n  \n  \n  DxvkExtensionList VrInstance::getInstanceExtensions() {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    return m_insExtensions;\n  }\n\n\n  DxvkExtensionList VrInstance::getDeviceExtensions(uint32_t adapterId) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    \n    if (adapterId < m_devExtensions.size())\n      return m_devExtensions[adapterId];\n    \n    return DxvkExtensionList();\n  }\n\n\n  void VrInstance::initInstanceExtensions() {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    if (m_no_vr || m_initializedDevExt)\n        return;\n\n    if (!m_vr_key)\n    {\n        LSTATUS status;\n\n        if ((status = RegOpenKeyExA(HKEY_CURRENT_USER, \"Software\\\\Wine\\\\VR\", 0, KEY_READ, &m_vr_key)))\n            Logger::info(str::format(\"OpenVR: could not open registry key, status \", status));\n    }\n\n    if (!m_vr_key && !m_compositor)\n      m_compositor = this->getCompositor();\n\n    if (!m_vr_key && !m_compositor)\n      return;\n    \n    m_insExtensions = this->queryInstanceExtensions();\n    m_initializedInsExt = true;\n  }\n\n\n  void VrInstance::initDeviceExtensions(const DxvkInstance* instance) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    if (m_no_vr || (!m_vr_key && !m_compositor) || m_initializedDevExt)\n      return;\n    \n    for (uint32_t i = 0; instance->enumAdapters(i) != nullptr; i++) {\n      m_devExtensions.push_back(this->queryDeviceExtensions(\n        instance->enumAdapters(i)));\n    }\n\n    m_initializedDevExt = true;\n    this->shutdown();\n  }\n\n  bool VrInstance::waitVrKeyReady() const {\n    DWORD type, value, wait_status, size;\n    LSTATUS status;\n    HANDLE event;\n\n    size = sizeof(value);\n    if ((status = RegQueryValueExA(m_vr_key, \"state\", nullptr, &type, reinterpret_cast<BYTE*>(&value), &size)))\n    {\n        Logger::err(str::format(\"OpenVR: could not query value, status \", status));\n        return false;\n    }\n    if (type != REG_DWORD)\n    {\n        Logger::err(str::format(\"OpenVR: unexpected value type \", type));\n        return false;\n    }\n\n    if (value)\n        return value == 1;\n\n    event = CreateEventA(nullptr, FALSE, FALSE, nullptr);\n    while (1)\n    {\n        if (RegNotifyChangeKeyValue(m_vr_key, FALSE, REG_NOTIFY_CHANGE_LAST_SET, event, TRUE))\n        {\n            Logger::err(\"Error registering registry change notification\");\n            goto done;\n        }\n        size = sizeof(value);\n        if ((status = RegQueryValueExA(m_vr_key, \"state\", nullptr, &type, reinterpret_cast<BYTE*>(&value), &size)))\n        {\n            Logger::err(str::format(\"OpenVR: could not query value, status \", status));\n            goto done;\n        }\n        if (value)\n            break;\n        while ((wait_status = WaitForSingleObject(event, 1000)) == WAIT_TIMEOUT)\n            Logger::warn(\"VR state wait timeout (retrying)\");\n\n        if (wait_status != WAIT_OBJECT_0)\n        {\n            Logger::err(str::format(\"Got unexpected wait status \", wait_status));\n            break;\n        }\n    }\n\n  done:\n    CloseHandle(event);\n    return value == 1;\n  }\n\n  DxvkExtensionList VrInstance::queryInstanceExtensions() const {\n    std::vector<char> extensionList;\n    DWORD len;\n\n    if (m_vr_key)\n    {\n        LSTATUS status;\n        DWORD type;\n\n        if (!this->waitVrKeyReady())\n            return DxvkExtensionList();\n\n        len = 0;\n        if ((status = RegQueryValueExA(m_vr_key, \"openvr_vulkan_instance_extensions\", nullptr, &type, nullptr, &len)))\n        {\n            Logger::err(str::format(\"OpenVR: could not query value, status \", status));\n            return DxvkExtensionList();\n        }\n        extensionList.resize(len);\n        if ((status = RegQueryValueExA(m_vr_key, \"openvr_vulkan_instance_extensions\", nullptr, &type, reinterpret_cast<BYTE*>(extensionList.data()), &len)))\n        {\n            Logger::err(str::format(\"OpenVR: could not query value, status \", status));\n            return DxvkExtensionList();\n        }\n    }\n    else\n    {\n        len = m_compositor->GetVulkanInstanceExtensionsRequired(nullptr, 0);\n        extensionList.resize(len);\n        len = m_compositor->GetVulkanInstanceExtensionsRequired(extensionList.data(), len);\n    }\n    return parseExtensionList(std::string(extensionList.data(), len));\n  }\n  \n  \n  DxvkExtensionList VrInstance::queryDeviceExtensions(Rc<DxvkAdapter> adapter) const {\n    std::vector<char> extensionList;\n    DWORD len;\n\n    if (m_vr_key)\n    {\n        LSTATUS status;\n        char name[256];\n        DWORD type;\n\n        if (!this->waitVrKeyReady())\n            return DxvkExtensionList();\n\n        sprintf(name, \"PCIID:%04x:%04x\",\n          adapter->deviceProperties().core.properties.vendorID,\n          adapter->deviceProperties().core.properties.deviceID);\n\n        len = 0;\n        if ((status = RegQueryValueExA(m_vr_key, name, nullptr, &type, nullptr, &len)))\n        {\n            Logger::err(str::format(\"OpenVR: could not query value, status \", status));\n            return DxvkExtensionList();\n        }\n        extensionList.resize(len);\n        if ((status = RegQueryValueExA(m_vr_key, name, nullptr, &type, reinterpret_cast<BYTE*>(extensionList.data()), &len)))\n        {\n            Logger::err(str::format(\"OpenVR: could not query value, status \", status));\n            return DxvkExtensionList();\n        }\n    }\n    else\n    {\n        len = m_compositor->GetVulkanDeviceExtensionsRequired(adapter->handle(), nullptr, 0);\n        extensionList.resize(len);\n        len = m_compositor->GetVulkanDeviceExtensionsRequired(adapter->handle(), extensionList.data(), len);\n    }\n    return parseExtensionList(std::string(extensionList.data(), len));\n  }\n  \n  \n  DxvkExtensionList VrInstance::parseExtensionList(const std::string& str) const {\n    DxvkExtensionList result;\n    \n    std::stringstream strstream(str);\n    std::string       section;\n    \n    while (std::getline(strstream, section, ' '))\n      result.push_back(vk::makeExtension(section.c_str()));\n    \n    return result;\n  }\n  \n  \n  vr::IVRCompositor* VrInstance::getCompositor() {\n    // Skip OpenVR initialization if requested\n    \n    // Locate the OpenVR DLL if loaded by the process. Some\n    // applications may not have OpenVR loaded at the time\n    // they create the DXGI instance, so we try our own DLL.\n    m_ovrApi = this->loadLibrary();\n    \n    if (!m_ovrApi) {\n      Logger::info(\"OpenVR: Failed to locate module\");\n      return nullptr;\n    }\n    \n    // Load method used to retrieve the IVRCompositor interface\n    g_vrFunctions.initInternal        = reinterpret_cast<VR_InitInternalProc>       (this->getSym(\"VR_InitInternal\"));\n    g_vrFunctions.shutdownInternal    = reinterpret_cast<VR_ShutdownInternalProc>   (this->getSym(\"VR_ShutdownInternal\"));\n    g_vrFunctions.getGenericInterface = reinterpret_cast<VR_GetGenericInterfaceProc>(this->getSym(\"VR_GetGenericInterface\"));\n    \n    if (!g_vrFunctions.getGenericInterface) {\n      Logger::warn(\"OpenVR: VR_GetGenericInterface not found\");\n      return nullptr;\n    }\n    \n    // Retrieve the compositor interface\n    vr::EVRInitError error = vr::VRInitError_None;\n    \n    vr::IVRCompositor* compositor = reinterpret_cast<vr::IVRCompositor*>(\n      g_vrFunctions.getGenericInterface(vr::IVRCompositor_Version, &error));\n    \n    if (error != vr::VRInitError_None || !compositor) {\n      if (!g_vrFunctions.initInternal\n       || !g_vrFunctions.shutdownInternal) {\n        Logger::warn(\"OpenVR: VR_InitInternal or VR_ShutdownInternal not found\");\n        return nullptr;\n      }\n\n      // If the app has not initialized OpenVR yet, we need\n      // to do it now in order to grab a compositor instance\n      g_vrFunctions.initInternal(&error, vr::VRApplication_Background);\n      m_initializedOpenVr = error == vr::VRInitError_None;\n\n      if (error != vr::VRInitError_None) {\n        Logger::warn(\"OpenVR: Failed to initialize OpenVR\");\n        return nullptr;\n      }\n\n      compositor = reinterpret_cast<vr::IVRCompositor*>(\n        g_vrFunctions.getGenericInterface(vr::IVRCompositor_Version, &error));\n      \n      if (error != vr::VRInitError_None || !compositor) {\n        Logger::warn(\"OpenVR: Failed to query compositor interface\");\n        this->shutdown();\n        return nullptr;\n      }\n    }\n    \n    Logger::info(\"OpenVR: Compositor interface found\");\n    return compositor;\n  }\n\n\n  void VrInstance::shutdown() {\n    if (m_vr_key)\n    {\n        RegCloseKey(m_vr_key);\n        m_vr_key = nullptr;\n    }\n\n    if (m_initializedOpenVr)\n      g_vrFunctions.shutdownInternal();\n\n    if (m_loadedOvrApi)\n      this->freeLibrary();\n    \n    m_initializedOpenVr = false;\n    m_loadedOvrApi      = false;\n  }\n\n\n  HMODULE VrInstance::loadLibrary() {\n    HMODULE handle;\n\n    // Use openvr_api.dll only if already loaded in the process (and reference it which GetModuleHandleEx does without\n    // GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT flag).\n    if (!::GetModuleHandleEx(0, \"openvr_api.dll\", &handle))\n      handle = ::LoadLibrary(\"openvr_api_dxvk.dll\");\n\n    m_loadedOvrApi = handle != nullptr;\n    return handle;\n  }\n\n\n  void VrInstance::freeLibrary() {\n    ::FreeLibrary(m_ovrApi);\n  }\n\n  \n  void* VrInstance::getSym(const char* sym) {\n    return reinterpret_cast<void*>(\n      ::GetProcAddress(m_ovrApi, sym));\n  }\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_openvr.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <vector>\n\n#include \"dxvk_extension_provider.h\"\n\nnamespace vr {\n  class IVRCompositor;\n  class IVRSystem;\n}\n\nnamespace dxvk {\n\n  class DxvkInstance;\n\n  /**\n   * \\brief OpenVR instance\n   * \n   * Loads Initializes OpenVR to provide\n   * access to Vulkan extension queries.\n   */\n  class VrInstance : public DxvkExtensionProvider {\n    \n  public:\n    \n    VrInstance();\n    ~VrInstance();\n\n    std::string_view getName();\n\n    DxvkExtensionList getInstanceExtensions();\n\n    DxvkExtensionList getDeviceExtensions(\n            uint32_t      adapterId);\n    \n    void initInstanceExtensions();\n\n    void initDeviceExtensions(\n      const DxvkInstance* instance);\n\n    static VrInstance s_instance;\n\n  private:\n\n    dxvk::mutex           m_mutex;\n    HKEY                  m_vr_key     = nullptr;\n    vr::IVRCompositor*    m_compositor = nullptr;\n    HMODULE               m_ovrApi     = nullptr;\n\n    bool m_no_vr;\n    bool m_loadedOvrApi      = false;\n    bool m_initializedOpenVr = false;\n    bool m_initializedInsExt = false;\n    bool m_initializedDevExt = false;\n\n    DxvkExtensionList              m_insExtensions;\n    std::vector<DxvkExtensionList> m_devExtensions;\n    \n    DxvkExtensionList queryInstanceExtensions() const;\n\n    DxvkExtensionList queryDeviceExtensions(\n            Rc<DxvkAdapter>           adapter) const;\n\n    DxvkExtensionList parseExtensionList(\n      const std::string&              str) const;\n    \n    vr::IVRCompositor* getCompositor();\n\n    void shutdown();\n\n    HMODULE loadLibrary();\n\n    void freeLibrary();\n\n    void* getSym(const char* sym);\n\n    bool waitVrKeyReady() const;\n  };\n\n  extern VrInstance g_vrInstance;\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_openxr.cpp",
    "content": "#include \"dxvk_instance.h\"\n#include \"dxvk_openxr.h\"\n\n#ifdef __GNUC__\n#pragma GCC diagnostic ignored \"-Wnon-virtual-dtor\"\n#endif\n\nusing PFN___wineopenxr_GetVulkanInstanceExtensions = int (WINAPI *)(uint32_t, uint32_t *, char *);\nusing PFN___wineopenxr_GetVulkanDeviceExtensions = int (WINAPI *)(uint32_t, uint32_t *, char *);\n\nnamespace dxvk {\n  \n  struct WineXrFunctions {\n    PFN___wineopenxr_GetVulkanInstanceExtensions __wineopenxr_GetVulkanInstanceExtensions = nullptr;\n    PFN___wineopenxr_GetVulkanDeviceExtensions __wineopenxr_GetVulkanDeviceExtensions = nullptr;\n  };\n  \n  WineXrFunctions g_winexrFunctions;\n  DxvkXrProvider DxvkXrProvider::s_instance;\n\n  DxvkXrProvider:: DxvkXrProvider() { }\n\n  DxvkXrProvider::~DxvkXrProvider() { }\n\n\n  std::string_view DxvkXrProvider::getName() {\n    return \"OpenXR\";\n  }\n  \n  \n  DxvkExtensionList DxvkXrProvider::getInstanceExtensions() {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    return m_insExtensions;\n  }\n\n\n  DxvkExtensionList DxvkXrProvider::getDeviceExtensions(uint32_t adapterId) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n    return m_devExtensions;\n  }\n\n\n  void DxvkXrProvider::initInstanceExtensions() {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    if (!m_wineOxr)\n      m_wineOxr = this->loadLibrary();\n\n    if (!m_wineOxr || m_initializedInsExt)\n      return;\n\n    if (!this->loadFunctions()) {\n      this->shutdown();\n      return;\n    }\n\n    m_insExtensions = this->queryInstanceExtensions();\n    m_initializedInsExt = true;\n  }\n\n\n  bool DxvkXrProvider::loadFunctions() {\n    g_winexrFunctions.__wineopenxr_GetVulkanInstanceExtensions =\n        reinterpret_cast<PFN___wineopenxr_GetVulkanInstanceExtensions>(this->getSym(\"__wineopenxr_GetVulkanInstanceExtensions\"));\n    g_winexrFunctions.__wineopenxr_GetVulkanDeviceExtensions =\n        reinterpret_cast<PFN___wineopenxr_GetVulkanDeviceExtensions>(this->getSym(\"__wineopenxr_GetVulkanDeviceExtensions\"));\n    return g_winexrFunctions.__wineopenxr_GetVulkanInstanceExtensions != nullptr\n      && g_winexrFunctions.__wineopenxr_GetVulkanDeviceExtensions != nullptr;\n  }\n\n\n  void DxvkXrProvider::initDeviceExtensions(const DxvkInstance* instance) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    if (!m_wineOxr || m_initializedDevExt)\n      return;\n    \n    m_devExtensions = this->queryDeviceExtensions();\n    m_initializedDevExt = true;\n\n    this->shutdown();\n  }\n\n\n  DxvkExtensionList DxvkXrProvider::queryInstanceExtensions() const {\n    int res;\n    uint32_t len;\n\n    res = g_winexrFunctions.__wineopenxr_GetVulkanInstanceExtensions(0, &len, nullptr);\n    if (res != 0) {\n      Logger::warn(\"OpenXR: Unable to get required Vulkan instance extensions size\");\n      return DxvkExtensionList();\n    }\n\n    std::vector<char> extensionList(len);\n    res = g_winexrFunctions.__wineopenxr_GetVulkanInstanceExtensions(len, &len, &extensionList[0]);\n    if (res != 0) {\n      Logger::warn(\"OpenXR: Unable to get required Vulkan instance extensions\");\n      return DxvkExtensionList();\n    }\n\n    return parseExtensionList(std::string(extensionList.data(), len));\n  }\n  \n  \n  DxvkExtensionList DxvkXrProvider::queryDeviceExtensions() const {\n    int res;\n\n    uint32_t len;\n    res = g_winexrFunctions.__wineopenxr_GetVulkanDeviceExtensions(0, &len, nullptr);\n    if (res != 0) {\n      Logger::warn(\"OpenXR: Unable to get required Vulkan Device extensions size\");\n      return DxvkExtensionList();\n    }\n\n    std::vector<char> extensionList(len);\n    res = g_winexrFunctions.__wineopenxr_GetVulkanDeviceExtensions(len, &len, &extensionList[0]);\n    if (res != 0) {\n      Logger::warn(\"OpenXR: Unable to get required Vulkan Device extensions\");\n      return DxvkExtensionList();\n    }\n\n    return parseExtensionList(std::string(extensionList.data(), len));\n  }\n  \n  \n  DxvkExtensionList DxvkXrProvider::parseExtensionList(const std::string& str) const {\n    DxvkExtensionList result;\n    \n    std::stringstream strstream(str);\n    std::string       section;\n    \n    while (std::getline(strstream, section, ' '))\n      result.push_back(vk::makeExtension(section.c_str()));\n    \n    return result;\n  }\n  \n  \n  void DxvkXrProvider::shutdown() {\n    if (m_loadedOxrApi)\n      this->freeLibrary();\n    \n    m_loadedOxrApi      = false;\n    m_wineOxr = nullptr;\n  }\n\n\n  HMODULE DxvkXrProvider::loadLibrary() {\n    HMODULE handle = ::LoadLibrary(\"wineopenxr.dll\");\n\n    m_loadedOxrApi = handle != nullptr;\n    return handle;\n  }\n\n\n  void DxvkXrProvider::freeLibrary() {\n    ::FreeLibrary(m_wineOxr);\n  }\n\n  \n  void* DxvkXrProvider::getSym(const char* sym) {\n    return reinterpret_cast<void*>(\n      ::GetProcAddress(m_wineOxr, sym));\n  }\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_openxr.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <vector>\n\n#include \"dxvk_extension_provider.h\"\n\nnamespace dxvk {\n\n  class DxvkInstance;\n\n  /**\n   * \\brief OpenXR instance\n   * \n   * Loads OpenXR to provide access to Vulkan extension queries.\n   */\n  class DxvkXrProvider : public DxvkExtensionProvider {\n    \n  public:\n    \n    DxvkXrProvider();\n    ~DxvkXrProvider();\n\n    std::string_view getName();\n\n    DxvkExtensionList getInstanceExtensions();\n\n    DxvkExtensionList getDeviceExtensions(\n            uint32_t      adapterId);\n    \n    void initInstanceExtensions();\n\n    void initDeviceExtensions(\n      const DxvkInstance* instance);\n\n    static DxvkXrProvider s_instance;\n\n  private:\n\n    dxvk::mutex           m_mutex;\n    HMODULE               m_wineOxr     = nullptr;\n\n    bool m_loadedOxrApi      = false;\n    bool m_initializedInsExt = false;\n    bool m_initializedDevExt = false;\n\n    DxvkExtensionList m_insExtensions;\n    DxvkExtensionList m_devExtensions;\n    \n    DxvkExtensionList queryInstanceExtensions() const;\n\n    DxvkExtensionList queryDeviceExtensions() const;\n\n    DxvkExtensionList parseExtensionList(\n      const std::string&              str) const;\n    \n    bool loadFunctions();\n\n    void shutdown();\n\n    HMODULE loadLibrary();\n\n    void freeLibrary();\n\n    void* getSym(const char* sym);\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_options.cpp",
    "content": "#include \"dxvk_options.h\"\n\nnamespace dxvk {\n\n  DxvkOptions::DxvkOptions(const Config& config) {\n    enableDebugUtils      = config.getOption<bool>    (\"dxvk.enableDebugUtils\",       false);\n    enableMemoryDefrag    = config.getOption<Tristate>(\"dxvk.enableMemoryDefrag\",     Tristate::Auto);\n    numCompilerThreads    = config.getOption<int32_t> (\"dxvk.numCompilerThreads\",     0);\n    enableGraphicsPipelineLibrary = config.getOption<Tristate>(\"dxvk.enableGraphicsPipelineLibrary\", Tristate::Auto);\n    enableDescriptorHeap  = config.getOption<Tristate>(\"dxvk.enableDescriptorHeap\",   Tristate::False);\n    enableDescriptorBuffer = config.getOption<Tristate>(\"dxvk.enableDescriptorBuffer\", Tristate::Auto);\n    enableUnifiedImageLayout = config.getOption<bool> (\"dxvk.enableUnifiedImageLayouts\", true);\n    enableImplicitResolves = config.getOption<bool>   (\"dxvk.enableImplicitResolves\", true);\n    trackPipelineLifetime = config.getOption<Tristate>(\"dxvk.trackPipelineLifetime\",  Tristate::Auto);\n    useRawSsbo            = config.getOption<Tristate>(\"dxvk.useRawSsbo\",             Tristate::Auto);\n    hud                   = config.getOption<std::string>(\"dxvk.hud\", \"\");\n    tearFree              = config.getOption<Tristate>(\"dxvk.tearFree\",               Tristate::Auto);\n    latencySleep          = config.getOption<Tristate>(\"dxvk.latencySleep\",           Tristate::Auto);\n    latencyTolerance      = config.getOption<int32_t> (\"dxvk.latencyTolerance\",       1000);\n    disableNvLowLatency2  = config.getOption<Tristate>(\"dxvk.disableNvLowLatency2\",   Tristate::Auto);\n    hideIntegratedGraphics = config.getOption<bool>   (\"dxvk.hideIntegratedGraphics\", false);\n    zeroMappedMemory      = config.getOption<bool>    (\"dxvk.zeroMappedMemory\",       false);\n    allowFse              = config.getOption<bool>    (\"dxvk.allowFse\",               false);\n    deviceFilter          = config.getOption<std::string>(\"dxvk.deviceFilter\",        \"\");\n    lowerSinCos           = config.getOption<Tristate>(\"dxvk.lowerSinCos\",            Tristate::Auto);\n    tilerMode             = config.getOption<Tristate>(\"dxvk.tilerMode\",              Tristate::Auto);\n\n    auto budget = config.getOption<int32_t>(\"dxvk.maxMemoryBudget\", 0);\n    maxMemoryBudget = VkDeviceSize(std::max(budget, 0)) << 20u;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_options.h",
    "content": "#pragma once\n\n#include \"../util/config/config.h\"\n\n#include \"../vulkan/vulkan_loader.h\"\n\nnamespace dxvk {\n\n  struct DxvkOptions {\n    DxvkOptions() { }\n    DxvkOptions(const Config& config);\n\n    /// Enable debug utils\n    bool enableDebugUtils = false;\n\n    /// Enable memory defragmentation\n    Tristate enableMemoryDefrag = Tristate::Auto;\n\n    /// Number of compiler threads\n    /// when using the state cache\n    int32_t numCompilerThreads = 0;\n\n    /// Enable graphics pipeline library\n    Tristate enableGraphicsPipelineLibrary = Tristate::Auto;\n\n    /// Enable descriptor heap\n    Tristate enableDescriptorHeap = Tristate::Auto;\n\n    /// Enable descriptor buffer\n    Tristate enableDescriptorBuffer = Tristate::Auto;\n\n    /// Enable unified image layout path\n    bool enableUnifiedImageLayout = true;\n\n    /// Enables pipeline lifetime tracking\n    Tristate trackPipelineLifetime = Tristate::Auto;\n\n    /// Shader-related options\n    Tristate useRawSsbo = Tristate::Auto;\n\n    /// HUD elements\n    std::string hud;\n\n    /// Forces swap chain into MAILBOX (if true)\n    /// or FIFO_RELAXED (if false) present mode\n    Tristate tearFree = Tristate::Auto;\n\n    /// Enables latency sleep\n    Tristate latencySleep = Tristate::Auto;\n\n    /// Latency tolerance, in microseconds\n    int32_t latencyTolerance = 0u;\n\n    /// Disable VK_NV_low_latency2. This extension\n    /// appears to be all sorts of broken on 32-bit.\n    Tristate disableNvLowLatency2 = Tristate::Auto;\n\n    // Hides integrated GPUs if dedicated GPUs are\n    // present. May be necessary for some games that\n    // incorrectly assume monitor layouts.\n    bool hideIntegratedGraphics = false;\n\n    /// Clears all mapped memory to zero.\n    bool zeroMappedMemory = false;\n\n    /// Allows full-screen exclusive mode on Windows\n    bool allowFse = false;\n\n    /// Whether to enable tiler optimizations\n    Tristate tilerMode = Tristate::Auto;\n\n    /// Overrides memory budget for DXVK\n    VkDeviceSize maxMemoryBudget = 0u;\n\n    /// Whether to use custom sin/cos approximation\n    Tristate lowerSinCos = Tristate::Auto;\n\n    /// Enables implicit resolves that are used to\n    /// deal with MSAA-related undefined behaviour.\n    bool enableImplicitResolves = true;\n\n    /// Device name\n    std::string deviceFilter;\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_pipelayout.cpp",
    "content": "#include <cstring>\n#include <vector>\n\n#include \"dxvk_device.h\"\n#include \"dxvk_descriptor_pool.h\"\n#include \"dxvk_limits.h\"\n#include \"dxvk_pipelayout.h\"\n\nnamespace dxvk {\n  \n  DxvkDescriptorSetLayoutKey::DxvkDescriptorSetLayoutKey() {\n\n  }\n\n\n  DxvkDescriptorSetLayoutKey::~DxvkDescriptorSetLayoutKey() {\n\n  }\n\n\n  uint32_t DxvkDescriptorSetLayoutKey::add(DxvkDescriptorSetLayoutBinding binding) {\n    uint32_t index = m_bindings.size();\n\n    m_bindings.push_back(binding);\n    return index;\n  }\n\n\n  bool DxvkDescriptorSetLayoutKey::eq(const DxvkDescriptorSetLayoutKey& other) const {\n    bool eq = m_bindings.size() == other.m_bindings.size();\n\n    for (size_t i = 0; i < m_bindings.size() && eq; i++)\n      eq = m_bindings[i].eq(other.m_bindings[i]);\n\n    return eq;\n  }\n\n\n  size_t DxvkDescriptorSetLayoutKey::hash() const {\n    DxvkHashState hash;\n\n    for (size_t i = 0; i < m_bindings.size(); i++)\n      hash.add(m_bindings[i].hash());\n\n    return hash;\n  }\n\n\n  DxvkDescriptorSetLayout::DxvkDescriptorSetLayout(\n          DxvkDevice*                 device,\n    const DxvkDescriptorSetLayoutKey& key)\n  : m_device(device), m_bindingCount(key.getBindingCount()) {\n    if (device->canUseDescriptorHeap()) {\n      initDescriptorHeapLayout(key);\n    } else {\n      initSetLayout(key);\n\n      if (m_device->canUseDescriptorBuffer())\n        initDescriptorBufferUpdate(key);\n    }\n  }\n\n\n  DxvkDescriptorSetLayout::~DxvkDescriptorSetLayout() {\n    auto vk = m_device->vkd();\n\n    if (!m_device->canUseDescriptorHeap()) {\n      vk->vkDestroyDescriptorSetLayout(vk->device(), m_legacy.layout, nullptr);\n      vk->vkDestroyDescriptorUpdateTemplate(vk->device(), m_legacy.updateTemplate, nullptr);\n    }\n  }\n\n\n  void DxvkDescriptorSetLayout::initSetLayout(const DxvkDescriptorSetLayoutKey& key) {\n    auto vk = m_device->vkd();\n\n    size_t descriptorCount = 0u;\n\n    small_vector<VkDescriptorSetLayoutBinding,    32> bindingInfos;\n    small_vector<VkDescriptorUpdateTemplateEntry, 32> templateInfos;\n\n    bindingInfos.reserve(key.getBindingCount());\n    templateInfos.reserve(key.getBindingCount());\n\n    for (uint32_t i = 0; i < key.getBindingCount(); i++) {\n      auto entry = key.getBinding(i);\n\n      if (entry.getDescriptorCount()) {\n        VkDescriptorSetLayoutBinding bindingInfo;\n        bindingInfo.binding = i;\n        bindingInfo.descriptorType = entry.getDescriptorType();\n        bindingInfo.descriptorCount = entry.getDescriptorCount();\n        bindingInfo.stageFlags = entry.getStageMask();\n        bindingInfo.pImmutableSamplers = nullptr;\n        bindingInfos.push_back(bindingInfo);\n\n        VkDescriptorUpdateTemplateEntry templateInfo;\n        templateInfo.dstBinding = i;\n        templateInfo.dstArrayElement = 0;\n        templateInfo.descriptorCount = entry.getDescriptorCount();\n        templateInfo.descriptorType = entry.getDescriptorType();\n        templateInfo.offset = sizeof(DxvkLegacyDescriptor) * descriptorCount;\n        templateInfo.stride = sizeof(DxvkLegacyDescriptor);\n        templateInfos.push_back(templateInfo);\n\n        descriptorCount += entry.getDescriptorCount();\n      }\n    }\n\n    VkDescriptorSetLayoutCreateInfo layoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };\n    layoutInfo.bindingCount = bindingInfos.size();\n    layoutInfo.pBindings = bindingInfos.data();\n\n    if (m_device->canUseDescriptorBuffer())\n      layoutInfo.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    if (vk->vkCreateDescriptorSetLayout(vk->device(), &layoutInfo, nullptr, &m_legacy.layout))\n      throw DxvkError(\"DxvkDescriptorSetLayout: Failed to create descriptor set layout\");\n\n    if (layoutInfo.bindingCount && !m_device->canUseDescriptorBuffer()) {\n      VkDescriptorUpdateTemplateCreateInfo templateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO };\n      templateInfo.descriptorUpdateEntryCount = templateInfos.size();\n      templateInfo.pDescriptorUpdateEntries = templateInfos.data();\n      templateInfo.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;\n      templateInfo.descriptorSetLayout = m_legacy.layout;\n\n      if (vk->vkCreateDescriptorUpdateTemplate(vk->device(), &templateInfo, nullptr, &m_legacy.updateTemplate))\n        throw DxvkError(\"DxvkDescriptorSetLayout: Failed to create descriptor update template\");\n    }\n  }\n\n\n  void DxvkDescriptorSetLayout::initDescriptorBufferUpdate(const DxvkDescriptorSetLayoutKey& key) {\n    auto vk = m_device->vkd();\n\n    vk->vkGetDescriptorSetLayoutSizeEXT(vk->device(), m_legacy.layout, &m_heap.memorySize);\n    m_heap.memorySize = align(m_heap.memorySize, m_device->getDescriptorProperties().getDescriptorSetAlignment());\n\n    small_vector<DxvkDescriptorUpdateInfo, 32u> descriptors;\n\n    for (uint32_t i = 0u; i < key.getBindingCount(); i++) {\n      const auto& binding = key.getBinding(i);\n\n      VkDeviceSize offset = 0u;\n      vk->vkGetDescriptorSetLayoutBindingOffsetEXT(vk->device(), m_legacy.layout, i, &offset);\n\n      auto& info = m_heap.bindingLayouts.emplace_back();\n      info.descriptorType = binding.getDescriptorType();\n      info.offset = uint32_t(offset);\n\n      for (uint32_t j = 0u; j < binding.getDescriptorCount(); j++) {\n        auto& e = descriptors.emplace_back();\n        e.descriptorType = binding.getDescriptorType();\n        e.offset = uint32_t(offset) + j * m_device->getDescriptorProperties().getDescriptorTypeInfo(e.descriptorType).size;\n      }\n    }\n\n    m_heap.update = DxvkDescriptorUpdateList(m_device,\n      m_heap.memorySize, descriptors.size(), descriptors.data());\n  }\n\n\n  void DxvkDescriptorSetLayout::initDescriptorHeapLayout(const DxvkDescriptorSetLayoutKey& key) {\n    // As a small optimization, order descriptors by size alignment from\n    // large to small. This way, we're guaranteed tight packing and Will\n    // only ever have one area of padding at the end of the set.\n    uint32_t typeAlignmentMask = 0u;\n\n    for (uint32_t i = 0u; i < key.getBindingCount(); i++) {\n      const auto& binding = key.getBinding(i);\n\n      auto size = m_device->getDescriptorProperties().getDescriptorTypeInfo(binding.getDescriptorType()).size;\n      size &= -size;\n\n      typeAlignmentMask |= size;\n    }\n\n    // Compute the actual binding layout by iterating over the bindings\n    // until we've processed all unique descriptor size alignments\n    m_heap.bindingLayouts.resize(key.getBindingCount());\n\n    uint32_t offset = 0u;\n\n    while (typeAlignmentMask) {\n      uint32_t msb = (0x80000000u >> bit::lzcnt(typeAlignmentMask));\n\n      for (uint32_t i = 0u; i < key.getBindingCount(); i++) {\n        const auto& binding = key.getBinding(i);\n\n        auto type = m_device->getDescriptorProperties().getDescriptorTypeInfo(binding.getDescriptorType());\n\n        if ((type.size & -type.size) != msb)\n          continue;\n\n        offset = align(offset, type.alignment);\n\n        auto& info = m_heap.bindingLayouts[i];\n        info.descriptorType = binding.getDescriptorType();\n        info.offset = offset;\n\n        offset += type.size * binding.getDescriptorCount();\n      }\n\n      typeAlignmentMask -= msb;\n    }\n\n    m_heap.memorySize = align(offset, m_device->getDescriptorProperties().getDescriptorSetAlignment());\n\n    // Iterate over everything again to create the descriptor update list\n    small_vector<DxvkDescriptorUpdateInfo, 32u> descriptors;\n\n    for (uint32_t i = 0u; i < key.getBindingCount(); i++) {\n      auto& info = m_heap.bindingLayouts[i];\n\n      for (uint32_t j = 0u; j < key.getBinding(i).getDescriptorCount(); j++) {\n        auto& e = descriptors.emplace_back();\n        e.descriptorType = info.descriptorType;\n        e.offset = info.offset + j * m_device->getDescriptorProperties().getDescriptorTypeInfo(e.descriptorType).size;\n      }\n    }\n\n    m_heap.update = DxvkDescriptorUpdateList(m_device,\n      m_heap.memorySize, descriptors.size(), descriptors.data());\n  }\n\n\n  DxvkPipelineLayout::DxvkPipelineLayout(\n          DxvkDevice*                 device,\n    const DxvkPipelineLayoutKey&      key)\n  : m_device(device), m_flags(key.getFlags()) {\n    initMetadata(key);\n\n    if (m_device->canUseDescriptorHeap())\n      initMappings(key);\n    else\n      initPipelineLayout(key);\n  }\n\n\n  DxvkPipelineLayout::~DxvkPipelineLayout() {\n    auto vk = m_device->vkd();\n\n    if (!m_device->canUseDescriptorHeap())\n      vk->vkDestroyPipelineLayout(vk->device(), m_legacy.layout, nullptr);\n  }\n\n\n  void DxvkPipelineLayout::initMetadata(\n    const DxvkPipelineLayoutKey&      key) {\n    // Determine bind point based on shader stages\n    m_bindPoint = (key.getStageMask() == VK_SHADER_STAGE_COMPUTE_BIT)\n      ? VK_PIPELINE_BIND_POINT_COMPUTE\n      : VK_PIPELINE_BIND_POINT_GRAPHICS;\n\n    // Get set layouts from pipeline layout key and compute memory size\n    for (uint32_t i = 0; i < key.getDescriptorSetCount(); i++) {\n      m_setLayouts[i] = key.getDescriptorSetLayout(i);\n\n      m_heap.setMemorySize += key.getDescriptorSetLayout(i)\n        ? key.getDescriptorSetLayout(i)->getMemorySize()\n        : 0u;\n    }\n\n    // Compute merged push data block from all used blocks\n    m_pushData.blockMask = key.getPushDataMask();\n\n    for (auto i : bit::BitMask(m_pushData.blockMask)) {\n      m_pushData.blocks[i] = key.getPushDataBlock(i);\n      m_pushData.mergedBlock.merge(m_pushData.blocks[i]);\n    }\n\n    // If we can use heaps, and if we're not on AMD or similarly working hardware,\n    // scale heap offsets by the minimum set alignment to make the driver aware.\n    if (m_device->canUseDescriptorHeap() && !m_device->perfHints().preferDescriptorByteOffsets)\n      m_heap.offsetShift = bit::tzcnt(m_device->getDescriptorProperties().getDescriptorSetAlignment());\n  }\n\n\n  void DxvkPipelineLayout::initPipelineLayout(\n    const DxvkPipelineLayoutKey&      key) {\n    auto vk = m_device->vkd();\n\n    // Gather descriptor set layout objects, some of these may be null.\n    small_vector<VkDescriptorSetLayout, DxvkPipelineLayoutKey::MaxSets + 1u> setLayouts;\n\n    if (m_flags.test(DxvkPipelineLayoutFlag::UsesSamplerHeap))\n      setLayouts.push_back(m_device->getSamplerDescriptorSet().layout);\n\n    for (uint32_t i = 0; i < key.getDescriptorSetCount(); i++)\n      setLayouts.push_back(m_setLayouts[i] ? m_setLayouts[i]->getSetLayout() : VK_NULL_HANDLE);\n\n    // Set up push constant range, if any\n    VkPushConstantRange pushConstantRange = { };\n    pushConstantRange.stageFlags = m_pushData.mergedBlock.getStageMask();\n    pushConstantRange.size = m_pushData.mergedBlock.getSize();\n\n    VkPipelineLayoutCreateInfo layoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };\n\n    if (key.getType() == DxvkPipelineLayoutType::Independent)\n      layoutInfo.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT;\n\n    layoutInfo.setLayoutCount = setLayouts.size();\n\n    if (layoutInfo.setLayoutCount)\n      layoutInfo.pSetLayouts = setLayouts.data();\n\n    if (pushConstantRange.size) {\n      layoutInfo.pushConstantRangeCount = 1u;\n      layoutInfo.pPushConstantRanges = &pushConstantRange;\n    }\n\n    if (vk->vkCreatePipelineLayout(vk->device(), &layoutInfo, nullptr, &m_legacy.layout))\n      throw DxvkError(\"DxvkPipelineLayout: Failed to create pipeline layout\");\n  }\n\n\n  void DxvkPipelineLayout::initMappings(\n    const DxvkPipelineLayoutKey&      key) {\n    uint32_t setIndex = 0u;\n\n    // Set up sampler heap mapping at binding (0,0) if used by the layout\n    if (m_flags.test(DxvkPipelineLayoutFlag::UsesSamplerHeap)) {\n      auto& entry = m_mapping.mappings.emplace_back();\n      entry.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_AND_BINDING_MAPPING_EXT;\n      entry.descriptorSet = setIndex++;\n      entry.firstBinding = 0u;\n      entry.bindingCount = 1u;\n      entry.resourceMask = VK_SPIRV_RESOURCE_TYPE_SAMPLER_BIT_EXT;\n      entry.source = VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT;\n\n      auto& samplers = entry.sourceData.constantOffset;\n      samplers.heapOffset = m_device->getSamplerDescriptorHeap().reservedSize;\n      samplers.heapArrayStride = m_device->getDescriptorProperties().getDescriptorTypeInfo(VK_DESCRIPTOR_TYPE_SAMPLER).size;\n    }\n\n    // We generally put set offsets first, unless it's a built-in\n    // pipeline with a hardcoded push data layout.\n    uint32_t pushBase = 0u;\n\n    if (key.getType() == DxvkPipelineLayoutType::BuiltIn)\n      pushBase = m_pushData.mergedBlock.getSize();\n\n    for (uint32_t set = 0u; set < m_setLayouts.size(); set++) {\n      auto layout = m_setLayouts[set];\n\n      if (!layout)\n        continue;\n\n      for (uint32_t i = 0u; i < layout->getBindingCount(); i++) {\n        auto bindingInfo = layout->getBindingInfo(i);\n\n        auto& entry = m_mapping.mappings.emplace_back();\n        entry.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_AND_BINDING_MAPPING_EXT;\n        entry.descriptorSet = setIndex + set;\n        entry.firstBinding = i;\n        entry.bindingCount = 1u;\n        entry.resourceMask = VK_SPIRV_RESOURCE_TYPE_ALL_EXT;\n        entry.source = VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_PUSH_INDEX_EXT;\n\n        auto& pushIndex = entry.sourceData.pushIndex;\n        pushIndex.heapOffset = bindingInfo.offset;\n        pushIndex.pushOffset = pushBase + sizeof(uint32_t) * set;\n        pushIndex.heapIndexStride = 1u << m_heap.offsetShift;\n        pushIndex.heapArrayStride = m_device->getDescriptorProperties().getDescriptorTypeInfo(bindingInfo.descriptorType).size;\n      }\n    }\n  }\n\n\n  DxvkShaderBindingMap::DxvkShaderBindingMap() {\n\n  }\n\n\n  DxvkShaderBindingMap::~DxvkShaderBindingMap() {\n\n  }\n\n\n  void DxvkShaderBindingMap::addBinding(DxvkShaderBinding srcBinding, DxvkShaderBinding dstBinding) {\n    m_bindings.insert_or_assign(srcBinding, dstBinding);\n  }\n\n\n  void DxvkShaderBindingMap::addPushData(const DxvkPushDataBlock& block, uint32_t offset) {\n    m_pushData.push_back(std::make_pair(block, offset));\n  }\n\n\n  const DxvkShaderBinding* DxvkShaderBindingMap::mapBinding(DxvkShaderBinding srcBinding) const {\n    auto entry = m_bindings.find(srcBinding);\n\n    if (entry == m_bindings.end())\n      return nullptr;\n\n    return &entry->second;\n  }\n\n\n  uint32_t DxvkShaderBindingMap::mapPushData(VkShaderStageFlags stage, uint32_t offset) const {\n    for (size_t i = 0u; i < m_pushData.size(); i++) {\n      const auto& block = m_pushData[i];\n\n      if ((block.first.getStageMask() & stage)\n       && offset >= block.first.getOffset()\n       && offset < block.first.getOffset() + block.first.getSize())\n        return block.second + offset - block.first.getOffset();\n    }\n\n    return -1u;\n  }\n\n\n  DxvkPipelineBindings::DxvkPipelineBindings(\n          DxvkDevice*                 device,\n          DxvkPipelineManager*        manager,\n    const DxvkPipelineLayoutBuilder&  builder) {\n    auto stageMask = builder.getStageMask();\n\n    // Fill metadata structures that are independent of set layouts\n    buildMetadata(builder);\n\n    // Build pipeline layout for graphics pipeline libraries if applicable\n    if ((stageMask & VK_SHADER_STAGE_ALL_GRAPHICS) && device->canUseGraphicsPipelineLibrary())\n      buildPipelineLayout(DxvkPipelineLayoutType::Independent, device, builder, manager);\n\n    // Build pipeline layout for monolithic pipelines if binding\n    // layouts for all shader stages are known\n    bool isComplete = stageMask == VK_SHADER_STAGE_COMPUTE_BIT;\n\n    if (stageMask & VK_SHADER_STAGE_ALL_GRAPHICS) {\n      isComplete = (stageMask & VK_SHADER_STAGE_FRAGMENT_BIT)\n                && (stageMask & VK_SHADER_STAGE_VERTEX_BIT);\n    }\n\n    if (isComplete)\n      buildPipelineLayout(DxvkPipelineLayoutType::Merged, device, builder, manager);\n  }\n\n\n  DxvkPipelineBindings::~DxvkPipelineBindings() {\n\n  }\n\n\n  void DxvkPipelineBindings::buildPipelineLayout(\n          DxvkPipelineLayoutType      type,\n          DxvkDevice*                 device,\n    const DxvkPipelineLayoutBuilder&  builder,\n          DxvkPipelineManager*        manager) {\n    auto flags = getPipelineLayoutFlags(type, builder);\n\n    auto setInfos = computeSetMaskAndCount(type,\n      builder.getStageMask(), builder.getBindings());\n\n    auto pushDataBlocks = buildPushDataBlocks(type, device, setInfos, builder, manager);\n\n    // Descriptor processing needs to know the exact push data offsets\n    auto setLayouts = buildDescriptorSetLayouts(type, flags, setInfos, builder, manager);\n\n    // Create the actual pipeline layout\n    DxvkPipelineLayoutKey key(type, flags, builder.getStageMask(),\n      pushDataBlocks.size(), pushDataBlocks.data(),\n      setLayouts.size(), setLayouts.data());\n\n    auto& layout = m_layouts[uint32_t(type)];\n    layout.layout = manager->createPipelineLayout(key);\n  }\n\n\n  small_vector<DxvkPushDataBlock, DxvkPushDataBlock::MaxBlockCount>\n  DxvkPipelineBindings::buildPushDataBlocks(\n          DxvkPipelineLayoutType      type,\n          DxvkDevice*                 device,\n    const SetInfos&                   setInfos,\n    const DxvkPipelineLayoutBuilder&  builder,\n          DxvkPipelineManager*        manager) {\n    auto& layout = m_layouts[uint32_t(type)];\n\n    // Process and re-map data blocks\n    small_vector<DxvkPushDataBlock, DxvkPushDataBlock::MaxBlockCount> pushDataBlocks(DxvkPushDataBlock::MaxBlockCount);\n\n    uint32_t pushDataMask = builder.getPushDataMask();\n    uint32_t pushDataSize = 0u;\n\n    // Reserve push data space for heap offsets\n    if (type != DxvkPipelineLayoutType::BuiltIn && device->canUseDescriptorHeap())\n      pushDataSize += sizeof(uint32_t) * setInfos.count;\n\n    if (type == DxvkPipelineLayoutType::Independent) {\n      // For independent layouts, we don't know in advance how the other stages\n      // are going to use their push constants, so allocate the maximum amount.\n      VkShaderStageFlags stageMask = VK_SHADER_STAGE_ALL_GRAPHICS & util::shaderStages(device->getShaderPipelineStages());\n\n      uint32_t index = DxvkPushDataBlock::computeIndex(stageMask);\n\n      pushDataSize = align(pushDataSize, sizeof(uint64_t));\n\n      pushDataBlocks[index] = DxvkPushDataBlock(stageMask,\n        pushDataSize, MaxSharedPushDataSize, 8u, 0u);\n      pushDataSize += MaxSharedPushDataSize;\n\n      pushDataMask |= 1u << index;\n\n      for (auto i : bit::BitMask(stageMask)) {\n        auto stage = VkShaderStageFlagBits(1u << i);\n        index = DxvkPushDataBlock::computeIndex(stage);\n\n        pushDataBlocks[index] = DxvkPushDataBlock(stage,\n          pushDataSize, MaxPerStagePushDataSize, 8u, 0u);\n\n        pushDataSize += MaxPerStagePushDataSize;\n        pushDataMask |= 1u << index;\n      }\n\n      // Move blocks to pre-computed locations\n      for (auto i : bit::BitMask(builder.getPushDataMask())) {\n        auto block = builder.getPushDataBlock(i);\n\n        block.rebase(\n          pushDataBlocks[i].getOffset(),\n          pushDataBlocks[i].getSize());\n\n        pushDataBlocks[i] = block;\n      }\n    } else {\n      // Pack push data as tightly as possible\n      for (auto i : bit::BitMask(builder.getPushDataMask())) {\n        auto block = builder.getPushDataBlock(i);\n\n        pushDataSize = align(pushDataSize, block.getAlignment());\n\n        pushDataBlocks[i] = block;\n        pushDataBlocks[i].rebase(pushDataSize, block.getSize());\n\n        pushDataSize += block.getSize();\n      }\n    }\n\n    for (auto i : bit::BitMask(builder.getPushDataMask()))\n      layout.bindingMap.addPushData(builder.getPushDataBlock(i), pushDataBlocks[i].getOffset());\n\n    // Compact the array based on the bit mask\n    uint32_t pushDataBlockCount = 0u;\n\n    for (auto i : bit::BitMask(pushDataMask))\n      pushDataBlocks[pushDataBlockCount++] = pushDataBlocks[i];\n\n    pushDataBlocks.resize(pushDataBlockCount);\n    return pushDataBlocks;\n  }\n\n\n  small_vector<const DxvkDescriptorSetLayout*, DxvkPipelineBindings::MaxSets>\n  DxvkPipelineBindings::buildDescriptorSetLayouts(\n          DxvkPipelineLayoutType      type,\n          DxvkPipelineLayoutFlags     flags,\n    const SetInfos&                   setInfos,\n    const DxvkPipelineLayoutBuilder&  builder,\n          DxvkPipelineManager*        manager) {\n    auto bindings = builder.getBindings();\n\n    auto& layout = m_layouts[uint32_t(type)];\n\n    // Generate descriptor set layout keys from all bindings\n    std::array<DxvkDescriptorSetLayoutKey, MaxSets> setLayoutKeys = { };\n\n    for (size_t i = 0; i < bindings.bindingCount; i++) {\n      auto binding = bindings.bindings[i];\n      auto set = computeSetForBinding(type, binding);\n\n      if (set < setInfos.map.size())\n        set = setInfos.map[set];\n\n      DxvkShaderBinding srcMapping(binding.getStageMask(), binding.getSet(), binding.getBinding());\n      DxvkShaderBinding dstMapping(srcMapping);\n\n      if (binding.usesDescriptor()) {\n        uint32_t realSet = set + uint32_t(flags.test(DxvkPipelineLayoutFlag::UsesSamplerHeap));\n\n        auto bindingIndex = setLayoutKeys[set].add(DxvkDescriptorSetLayoutBinding(binding));\n        dstMapping = DxvkShaderBinding(binding.getStageMask(), realSet, bindingIndex);\n\n        layout.bindingMap.addBinding(srcMapping, dstMapping);\n        layout.setStateMasks[set] |= computeStateMask(binding);\n      }\n\n      if (binding.getDescriptorCount()) {\n        if (binding.usesDescriptor()) {\n          appendDescriptors(layout.setDescriptors[set], binding, dstMapping);\n\n          if (binding.isUniformBuffer())\n            appendDescriptors(layout.setUniformBuffers[set], binding, dstMapping);\n          else\n            appendDescriptors(layout.setResources[set], binding, dstMapping);\n        } else {\n          // Compute correct push data offset for the resource\n          auto offset = layout.bindingMap.mapPushData(\n            binding.getStageMask(), binding.getBlockOffset());\n\n          if (offset == -1u)\n            throw DxvkError(str::format(\"No push data mapping found for offset \", binding.getBlockOffset()));\n\n          binding.setBlockOffset(offset);\n\n          // This can be either a sampler or raw buffer address\n          if (binding.getDescriptorType() == VK_DESCRIPTOR_TYPE_SAMPLER)\n            appendDescriptors(layout.samplers, binding, dstMapping);\n          else\n            appendDescriptors(layout.vaBindings, binding, dstMapping);\n        }\n      }\n    }\n\n    // Remap sampler descriptor heap bindings\n    if (flags.test(DxvkPipelineLayoutFlag::UsesSamplerHeap)) {\n      DxvkShaderBinding dstMapping(builder.getStageMask(), 0u, 0u);\n\n      for (uint32_t i = 0u; i < builder.getSamplerHeapBindingCount(); i++) {\n        layout.bindingMap.addBinding(builder.getSamplerHeapBinding(i), dstMapping);\n      }\n    }\n\n    // Create the actual descriptor set layout objects\n    small_vector<const DxvkDescriptorSetLayout*, MaxSets> setLayouts(setInfos.count);\n\n    for (uint32_t i = 0u; i < setInfos.count; i++) {\n      if (setInfos.mask & (1u << i))\n        setLayouts[i] = manager->createDescriptorSetLayout(setLayoutKeys[i]);\n    }\n\n    return setLayouts;\n  }\n\n\n  void DxvkPipelineBindings::buildMetadata(\n    const DxvkPipelineLayoutBuilder&  builder) {\n    auto bindings = builder.getBindings();\n\n    for (size_t i = 0; i < bindings.bindingCount; i++) {\n      auto binding = bindings.bindings[i];\n\n      DxvkShaderBinding srcMapping(\n        binding.getStageMask(),\n        binding.getSet(),\n        binding.getBinding());\n\n      if (binding.getDescriptorType() == VK_DESCRIPTOR_TYPE_SAMPLER && binding.usesDescriptor())\n        throw DxvkError(\"Sampler descriptor without push index found\");\n\n      if (binding.getDescriptorType() == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)\n        throw DxvkError(\"Combined image/sampler descriptors not supported\");\n\n      if (binding.getDescriptorCount()) {\n        if (binding.getDescriptorType() != VK_DESCRIPTOR_TYPE_SAMPLER) {\n          if (binding.getAccess() & vk::AccessWriteMask) {\n            appendDescriptors(m_readWriteResources, binding, srcMapping);\n\n            if (binding.getAccessOp() == DxvkAccessOp::None)\n              m_hazardousStageMask |= binding.getStageMask();\n          }\n\n          if (!(binding.getAccess() & vk::AccessWriteMask)) {\n            for (auto stageIndex : bit::BitMask(uint32_t(binding.getStageMask())))\n              appendDescriptors(m_readOnlyResources[stageIndex], binding, srcMapping);\n          }\n        }\n\n        m_nonemptyStageMask |= binding.getStageMask();\n\n        m_barrier.stages |= util::pipelineStages(binding.getStageMask());\n        m_barrier.access |= binding.getAccess();\n\n        m_descriptorCount += binding.getDescriptorCount();\n      }\n    }\n  }\n\n\n  DxvkPipelineLayoutFlags DxvkPipelineBindings::getPipelineLayoutFlags(\n          DxvkPipelineLayoutType      type,\n    const DxvkPipelineLayoutBuilder&  builder) {\n    auto bindings = builder.getBindings();\n\n    DxvkPipelineLayoutFlags result;\n\n    if (type == DxvkPipelineLayoutType::Independent) {\n      // Always need to assume that at least one stage uses samplers\n      result.set(DxvkPipelineLayoutFlag::UsesSamplerHeap);\n    } else {\n      for (size_t i = 0; i < bindings.bindingCount; i++) {\n        auto binding = bindings.bindings[i];\n\n        if (binding.getDescriptorType() == VK_DESCRIPTOR_TYPE_SAMPLER && !binding.usesDescriptor()) {\n          result.set(DxvkPipelineLayoutFlag::UsesSamplerHeap);\n          break;\n        }\n      }\n    }\n\n    return result;\n  }\n\n\n  uint32_t DxvkPipelineBindings::computeStateMask(const DxvkShaderDescriptor& binding) {\n    return DxvkDescriptorState::computeMask(binding.getStageMask(),\n      binding.isUniformBuffer() ? DxvkDescriptorClass::Buffer : DxvkDescriptorClass::View);\n  }\n\n\n  uint32_t DxvkPipelineBindings::computeSetForBinding(\n          DxvkPipelineLayoutType    type,\n    const DxvkShaderDescriptor&     binding) {\n    VkShaderStageFlags stage = binding.getStageMask();\n\n    if (!binding.usesDescriptor())\n      return -1u;\n\n    if (stage == VK_SHADER_STAGE_COMPUTE_BIT)\n      return DxvkDescriptorSets::CpResources;\n\n    if (type == DxvkPipelineLayoutType::Independent) {\n      return stage & VK_SHADER_STAGE_FRAGMENT_BIT\n        ? DxvkDescriptorSets::GpIndependentFsResources\n        : DxvkDescriptorSets::GpIndependentVsResources;\n    }\n\n    if (binding.isUniformBuffer())\n      return DxvkDescriptorSets::GpBuffers;\n\n    return DxvkDescriptorSets::GpViews;\n  }\n\n\n  DxvkPipelineBindings::SetInfos DxvkPipelineBindings::computeSetMaskAndCount(\n          DxvkPipelineLayoutType          type,\n          VkShaderStageFlags              stages,\n          DxvkPipelineBindingRange        bindings) {\n    SetInfos result = { };\n\n    if (type == DxvkPipelineLayoutType::Independent) {\n      // For independent layouts, we need to keep the set mapping consistent\n      result.count = DxvkDescriptorSets::GpIndependentSetCount;\n\n      if (stages & VK_SHADER_STAGE_FRAGMENT_BIT)\n        result.mask |= 1u << DxvkDescriptorSets::GpIndependentFsResources;\n\n      if (stages & VK_SHADER_STAGE_VERTEX_BIT)\n        result.mask |= 1u << DxvkDescriptorSets::GpIndependentVsResources;\n\n      for (uint32_t i = 0u; i < result.count; i++)\n        result.map[i] = uint8_t(i);\n    } else {\n      // Iterate over bindings to check which sets are actively used, then\n      // filter out any empty sets in order to reduce some overhead that\n      // we may otherwise get when there are gaps in used sets.\n      std::array<uint16_t, MaxSets> setSizes = { };\n\n      for (size_t i = 0u; i < bindings.bindingCount; i++) {\n        if (bindings.bindings[i].usesDescriptor()) {\n          uint32_t set = computeSetForBinding(type, bindings.bindings[i]);\n          setSizes[set] += bindings.bindings[i].getDescriptorCount();\n        }\n      }\n\n      // Compute mapping from logical set to real set index\n      for (size_t i = 0u; i < MaxSets; i++) {\n        if (setSizes[i])\n          result.map[i] = result.count++;\n      }\n\n      // Compute compact mask of all used sets\n      result.mask = (1u << result.count) - 1u;\n    }\n\n    return result;\n  }\n\n\n  DxvkPipelineLayoutBuilder::DxvkPipelineLayoutBuilder() {\n\n  }\n\n\n  DxvkPipelineLayoutBuilder::DxvkPipelineLayoutBuilder(VkShaderStageFlags stageMask)\n  : m_stageMask(stageMask) {\n\n  }\n\n\n  DxvkPipelineLayoutBuilder::~DxvkPipelineLayoutBuilder() {\n\n  }\n\n\n  void DxvkPipelineLayoutBuilder::addPushData(\n          DxvkPushDataBlock         block) {\n    uint32_t index = DxvkPushDataBlock::computeIndex(block.getStageMask());\n\n    if (!block.isEmpty()) {\n      m_pushMask |= 1u << index;\n      m_pushData[index].merge(block);\n    }\n  }\n\n\n  void DxvkPipelineLayoutBuilder::addBindings(\n          uint32_t                  bindingCount,\n    const DxvkShaderDescriptor*     bindings) {\n    size_t size = m_bindings.size();\n    m_bindings.resize(size + bindingCount);\n\n    for (uint32_t i = 0; i < bindingCount; i++) {\n      size_t last = size + i;\n\n      while (last && bindings[i].lt(m_bindings[last - 1u])) {\n        m_bindings[last] = m_bindings[last - 1u];\n        last -= 1u;\n      }\n\n      m_bindings[last] = bindings[i];\n    }\n  }\n\n\n  void DxvkPipelineLayoutBuilder::addSamplerHeap(\n    const DxvkShaderBinding&        binding) {\n    m_samplerHeaps.push_back(binding);\n  }\n\n\n  void DxvkPipelineLayoutBuilder::addLayout(\n    const DxvkPipelineLayoutBuilder& layout) {\n    m_stageMask |= layout.m_stageMask;\n    m_pushMask |= layout.m_pushMask;\n\n    for (auto i : bit::BitMask(layout.getPushDataMask())) {\n      auto srcBlock = layout.getPushDataBlock(i);\n\n      if (m_pushData[i].isEmpty())\n        m_pushData[i] = srcBlock;\n      else\n        m_pushData[i].merge(srcBlock);\n    }\n\n    addBindings(layout.m_bindings.size(), layout.m_bindings.data());\n\n    for (uint32_t i = 0u; i < layout.getSamplerHeapBindingCount(); i++)\n      addSamplerHeap(layout.getSamplerHeapBinding(i));\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_pipelayout.h",
    "content": "#pragma once\n\n#include <optional>\n#include <unordered_map>\n#include <vector>\n\n#include \"../util/util_small_vector.h\"\n\n#include \"dxvk_descriptor_info.h\"\n#include \"dxvk_hash.h\"\n#include \"dxvk_include.h\"\n#include \"dxvk_limits.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n  class DxvkPipelineManager;\n\n  /**\n   * \\brief Order-invariant atomic access operation\n   *\n   * Information used to optimize barriers when a resource\n   * is accessed exlusively via order-invariant stores.\n   */\n  struct DxvkAccessOp {\n    static constexpr uint32_t StoreValueBits = 12u;\n\n    enum OpType : uint16_t {\n      None      = 0x0u,\n      Or        = 0x1u,\n      And       = 0x2u,\n      Xor       = 0x3u,\n      Add       = 0x4u,\n      IMin      = 0x5u,\n      IMax      = 0x6u,\n      UMin      = 0x7u,\n      UMax      = 0x8u,\n      Load      = 0x9u,\n\n      StoreF    = 0xdu,\n      StoreUi   = 0xeu,\n      StoreSi   = 0xfu,\n    };\n\n    DxvkAccessOp() = default;\n    DxvkAccessOp(OpType t)\n    : op(uint16_t(t)) { }\n\n    DxvkAccessOp(OpType t, uint16_t constant)\n    : op(uint16_t(t) | (constant << 4u)) { }\n\n    uint16_t op = 0u;\n\n    bool operator == (const DxvkAccessOp& t) const { return op == t.op; }\n    bool operator != (const DxvkAccessOp& t) const { return op != t.op; }\n\n    template<typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>\n    explicit operator T() const { return op; }\n  };\n\n  static_assert(sizeof(DxvkAccessOp) == sizeof(uint16_t));\n\n\n  /**\n   * \\brief Descriptor set indices\n   */\n  struct DxvkDescriptorSets {\n    // For fast-linked pipelines, use the bare minimum of one set\n    // for the fragment shader and one for vertex shader resources.\n    static constexpr uint32_t GpIndependentSetCount     = 2u;\n    static constexpr uint32_t GpIndependentFsResources  = 0u;\n    static constexpr uint32_t GpIndependentVsResources  = 1u;\n\n    // For merged pipelines, use one set containing per unique\n    // descriptor class. We can reasonably expect uniform buffers\n    // to be rebound more often than views and samplers.\n    static constexpr uint32_t GpSetCount                = 2u;\n    static constexpr uint32_t GpViews                   = 0u;\n    static constexpr uint32_t GpBuffers                 = 1u;\n\n    // For compute shaders, put everything into one set since it is\n    // very likely that all types of resources get changed at once.\n    static constexpr uint32_t CpResources               = 0u;\n    static constexpr uint32_t CpSetCount                = 1u;\n\n    // Maximum number of descriptor sets per layout\n    static constexpr uint32_t SetCount                  = 2u;\n  };\n\n\n  /**\n   * \\brief Descriptor class\n   */\n  struct DxvkDescriptorClass {\n    static constexpr uint32_t Buffer  = 1u << 0u;\n    static constexpr uint32_t View    = 1u << 8u;\n    static constexpr uint32_t Sampler = 1u << 16u;\n    static constexpr uint32_t Va      = 1u << 24u;\n\n    static constexpr uint32_t All = Buffer | View | Sampler | Va;\n  };\n\n\n  /**\n   * \\brief Dirty descriptor set tracker\n   */\n  class DxvkDescriptorState {\n    constexpr static VkShaderStageFlags AllStageMask =\n      VK_SHADER_STAGE_ALL_GRAPHICS | VK_SHADER_STAGE_COMPUTE_BIT;\n  public:\n\n    DxvkDescriptorState() = default;\n\n    void dirtyBuffers(VkShaderStageFlags stages) {\n      m_dirtyMask |= computeMask(stages, DxvkDescriptorClass::Buffer | DxvkDescriptorClass::Va);\n    }\n\n    void dirtyViews(VkShaderStageFlags stages) {\n      m_dirtyMask |= computeMask(stages, DxvkDescriptorClass::View | DxvkDescriptorClass::Va);\n    }\n\n    void dirtySamplers(VkShaderStageFlags stages) {\n      m_dirtyMask |= computeMask(stages, DxvkDescriptorClass::Sampler);\n    }\n\n    void dirtyStages(VkShaderStageFlags stages) {\n      m_dirtyMask |= computeMask(stages, DxvkDescriptorClass::All);\n    }\n\n    void clearStages(VkShaderStageFlags stages) {\n      m_dirtyMask &= ~(computeMask(stages, DxvkDescriptorClass::All));\n    }\n\n    bool hasDirtyResources(VkShaderStageFlags stages) const {\n      return bool(m_dirtyMask & computeMask(stages, DxvkDescriptorClass::All));\n    }\n\n    bool hasDirtySamplers(VkShaderStageFlags stages) {\n      return bool(m_dirtyMask & computeMask(stages, DxvkDescriptorClass::Sampler));\n    }\n\n    bool hasDirtyVas(VkShaderStageFlags stages) {\n      return bool(m_dirtyMask & computeMask(stages, DxvkDescriptorClass::Va));\n    }\n\n    bool testDirtyMask(uint32_t mask) const {\n      return m_dirtyMask & mask;\n    }\n\n    VkShaderStageFlags getDirtyStageMask(uint32_t classes) const {\n      VkShaderStageFlags result = 0u;\n\n      for (auto classShift : bit::BitMask(classes))\n        result |= m_dirtyMask >> classShift;\n\n      return result & AllStageMask;\n    }\n\n    static constexpr uint32_t computeMask(VkShaderStageFlags stages, uint32_t classes) {\n      return uint32_t(stages) * classes;\n    }\n\n  private:\n\n    uint32_t m_dirtyMask = 0u;\n\n  };\n\n\n  /**\n   * \\brief Pipeline layout type\n   *\n   * Determines whether to use a pipeline layout with stage-separated\n   * descriptor sets, or one with merged sets.\n   */\n  enum class DxvkPipelineLayoutType : uint16_t {\n    Independent = 0u, ///< Fragment and pre-raster shaders use separate sets\n    Merged      = 1u, ///< Fragment and pre-raster shaders use the same sets\n    BuiltIn     = 2u, ///< Built-in pipeline with special push data layout\n  };\n\n\n  /**\n   * \\brief Pipeline layout flag\n   *\n   * Defines properties of the layout.\n   */\n  enum class DxvkPipelineLayoutFlag : uint8_t {\n    UsesSamplerHeap = 0u, ///< Requires global sampler heap\n  };\n\n  using DxvkPipelineLayoutFlags = Flags<DxvkPipelineLayoutFlag>;\n\n\n  /**\n   * \\brief Descriptor flags\n   */\n  enum class DxvkDescriptorFlag : uint8_t {\n    /** Resource is a plain (uniform) buffer, not a view */\n    UniformBuffer   = 0u,\n    /** Image resource may be be multisampled */\n    Multisampled    = 1u,\n    /** Resource is accessed via push data */\n    PushData        = 2u,\n  };\n\n  using DxvkDescriptorFlags = Flags<DxvkDescriptorFlag>;\n\n\n  /**\n   * \\brief Binding info\n   *\n   * Stores metadata for a single binding in\n   * a given shader, or for the whole pipeline.\n   */\n  struct DxvkBindingInfo {\n    /** Shader-defined descriptor set index */\n    uint32_t set = 0u;\n    /** Shader-defined binding index */\n    uint32_t binding = 0u;\n    /** Binding slot for the resource */\n    uint32_t resourceIndex = 0u;\n    /** Descriptor type */\n    VkDescriptorType descriptorType  = VK_DESCRIPTOR_TYPE_MAX_ENUM;\n    /** Size of descriptor array */\n    uint32_t descriptorCount = 1u;\n    /** Image view type */\n    VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n    /** Access flags for the resource in the shader */\n    VkAccessFlags access = 0u;\n    /** Additional binding properties */\n    DxvkDescriptorFlags flags = 0u;\n    /** Order-invariant access type, if any */\n    DxvkAccessOp accessOp = DxvkAccessOp::None;\n    /** Byte offset of raw address or descriptor index within\n     *  the shader's push data block. This will get remapped\n     *  when chaining push constant blocks. */\n    uint32_t blockOffset = 0u;\n  };\n\n\n  /**\n   * \\brief Descriptor metadata\n   *\n   * Stores all the information required to map a bound resource\n   * to an actual shader descriptor. Note that this refers to one\n   * single descriptor rather than a descriptor array.\n   */\n  class DxvkShaderDescriptor {\n\n  public:\n\n    DxvkShaderDescriptor() = default;\n\n    DxvkShaderDescriptor(const DxvkBindingInfo& binding, VkShaderStageFlags stages)\n    : m_type      (uint8_t(binding.descriptorType)),\n      m_stages    (uint8_t(stages)),\n      m_index     (uint16_t(binding.resourceIndex)),\n      m_viewType  (uint8_t(binding.viewType)),\n      m_access    (uint8_t(binding.access)),\n      m_accessOp  (binding.accessOp),\n      m_flags     (binding.flags),\n      m_set       (uint8_t(binding.set)),\n      m_binding   (uint16_t(binding.binding)),\n      m_arrayIndex(0u),\n      m_arraySize (uint16_t(binding.descriptorCount)),\n      m_blockOffset(uint16_t(binding.blockOffset)) { }\n\n    /**\n     * \\brief Queries descriptor type\n     * \\returns Descriptor type\n     */\n    VkDescriptorType getDescriptorType() const {\n      return VkDescriptorType(m_type);\n    }\n\n    /**\n     * \\brief Queries descriptor count in array\n     * \\returns Number of descriptors in descriptor array\n     */\n    uint32_t getDescriptorCount() const {\n      return m_arraySize;\n    }\n\n    /**\n     * \\brief Queries shader stage mask\n     * \\returns Shader stage mask\n     */\n    VkShaderStageFlags getStageMask() const {\n      return VkShaderStageFlags(m_stages);\n    }\n\n    /**\n     * \\brief Queries resource index\n     * \\returns Resource index\n     */\n    uint32_t getResourceIndex() const {\n      return m_index;\n    }\n\n    /**\n     * \\brief Queries view type\n     * \\returns View type\n     */\n    VkImageViewType getViewType() const {\n      return m_viewType < 0 ? VK_IMAGE_VIEW_TYPE_MAX_ENUM : VkImageViewType(m_viewType);\n    }\n\n    /**\n     * \\brief Checks whether a multisampled view is required\n     * \\returns \\c true if a multisampled view is needed\n     */\n    bool isMultisampled() const {\n      return m_flags.test(DxvkDescriptorFlag::Multisampled);\n    }\n\n    /**\n     * \\brief Checks whether the resource is a raw buffer\n     *\n     * Changes whether the resource is sourced as a raw buffer or from\n     * a buffer view object. Relevant for storage buffer descriptors\n     * that are used as a uniform buffer.\n     * \\returns \\c true for raw buffer descriptors\n     */\n    bool isUniformBuffer() const {\n      return m_flags.test(DxvkDescriptorFlag::UniformBuffer);\n    }\n\n    /**\n     * \\brief Checks whether the resource uses a descriptor\n     *\n     * Resources may either be accessed through a descriptor stored in the\n     * descriptor set, or through the raw GPU address or a descriptor index\n     * that is passed in as a push constant.\n     * \\returns \\c true for any descriptor-backed resource\n     */\n    bool usesDescriptor() const {\n      return !m_flags.test(DxvkDescriptorFlag::PushData);\n    }\n\n    /**\n     * \\brief Queries shader access types\n     * \\returns Access types\n     */\n    VkAccessFlags getAccess() const {\n      return VkAccessFlags(m_access);\n    }\n\n    /**\n     * \\brief Queries access op\n     * \\returns Access op\n     */\n    DxvkAccessOp getAccessOp() const {\n      return m_accessOp;\n    }\n\n    /**\n     * \\brief Queries descriptor set index\n     * \\returns Descriptor set index\n     */\n    uint32_t getSet() const {\n      return m_set;\n    }\n\n    /**\n     * \\brief Queries descriptor binding index\n     * \\returns Descriptor binding index\n     */\n    uint32_t getBinding() const {\n      return m_binding;\n    }\n\n    /**\n     * \\brief Queries offset in push data block\n     *\n     * For GPU addresses, the size of each element will be 8 bytes.\n     * For descripor-backed resources, this carries no meaning.\n     * \\returns Byte offset into push data block\n     */\n    uint32_t getBlockOffset() const {\n      return m_blockOffset;\n    }\n\n    /**\n     * \\brief Queries descriptor index in array\n     * \\returns Index into descriptor array\n     */\n    uint32_t getArrayIndex() const {\n      return m_arrayIndex;\n    }\n\n    /**\n     * \\brief Retrives info for a specific array element\n     *\n     * \\param [in] index Array index to extract\n     * \\returns Descriptor info with adjusted index\n     */\n    DxvkShaderDescriptor getArrayElement(uint32_t index) const {\n      uint8_t baseIndex = m_arrayIndex;\n\n      DxvkShaderDescriptor result = *this;\n      result.m_index += index - baseIndex;\n      result.m_arrayIndex = index - baseIndex;\n      result.m_arraySize = 1u;\n      result.m_blockOffset += getBlockEntrySize() * uint32_t(index - baseIndex);\n      return result;\n    }\n\n    /**\n     * \\brief Changes set and binding indices\n     *\n     * \\param [in] set New set index\n     * \\param [in] binding New binding index\n     * \\returns Adjusted descriptor info\n     */\n    DxvkShaderDescriptor remapBinding(uint32_t set, uint32_t binding) const {\n      DxvkShaderDescriptor result = *this;\n      result.m_set = set;\n      result.m_binding = binding;\n      return result;\n    }\n\n    /**\n     * \\bief Changes block offset\n     *\n     * Used when remapping push data to its final memory layout.\n     * \\param [in] offset New absolute block offset\n     */\n    void setBlockOffset(uint32_t offset) {\n      m_blockOffset = offset;\n    }\n\n    /**\n     * \\brief Queries resource info size within the push data block\n     *\n     * Only returns info for a single element. To get the full data\n     * size, multiply with the descriptor count of the binding.\n     *\n     * The returned size depends on the descriptor type.\n     * \\returns Element size in push data block\n     */\n    uint32_t getBlockEntrySize() const {\n      switch (getDescriptorType()) {\n        case VK_DESCRIPTOR_TYPE_SAMPLER:\n          return sizeof(uint16_t);\n\n        case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:\n        case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:\n          return sizeof(VkDeviceAddress);\n\n        default:\n          return 0u;\n      }\n    }\n\n    /**\n     * \\brief Checks whether a binding is ordered before another\n     *\n     * Bindings are ordered by stage, descriptor type and array\n     * properties in various places in order to keep the number\n     * of unique descriptor set and pipeline layouts low.\n     * \\param [in] other Binding to compare against\n     * \\returns \\c true if this binding is ordered before the other.\n     */\n    bool lt(const DxvkShaderDescriptor& descriptor) const {\n      return encodeNumeric() < descriptor.encodeNumeric();\n    }\n\n  private:\n\n    uint8_t             m_type              = 0u;\n    uint8_t             m_stages            = 0u;\n    uint16_t            m_index             = 0u;\n    int8_t              m_viewType          = 0u;\n    uint8_t             m_access            = 0u;\n    DxvkAccessOp        m_accessOp          = DxvkAccessOp::None;\n    DxvkDescriptorFlags m_flags             = 0u;\n    uint8_t             m_set               = 0u;\n    uint16_t            m_binding           = 0u;\n    uint8_t             m_arrayIndex        = 0u;\n    uint8_t             m_arraySize         = 0u;\n    uint16_t            m_blockOffset       = 0u;\n\n    uint64_t encodeNumeric() const {\n      return uint64_t(m_arrayIndex + m_blockOffset)\n          | (uint64_t(m_binding)     << 16u)\n          | (uint64_t(m_set)         << 32u)\n          | (uint64_t(m_type)        << 40u)\n          | (uint64_t(m_stages)      << 48u);\n    }\n\n  };\n\n\n  /**\n   * \\brief Push data block\n   *\n   * Maps to a shader-defined push constant range, which may\n   * contain user-provided data or raw shader bindings.\n   *\n   * For graphics pipelines, there are two types of push data\n   * blocks: Global data, which is available to all stages in\n   * the pipeline, and per-stage data.\n   */\n  class DxvkPushDataBlock {\n\n  public:\n\n    // One shared block and one per shader stage\n    constexpr static uint32_t MaxBlockCount = 6u;\n\n    DxvkPushDataBlock() = default;\n\n    DxvkPushDataBlock(\n            VkShaderStageFlags        stages,\n            uint32_t                  offset,\n            uint32_t                  size,\n            uint32_t                  alignment,\n            uint64_t                  resourceMask)\n    : m_stageMask   (uint16_t(stages)),\n      m_alignment   (uint16_t(alignment)),\n      m_offset      (uint16_t(offset)),\n      m_size        (uint16_t(size)),\n      m_resourceMask(uint64_t(resourceMask)) { }\n\n    DxvkPushDataBlock(\n            uint32_t                  offset,\n            uint32_t                  size,\n            uint32_t                  alignment,\n            uint64_t                  resourceMask)\n    : DxvkPushDataBlock(0u, offset, size, alignment, resourceMask) { }\n\n    /**\n     * \\brief Queries stage mask\n     * \\returns Stage mask\n     */\n    VkShaderStageFlags getStageMask() const {\n      return m_stageMask;\n    }\n\n    /**\n     * \\brief Checks whether the block is shared\n     *\n     * Any push constant block with more than\n     * one stage flag is considered shared.\n     * \\returns \\c true if the block is shared\n     */\n    bool isShared() const {\n      return bool(m_stageMask & (m_stageMask - 1u));\n    }\n\n    /**\n     * \\brief Checks whether the block is empty\n     * \\returns \\c true for an empty block\n     */\n    bool isEmpty() const {\n      return !m_size;\n    }\n\n    /**\n     * \\brief Queries block size\n     * \\returns Data block size\n     */\n    uint32_t getSize() const {\n      return m_size;\n    }\n\n    /**\n     * \\brief Queries required block alignment\n     *\n     * Will be at least 4 bytes, but may be higher\n     * if the block stores 64-bit data types.\n     * \\returns Required data alignment\n     */\n    uint32_t getAlignment() const {\n      return m_alignment;\n    }\n\n    /**\n     * \\brief Push data offset\n     *\n     * Depending on the context, this either contains the\n     * shader-defined push constant offset, or the real\n     * offset as defined in the final push constant layout.\n     *\n     * When remapping, the offsets of all push constant\n     * block members within this range will be changed.\n     *\n     * The shared push constant block will always be\n     * mapped to offset 0.\n     * \\returns Push data offset\n     */\n    uint32_t getOffset() const {\n      return m_offset;\n    }\n\n    /**\n     * \\brief Queries mask of dwords used for resource data\n     *\n     * The dword corresponding to each set bit in the mask will\n     * not be taken from userdata, but will instead contain a\n     * resource index or address.\n     *\n     * Bit 0 corresponds to the first dword int the block.\n     * \\returns Resource mask\n     */\n    uint64_t getResourceDwordMask() const {\n      return m_resourceMask;\n    }\n\n    /**\n     * \\brief Merges block with another\n     *\n     * Useful when dealing with shared push constant block\n     * definitions coming in from multiple shaders.\n     *\n     * If neither block is empty, then the offsets of both\n     * blocks must be identical; different shaders using\n     * this block must agree on its layout.\n     * \\param [in] other The block to merge with\n     */\n    void merge(const DxvkPushDataBlock& other) {\n      uint32_t oldOffset = m_offset;\n      uint32_t newOffset = other.m_offset;\n\n      m_stageMask    |= other.m_stageMask;\n      m_alignment     = std::max(m_alignment, other.m_alignment);\n      m_offset        = std::min(newOffset, m_size ? oldOffset : newOffset);\n      m_size          = align(std::max(oldOffset + m_size, newOffset + other.m_size) - m_offset, m_alignment);\n\n      // Preserve correct bit location of resource masks\n      m_resourceMask <<= (oldOffset / sizeof(uint32_t));\n      m_resourceMask |= other.m_resourceMask << (newOffset / sizeof(uint32_t));\n      m_resourceMask >>= m_offset / sizeof(uint32_t);\n    }\n\n    /**\n     * \\brief Shifts block to a certain offset\n     *\n     * Useful when remapping push constant ranges.\n     * \\param [in] newOffset New block offset\n     * \\param [in] newSize New block size\n     */\n    void rebase(uint32_t newOffset, uint32_t newSize) {\n      m_offset = newOffset;\n      m_size = newSize;\n    }\n\n    /**\n     * \\brief Makes block absolute\n     *\n     * Changes the block to have an offset of 0 while keeping\n     * all data in its place. The resulting block may thus\n     * be larger than the original.\n     */\n    void makeAbsolute() {\n      m_resourceMask <<= m_offset;\n      m_size += m_offset;\n\n      m_offset = 0u;\n    }\n\n    /**\n     * \\brief Checks for equality\n     *\n     * \\param [in] other Block to compare to\n     * \\returns \\c true if the two blocks are identical\n     */\n    bool eq(const DxvkPushDataBlock& other) const {\n      return m_stageMask    == other.m_stageMask\n          && m_alignment    == other.m_alignment\n          && m_offset       == other.m_offset\n          && m_size         == other.m_size\n          && m_resourceMask == other.m_resourceMask;\n    }\n\n    /**\n     * \\brief Computes hash\n     * \\returns Hash value\n     */\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(m_stageMask);\n      hash.add(m_alignment);\n      hash.add(m_offset);\n      hash.add(m_size);\n      hash.add(m_resourceMask);\n      return hash;\n    }\n\n    /**\n     * \\brief Computes push data index for given stage mask\n     *\n     * If this is a shared or compute block, the index will\n     * always be 0, otherwise it depends on the exact stage.\n     * \\param [in] stageMask Stage mask\n     * \\returns Push data block index\n     */\n    static uint32_t computeIndex(VkShaderStageFlags stageMask) {\n      if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT)\n        return 0u;\n\n      uint32_t remainder = stageMask & (stageMask - 1u);\n      return remainder ? 0u : (bit::tzcnt(uint32_t(stageMask)) + 1u);\n    }\n\n  private:\n\n    uint16_t  m_stageMask     = 0u;\n    uint16_t  m_alignment     = 0u;\n    uint16_t  m_offset        = 0u;\n    uint16_t  m_size          = 0u;\n    uint64_t  m_resourceMask  = 0u;\n\n  };\n\n\n  /**\n   * \\brief Descriptor set binding info\n   *\n   * Stores unique info for a single binding. The\n   * binding index is implied by its position in\n   * the binding list.\n   */\n  struct DxvkDescriptorSetLayoutBinding {\n\n  public:\n\n    DxvkDescriptorSetLayoutBinding() = default;\n\n    DxvkDescriptorSetLayoutBinding(\n            VkDescriptorType        type,\n            uint32_t                count,\n            VkShaderStageFlags      stages)\n    : m_type    (uint8_t(type)),\n      m_stages  (uint8_t(stages)),\n      m_count   (uint16_t(count)) { }\n\n    DxvkDescriptorSetLayoutBinding(\n      const DxvkShaderDescriptor&   descriptor)\n    : DxvkDescriptorSetLayoutBinding(\n        descriptor.getDescriptorType(),\n        descriptor.getDescriptorCount(),\n        descriptor.getStageMask()) { }\n\n    /**\n     * \\brief Queries descriptor type\n     * \\returns Vulkan descriptor type\n     */\n    VkDescriptorType getDescriptorType() const {\n      return VkDescriptorType(m_type);\n    }\n\n    /**\n     * \\brief Queries descriptor count\n     * \\returns Size of descriptor array\n     */\n    uint32_t getDescriptorCount() const {\n      return m_count;\n    }\n\n    /**\n     * \\brief Queries stage mask\n     * \\returns Pipeline stages that can access the descriptor\n     */\n    VkShaderStageFlags getStageMask() const {\n      return VkShaderStageFlags(m_stages);\n    }\n\n    /**\n     * \\brief Checks equality\n     *\n     * \\param [in] other Other binding to check\n     * \\returns \\c true if the two bindings match\n     */\n    bool eq(const DxvkDescriptorSetLayoutBinding& other) const {\n      return m_type   == other.m_type\n          && m_stages == other.m_stages\n          && m_count  == other.m_count;\n    }\n\n    /**\n     * \\brief Computes hash\n     * \\returns Binding hash\n     */\n    size_t hash() const {\n      return size_t(m_type)\n          | (size_t(m_stages) << 8u)\n          | (size_t(m_count) << 16u);\n    }\n\n  private:\n\n    uint8_t   m_type    = 0u;\n    uint8_t   m_stages  = 0u;\n    uint16_t  m_count   = 0u;\n\n  };\n\n\n  /**\n   * \\brief Descriptor set layout key\n   *\n   * Stores relevant information to look\n   * up unique descriptor set layouts.\n   */\n  class DxvkDescriptorSetLayoutKey {\n\n  public:\n\n    DxvkDescriptorSetLayoutKey();\n    ~DxvkDescriptorSetLayoutKey();\n\n    /**\n     * \\brief Retrieves binding count\n     * \\returns Binding count\n     */\n    uint32_t getBindingCount() const {\n      return uint32_t(m_bindings.size());\n    }\n\n    /**\n     * \\brief Retrieves binding info\n     *\n     * \\param [in] index Binding index\n     * \\returns Binding info\n     */\n    DxvkDescriptorSetLayoutBinding getBinding(uint32_t index) const {\n      return m_bindings[index];\n    }\n\n    /**\n     * \\brief Adds a binding to the key\n     *\n     * Useful to construct set layouts on the fly.\n     * \\param [in] binding Binding info\n     * \\returns Binding index\n     */\n    uint32_t add(DxvkDescriptorSetLayoutBinding binding);\n\n    /**\n     * \\brief Checks for equality\n     *\n     * \\param [in] other Binding layout to compare to\n     * \\returns \\c true if both binding layouts are equal\n     */\n    bool eq(const DxvkDescriptorSetLayoutKey& other) const;\n\n    /**\n     * \\brief Hashes binding layout\n     * \\returns Binding layout hash\n     */\n    size_t hash() const;\n\n  private:\n\n    small_vector<DxvkDescriptorSetLayoutBinding, 32> m_bindings;\n\n  };\n\n\n  /**\n   * \\brief Descriptor set layout\n   *\n   * Manages a Vulkan descriptor set layout\n   * object for a given binding list.\n   */\n  class DxvkDescriptorSetLayout {\n\n  public:\n\n    DxvkDescriptorSetLayout(\n            DxvkDevice*                 device,\n      const DxvkDescriptorSetLayoutKey& key);\n\n    ~DxvkDescriptorSetLayout();\n\n    /**\n     * \\brief Checks whether the set layout is empty\n     *\n     * Empty set layouts are sometimes needed to create valid\n     * pipeline layouts for pipeline libraries.\n     * \\returns \\c true if the set layout contains no descriptors\n     */\n    bool isEmpty() const {\n      return !m_bindingCount;\n    }\n\n    /**\n     * \\brif Queries number of bindings in set\n     * \\returns Number of bindings\n     */\n    uint32_t getBindingCount() const {\n      return m_bindingCount;\n    }\n\n    /**\n     * \\brief Queries descriptor set layout\n     * \\returns Descriptor set layout\n     */\n    VkDescriptorSetLayout getSetLayout() const {\n      return m_legacy.layout;\n    }\n\n    /**\n     * \\brief Queries descriptor template\n     * \\returns Descriptor update template\n     */\n    VkDescriptorUpdateTemplate getSetUpdateTemplate() const {\n      return m_legacy.updateTemplate;\n    }\n\n    /**\n     * \\brief Queries allocation size for the set\n     * \\returns Space required in descriptor heap\n     */\n    VkDeviceSize getMemorySize() const {\n      return m_heap.memorySize;\n    }\n\n    /**\n     * \\brief Queries binding type and offset in the set\n     *\n     * Can only be used when not using the legacy binding model.\n     * \\param [in] binding Binding index\n     * \\returns Binding layout info\n     */\n    DxvkDescriptorUpdateInfo getBindingInfo(uint32_t binding) const {\n      return binding < m_heap.bindingLayouts.size()\n        ? m_heap.bindingLayouts[binding]\n        : DxvkDescriptorUpdateInfo();\n    }\n\n    /**\n     * \\brief Updates descriptor memory\n     *\n     * Uses the pre-computed update list to write descriptors.\n     * \\param [in] dst Pointer to descriptor memory\n     * \\param [in] descriptors Pointer to source descriptor list\n     */\n    void update(\n            void*                   dst,\n      const DxvkDescriptor**        descriptors) const {\n      m_heap.update.update(dst, descriptors);\n    }\n\n  private:\n\n    DxvkDevice*                   m_device;\n    uint32_t                      m_bindingCount = 0u;\n\n    struct {\n      VkDescriptorSetLayout       layout          = VK_NULL_HANDLE;\n      VkDescriptorUpdateTemplate  updateTemplate  = VK_NULL_HANDLE;\n    } m_legacy;\n\n    struct {\n      VkDeviceSize                memorySize = 0u;\n      DxvkDescriptorUpdateList    update;\n\n      small_vector<DxvkDescriptorUpdateInfo, 32> bindingLayouts;\n    } m_heap;\n\n    void initSetLayout(const DxvkDescriptorSetLayoutKey& key);\n\n    void initDescriptorBufferUpdate(const DxvkDescriptorSetLayoutKey& key);\n\n    void initDescriptorHeapLayout(const DxvkDescriptorSetLayoutKey& key);\n\n  };\n\n\n  /**\n   * \\brief Pipeline layout key\n   *\n   * Used to look up pipeline layout objects. Stores a reference to the\n   * descriptor sets used in this layout, as well as push constant info.\n   * Uses the fact that descriptor set layout objects are persistent and\n   * unique, and thus pipeline layouts using different set layouts are\n   * unique as well.\n   */\n  class DxvkPipelineLayoutKey {\n\n  public:\n\n    constexpr static uint32_t MaxSets = uint32_t(DxvkDescriptorSets::SetCount);\n\n    DxvkPipelineLayoutKey() = default;\n\n    DxvkPipelineLayoutKey(\n            DxvkPipelineLayoutType    type,\n            DxvkPipelineLayoutFlags   flags)\n    : m_type          (type),\n      m_flags         (flags) { }\n\n    DxvkPipelineLayoutKey(\n            DxvkPipelineLayoutType    type,\n            DxvkPipelineLayoutFlags   flags,\n            VkShaderStageFlags        stageMask,\n            uint32_t                  pushDataBlockCount,\n      const DxvkPushDataBlock*        pushDataBlocks,\n            uint32_t                  setCount,\n      const DxvkDescriptorSetLayout** setLayouts)\n    : m_type          (type),\n      m_flags         (flags),\n      m_stages        (uint8_t(stageMask)) {\n      for (uint32_t i = 0u; i < pushDataBlockCount; i++)\n        addPushData(pushDataBlocks[i]);\n\n      setDescriptorSetLayouts(setCount, setLayouts);\n    }\n\n    /**\n     * \\brief Queries layout type\n     * \\returns Layout type\n     */\n    DxvkPipelineLayoutType getType() const {\n      return m_type;\n    }\n\n    /**\n     * \\brief Queries layout flags\n     * \\returns Layout flags\n     */\n    DxvkPipelineLayoutFlags getFlags() const {\n      return m_flags;\n    }\n\n    /**\n     * \\brief Adds a set of shader stages\n     * \\param [in] stageMask Shader stages to add\n     */\n    void addStages(VkShaderStageFlags stageMask) {\n      m_stages |= uint8_t(stageMask);\n    }\n\n    /**\n     * \\brief Adds a push data block\n     *\n     * If a shared block is added and another shared push\n     * constant block already exists, they will be merged.\n     * \\param [in] block Push data block to add\n     */\n    void addPushData(const DxvkPushDataBlock& block) {\n      uint32_t index = DxvkPushDataBlock::computeIndex(block.getStageMask());\n\n      if (!block.isEmpty()) {\n        m_pushMask |= 1u << index;\n        m_pushData[index].merge(block);\n      }\n    }\n\n    /**\n     * \\brief Sets flags\n     * \\param [in] flags Flags to set\n     */\n    void setFlags(DxvkPipelineLayoutFlags flags) {\n      m_flags.set(flags);\n    }\n\n    /**\n     * \\brief Queries push data block mask\n     * \\returns Mask of active push data blocks\n     */\n    uint32_t getPushDataMask() const {\n      return m_pushMask;\n    }\n\n    /**\n     * \\brief Queries push data block info\n     *\n     * \\param [in] index Block index\n     * \\returns Push data block info\n     */\n    DxvkPushDataBlock getPushDataBlock(uint32_t index) const {\n      return m_pushData[index];\n    }\n\n    /**\n     * \\brief Sets a descriptor set layouts\n     *\n     * This always assigns all sets in one go. Sets may be \\c nullptr\n     * when creating pipeline layouts for pipeline libraries. It is\n     * valid to create pipeline layouts with no descriptor sets.\n     * \\param [in] setCount Number of sets to assign\n     * \\param [in] setLayouts Descriptor set layout objects\n     */\n    void setDescriptorSetLayouts(\n            uint32_t                  setCount,\n      const DxvkDescriptorSetLayout** setLayouts) {\n      m_setCount = uint8_t(setCount);\n\n      for (uint32_t i = 0; i < MaxSets; i++)\n        m_sets[i] = i < setCount ? setLayouts[i] : nullptr;\n    }\n\n    /**\n     * \\brief Queries shader stage mask\n     * \\returns All stages covered by this layout\n     */\n    VkShaderStageFlags getStageMask() const {\n      return m_stages;\n    }\n\n    /**\n     * \\brief Queries descriptor set count\n     * \\returns Number of descriptor sets\n     */\n    uint32_t getDescriptorSetCount() const {\n      return m_setCount;\n    }\n\n    /**\n     * \\brief Computes set mask\n     *\n     * Sets a bit for each included set that is not \\c nullptr.\n     * \\returns Descriptor set mask\n     */\n    uint32_t computeDescriptorSetMask() const {\n      uint32_t mask = 0u;\n\n      for (uint32_t i = 0u; i < MaxSets; i++)\n        mask |= (m_sets[i] ? 1u : 0u) << i;\n\n      return mask;\n    }\n\n    /**\n     * \\brief Queries descriptor set layout\n     *\n     * \\param [in] set Descriptor set index\n     * \\returns Descriptor set layout object\n     */\n    const DxvkDescriptorSetLayout* getDescriptorSetLayout(uint32_t setIndex) const {\n      return m_sets[setIndex];\n    }\n\n    /**\n     * \\brief Checks for equality\n     *\n     * \\param [in] other Pipeline layout key to compare to\n     * \\returns \\c true if both layout keys are equal\n     */\n    bool eq(const DxvkPipelineLayoutKey& other) const {\n      bool eq = m_type      == other.m_type\n             && m_flags     == other.m_flags\n             && m_stages    == other.m_stages\n             && m_pushMask  == other.m_pushMask\n             && m_setCount  == other.m_setCount;\n\n      for (auto i : bit::BitMask(uint32_t(m_pushMask)))\n        eq &= m_pushData[i].eq(other.m_pushData[i]);\n\n      for (uint32_t i = 0; i < m_setCount && eq; i++)\n        eq = m_sets[i] == other.m_sets[i];\n\n      return eq;\n    }\n\n    /**\n     * \\brief Computes hash\n     * \\returns Hash\n     */\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(uint16_t(m_type));\n      hash.add(m_flags.raw());\n      hash.add(m_stages);\n      hash.add(m_setCount);\n      hash.add(m_pushMask);\n\n      for (auto i : bit::BitMask(uint32_t(m_pushMask)))\n        hash.add(m_pushData[i].hash());\n\n      for (uint32_t i = 0; i < m_setCount; i++)\n        hash.add(reinterpret_cast<uintptr_t>(m_sets[i]));\n\n      return hash;\n    }\n\n  private:\n\n    DxvkPipelineLayoutType  m_type      = DxvkPipelineLayoutType::Independent;\n    DxvkPipelineLayoutFlags m_flags     = 0u;\n    uint8_t                 m_stages    = 0u;\n    uint8_t                 m_pushMask  = 0u;\n    uint8_t                 m_setCount  = 0u;\n\n    std::array<DxvkPushDataBlock, DxvkPushDataBlock::MaxBlockCount> m_pushData = { };\n\n    std::array<const DxvkDescriptorSetLayout*, MaxSets> m_sets = { };\n\n  };\n\n\n  /**\n   * \\brief Pipeline layout\n   *\n   * Manages Vulkan pipeline layout objects for use with pipeline\n   * libraries as well as full pipelines where appropriate.\n   */\n  class DxvkPipelineLayout {\n\n  public:\n\n    DxvkPipelineLayout(\n            DxvkDevice*                 device,\n      const DxvkPipelineLayoutKey&      key);\n\n    ~DxvkPipelineLayout();\n\n    /**\n     * \\brief Queries pipeline bind point\n     * \\returns Pipeline bind point\n     */\n    VkPipelineBindPoint getBindPoint() const {\n      return m_bindPoint;\n    }\n\n    /**\n     * \\brief Queries Vulkan pipeline layout\n     *\n     * Will be \\c VK_NULL_HANDLE when descriptor heaps are used.\n     * \\param [in] independent Whether to return a pipeline\n     *    layout that can be used with pipeline libraries.\n     * \\returns Pipeline layout handle\n     */\n    VkPipelineLayout getPipelineLayout() const {\n      return m_legacy.layout;\n    }\n\n    /**\n     * \\brief Checks whether the pipeline layout uses the sampler heap\n     *\n     * Affects the pipeline layout as well as resource binding.\n     * \\returns \\c true if the sampler heap is used\n     */\n    bool usesSamplerHeap() const {\n      return m_flags.test(DxvkPipelineLayoutFlag::UsesSamplerHeap);\n    }\n\n    /**\n     * \\brief Queries specific descriptor set layout\n     *\n     * Invalid when descriptor heaps are used.\n     * \\param [in] set Set index\n     * \\returns Set layout\n     */\n    const DxvkDescriptorSetLayout* getDescriptorSetLayout(uint32_t set) const {\n      return m_setLayouts[set];\n    }\n\n    /**\n     * \\brief Queries descriptor offset scale\n     *\n     * Returns the number of bits by which offsets must be\n     * shifted prior to passing them in as push data for\n     * heaps. With descriptor buffers, this will always be 0.\n     */\n    uint32_t getDescriptorOffsetShift() const {\n      return m_heap.offsetShift;\n    }\n\n    /**\n     * \\brief Queries total descriptor memory size\n     *\n     * Returns the memory required for all descriptor sets.\n     * Use this to determine whether to allocate a new\n     * descriptor range from the resource heap.\n     */\n    VkDeviceSize getDescriptorMemorySize() const {\n      return m_heap.setMemorySize;\n    }\n\n    /**\n     * \\brief Queries non-empty push data block mask\n     */\n    uint32_t getPushDataMask() const {\n      return m_pushData.blockMask;\n    }\n\n    /**\n     * \\brief Queries merged push data block\n     *\n     * This block includes all stages and all bytes.\n     */\n    DxvkPushDataBlock getPushData() const {\n      return m_pushData.mergedBlock;\n    }\n\n    /**\n     * \\brief Queries push data block\n     *\n     * \\param [in] index Block index\n     * \\returns Push data block\n     */\n    DxvkPushDataBlock getPushDataBlock(uint32_t index) const {\n      return m_pushData.blocks[index];\n    }\n\n    /**\n     * \\brief Queries number of binding mappings\n     *\n     * Used when creating pipelines with descriptor heaps.\n     * \\returns Binding mapping count\n     */\n    VkShaderDescriptorSetAndBindingMappingInfoEXT getMappingInfo() const {\n      VkShaderDescriptorSetAndBindingMappingInfoEXT result = { VK_STRUCTURE_TYPE_SHADER_DESCRIPTOR_SET_AND_BINDING_MAPPING_INFO_EXT };\n      result.mappingCount = m_mapping.mappings.size();\n      result.pMappings = m_mapping.mappings.data();\n      return result;\n    }\n\n  private:\n\n    DxvkDevice*             m_device;\n\n    DxvkPipelineLayoutFlags m_flags;\n    VkPipelineBindPoint     m_bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;\n\n    std::array<const DxvkDescriptorSetLayout*, DxvkPipelineLayoutKey::MaxSets> m_setLayouts = { };\n\n    struct {\n      VkPipelineLayout  layout = VK_NULL_HANDLE;\n    } m_legacy;\n\n    struct {\n      DxvkPushDataBlock mergedBlock = { };\n      uint32_t          blockMask   = 0u;\n      std::array<DxvkPushDataBlock, DxvkPushDataBlock::MaxBlockCount> blocks = { };\n    } m_pushData;\n\n    struct {\n      uint32_t          offsetShift   = 0u;\n      VkDeviceSize      setMemorySize = 0u;\n    } m_heap;\n\n    struct {\n      std::vector<VkDescriptorMappingSourcePushIndexEXT>  pushIndex;\n      std::vector<VkDescriptorSetAndBindingMappingEXT>    mappings;\n    } m_mapping;\n\n    void initMetadata(\n      const DxvkPipelineLayoutKey&      key);\n\n    void initPipelineLayout(\n      const DxvkPipelineLayoutKey&      key);\n\n    void initMappings(\n      const DxvkPipelineLayoutKey&      key);\n\n  };\n\n\n  /**\n   * \\brief Shader resource binding info\n   *\n   * Stores the set and binding index for a given binding that is\n   * used in a shader. Used to patch binding numbers as necessary.\n   */\n  class DxvkShaderBinding {\n\n  public:\n\n    DxvkShaderBinding() = default;\n\n    DxvkShaderBinding(\n            VkShaderStageFlags    stages,\n            uint32_t              set,\n            uint32_t              binding)\n    : m_stages  (uint8_t(stages)),\n      m_set     (uint8_t(set)),\n      m_binding (uint16_t(binding)) { }\n\n    /**\n     * \\brief Queries stage mask\n     * \\returns Stage mask\n     */\n    VkShaderStageFlags getStageMask() const {\n      return VkShaderStageFlags(m_stages);\n    }\n\n    /**\n     * \\brief Queries set index\n     * \\returns Set index\n     */\n    uint32_t getSet() const {\n      return m_set;\n    }\n\n    /**\n     * \\brief Queries binding index\n     * \\returns Binding index\n     */\n    uint32_t getBinding() const {\n      return m_binding;\n    }\n\n    /**\n     * \\brief Checks for equality\n     *\n     * \\param [in] other Other binding entry\n     * \\returns \\c true if all properties match\n     */\n    bool eq(const DxvkShaderBinding& other) const {\n      return m_stages   == other.m_stages\n          && m_set      == other.m_set\n          && m_binding  == other.m_binding;\n    }\n\n    /**\n     * \\brief Computes hash\n     * \\returns Hash\n     */\n    size_t hash() const {\n      return size_t(m_stages)\n          | (size_t(m_set) << 8)\n          | (size_t(m_binding) << 16);\n    }\n\n  private:\n\n    uint8_t   m_stages   = 0u;\n    uint8_t   m_set      = 0u;\n    uint16_t  m_binding  = 0u;\n\n  };\n\n\n  /**\n   * \\brief Binding map\n   *\n   * Used to assign correct descriptor bindings to shaders.\n   */\n  class DxvkShaderBindingMap {\n\n  public:\n\n    DxvkShaderBindingMap();\n\n    ~DxvkShaderBindingMap();\n\n    /**\n     * \\brief Adds entry\n     *\n     * \\param [in] srcBinding Shader binding\n     * \\param [in] dstBinding Actual descriptor set binding\n     */\n    void addBinding(DxvkShaderBinding srcBinding, DxvkShaderBinding dstBinding);\n\n    /**\n     * \\brief Adds push data block\n     *\n     * \\param [in] block Push constant block\n     * \\param [in] offset New offset\n     */\n    void addPushData(const DxvkPushDataBlock& block, uint32_t offset);\n\n    /**\n     * \\brief Looks up shader binding\n     *\n     * \\param [in] srcBinding Shader-defined binding\n     * \\returns Pointer to remapped binding, or \\c nullptr\n     */\n    const DxvkShaderBinding* mapBinding(DxvkShaderBinding srcBinding) const;\n\n    /**\n     * \\brief Looks up push constant data\n     *\n     * Finds a push data block containing the given\n     * offset and computes the remapped offset.\n     * \\param [in] stage Shader stage\n     * \\param [in] offset Push data offset to look up\n     * \\returns Remapped offset, or -1\n     */\n    uint32_t mapPushData(VkShaderStageFlags stage, uint32_t offset) const;\n\n  private:\n\n    std::unordered_map<DxvkShaderBinding, DxvkShaderBinding, DxvkHash, DxvkEq> m_bindings;\n\n    small_vector<std::pair<DxvkPushDataBlock, uint32_t>, DxvkPushDataBlock::MaxBlockCount> m_pushData;\n\n  };\n\n\n  /**\n   * \\brief Binding range\n   *\n   * Stores number of bindings in a list, as well as the pointer to\n   * the first binding. Set and descriptor numbers are discarded\n   * here and must be inferred as necessary.\n   */\n  struct DxvkPipelineBindingRange {\n    size_t                      bindingCount  = 0u;\n    const DxvkShaderDescriptor* bindings      = nullptr;\n  };\n\n\n  /**\n   * \\brief Pipeline layout builder\n   *\n   * Accumulates bindings and push constant ranges and provides\n   * functionality to process them into a structure that can be\n   * used to create compatible pipeline layouts and metadata.\n   */\n  class DxvkPipelineLayoutBuilder {\n\n  public:\n\n    DxvkPipelineLayoutBuilder();\n\n    /**\n     * \\brief Initializes builder\n     *\n     * \\param [in] stageMask Known shader stages. When building a pipeline layout\n     *    for full graphics pipelines and not just libraries, this \\e must include\n     *    the fragment shader stage even if no fragment shader is used.\n     */\n    DxvkPipelineLayoutBuilder(VkShaderStageFlags stageMask);\n\n    ~DxvkPipelineLayoutBuilder();\n\n    /**\n     * \\brief Queries shader stage mask\n     * \\returns Shader stage mask\n     */\n    VkShaderStageFlags getStageMask() const {\n      return m_stageMask;\n    }\n\n    /**\n     * \\brief Queries push data block mask\n     * \\returns Mask of active push data blocks\n     */\n    uint32_t getPushDataMask() const {\n      return m_pushMask;\n    }\n\n    /**\n     * \\brief Queries push data block info\n     *\n     * \\param [in] index Block index\n     * \\returns Push data block info\n     */\n    DxvkPushDataBlock getPushDataBlock(uint32_t index) const {\n      return m_pushData[index];\n    }\n\n    /**\n     * \\brief Queries descriptor bindings\n     * \\returns Descriptor bindings\n     */\n    DxvkPipelineBindingRange getBindings() const {\n      DxvkPipelineBindingRange range;\n      range.bindingCount = m_bindings.size();\n      range.bindings = m_bindings.data();\n      return range;\n    }\n\n    /**\n     * \\brief Queries number of sampler heap bindings\n     * \\returns Sampler heap binding count\n     */\n    uint32_t getSamplerHeapBindingCount() const {\n      return m_samplerHeaps.size();\n    }\n\n    /**\n     * \\brief Queries sampler heap binding info\n     *\n     * \\param [in] index Sampler heap binding index\n     * \\returns Set and binding for a given shader stage\n     */\n    DxvkShaderBinding getSamplerHeapBinding(uint32_t index) const {\n      return m_samplerHeaps[index];\n    }\n\n    /**\n     * \\brief Adds push data block\n     * \\param [in] range Push data block\n     */\n    void addPushData(\n            DxvkPushDataBlock         range);\n\n    /**\n     * \\brief Adds bindings\n     *\n     * Ensures that bindings are properly ordered.\n     * \\param [in] bindingCount Number of bindings\n     * \\param [in] bindings Binding infos with regular\n     *    shader-defined set and binding indices\n     */\n    void addBindings(\n            uint32_t                  bindingCount,\n      const DxvkShaderDescriptor*     bindings);\n\n    /**\n     * \\brief Adds sampler heap declaration\n     *\n     * Used so that the sampler binding can be remapped.\n     * \\param [in] binding Sampler heap binding info\n     */\n    void addSamplerHeap(\n      const DxvkShaderBinding&        binding);\n\n    /**\n     * \\brief Merges another layout\n     *\n     * Adds push constants and bindings from the given\n     * layout into the calling object.\n     * \\param [in] layout The layout to add to this one\n     */\n    void addLayout(\n      const DxvkPipelineLayoutBuilder& layout);\n\n  private:\n\n    VkShaderStageFlags      m_stageMask     = 0u;\n\n    uint32_t                                                        m_pushMask = 0u;\n    std::array<DxvkPushDataBlock, DxvkPushDataBlock::MaxBlockCount> m_pushData = { };\n\n    small_vector<DxvkShaderDescriptor, 32u> m_bindings;\n    small_vector<DxvkShaderBinding, 4u> m_samplerHeaps;\n\n  };\n\n\n  /**\n   * \\brief Global resource barrier\n   *\n   * Stores the way any resources will be\n   * accessed by this pipeline.\n   */\n  struct DxvkGlobalPipelineBarrier {\n    VkPipelineStageFlags  stages = 0u;\n    VkAccessFlags         access = 0u;\n  };\n\n\n  /**\n   * \\brief Pipeline layout metadata\n   *\n   * Stores all sorts of information on shader bindings used in\n   * a pipeline, including binding lists to iterate over for\n   * various different purposes.\n   */\n  class DxvkPipelineBindings {\n    constexpr static uint32_t MaxSets = DxvkPipelineLayoutKey::MaxSets;\n    constexpr static uint32_t MaxStages = 6u;\n\n    using BindingList = small_vector<DxvkShaderDescriptor, 32>;\n  public:\n\n    DxvkPipelineBindings(\n            DxvkDevice*                 device,\n            DxvkPipelineManager*        manager,\n      const DxvkPipelineLayoutBuilder&  builder);\n\n    ~DxvkPipelineBindings();\n\n    /**\n     * \\brief Queries available pipeline stages\n     * \\returns All stages with descriptors\n     */\n    VkShaderStageFlags getNonemptyStageMask() const {\n      return m_nonemptyStageMask;\n    }\n\n    /**\n     * \\brief Queries pipeline layout\n     * \\returns Pipeline layout\n     */\n    const DxvkPipelineLayout* getLayout(DxvkPipelineLayoutType type) const {\n      return m_layouts[uint32_t(type)].layout;\n    }\n\n    /**\n     * \\brief Compute dirty sets for the given descriptor state\n     *\n     * \\param [in] type Pipeline layout type\n     * \\param [in] state Descriptor state\n     * \\returns Mask of sets that need updating\n     */\n    uint32_t getDirtySetMask(\n            DxvkPipelineLayoutType  type,\n      const DxvkDescriptorState&    state) const {\n      const auto& layout = m_layouts[uint32_t(type)];\n\n      uint32_t result = 0u;\n\n      for (uint32_t set = 0u; set < layout.setStateMasks.size(); set++) {\n        if (state.testDirtyMask(layout.setStateMasks[set]))\n          result |= 1u << set;\n      }\n\n      return result;\n    }\n\n    /**\n     * \\brief Gets total number of descriptors in all sets\n     *\n     * Can be used to determine an upper bound of descriptor infos\n     * to allocate when exact information is not yet available.\n     * \\returns Total descriptor count in all sets\n     */\n    uint32_t getDescriptorCount() const {\n      return m_descriptorCount;\n    }\n\n    /**\n     * \\brief Queries all available bindings in a given set\n     *\n     * Primarily useful to update dirty descriptor sets.\n     * \\param [in] type Pipeline layout type\n     * \\param [in] set Set index\n     * \\returns List of all bindings in the set\n     */\n    DxvkPipelineBindingRange getAllDescriptorsInSet(DxvkPipelineLayoutType type, uint32_t set) const {\n      return makeBindingRange(m_layouts[uint32_t(type)].setDescriptors[set]);\n    }\n\n    /**\n     * \\brief Queries all shader resources in a given set\n     *\n     * Includes all resources that are bound via an image or buffer view,\n     * even unformatted buffer views (i.e. storage buffers). Does not\n     * include pure samplers or buffers that are only bound via a buffer\n     * range or address, i.e. uniform buffers.\n     * \\param [in] type Pipeline layout type\n     * \\param [in] set Set index\n     * \\returns List of all resource bindings in the set\n     */\n    DxvkPipelineBindingRange getResourcesInSet(DxvkPipelineLayoutType type, uint32_t set) const {\n      return makeBindingRange(m_layouts[uint32_t(type)].setResources[set]);\n    }\n\n    /**\n     * \\brief Queries all uniform buffers in a given set\n     *\n     * Includes all uniform buffer resources, i.e. resources that are only\n     * bound via a buffer range and do not use views, and use the uniform\n     * or storage buffer descriptor type.\n     * \\param [in] type Pipeline layout type\n     * \\param [in] set Set index\n     * \\returns List of all uniform buffer bindings in the set\n     */\n    DxvkPipelineBindingRange getUniformBuffersInSet(DxvkPipelineLayoutType type, uint32_t set) const {\n      return makeBindingRange(m_layouts[uint32_t(type)].setUniformBuffers[set]);\n    }\n\n    /**\n     * \\brief Queries all sampler bindings\n     *\n     * This includes only pure samplers, not\n     * combined image and sampler descriptors.\n     * \\param [in] type Pipeline layout type\n     * \\returns List of all sampler bindings in the set\n     */\n    DxvkPipelineBindingRange getSamplers(DxvkPipelineLayoutType type) const {\n      return makeBindingRange(m_layouts[uint32_t(type)].samplers);\n    }\n\n    /**\n     * \\brief Queries all virtual address bindings\n     *\n     * \\param [in] type Pipeline layout type\n     * \\param [in] set Set index\n     * \\returns List of all non-descriptor bindings.\n     */\n    DxvkPipelineBindingRange getVaBindings(DxvkPipelineLayoutType type) const {\n      return makeBindingRange(m_layouts[uint32_t(type)].vaBindings);\n    }\n\n    /**\n     * \\brief Queries all read-only resources for a given stage\n     *\n     * Subset of \\c getResourcesInSet that only includes bindings that are\n     * read and not written by the shader. Useful for hazard tracking.\n     * \\returns List of all read-only resources in the set\n     */\n    DxvkPipelineBindingRange getReadOnlyResourcesForStage(VkShaderStageFlagBits stage) const {\n      return makeBindingRange(m_readOnlyResources[bit::bsf(uint32_t(stage))]);\n    }\n\n    /**\n     * \\brief Queries all writable resources in the layout\n     *\n     * Not tied to any particular set, this includes resources that\n     * are written by the shader. Useful for hazard tracking.\n     * \\returns List of all written resources in the layout\n     */\n    DxvkPipelineBindingRange getReadWriteResources() const {\n      return makeBindingRange(m_readWriteResources);\n    }\n\n    /**\n     * \\brief Queries shader stages with hazardous descriptors\n     *\n     * If non-zero, back-to-back draws or dispatches using the same\n     * set of resources must be synchronized, otherwise it is safe\n     * to only check for hazards when bound resources have changed.\n     * \\returns Stages that have hazardous descriptors\n     */\n    VkShaderStageFlags getHazardousStageMask() const {\n      return m_hazardousStageMask;\n    }\n\n    /**\n     * \\brief Queries global resource barrier\n     *\n     * Can be used to determine whether the pipeline\n     * reads or writes any resources, and which shader\n     * stages it uses.\n     * \\returns Global barrier\n     */\n    DxvkGlobalPipelineBarrier getGlobalBarrier() const {\n      return m_barrier;\n    }\n\n    /**\n     * \\brief Queries binding map\n     *\n     * The binding map is primarily useful for shader patching.\n     * \\param [in] type Pipeline layout type\n     * \\returns Pointer to binding map\n     */\n    const DxvkShaderBindingMap* getBindingMap(DxvkPipelineLayoutType type) const {\n      return &m_layouts[uint32_t(type)].bindingMap;\n    }\n\n  private:\n\n    struct PerLayoutInfo {\n      std::array<BindingList, MaxSets>  setDescriptors       = { };\n      std::array<BindingList, MaxSets>  setResources         = { };\n      std::array<BindingList, MaxSets>  setUniformBuffers    = { };\n\n      std::array<uint32_t, MaxSets>     setStateMasks = { };\n\n      BindingList                       samplers = { };\n      BindingList                       vaBindings = { };\n      DxvkShaderBindingMap              bindingMap = { };\n\n      const DxvkPipelineLayout*         layout = nullptr;\n    };\n\n    struct SetInfos {\n      uint32_t mask   = { };\n      uint32_t count  = { };\n      std::array<uint8_t, MaxSets> map = { };\n    };\n\n    VkShaderStageFlags        m_nonemptyStageMask = 0u;\n    VkShaderStageFlags        m_hazardousStageMask = 0u;\n\n    DxvkGlobalPipelineBarrier m_barrier = { };\n    uint32_t                  m_descriptorCount = 0u;\n\n    std::array<PerLayoutInfo, 2u> m_layouts = { };\n\n    std::array<BindingList, MaxStages>  m_readOnlyResources = { };\n    BindingList                         m_readWriteResources = { };\n\n    void buildPipelineLayout(\n            DxvkPipelineLayoutType      type,\n            DxvkDevice*                 device,\n      const DxvkPipelineLayoutBuilder&  builder,\n            DxvkPipelineManager*        manager);\n\n    small_vector<DxvkPushDataBlock, DxvkPushDataBlock::MaxBlockCount>\n    buildPushDataBlocks(\n            DxvkPipelineLayoutType      type,\n            DxvkDevice*                 device,\n      const SetInfos&                   setInfos,\n      const DxvkPipelineLayoutBuilder&  builder,\n            DxvkPipelineManager*        manager);\n\n    small_vector<const DxvkDescriptorSetLayout*, MaxSets>\n    buildDescriptorSetLayouts(\n            DxvkPipelineLayoutType      type,\n            DxvkPipelineLayoutFlags     flags,\n      const SetInfos&                   setInfos,\n      const DxvkPipelineLayoutBuilder&  builder,\n            DxvkPipelineManager*        manager);\n\n    void buildMetadata(\n      const DxvkPipelineLayoutBuilder&  builder);\n\n    DxvkPipelineLayoutFlags getPipelineLayoutFlags(\n            DxvkPipelineLayoutType      type,\n      const DxvkPipelineLayoutBuilder&  builder);\n\n    static uint32_t computeStateMask(\n      const DxvkShaderDescriptor&     binding);\n\n    static uint32_t computeSetForBinding(\n            DxvkPipelineLayoutType    type,\n      const DxvkShaderDescriptor&     binding);\n\n    static SetInfos computeSetMaskAndCount(\n            DxvkPipelineLayoutType    type,\n            VkShaderStageFlags        stageMask,\n            DxvkPipelineBindingRange  bindings);\n\n    static DxvkPipelineBindingRange makeBindingRange(const BindingList& list) {\n      DxvkPipelineBindingRange result;\n      result.bindingCount = list.size();\n      result.bindings = list.data();\n      return result;\n    }\n\n    static void appendDescriptors(\n            BindingList&          list,\n      const DxvkShaderDescriptor& binding,\n      const DxvkShaderBinding&    mapping) {\n      for (uint32_t i = 0; i < binding.getDescriptorCount(); i++)\n        list.push_back(binding.remapBinding(mapping.getSet(), mapping.getBinding()).getArrayElement(i));\n    }\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_pipemanager.cpp",
    "content": "#include <optional>\n\n#include \"dxvk_device.h\"\n#include \"dxvk_pipemanager.h\"\n\nnamespace dxvk {\n  \n  DxvkPipelineWorkers::DxvkPipelineWorkers(\n          DxvkDevice*                     device)\n  : m_device(device) {\n\n  }\n\n\n  DxvkPipelineWorkers::~DxvkPipelineWorkers() {\n    this->stopWorkers();\n  }\n\n\n  void DxvkPipelineWorkers::compilePipelineLibrary(\n          DxvkShaderPipelineLibrary*      library,\n          DxvkPipelinePriority            priority) {\n    std::unique_lock lock(m_lock);\n    this->startWorkers();\n\n    m_tasksTotal += 1;\n\n    m_buckets[uint32_t(priority)].queue.emplace(library);\n    notifyWorkers(priority);\n  }\n\n\n  void DxvkPipelineWorkers::compileGraphicsPipeline(\n          DxvkGraphicsPipeline*           pipeline,\n    const DxvkGraphicsPipelineStateInfo&  state,\n          DxvkPipelinePriority            priority) {\n    std::unique_lock lock(m_lock);\n    this->startWorkers();\n\n    pipeline->acquirePipeline();\n    m_tasksTotal += 1;\n\n    m_buckets[uint32_t(priority)].queue.emplace(pipeline, state);\n    notifyWorkers(priority);\n  }\n\n\n  void DxvkPipelineWorkers::stopWorkers() {\n    { std::unique_lock lock(m_lock);\n\n      if (!m_workersRunning)\n        return;\n\n      m_workersRunning = false;\n\n      for (uint32_t i = 0; i < m_buckets.size(); i++)\n        m_buckets[i].cond.notify_all();\n    }\n\n    for (auto& worker : m_workers)\n      worker.join();\n\n    m_workers.clear();\n  }\n\n\n  void DxvkPipelineWorkers::notifyWorkers(DxvkPipelinePriority priority) {\n    uint32_t index = uint32_t(priority);\n\n    // If any workers are idle in a suitable set, notify the corresponding\n    // condition variable. If all workers are busy anyway, we know that the\n    // job is going to be picked up at some point anyway.\n    for (uint32_t i = index; i < m_buckets.size(); i++) {\n      if (m_buckets[i].idleWorkers) {\n        m_buckets[i].cond.notify_one();\n        break;\n      }\n    }\n  }\n\n\n  void DxvkPipelineWorkers::startWorkers() {\n    if (!std::exchange(m_workersRunning, true)) {\n      // Use all available cores by default\n      uint32_t workerCount = dxvk::thread::hardware_concurrency();\n\n      if (workerCount <  1) workerCount =  1;\n      if (workerCount > 64) workerCount = 64;\n\n      // Reduce worker count on 32-bit to save adderss space\n      if (env::is32BitHostPlatform())\n        workerCount = std::min(workerCount, 16u);\n\n      if (m_device->config().numCompilerThreads > 0)\n        workerCount = m_device->config().numCompilerThreads;\n\n      // Number of workers that can process pipeline pipelines with normal\n      // priority. Any other workers can only build high-priority pipelines.\n      uint32_t npWorkerCount = std::max(((workerCount - 1) * 5) / 7, 1u);\n      uint32_t lpWorkerCount = std::max(((workerCount - 1) * 2) / 7, 1u);\n\n      m_workers.reserve(workerCount);\n\n      for (size_t i = 0; i < workerCount; i++) {\n        DxvkPipelinePriority priority = DxvkPipelinePriority::Normal;\n\n        if (i >= npWorkerCount)\n          priority = DxvkPipelinePriority::High;\n        else if (i < lpWorkerCount)\n          priority = DxvkPipelinePriority::Low;\n\n        auto& worker = m_workers.emplace_back([this, priority] {\n          runWorker(priority);\n        });\n        \n        worker.set_priority(ThreadPriority::Lowest);\n      }\n\n      Logger::info(str::format(\"DXVK: Using \", workerCount, \" compiler threads\"));\n    }\n  }\n\n\n  void DxvkPipelineWorkers::runWorker(DxvkPipelinePriority maxPriority) {\n    static const std::array<char, 3> suffixes = { 'h', 'n', 'l' };\n\n    const uint32_t maxPriorityIndex = uint32_t(maxPriority);\n    env::setThreadName(str::format(\"dxvk-shader-\", suffixes.at(maxPriorityIndex)));\n\n    while (true) {\n      PipelineEntry entry;\n\n      { std::unique_lock lock(m_lock);\n        auto& bucket = m_buckets[maxPriorityIndex];\n\n        bucket.idleWorkers += 1;\n        bucket.cond.wait(lock, [this, maxPriorityIndex, &entry] {\n          // Attempt to fetch a work item from the\n          // highest-priority queue that is not empty\n          for (uint32_t i = 0; i <= maxPriorityIndex; i++) {\n            if (!m_buckets[i].queue.empty()) {\n              entry = m_buckets[i].queue.front();\n              m_buckets[i].queue.pop();\n              return true;\n            }\n          }\n\n          return !m_workersRunning;\n        });\n\n        bucket.idleWorkers -= 1;\n\n        // Skip pending work, exiting early is\n        // more important in this case.\n        if (!m_workersRunning)\n          break;\n      }\n\n      if (entry.pipelineLibrary) {\n        entry.pipelineLibrary->compilePipeline();\n      } else if (entry.graphicsPipeline) {\n        entry.graphicsPipeline->compilePipeline(entry.graphicsState);\n        entry.graphicsPipeline->releasePipeline();\n      }\n\n      m_tasksCompleted += 1;\n    }\n  }\n\n\n  DxvkPipelineManager::DxvkPipelineManager(\n          DxvkDevice*         device)\n  : m_device    (device),\n    m_workers   (device) {\n    Logger::info(str::format(\"Graphics pipeline libraries \",\n      (m_device->canUseGraphicsPipelineLibrary() ? \"supported\" : \"not supported\")));\n\n    createNullFsPipelineLibrary()->compilePipeline();\n  }\n  \n  \n  DxvkPipelineManager::~DxvkPipelineManager() {\n    \n  }\n  \n  \n  DxvkComputePipeline* DxvkPipelineManager::createComputePipeline(\n    const DxvkComputePipelineShaders& shaders) {\n    if (shaders.cs == nullptr)\n      return nullptr;\n    \n    std::lock_guard<dxvk::mutex> lock(m_pipelineMutex);\n    \n    auto pair = m_computePipelines.find(shaders);\n    if (pair != m_computePipelines.end())\n      return &pair->second;\n\n    DxvkShaderPipelineLibraryKey key;\n    key.addShader(shaders.cs);\n\n    auto library = findPipelineLibraryLocked(key);\n\n    auto iter = m_computePipelines.emplace(\n      std::piecewise_construct,\n      std::tuple(shaders),\n      std::tuple(m_device, this, shaders, library));\n    return &iter.first->second;\n  }\n  \n  \n  DxvkGraphicsPipeline* DxvkPipelineManager::createGraphicsPipeline(\n    const DxvkGraphicsPipelineShaders& shaders) {\n    if (shaders.vs == nullptr)\n      return nullptr;\n    \n    std::lock_guard<dxvk::mutex> lock(m_pipelineMutex);\n\n    auto pair = m_graphicsPipelines.find(shaders);\n    if (pair != m_graphicsPipelines.end())\n      return &pair->second;\n\n    DxvkShaderPipelineLibraryKey vsKey;\n    vsKey.addShader(shaders.vs);\n\n    if (shaders.tcs != nullptr) vsKey.addShader(shaders.tcs);\n    if (shaders.tes != nullptr) vsKey.addShader(shaders.tes);\n    if (shaders.gs  != nullptr) vsKey.addShader(shaders.gs);\n\n    DxvkShaderPipelineLibrary* vsLibrary = findPipelineLibraryLocked(vsKey);\n\n    if (!vsLibrary) {\n      // If multiple shader stages are participating, create a\n      // pipeline library so that it can potentially be reused.\n      // Don't dispatch the pipeline library to a worker thread\n      // since it should be compiled on demand anyway.\n      vsLibrary = createPipelineLibraryLocked(vsKey);\n    }\n\n    DxvkShaderPipelineLibraryKey fsKey;\n\n    if (shaders.fs != nullptr)\n      fsKey.addShader(shaders.fs);\n\n    DxvkShaderPipelineLibrary* fsLibrary = findPipelineLibraryLocked(fsKey);\n\n    auto iter = m_graphicsPipelines.emplace(\n      std::piecewise_construct,\n      std::tuple(shaders),\n      std::tuple(m_device, this, shaders, vsLibrary, fsLibrary));\n    return &iter.first->second;\n  }\n\n  \n  DxvkShaderPipelineLibrary* DxvkPipelineManager::createShaderPipelineLibrary(\n    const DxvkShaderPipelineLibraryKey& key) {\n    std::lock_guard<dxvk::mutex> lock(m_pipelineMutex);\n    return createPipelineLibraryLocked(key);\n  }\n\n\n  DxvkGraphicsPipelineVertexInputLibrary* DxvkPipelineManager::createVertexInputLibrary(\n    const DxvkGraphicsPipelineVertexInputState& state) {\n    std::lock_guard<dxvk::mutex> lock(m_pipelineMutex);\n\n    auto pair = m_vertexInputLibraries.find(state);\n    if (pair != m_vertexInputLibraries.end())\n      return &pair->second;\n\n    auto iter = m_vertexInputLibraries.emplace(\n      std::piecewise_construct,\n      std::tuple(state),\n      std::tuple(m_device, state));\n    return &iter.first->second;\n  }\n\n\n  DxvkGraphicsPipelineFragmentOutputLibrary* DxvkPipelineManager::createFragmentOutputLibrary(\n    const DxvkGraphicsPipelineFragmentOutputState& state) {\n    std::lock_guard<dxvk::mutex> lock(m_pipelineMutex);\n\n    auto pair = m_fragmentOutputLibraries.find(state);\n    if (pair != m_fragmentOutputLibraries.end())\n      return &pair->second;\n\n    auto iter = m_fragmentOutputLibraries.emplace(\n      std::piecewise_construct,\n      std::tuple(state),\n      std::tuple(m_device, state));\n    return &iter.first->second;\n  }\n  \n  \n  void DxvkPipelineManager::registerShader(\n    const Rc<DxvkShader>&         shader) {\n    DxvkShaderPipelineLibraryKey key;\n    key.addShader(shader);\n\n    auto library = createShaderPipelineLibrary(key);\n    m_workers.compilePipelineLibrary(library, DxvkPipelinePriority::Normal);\n  }\n\n\n  void DxvkPipelineManager::requestCompileShader(\n    const Rc<DxvkShader>&         shader) {\n    // Notify immediately so that this only gets called\n    // once, even if compilation does ot start immediately\n    if (!shader->notifyCompile())\n      return;\n\n    // Dispatch high-priority compile job\n    DxvkShaderPipelineLibraryKey key;\n    key.addShader(shader);\n\n    auto library = findPipelineLibrary(key);\n\n    if (library)\n      m_workers.compilePipelineLibrary(library, DxvkPipelinePriority::High);\n  }\n\n\n  DxvkPipelineCount DxvkPipelineManager::getPipelineCount() const {\n    DxvkPipelineCount result;\n    result.numGraphicsPipelines = m_stats.numGraphicsPipelines.load();\n    result.numGraphicsLibraries = m_stats.numGraphicsLibraries.load();\n    result.numComputePipelines  = m_stats.numComputePipelines.load();\n    return result;\n  }\n\n\n  void DxvkPipelineManager::stopWorkerThreads() {\n    m_workers.stopWorkers();\n  }\n\n\n  const DxvkDescriptorSetLayout* DxvkPipelineManager::createDescriptorSetLayout(\n    const DxvkDescriptorSetLayoutKey& key) {\n    std::lock_guard<dxvk::mutex> lock(m_layoutMutex);\n\n    auto pair = m_descriptorSetLayouts.find(key);\n    if (pair != m_descriptorSetLayouts.end())\n      return &pair->second;\n\n    auto iter = m_descriptorSetLayouts.emplace(\n      std::piecewise_construct,\n      std::tuple(key),\n      std::tuple(m_device, key));\n    return &iter.first->second;\n  }\n\n\n  const DxvkPipelineLayout* DxvkPipelineManager::createPipelineLayout(\n    const DxvkPipelineLayoutKey& key) {\n    std::lock_guard<dxvk::mutex> lock(m_layoutMutex);\n\n    auto pair = m_pipelineLayouts.find(key);\n    if (pair != m_pipelineLayouts.end())\n      return &pair->second;\n\n    auto iter = m_pipelineLayouts.emplace(\n      std::piecewise_construct,\n      std::tuple(key),\n      std::tuple(m_device, key));\n    return &iter.first->second;\n  }\n\n\n  DxvkShaderPipelineLibrary* DxvkPipelineManager::createPipelineLibraryLocked(\n    const DxvkShaderPipelineLibraryKey& key) {\n    auto iter = m_shaderLibraries.emplace(\n      std::piecewise_construct,\n      std::tuple(key),\n      std::tuple(m_device, this, key));\n    return &iter.first->second;\n  }\n\n\n  DxvkShaderPipelineLibrary* DxvkPipelineManager::createNullFsPipelineLibrary() {\n    std::lock_guard<dxvk::mutex> lock(m_pipelineMutex);\n    DxvkShaderPipelineLibraryKey key;\n\n    auto iter = m_shaderLibraries.emplace(\n      std::piecewise_construct,\n      std::tuple(),\n      std::tuple(m_device, this, key));\n    return &iter.first->second;\n  }\n\n\n  DxvkShaderPipelineLibrary* DxvkPipelineManager::findPipelineLibrary(\n    const DxvkShaderPipelineLibraryKey& key) {\n    std::lock_guard<dxvk::mutex> lock(m_pipelineMutex);\n    return findPipelineLibraryLocked(key);\n  }\n\n\n  DxvkShaderPipelineLibrary* DxvkPipelineManager::findPipelineLibraryLocked(\n    const DxvkShaderPipelineLibraryKey& key) {\n    auto pair = m_shaderLibraries.find(key);\n\n    if (pair != m_shaderLibraries.end())\n      return &pair->second;\n\n    return createPipelineLibraryLocked(key);\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_pipemanager.h",
    "content": "\n#pragma once\n\n#include <mutex>\n#include <queue>\n#include <unordered_map>\n\n#include \"dxvk_compute.h\"\n#include \"dxvk_graphics.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n\n  /**\n   * \\brief Pipeline count\n   * \n   * Stores number of graphics and\n   * compute pipelines, individually.\n   */\n  struct DxvkPipelineCount {\n    uint32_t numGraphicsPipelines;\n    uint32_t numGraphicsLibraries;\n    uint32_t numComputePipelines;\n  };\n\n  /**\n   * \\brief Pipeline stats\n   */\n  struct DxvkPipelineStats {\n    std::atomic<uint32_t> numGraphicsPipelines  = { 0u };\n    std::atomic<uint32_t> numGraphicsLibraries  = { 0u };\n    std::atomic<uint32_t> numComputePipelines   = { 0u };\n  };\n\n  struct DxvkPipelineWorkerStats {\n    uint64_t tasksCompleted;\n    uint64_t tasksTotal;\n  };\n\n  /**\n   * \\brief Pipeline priority\n   */\n  enum class DxvkPipelinePriority : uint32_t {\n    High    = 0,\n    Normal  = 1,\n    Low     = 2,\n  };\n\n  /**\n   * \\brief Pipeline manager worker threads\n   *\n   * Spawns worker threads to compile shader pipeline\n   * libraries and optimized pipelines asynchronously.\n   */\n  class DxvkPipelineWorkers {\n\n  public:\n\n    DxvkPipelineWorkers(\n            DxvkDevice*                     device);\n\n    ~DxvkPipelineWorkers();\n\n    /**\n     * \\brief Queries worker statistics\n     *\n     * The returned result may be immediately out of date.\n     * \\returns Worker statistics\n     */\n    DxvkPipelineWorkerStats getStats() const {\n      DxvkPipelineWorkerStats result;\n      result.tasksCompleted = m_tasksCompleted.load(std::memory_order_acquire);\n      result.tasksTotal = m_tasksTotal.load(std::memory_order_relaxed);\n      return result;\n    }\n\n    /**\n     * \\brief Compiles a pipeline library\n     *\n     * Asynchronously compiles a basic variant of\n     * the pipeline with default compile arguments.\n     * Note that pipeline libraries are high priority.\n     * \\param [in] library The pipeline library\n     * \\param [in] priority Pipeline priority\n     */\n    void compilePipelineLibrary(\n            DxvkShaderPipelineLibrary*      library,\n            DxvkPipelinePriority            priority);\n\n    /**\n     * \\brief Compiles an optimized graphics pipeline\n     *\n     * \\param [in] pipeline Compute pipeline\n     * \\param [in] state Pipeline state\n     */\n    void compileGraphicsPipeline(\n            DxvkGraphicsPipeline*           pipeline,\n      const DxvkGraphicsPipelineStateInfo&  state,\n            DxvkPipelinePriority            priority);\n\n    /**\n     * \\brief Stops all worker threads\n     *\n     * Stops threads and waits for their current work\n     * to complete. Queued work will be discarded.\n     */\n    void stopWorkers();\n\n  private:\n\n    struct PipelineEntry {\n      PipelineEntry()\n      : pipelineLibrary(nullptr), graphicsPipeline(nullptr) { }\n\n      PipelineEntry(DxvkShaderPipelineLibrary* l)\n      : pipelineLibrary(l), graphicsPipeline(nullptr) { }\n\n      PipelineEntry(DxvkGraphicsPipeline* p, const DxvkGraphicsPipelineStateInfo& s)\n      : pipelineLibrary(nullptr), graphicsPipeline(p), graphicsState(s) { }\n\n      DxvkShaderPipelineLibrary*    pipelineLibrary;\n      DxvkGraphicsPipeline*         graphicsPipeline;\n      DxvkGraphicsPipelineStateInfo graphicsState;\n    };\n\n    struct PipelineBucket {\n      dxvk::condition_variable  cond;\n      std::queue<PipelineEntry> queue;\n      uint32_t                  idleWorkers = 0;\n    };\n\n    DxvkDevice*                       m_device;\n\n    std::atomic<uint64_t>             m_tasksTotal     = { 0ull };\n    std::atomic<uint64_t>             m_tasksCompleted = { 0ull };\n\n    dxvk::mutex                       m_lock;\n    std::array<PipelineBucket, 3>     m_buckets;\n\n    bool                              m_workersRunning = false;\n    std::vector<dxvk::thread>         m_workers;\n\n    void notifyWorkers(DxvkPipelinePriority priority);\n\n    void startWorkers();\n\n    void runWorker(DxvkPipelinePriority maxPriority);\n\n  };\n\n  \n  /**\n   * \\brief Pipeline manager\n   * \n   * Creates and stores graphics pipelines and compute\n   * pipelines for each combination of shaders that is\n   * used within the application. This is necessary\n   * because DXVK does not expose the concept of shader\n   * pipeline objects to the client API.\n   */\n  class DxvkPipelineManager {\n    friend class DxvkComputePipeline;\n    friend class DxvkGraphicsPipeline;\n    friend class DxvkShaderPipelineLibrary;\n  public:\n    \n    DxvkPipelineManager(\n            DxvkDevice*         device);\n    \n    ~DxvkPipelineManager();\n    \n    /**\n     * \\brief Retrieves a compute pipeline object\n     * \n     * If a pipeline for the given shader stage object\n     * already exists, it will be returned. Otherwise,\n     * a new pipeline will be created.\n     * \\param [in] shaders Shaders for the pipeline\n     * \\returns Compute pipeline object\n     */\n    DxvkComputePipeline* createComputePipeline(\n      const DxvkComputePipelineShaders& shaders);\n    \n    /**\n     * \\brief Retrieves a graphics pipeline object\n     * \n     * If a pipeline for the given shader stage objects\n     * already exists, it will be returned. Otherwise,\n     * a new pipeline will be created.\n     * \\param [in] shaders Shaders for the pipeline\n     * \\returns Graphics pipeline object\n     */\n    DxvkGraphicsPipeline* createGraphicsPipeline(\n      const DxvkGraphicsPipelineShaders& shaders);\n\n    /**\n     * \\brief Creates a pipeline library with a given set of shaders\n     *\n     * If a pipeline library already exists, it will be returned.\n     * Otherwise, a new pipeline library will be created.\n     * \\param [in] key Shader set\n     */\n    DxvkShaderPipelineLibrary* createShaderPipelineLibrary(\n      const DxvkShaderPipelineLibraryKey& key);\n\n    /**\n     * \\brief Retrieves a vertex input pipeline library\n     *\n     * \\param [in] state Vertex input state\n     * \\returns Pipeline library object\n     */\n    DxvkGraphicsPipelineVertexInputLibrary* createVertexInputLibrary(\n      const DxvkGraphicsPipelineVertexInputState& state);\n\n    /**\n     * \\brief Retrieves a fragment output pipeline library\n     *\n     * \\param [in] state Fragment output state\n     * \\returns Pipeline library object\n     */\n    DxvkGraphicsPipelineFragmentOutputLibrary* createFragmentOutputLibrary(\n      const DxvkGraphicsPipelineFragmentOutputState& state);\n\n    /**\n     * \\brief Creates a descriptor set layout\n     *\n     * \\param [in] key Descriptor set layout key\n     * \\returns Descriptor set layout object\n     */\n    const DxvkDescriptorSetLayout* createDescriptorSetLayout(\n      const DxvkDescriptorSetLayoutKey& key);\n\n    /**\n     * \\brief Creates a pipeline layout\n     *\n     * \\param [in] key Pipeline layout key\n     * \\returns Descriptor set layout object\n     */\n    const DxvkPipelineLayout* createPipelineLayout(\n      const DxvkPipelineLayoutKey& key);\n\n    /**\n     * \\brief Registers a shader\n     * \n     * Starts compiling pipelines asynchronously\n     * in case the state cache contains state\n     * vectors for this shader.\n     * \\param [in] shader Newly compiled shader\n     */\n    void registerShader(\n      const Rc<DxvkShader>&         shader);\n\n    /**\n     * \\brief Prioritizes compilation of a given shader\n     *\n     * Adds the pipeline library for the given shader\n     * to the high-priority queue of the background\n     * workers to make sure it gets compiled quickly.\n     * \\param [in] shader Newly compiled shader\n     */\n    void requestCompileShader(\n      const Rc<DxvkShader>&         shader);\n\n    /**\n     * \\brief Retrieves total pipeline count\n     * \\returns Number of compute/graphics pipelines\n     */\n    DxvkPipelineCount getPipelineCount() const;\n\n    /**\n     * \\brief Checks whether async compiler is busy\n     * \\returns \\c true if shaders are being compiled\n     */\n    DxvkPipelineWorkerStats getWorkerStats() const {\n      return m_workers.getStats();\n    }\n\n    /**\n     * \\brief Stops async compiler threads\n     */\n    void stopWorkerThreads();\n    \n  private:\n    \n    DxvkDevice*               m_device;\n    DxvkPipelineWorkers       m_workers;\n    DxvkPipelineStats         m_stats;\n    \n    dxvk::mutex m_layoutMutex;\n    \n    std::unordered_map<\n      DxvkDescriptorSetLayoutKey,\n      DxvkDescriptorSetLayout,\n      DxvkHash, DxvkEq> m_descriptorSetLayouts;\n\n    std::unordered_map<\n      DxvkPipelineLayoutKey,\n      DxvkPipelineLayout,\n      DxvkHash, DxvkEq> m_pipelineLayouts;\n\n    dxvk::mutex m_pipelineMutex;\n\n    std::unordered_map<\n      DxvkGraphicsPipelineVertexInputState,\n      DxvkGraphicsPipelineVertexInputLibrary,\n      DxvkHash, DxvkEq> m_vertexInputLibraries;\n\n    std::unordered_map<\n      DxvkGraphicsPipelineFragmentOutputState,\n      DxvkGraphicsPipelineFragmentOutputLibrary,\n      DxvkHash, DxvkEq> m_fragmentOutputLibraries;\n\n    std::unordered_map<\n      DxvkShaderPipelineLibraryKey,\n      DxvkShaderPipelineLibrary,\n      DxvkHash, DxvkEq> m_shaderLibraries;\n\n    std::unordered_map<\n      DxvkComputePipelineShaders,\n      DxvkComputePipeline,\n      DxvkHash, DxvkEq> m_computePipelines;\n\n    std::unordered_map<\n      DxvkGraphicsPipelineShaders,\n      DxvkGraphicsPipeline,\n      DxvkHash, DxvkEq> m_graphicsPipelines;\n\n    DxvkShaderPipelineLibrary* createPipelineLibraryLocked(\n      const DxvkShaderPipelineLibraryKey& key);\n\n    DxvkShaderPipelineLibrary* createNullFsPipelineLibrary();\n\n    DxvkShaderPipelineLibrary* findPipelineLibrary(\n      const DxvkShaderPipelineLibraryKey& key);\n\n    DxvkShaderPipelineLibrary* findPipelineLibraryLocked(\n      const DxvkShaderPipelineLibraryKey& key);\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_platform_exts.cpp",
    "content": "#include \"dxvk_platform_exts.h\"\n#include \"../wsi/wsi_platform.h\"\n\nnamespace dxvk {\n\n  DxvkPlatformExts DxvkPlatformExts::s_instance;\n\n  std::string_view DxvkPlatformExts::getName() {\n    return \"Platform WSI\";\n  }\n\n\n  DxvkExtensionList DxvkPlatformExts::getInstanceExtensions() {\n    std::vector<const char *> extensionNames = wsi::getInstanceExtensions();\n\n    DxvkExtensionList names;\n    for (const char* name : extensionNames)\n      names.push_back(vk::makeExtension(name));\n\n    return names;\n  }\n\n\n  DxvkExtensionList DxvkPlatformExts::getDeviceExtensions(\n          uint32_t      adapterId) {\n    return DxvkExtensionList();\n  }\n\n\n  void DxvkPlatformExts::initInstanceExtensions() {\n\n  }\n\n\n  void DxvkPlatformExts::initDeviceExtensions(\n    const DxvkInstance* instance) {\n\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_platform_exts.h",
    "content": "#pragma once\n\n#include \"dxvk_extension_provider.h\"\n\nnamespace dxvk {\n\n  class DxvkPlatformExts : public DxvkExtensionProvider {\n\n  public:\n\n    std::string_view getName();\n\n    DxvkExtensionList getInstanceExtensions();\n\n    DxvkExtensionList getDeviceExtensions(\n            uint32_t      adapterId);\n    \n    void initInstanceExtensions();\n\n    void initDeviceExtensions(\n      const DxvkInstance* instance);\n\n    static DxvkPlatformExts s_instance;\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_presenter.cpp",
    "content": "#include <algorithm>\n\n#include \"dxvk_device.h\"\n#include \"dxvk_presenter.h\"\n\n#include \"../wsi/wsi_window.h\"\n\nnamespace dxvk {\n\n  const std::array<std::pair<VkColorSpaceKHR, VkColorSpaceKHR>, 2> Presenter::s_colorSpaceFallbacks = {{\n    { VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, VK_COLOR_SPACE_HDR10_ST2084_EXT },\n\n    { VK_COLOR_SPACE_HDR10_ST2084_EXT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT },\n  }};\n\n\n  Presenter::Presenter(\n    const Rc<DxvkDevice>&   device,\n    const Rc<sync::Signal>& signal,\n    const PresenterDesc&    desc,\n          PresenterSurfaceProc&& proc)\n  : m_device(device), m_signal(signal),\n    m_vki(device->instance()->vki()),\n    m_vkd(device->vkd()),\n    m_surfaceProc(std::move(proc)) {\n    // Only enable FSE if the user explicitly opts in. On Windows, FSE\n    // is required to support VRR or HDR, but blocks alt-tabbing or\n    // overlapping windows, which breaks a number of games.\n    m_fullscreenMode = m_device->config().allowFse\n      ? VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT\n      : VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT;\n\n    // Create Vulkan surface immediately if possible, but ignore\n    // certain errors since the app window may still be in use in\n    // some way at this point, e.g. by a different device.\n    if (!desc.deferSurfaceCreation) {\n      VkResult vr = createSurface();\n\n      if (vr != VK_SUCCESS && vr != VK_ERROR_NATIVE_WINDOW_IN_USE_KHR)\n        throw DxvkError(str::format(\"Failed to create Vulkan surface, \", vr));\n    }\n\n    // KHR and EXT variants of this extension are completely identical\n    m_hasSwapchainMaintenance1 = m_device->features().khrSwapchainMaintenance1.swapchainMaintenance1\n                              || m_device->features().extSwapchainMaintenance1.swapchainMaintenance1;\n\n    // Gamescope WSI is currently broken and doesn't properly signal\n    // the present fence if presentation is queued but fails.\n    // TODO Remove this hack when this gets fixed in stable SteamOS.\n    m_hasGamescopeFenceSignalBug = env::getEnvVar(\"ENABLE_GAMESCOPE_WSI\") == \"1\";\n  }\n\n  \n  Presenter::~Presenter() {\n    destroySwapchain();\n    destroySurface();\n    destroyLatencySemaphore();\n\n    if (m_frameThread.joinable()) {\n      { std::lock_guard lock(m_frameMutex);\n\n        m_frameQueue.push(PresenterFrame());\n        m_frameCond.notify_one();\n      }\n\n      m_frameThread.join();\n    }\n  }\n\n\n  VkResult Presenter::checkSwapChainStatus() {\n    std::lock_guard lock(m_surfaceMutex);\n\n    if (!m_swapchain)\n      return recreateSwapChain();\n\n    return VK_SUCCESS;\n  }\n\n\n  VkResult Presenter::acquireNextImage(PresenterSync& sync, Rc<DxvkImage>& image) {\n    std::unique_lock lock(m_surfaceMutex);\n\n    // Don't acquire more than one image at a time\n    VkResult status = VK_SUCCESS;\n\n    m_surfaceCond.wait(lock, [this, &status] {\n      status = m_device->getDeviceStatus();\n      return !m_presentPending || status < 0;\n    });\n\n    if (status < 0)\n      return status;\n\n    // Ensure that the swap chain gets recreated if it is dirty\n    bool hasSwapchain = m_swapchain != VK_NULL_HANDLE;\n\n    updateSwapChain();\n\n    // Don't acquire if we already did so after present\n    if (m_acquireStatus == VK_NOT_READY && m_swapchain) {\n      PresenterSync sync = m_semaphores.at(m_frameIndex);\n\n      waitForSwapchainFence(sync);\n\n      m_acquireStatus = m_vkd->vkAcquireNextImageKHR(m_vkd->device(),\n        m_swapchain, std::numeric_limits<uint64_t>::max(),\n        sync.acquire, VK_NULL_HANDLE, &m_imageIndex);\n    }\n\n    // This is a normal occurence, but may be useful for\n    // debugging purposes in case WSI goes wrong somehow.\n    if (m_acquireStatus != VK_SUCCESS && m_swapchain)\n      Logger::info(str::format(\"Presenter: Got \", m_acquireStatus, \", recreating swapchain\"));\n\n    // If the swap chain is out of date, recreate it and retry. It\n    // is possible that we do not get a new swap chain here, e.g.\n    // because the window is minimized.\n    if (m_acquireStatus != VK_SUCCESS || !m_swapchain) {\n      VkResult vr = recreateSwapChain();\n\n      if (vr == VK_NOT_READY && hasSwapchain)\n        Logger::info(\"Presenter: Surface does not allow swapchain creation.\");\n\n      if (vr != VK_SUCCESS)\n        return softError(vr);\n\n      PresenterSync sync = m_semaphores.at(m_frameIndex);\n\n      m_acquireStatus = m_vkd->vkAcquireNextImageKHR(m_vkd->device(),\n        m_swapchain, std::numeric_limits<uint64_t>::max(),\n        sync.acquire, VK_NULL_HANDLE, &m_imageIndex);\n\n      if (m_acquireStatus < 0) {\n        Logger::info(str::format(\"Presenter: Got \", m_acquireStatus, \" from fresh swapchain\"));\n        return softError(m_acquireStatus);\n      }\n    }\n\n    // Update HDR metadata after a successful acquire. We know\n    // that there won't be a present in flight at this point.\n    if (m_hdrMetadataDirty && m_hdrMetadata) {\n      m_hdrMetadataDirty = false;\n\n      if (m_device->features().extHdrMetadata) {\n        m_vkd->vkSetHdrMetadataEXT(m_vkd->device(),\n          1, &m_swapchain, &(*m_hdrMetadata));\n      }\n    }\n\n    // Apply latency sleep mode if the swapchain supports it\n    if (m_latencySleepModeDirty && m_latencySleepMode) {\n      m_latencySleepModeDirty = false;\n\n      if (m_latencySleepSupported) {\n        m_vkd->vkSetLatencySleepModeNV(m_vkd->device(),\n          m_swapchain, &(*m_latencySleepMode));\n      }\n    }\n\n    // Set dynamic present mode for the next frame if possible\n    if (!m_dynamicModes.empty())\n      m_presentMode = m_dynamicModes.at(m_preferredSyncInterval ? 1u : 0u); \n\n    // Return relevant Vulkan objects for the acquired image\n    sync = m_semaphores.at(m_frameIndex);\n    image = m_images.at(m_imageIndex);\n\n    m_presentPending = true;\n    return m_acquireStatus;\n  }\n\n\n  VkResult Presenter::presentImage(uint64_t frameId, const Rc<DxvkLatencyTracker>& tracker) {\n    PresenterSync& currSync = m_semaphores.at(m_frameIndex);\n\n    VkPresentIdKHR presentId = { VK_STRUCTURE_TYPE_PRESENT_ID_KHR };\n    presentId.swapchainCount = 1;\n    presentId.pPresentIds   = &frameId;\n\n    VkPresentId2KHR presentId2 = { VK_STRUCTURE_TYPE_PRESENT_ID_2_KHR };\n    presentId2.swapchainCount = 1;\n    presentId2.pPresentIds  = &frameId;\n\n    VkSwapchainPresentFenceInfoKHR fenceInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_KHR };\n    fenceInfo.swapchainCount = 1;\n    fenceInfo.pFences       = &currSync.fence;\n\n    VkSwapchainPresentModeInfoKHR modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_KHR };\n    modeInfo.swapchainCount = 1;\n    modeInfo.pPresentModes  = &m_presentMode;\n\n    VkPresentInfoKHR info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };\n    info.waitSemaphoreCount = 1;\n    info.pWaitSemaphores    = &currSync.present;\n    info.swapchainCount     = 1;\n    info.pSwapchains        = &m_swapchain;\n    info.pImageIndices      = &m_imageIndex;\n\n    if (frameId && m_hasPresentId) {\n      if (m_device->features().khrPresentId2.presentId2)\n        presentId2.pNext = const_cast<void*>(std::exchange(info.pNext, &presentId2));\n      else\n        presentId.pNext = const_cast<void*>(std::exchange(info.pNext, &presentId));\n    }\n\n    if (m_hasSwapchainMaintenance1) {\n      modeInfo.pNext = const_cast<void*>(std::exchange(info.pNext, &modeInfo));\n      fenceInfo.pNext = const_cast<void*>(std::exchange(info.pNext, &fenceInfo));\n    }\n\n    VkResult status = m_vkd->vkQueuePresentKHR(\n      m_device->queues().graphics.queueHandle, &info);\n\n    // Maintain valid state if presentation succeeded, even if we want to\n    // recreate the swapchain. Spec says that 'queue' operations, i.e. the\n    // semaphore and fence signals, still happen if present fails with\n    // normal swapchain errors, such as OUT_OF_DATE or SURFACE_LOST.\n    if (m_hasSwapchainMaintenance1) {\n      currSync.fenceSignaled = status != VK_ERROR_OUT_OF_DEVICE_MEMORY\n                            && status != VK_ERROR_OUT_OF_HOST_MEMORY\n                            && status != VK_ERROR_DEVICE_LOST;\n\n      if (m_hasGamescopeFenceSignalBug)\n        currSync.fenceSignaled = status >= 0;\n    }\n\n    if (status >= 0) {\n      m_acquireStatus = VK_NOT_READY;\n\n      m_frameIndex += 1;\n      m_frameIndex %= m_semaphores.size();\n    }\n\n    // Add frame to waiter queue with current properties\n    if (m_hasPresentWait) {\n      std::lock_guard lock(m_frameMutex);\n\n      auto& frame = m_frameQueue.emplace();\n      frame.frameId = frameId;\n      frame.tracker = tracker;\n      frame.mode = m_presentMode;\n      frame.result = status;\n\n      m_frameCond.notify_one();\n    }\n\n    // On a successful present, try to acquire next image already, in\n    // order to hide potential delays from the application thread.\n    if (status == VK_SUCCESS) {\n      PresenterSync& nextSync = m_semaphores.at(m_frameIndex);\n      waitForSwapchainFence(nextSync);\n\n      m_acquireStatus = m_vkd->vkAcquireNextImageKHR(m_vkd->device(),\n        m_swapchain, std::numeric_limits<uint64_t>::max(),\n        nextSync.acquire, VK_NULL_HANDLE, &m_imageIndex);\n    }\n\n    // Recreate the swapchain on the next acquire, even if we get suboptimal.\n    // There is no guarantee that suboptimal state is returned by both functions.\n    std::lock_guard lock(m_surfaceMutex);\n\n    if (status != VK_SUCCESS) {\n      Logger::info(str::format(\"Presenter: Got \", status, \", recreating swapchain\"));\n\n      m_dirtySwapchain = true;\n    }\n\n    m_presentPending = false;\n    m_surfaceCond.notify_one();\n    return status;\n  }\n\n\n  void Presenter::signalFrame(\n          uint64_t                frameId,\n    const Rc<DxvkLatencyTracker>& tracker) {\n    if (m_signal == nullptr || !frameId)\n      return;\n\n    if (m_hasPresentWait) {\n      bool canSignal = false;\n\n      { std::unique_lock lock(m_frameMutex);\n\n        m_lastSignaled = frameId;\n        canSignal = m_lastCompleted >= frameId;\n      }\n\n      if (canSignal)\n        m_signal->signal(frameId);\n    } else {\n      m_fpsLimiter.delay();\n      m_signal->signal(frameId);\n\n      if (tracker)\n        tracker->notifyGpuPresentEnd(frameId);\n    }\n  }\n\n\n  bool Presenter::supportsColorSpace(VkColorSpaceKHR colorspace) {\n    std::lock_guard lock(m_surfaceMutex);\n\n    if (!m_surface) {\n      VkResult vr = createSurface();\n\n      if (vr != VK_SUCCESS)\n        return false;\n    }\n\n    std::vector<VkSurfaceFormatKHR> surfaceFormats;\n    getSupportedFormats(surfaceFormats);\n\n    for (const auto& surfaceFormat : surfaceFormats) {\n      if (surfaceFormat.colorSpace == colorspace)\n        return true;\n\n      for (const auto& fallback : s_colorSpaceFallbacks) {\n        if (fallback.first == colorspace && fallback.second == surfaceFormat.colorSpace)\n          return true;\n      }\n    }\n\n    return false;\n  }\n\n\n  void Presenter::invalidateSurface() {\n    std::lock_guard lock(m_surfaceMutex);\n\n    m_dirtySurface = true;\n  }\n\n\n  void Presenter::destroyResources() {\n    std::unique_lock lock(m_surfaceMutex);\n\n    m_surfaceCond.wait(lock, [this] {\n      VkResult status = m_device->getDeviceStatus();\n      return !m_presentPending || status < 0;\n    });\n\n    destroySwapchain();\n    destroySurface();\n  }\n\n\n  void Presenter::setLatencySleepModeNv(\n    const VkLatencySleepModeInfoNV& sleepMode) {\n    std::unique_lock lock(m_surfaceMutex);\n\n    if (sleepMode.sType != VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV)\n      return;\n\n    if (sleepMode.pNext)\n      Logger::warn(\"Presenter: Extended sleep mode info not supported\");\n\n    // Avoid creating a swapchain with low-latency features\n    // enabled if the functionality isn't required\n    bool isDefault = !sleepMode.lowLatencyMode\n                  && !sleepMode.lowLatencyBoost\n                  && !sleepMode.minimumIntervalUs;\n\n    if (!m_latencySleepMode && isDefault)\n      return;\n\n    m_dirtySwapchain |= !m_latencySleepMode;\n\n    if (m_latencySleepMode) {\n      m_latencySleepModeDirty |=\n        m_latencySleepMode->lowLatencyMode != sleepMode.lowLatencyMode ||\n        m_latencySleepMode->lowLatencyBoost != sleepMode.lowLatencyBoost ||\n        m_latencySleepMode->minimumIntervalUs != sleepMode.minimumIntervalUs;\n    }\n\n    m_latencySleepMode = sleepMode;\n    m_latencySleepMode->pNext = nullptr;\n  }\n\n\n  dxvk::high_resolution_clock::time_point Presenter::setLatencyMarkerNv(\n          uint64_t                frameId,\n          VkLatencyMarkerNV       marker) {\n    std::unique_lock lock(m_surfaceMutex);\n\n    if (!m_latencySleepMode) {\n      // Applications may use latency markers without enabling\n      // low-latency mode, make sure we have a compatible swapchain\n      m_latencySleepMode = { VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV };\n      m_dirtySwapchain = true;\n\n      return dxvk::high_resolution_clock::now();\n    }\n\n    // Return a CPU timestamp to correlate timestamps from\n    // latency frame reports with actual CPU timestamps\n    auto t0 = dxvk::high_resolution_clock::now();\n\n    if (m_latencySleepSupported) {\n      VkSetLatencyMarkerInfoNV info = { VK_STRUCTURE_TYPE_SET_LATENCY_MARKER_INFO_NV };\n      info.presentID = frameId;\n      info.marker = marker;\n\n      m_vkd->vkSetLatencyMarkerNV(m_vkd->device(), m_swapchain, &info);\n    }\n\n    auto t1 = dxvk::high_resolution_clock::now();\n    return t0 + (t1 - t0) / 2u;\n  }\n\n\n  dxvk::high_resolution_clock::duration Presenter::latencySleepNv() {\n    std::unique_lock lock(m_surfaceMutex);\n\n    if (!m_latencySleepSupported)\n      return dxvk::high_resolution_clock::duration(0u);\n\n    if (!m_latencySemaphore) {\n      if (createLatencySemaphore() != VK_SUCCESS)\n        return dxvk::high_resolution_clock::duration(0u);\n    }\n\n    VkLatencySleepInfoNV info = { VK_STRUCTURE_TYPE_LATENCY_SLEEP_INFO_NV };\n    info.signalSemaphore = m_latencySemaphore;\n    info.value = ++m_latencySleepCounter;\n\n    m_vkd->vkLatencySleepNV(m_vkd->device(), m_swapchain, &info);\n\n    lock.unlock();\n\n    auto t0 = dxvk::high_resolution_clock::now();\n\n    VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };\n    waitInfo.semaphoreCount = 1;\n    waitInfo.pSemaphores = &info.signalSemaphore;\n    waitInfo.pValues = &info.value;\n\n    m_vkd->vkWaitSemaphores(m_vkd->device(), &waitInfo, ~0ull);\n\n    auto t1 = dxvk::high_resolution_clock::now();\n    return t1 - t0;\n  }\n\n\n  uint32_t Presenter::getLatencyTimingsNv(\n          uint32_t                timingCount,\n          VkLatencyTimingsFrameReportNV* timings) {\n    std::unique_lock lock(m_surfaceMutex);\n\n    if (!m_latencySleepSupported)\n      return 0u;\n\n    VkGetLatencyMarkerInfoNV info = { VK_STRUCTURE_TYPE_GET_LATENCY_MARKER_INFO_NV };\n    info.timingCount = timingCount;\n    info.pTimings = timings;\n\n    m_vkd->vkGetLatencyTimingsNV(m_vkd->device(), m_swapchain, &info);\n    return info.timingCount;\n  }\n\n\n  void Presenter::setSyncInterval(uint32_t syncInterval) {\n    std::lock_guard lock(m_surfaceMutex);\n\n    // Normalize sync interval for present modes. We currently\n    // cannot support anything other than 1 natively anyway.\n    syncInterval = std::min(syncInterval, 1u);\n\n    if (m_preferredSyncInterval != syncInterval) {\n      m_preferredSyncInterval = syncInterval;\n\n      if (m_dynamicModes.empty())\n        m_dirtySwapchain = true;\n    }\n  }\n\n\n  void Presenter::setFrameRateLimit(double frameRate, uint32_t maxLatency) {\n    m_fpsLimiter.setTargetFrameRate(frameRate, maxLatency);\n  }\n\n\n  void Presenter::setSurfaceFormat(VkSurfaceFormatKHR format) {\n    std::lock_guard lock(m_surfaceMutex);\n\n    if (m_preferredFormat.format != format.format || m_preferredFormat.colorSpace != format.colorSpace) {\n      m_preferredFormat = format;\n      m_dirtySwapchain = true;\n    }\n  }\n\n\n  void Presenter::setSurfaceExtent(VkExtent2D extent) {\n    std::lock_guard lock(m_surfaceMutex);\n\n    if (m_preferredExtent != extent) {\n      m_preferredExtent = extent;\n      m_dirtySwapchain = true;\n    }\n  }\n\n\n  void Presenter::setHdrMetadata(VkHdrMetadataEXT hdrMetadata) {\n    std::lock_guard lock(m_surfaceMutex);\n\n    if (hdrMetadata.sType != VK_STRUCTURE_TYPE_HDR_METADATA_EXT) {\n      m_hdrMetadata = std::nullopt;\n      return;\n    }\n\n    if (hdrMetadata.pNext)\n      Logger::warn(\"Presenter: HDR metadata extensions not currently supported.\");\n\n    m_hdrMetadata = hdrMetadata;\n    m_hdrMetadata->pNext = nullptr;\n\n    m_hdrMetadataDirty = true;\n  }\n\n\n  VkResult Presenter::recreateSwapChain() {\n    VkResult vr;\n\n    if (m_swapchain)\n      destroySwapchain();\n\n    if (m_surface) {\n      vr = createSwapChain();\n\n      if (vr == VK_ERROR_SURFACE_LOST_KHR)\n        destroySurface();\n    }\n\n    if (!m_surface) {\n      vr = createSurface();\n\n      if (vr == VK_SUCCESS)\n        vr = createSwapChain();\n    }\n\n    return vr;\n  }\n\n\n  void Presenter::updateSwapChain() {\n    if (m_dirtySurface || m_dirtySwapchain) {\n      destroySwapchain();\n      m_dirtySwapchain = false;\n    }\n\n    if (m_dirtySurface) {\n      destroySurface();\n      m_dirtySurface = false;\n    }\n  }\n\n\n  VkResult Presenter::createSwapChain() {\n    VkSurfaceFullScreenExclusiveInfoEXT fullScreenExclusiveInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };\n    fullScreenExclusiveInfo.fullScreenExclusive = m_fullscreenMode;\n\n    VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR };\n    surfaceInfo.surface = m_surface;\n\n    if (m_device->features().extFullScreenExclusive)\n      surfaceInfo.pNext = &fullScreenExclusiveInfo;\n\n    // Query surface capabilities. Some properties might have changed,\n    // including the size limits and supported present modes, so we'll\n    // just query everything again.\n    VkSurfaceCapabilitiesPresentWait2KHR presentWait2Caps = { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_PRESENT_WAIT_2_KHR };\n    VkSurfaceCapabilitiesPresentId2KHR presentId2Caps = { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_PRESENT_ID_2_KHR };\n\n    VkSurfaceCapabilities2KHR caps = { VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR };\n\n    if (m_device->features().khrPresentId2.presentId2) {\n      presentId2Caps.pNext = std::exchange(caps.pNext, &presentId2Caps);\n\n      if (m_device->features().khrPresentWait2.presentWait2)\n        presentWait2Caps.pNext = std::exchange(caps.pNext, &presentWait2Caps);\n    }\n\n    std::vector<VkSurfaceFormatKHR> formats;\n    std::vector<VkPresentModeKHR> modes;\n\n    VkResult status;\n\n    if (m_device->instance()->extensions().khrGetSurfaceCapabilities2.specVersion) {\n      status = m_vki->vkGetPhysicalDeviceSurfaceCapabilities2KHR(\n        m_device->adapter()->handle(), &surfaceInfo, &caps);\n    } else {\n      status = m_vki->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(\n        m_device->adapter()->handle(), m_surface, &caps.surfaceCapabilities);\n    }\n\n    if (status) {\n      Logger::err(str::format(\"Presenter: Failed to get surface capabilities: \", status));\n      return status;\n    }\n\n    // Select image extent based on current surface capabilities, and return\n    // immediately if we cannot create an actual swap chain.\n    VkExtent2D imageExtent = pickImageExtent(caps.surfaceCapabilities, m_preferredExtent);\n\n    if (!imageExtent.width || !imageExtent.height)\n      return VK_NOT_READY;\n\n    // Select format based on swap chain properties\n    if ((status = getSupportedFormats(formats)))\n      return status;\n\n    VkSurfaceFormatKHR surfaceFormat = pickSurfaceFormat(formats.size(), formats.data(), m_preferredFormat);\n\n    // Set up image format list for mutable swap chain if necessary\n    small_vector<VkFormat, 2> viewFormats = { };\n\n    auto formatPair = vk::getSrgbFormatPair(surfaceFormat.format);\n\n    if (formatPair.second) {\n      viewFormats.push_back(formatPair.first);\n      viewFormats.push_back(formatPair.second);\n    }\n\n    // Select a present mode for the current sync interval\n    if ((status = getSupportedPresentModes(modes)))\n      return status;\n\n    m_presentMode = pickPresentMode(modes.size(), modes.data(), m_preferredSyncInterval);\n\n    // Check whether we can change present modes dynamically. This may\n    // influence the image count as well as further swap chain creation.\n    std::vector<VkPresentModeKHR> dynamicModes = {{\n      pickPresentMode(modes.size(), modes.data(), 0),\n      pickPresentMode(modes.size(), modes.data(), 1),\n    }};\n\n    std::vector<VkPresentModeKHR> compatibleModes;\n\n    // As for the minimum image count, start with the most generic value\n    // that works with all present modes.\n    uint32_t minImageCount = caps.surfaceCapabilities.minImageCount;\n    uint32_t maxImageCount = caps.surfaceCapabilities.maxImageCount;\n\n    if (m_hasSwapchainMaintenance1) {\n      VkSurfacePresentModeCompatibilityKHR compatibleModeInfo = { VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_KHR };\n\n      VkSurfacePresentModeKHR presentModeInfo = { VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_KHR };\n      presentModeInfo.pNext = const_cast<void*>(std::exchange(surfaceInfo.pNext, &presentModeInfo));\n      presentModeInfo.presentMode = m_presentMode;\n\n      caps.pNext = &compatibleModeInfo;\n\n      if ((status = m_vki->vkGetPhysicalDeviceSurfaceCapabilities2KHR(\n          m_device->adapter()->handle(), &surfaceInfo, &caps))) {\n        Logger::err(str::format(\"Presenter: Failed to get surface capabilities: \", status));\n        return status;\n      }\n\n      compatibleModes.resize(compatibleModeInfo.presentModeCount);\n      compatibleModeInfo.pPresentModes = compatibleModes.data();\n\n      if ((status = m_vki->vkGetPhysicalDeviceSurfaceCapabilities2KHR(\n          m_device->adapter()->handle(), &surfaceInfo, &caps))) {\n        Logger::err(str::format(\"Presenter: Failed to get surface capabilities: \", status));\n        return status;\n      }\n\n      // Remove modes we don't need for the purpose of finding the minimum\n      // image count, as well as for swap chain creation later.\n      compatibleModes.erase(std::remove_if(compatibleModes.begin(), compatibleModes.end(),\n        [&dynamicModes] (VkPresentModeKHR mode) {\n          return std::find(dynamicModes.begin(), dynamicModes.end(), mode) == dynamicModes.end();\n        }), compatibleModes.end());\n\n      minImageCount = 0;\n      caps.pNext = nullptr;\n\n      for (auto mode : compatibleModes) {\n        presentModeInfo.presentMode = mode;\n\n        if ((status = m_vki->vkGetPhysicalDeviceSurfaceCapabilities2KHR(\n            m_device->adapter()->handle(), &surfaceInfo, &caps))) {\n          Logger::err(str::format(\"Presenter: Failed to get surface capabilities: \", status));\n          return status;\n        }\n\n        minImageCount = std::max(minImageCount, caps.surfaceCapabilities.minImageCount);\n\n        if (caps.surfaceCapabilities.maxImageCount) {\n          maxImageCount = maxImageCount\n            ? std::min(maxImageCount, caps.surfaceCapabilities.maxImageCount)\n            : caps.surfaceCapabilities.maxImageCount;\n        }\n      }\n\n      // If any required mode is not supported for dynamic present\n      // mode switching, clear the dynamic mode array.\n      for (auto mode : dynamicModes) {\n        if (std::find(compatibleModes.begin(), compatibleModes.end(), mode) == compatibleModes.end()) {\n          dynamicModes.clear();\n          break;\n        }\n      }\n    } else if (dynamicModes[0] != dynamicModes[1]) {\n      // If we can't switch modes dynamically, clear the\n      // array so that setSyncInterval errors out properly.\n      dynamicModes.clear();\n    }\n\n    // Compute swap chain image count based on available info\n    VkSurfaceFullScreenExclusiveInfoEXT fullScreenInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };\n    fullScreenInfo.fullScreenExclusive = m_fullscreenMode;\n\n    VkSwapchainPresentModesCreateInfoKHR modeInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODES_CREATE_INFO_KHR };\n    modeInfo.presentModeCount       = compatibleModes.size();\n    modeInfo.pPresentModes          = compatibleModes.data();\n\n    VkSwapchainLatencyCreateInfoNV latencyInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_LATENCY_CREATE_INFO_NV };\n    latencyInfo.latencyModeEnable   = m_latencySleepMode.has_value();\n\n    VkImageFormatListCreateInfo formatList = { VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO };\n    formatList.viewFormatCount      = viewFormats.size();\n    formatList.pViewFormats         = viewFormats.data();\n\n    VkSwapchainCreateInfoKHR swapInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };\n    swapInfo.surface                = m_surface;\n    swapInfo.minImageCount          = pickImageCount(minImageCount, maxImageCount);\n    swapInfo.imageFormat            = surfaceFormat.format;\n    swapInfo.imageColorSpace        = surfaceFormat.colorSpace;\n    swapInfo.imageExtent            = imageExtent;\n    swapInfo.imageArrayLayers       = 1;\n    swapInfo.imageUsage             = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n                                    | VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n    swapInfo.imageSharingMode       = VK_SHARING_MODE_EXCLUSIVE;\n    swapInfo.preTransform           = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;\n    swapInfo.compositeAlpha         = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;\n    swapInfo.presentMode            = m_presentMode;\n    swapInfo.clipped                = VK_TRUE;\n\n    if (m_device->features().khrSwapchainMutableFormat && formatList.viewFormatCount) {\n      swapInfo.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR;\n      formatList.pNext = std::exchange(swapInfo.pNext, &formatList);\n    }\n\n    if (presentId2Caps.presentId2Supported)\n      swapInfo.flags |= VK_SWAPCHAIN_CREATE_PRESENT_ID_2_BIT_KHR;\n\n    if (presentWait2Caps.presentWait2Supported)\n      swapInfo.flags |= VK_SWAPCHAIN_CREATE_PRESENT_WAIT_2_BIT_KHR;\n\n    if (m_device->features().extFullScreenExclusive)\n      fullScreenInfo.pNext = const_cast<void*>(std::exchange(swapInfo.pNext, &fullScreenInfo));\n\n    if (m_hasSwapchainMaintenance1)\n      modeInfo.pNext = std::exchange(swapInfo.pNext, &modeInfo);\n\n    if (m_device->features().nvLowLatency2)\n      latencyInfo.pNext = std::exchange(swapInfo.pNext, &latencyInfo);\n\n    Logger::info(str::format(\n      \"Presenter: Actual swapchain properties:\"\n      \"\\n  Format:       \", swapInfo.imageFormat,\n      \"\\n  Color space:  \", swapInfo.imageColorSpace,\n      \"\\n  Present mode: \", swapInfo.presentMode, \" (dynamic: \", (dynamicModes.empty() ? \"no)\" : \"yes)\"),\n      \"\\n  Buffer size:  \", swapInfo.imageExtent.width, \"x\", swapInfo.imageExtent.height,\n      \"\\n  Image count:  \", swapInfo.minImageCount));\n    \n    if ((status = m_vkd->vkCreateSwapchainKHR(m_vkd->device(), &swapInfo, nullptr, &m_swapchain))) {\n      Logger::err(str::format(\"Presenter: Failed to create Vulkan swapchain: \", status));\n      return status;\n    }\n    \n    // Import actual swap chain images\n    std::vector<VkImage> images;\n\n    if ((status = getSwapImages(images)))\n      return status;\n    \n    for (uint32_t i = 0; i < images.size(); i++) {\n      std::string debugName = str::format(\"Vulkan swap image \", i);\n\n      DxvkImageCreateInfo imageInfo = { };\n      imageInfo.type        = VK_IMAGE_TYPE_2D;\n      imageInfo.format      = swapInfo.imageFormat;\n      imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;\n      imageInfo.extent      = { swapInfo.imageExtent.width, swapInfo.imageExtent.height, 1u };\n      imageInfo.numLayers   = swapInfo.imageArrayLayers;\n      imageInfo.mipLevels   = 1u;\n      imageInfo.usage       = swapInfo.imageUsage;\n      imageInfo.tiling      = VK_IMAGE_TILING_OPTIMAL;\n      imageInfo.layout      = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;\n      imageInfo.colorSpace  = swapInfo.imageColorSpace;\n      imageInfo.shared      = VK_TRUE;\n      imageInfo.debugName   = debugName.c_str();\n\n      // If possible, expose the image with an sRGB format internally so\n      // that it will be used as the default format for composition.\n      if (swapInfo.flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) {\n        imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;\n        imageInfo.viewFormatCount = formatList.viewFormatCount;\n        imageInfo.viewFormats = formatList.pViewFormats;\n\n        if (formatPair.second)\n          imageInfo.format = formatPair.second;\n      }\n\n      m_images.push_back(m_device->importImage(imageInfo, images[i],\n        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));\n    }\n\n    // Create one set of semaphores per swap image, as well as a fence\n    // that we use to ensure that semaphores are safe to access.\n    uint32_t semaphoreCount = images.size();\n\n    if (!m_hasSwapchainMaintenance1) {\n      // Without support for present fences, just give up and allocate extra\n      // semaphores. We have no real guarantees when they are safe to access.\n      semaphoreCount *= 2u;\n    }\n\n    m_semaphores.resize(semaphoreCount);\n\n    for (uint32_t i = 0; i < semaphoreCount; i++) {\n      VkSemaphoreCreateInfo semInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };\n\n      if ((status = m_vkd->vkCreateSemaphore(m_vkd->device(),\n          &semInfo, nullptr, &m_semaphores[i].acquire))) {\n        Logger::err(str::format(\"Presenter: Failed to create semaphore: \", status));\n        return status;\n      }\n\n      if ((status = m_vkd->vkCreateSemaphore(m_vkd->device(),\n          &semInfo, nullptr, &m_semaphores[i].present))) {\n        Logger::err(str::format(\"Presenter: Failed to create semaphore: \", status));\n        return status;\n      }\n\n      VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };\n\n      if ((status = m_vkd->vkCreateFence(m_vkd->device(),\n          &fenceInfo, nullptr, &m_semaphores[i].fence))) {\n        Logger::err(str::format(\"Presenter: Failed to create fence: \", status));\n        return status;\n      }\n    }\n    \n    // Invalidate indices\n    m_latencySleepSupported = m_device->features().nvLowLatency2 && latencyInfo.latencyModeEnable;\n\n    m_imageIndex = 0;\n    m_frameIndex = 0;\n\n    m_dynamicModes = std::move(dynamicModes);\n\n    // Set up feature support for present wait / id, and launch sync thread as necessary\n    m_hasPresentId = presentId2Caps.presentId2Supported || m_device->features().khrPresentId.presentId;\n    m_hasPresentWait = presentWait2Caps.presentWait2Supported || m_device->features().khrPresentWait.presentWait;\n\n    if (m_signal && m_hasPresentWait && !m_frameThread.joinable())\n      m_frameThread = dxvk::thread([this] { runFrameThread(); });\n\n    return VK_SUCCESS;\n  }\n\n\n  VkResult Presenter::getSupportedFormats(std::vector<VkSurfaceFormatKHR>& formats) const {\n    uint32_t numFormats = 0;\n\n    VkSurfaceFullScreenExclusiveInfoEXT fullScreenInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };\n    fullScreenInfo.fullScreenExclusive = m_fullscreenMode;\n\n    VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, &fullScreenInfo };\n    surfaceInfo.surface = m_surface;\n\n    VkResult status;\n    \n    if (m_device->features().extFullScreenExclusive) {\n      status = m_vki->vkGetPhysicalDeviceSurfaceFormats2KHR(\n        m_device->adapter()->handle(), &surfaceInfo, &numFormats, nullptr);\n    } else {\n      status = m_vki->vkGetPhysicalDeviceSurfaceFormatsKHR(\n        m_device->adapter()->handle(), m_surface, &numFormats, nullptr);\n    }\n\n    if (status != VK_SUCCESS) {\n      Logger::err(str::format(\"Presenter: Failed to query surface formats: \", status));\n      return status;\n    }\n    \n    formats.resize(numFormats);\n\n    if (m_device->features().extFullScreenExclusive) {\n      std::vector<VkSurfaceFormat2KHR> tmpFormats(numFormats, \n        { VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR, nullptr, VkSurfaceFormatKHR() });\n\n      status = m_vki->vkGetPhysicalDeviceSurfaceFormats2KHR(\n        m_device->adapter()->handle(), &surfaceInfo, &numFormats, tmpFormats.data());\n\n      for (uint32_t i = 0; i < numFormats; i++)\n        formats[i] = tmpFormats[i].surfaceFormat;\n    } else {\n      status = m_vki->vkGetPhysicalDeviceSurfaceFormatsKHR(\n        m_device->adapter()->handle(), m_surface, &numFormats, formats.data());\n    }\n\n    if (status != VK_SUCCESS)\n      Logger::err(str::format(\"Presenter: Failed to query surface formats: \", status));\n\n    return status;\n  }\n\n  \n  VkResult Presenter::getSupportedPresentModes(std::vector<VkPresentModeKHR>& modes) const {\n    uint32_t numModes = 0;\n\n    VkSurfaceFullScreenExclusiveInfoEXT fullScreenInfo = { VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT };\n    fullScreenInfo.fullScreenExclusive = m_fullscreenMode;\n\n    VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, &fullScreenInfo };\n    surfaceInfo.surface = m_surface;\n\n    VkResult status;\n\n    if (m_device->features().extFullScreenExclusive) {\n      status = m_vki->vkGetPhysicalDeviceSurfacePresentModes2EXT(\n        m_device->adapter()->handle(), &surfaceInfo, &numModes, nullptr);\n    } else {\n      status = m_vki->vkGetPhysicalDeviceSurfacePresentModesKHR(\n        m_device->adapter()->handle(), m_surface, &numModes, nullptr);\n    }\n\n    if (status != VK_SUCCESS) {\n      Logger::err(str::format(\"Presenter: Failed to query present modes: \", status));\n      return status;\n    }\n    \n    modes.resize(numModes);\n\n    if (m_device->features().extFullScreenExclusive) {\n      status = m_vki->vkGetPhysicalDeviceSurfacePresentModes2EXT(\n        m_device->adapter()->handle(), &surfaceInfo, &numModes, modes.data());\n    } else {\n      status = m_vki->vkGetPhysicalDeviceSurfacePresentModesKHR(\n        m_device->adapter()->handle(), m_surface, &numModes, modes.data());\n    }\n\n    if (status != VK_SUCCESS)\n      Logger::err(str::format(\"Presenter: Failed to query present modes: \", status));\n\n    return status;\n  }\n\n\n  VkResult Presenter::getSwapImages(std::vector<VkImage>& images) {\n    uint32_t imageCount = 0;\n\n    VkResult status = m_vkd->vkGetSwapchainImagesKHR(\n      m_vkd->device(), m_swapchain, &imageCount, nullptr);\n    \n    if (status != VK_SUCCESS) {\n      Logger::err(str::format(\"Presenter: Failed to query swapchain images: \", status));\n      return status;\n    }\n    \n    images.resize(imageCount);\n\n    status = m_vkd->vkGetSwapchainImagesKHR(\n      m_vkd->device(), m_swapchain, &imageCount, images.data());\n\n    if (status != VK_SUCCESS)\n      Logger::err(str::format(\"Presenter: Failed to query swapchain images: \", status));\n\n    return status;\n  }\n\n\n  VkSurfaceFormatKHR Presenter::pickSurfaceFormat(\n          uint32_t                  numSupported,\n    const VkSurfaceFormatKHR*       pSupported,\n    const VkSurfaceFormatKHR&       desired) {\n    VkSurfaceFormatKHR result = { };\n    result.colorSpace = pickColorSpace(numSupported, pSupported, desired.colorSpace);\n    result.format = pickFormat(numSupported, pSupported, result.colorSpace,\n      result.colorSpace == desired.colorSpace ? desired.format : VK_FORMAT_UNDEFINED);\n    return result;\n  }\n\n\n  VkColorSpaceKHR Presenter::pickColorSpace(\n          uint32_t                  numSupported,\n    const VkSurfaceFormatKHR*       pSupported,\n          VkColorSpaceKHR           desired) {\n    VkColorSpaceKHR fallback = pSupported[0].colorSpace;\n\n    for (uint32_t i = 0; i < numSupported; i++) {\n      if (pSupported[i].colorSpace == desired)\n        return desired;\n\n      if (pSupported[i].colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR)\n        fallback = pSupported[i].colorSpace;\n    }\n\n    for (const auto& f : s_colorSpaceFallbacks) {\n      if (f.first != desired)\n        continue;\n\n      for (uint32_t i = 0; i < numSupported; i++) {\n        if (pSupported[i].colorSpace == f.second)\n          return f.second;\n      }\n    }\n\n    Logger::warn(str::format(\"No fallback color space found for \", desired, \", using \", fallback));\n    return fallback;\n  }\n\n\n  VkFormat Presenter::pickFormat(\n          uint32_t                  numSupported,\n    const VkSurfaceFormatKHR*       pSupported,\n          VkColorSpaceKHR           colorSpace,\n          VkFormat                  format) {\n    static const std::array<VkFormat, 13> srgbFormatList = {\n      VK_FORMAT_B5G5R5A1_UNORM_PACK16,\n      VK_FORMAT_R5G5B5A1_UNORM_PACK16,\n      VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR,\n      VK_FORMAT_R5G6B5_UNORM_PACK16,\n      VK_FORMAT_B5G6R5_UNORM_PACK16,\n      VK_FORMAT_R8G8B8A8_UNORM,\n      VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_A8B8G8R8_UNORM_PACK32,\n      VK_FORMAT_A2R10G10B10_UNORM_PACK32,\n      VK_FORMAT_A2B10G10R10_UNORM_PACK32,\n      VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,\n      VK_FORMAT_R16G16B16A16_UNORM,\n      VK_FORMAT_R16G16B16A16_SFLOAT,\n    };\n\n    static const std::array<VkFormat, 5> hdr10FormatList = {\n      VK_FORMAT_A2R10G10B10_UNORM_PACK32,\n      VK_FORMAT_A2B10G10R10_UNORM_PACK32,\n      VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,\n      VK_FORMAT_R16G16B16A16_UNORM,\n      VK_FORMAT_R16G16B16A16_SFLOAT,\n    };\n\n    static const std::array<VkFormat, 1> scRGBFormatList = {\n      VK_FORMAT_R16G16B16A16_SFLOAT,\n    };\n\n    static const std::array<PresenterFormatList, 3> compatLists = {{\n      { VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,\n        srgbFormatList.size(), srgbFormatList.data() },\n      { VK_COLOR_SPACE_HDR10_ST2084_EXT,\n        hdr10FormatList.size(), hdr10FormatList.data() },\n      { VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT,\n        scRGBFormatList.size(), scRGBFormatList.data() },\n    }};\n\n    // Some third-party overlays don't handle sRGB image formats\n    // correctly, so use the corresponding UNORM format instead.\n    auto formatPair = vk::getSrgbFormatPair(format);\n\n    if (formatPair.first)\n      format = formatPair.first;\n\n    // If the desired format is supported natively, use it\n    VkFormat fallback = VK_FORMAT_UNDEFINED;\n\n    for (uint32_t i = 0; i < numSupported; i++) {\n      if (pSupported[i].colorSpace == colorSpace) {\n        if (pSupported[i].format == format)\n          return pSupported[i].format;\n\n        if (!fallback)\n          fallback = pSupported[i].format;\n      }\n    }\n\n    // Otherwise, find a supported format for the color space\n    const PresenterFormatList* compatList = nullptr;\n\n    for (const auto& l : compatLists) {\n      if (l.colorSpace == colorSpace)\n        compatList = &l;\n    }\n\n    if (!compatList)\n      return fallback;\n\n    bool desiredFound = false;\n\n    for (uint32_t i = 0; i < compatList->formatCount; i++) {\n      bool isSupported = false;\n\n      if (compatList->formats[i] == format)\n        desiredFound = true;\n\n      for (uint32_t j = 0; j < numSupported && !isSupported; j++)\n        isSupported = pSupported[j].colorSpace == colorSpace && pSupported[j].format == compatList->formats[i];\n\n      if (isSupported) {\n        fallback = compatList->formats[i];\n\n        if (desiredFound)\n          break;\n      }\n    }\n\n    if (!desiredFound && format)\n      Logger::warn(str::format(\"Desired format \", format, \" not in compatibility list for \", colorSpace, \", using \", fallback));\n\n    return fallback;\n  }\n\n\n  VkPresentModeKHR Presenter::pickPresentMode(\n          uint32_t                  numSupported,\n    const VkPresentModeKHR*         pSupported,\n          uint32_t                  syncInterval) {\n    std::array<VkPresentModeKHR, 2> desired = { };\n    uint32_t numDesired = 0;\n\n    Tristate tearFree = m_device->config().tearFree;\n\n    if (!syncInterval) {\n      if (tearFree != Tristate::True)\n        desired[numDesired++] = VK_PRESENT_MODE_IMMEDIATE_KHR;\n      desired[numDesired++] = VK_PRESENT_MODE_MAILBOX_KHR;\n    } else {\n      if (tearFree == Tristate::False)\n        desired[numDesired++] = VK_PRESENT_MODE_FIFO_RELAXED_KHR;\n    }\n\n    // Just pick the first desired and supported mode\n    for (uint32_t i = 0; i < numDesired; i++) {\n      for (uint32_t j = 0; j < numSupported; j++) {\n        if (pSupported[j] == desired[i])\n          return pSupported[j];\n      }\n    }\n    \n    // Guaranteed to be available\n    return VK_PRESENT_MODE_FIFO_KHR;\n  }\n\n\n  VkExtent2D Presenter::pickImageExtent(\n    const VkSurfaceCapabilitiesKHR& caps,\n          VkExtent2D                desired) {\n    if (caps.currentExtent.width != std::numeric_limits<uint32_t>::max())\n      return caps.currentExtent;\n\n    VkExtent2D actual;\n    actual.width  = clamp(desired.width,  caps.minImageExtent.width,  caps.maxImageExtent.width);\n    actual.height = clamp(desired.height, caps.minImageExtent.height, caps.maxImageExtent.height);\n    return actual;\n  }\n\n\n  uint32_t Presenter::pickImageCount(\n          uint32_t                  minImageCount,\n          uint32_t                  maxImageCount) {\n    uint32_t count = minImageCount + 1;\n\n    if (count > maxImageCount && maxImageCount != 0)\n      count = maxImageCount;\n\n    return count;\n  }\n\n\n  VkResult Presenter::createSurface() {\n    VkResult vr = m_surfaceProc(&m_surface);\n\n    if (vr != VK_SUCCESS)\n      Logger::err(str::format(\"Presenter: Failed to create Vulkan surface: \", vr));\n\n    return vr;\n  }\n\n\n  VkResult Presenter::createLatencySemaphore() {\n    VkSemaphoreTypeCreateInfo typeInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO };\n    typeInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;\n\n    VkSemaphoreCreateInfo info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &typeInfo };\n    VkResult vr = m_vkd->vkCreateSemaphore(m_vkd->device(), &info, nullptr, &m_latencySemaphore);\n\n    if (vr != VK_SUCCESS)\n      Logger::err(str::format(\"Presenter: Failed to create latency semaphore: \", vr));\n\n    return vr;\n  }\n\n\n  void Presenter::destroySwapchain() {\n    // Without present fence support, waiting for the queue or device to go idle\n    // is the only way to properly synchronize swapchain teardown. Care must be\n    // taken not to call this method while the submission queue is locked.\n    if (!m_hasSwapchainMaintenance1)\n      m_device->waitForIdle();\n\n    // Wait for the presentWait worker to finish using\n    // the swapchain before destroying it.\n    std::unique_lock lock(m_frameMutex);\n\n    m_frameDrain.wait(lock, [this] {\n      return m_frameQueue.empty();\n    });\n\n    for (auto& sem : m_semaphores)\n      waitForSwapchainFence(sem);\n\n    for (const auto& sem : m_semaphores) {\n      m_vkd->vkDestroySemaphore(m_vkd->device(), sem.acquire, nullptr);\n      m_vkd->vkDestroySemaphore(m_vkd->device(), sem.present, nullptr);\n      m_vkd->vkDestroyFence(m_vkd->device(), sem.fence, nullptr);\n    }\n\n    // The conditional is here because some third party layers don't properly handle null swapchains\n    if (m_swapchain)\n      m_vkd->vkDestroySwapchainKHR(m_vkd->device(), m_swapchain, nullptr);\n\n    m_images.clear();\n    m_semaphores.clear();\n    m_dynamicModes.clear();\n\n    m_swapchain = VK_NULL_HANDLE;\n    m_acquireStatus = VK_NOT_READY;\n\n    m_presentPending = false;\n\n    m_hdrMetadataDirty = true;\n\n    m_latencySleepModeDirty = true;\n    m_latencySleepSupported = false;\n\n    m_hasPresentId = false;\n    m_hasPresentWait = false;\n  }\n\n\n  void Presenter::destroySurface() {\n    m_vki->vkDestroySurfaceKHR(m_vki->instance(), m_surface, nullptr);\n\n    m_surface = VK_NULL_HANDLE;\n  }\n\n\n  void Presenter::destroyLatencySemaphore() {\n    m_vkd->vkDestroySemaphore(m_vkd->device(), m_latencySemaphore, nullptr);\n\n    m_latencySemaphore = VK_NULL_HANDLE;\n  }\n\n\n  void Presenter::waitForSwapchainFence(\n          PresenterSync&            sync) {\n    if (!sync.fenceSignaled)\n      return;\n\n    VkResult vr = m_vkd->vkWaitForFences(m_vkd->device(),\n      1, &sync.fence, VK_TRUE, ~0ull);\n\n    if (vr)\n      Logger::err(str::format(\"Presenter: Failed to wait for WSI fence: \", vr));\n\n    if ((vr = m_vkd->vkResetFences(m_vkd->device(), 1, &sync.fence)))\n      Logger::err(str::format(\"Presenter: Failed to reset WSI fence: \", vr));\n\n    sync.fenceSignaled = VK_FALSE;\n  }\n\n\n  void Presenter::runFrameThread() {\n    env::setThreadName(\"dxvk-frame\");\n\n    while (true) {\n      PresenterFrame frame = { };\n\n      // Wait for all GPU work for this frame to complete in order to maintain\n      // ordering guarantees of the frame signal w.r.t. objects being released\n      { std::unique_lock lock(m_frameMutex);\n\n        m_frameCond.wait(lock, [this] {\n          return !m_frameQueue.empty();\n        });\n\n        // Use a frame ID of 0 as an exit condition\n        frame = m_frameQueue.front();\n\n        if (!frame.frameId) {\n          m_frameQueue.pop();\n          return;\n        }\n      }\n\n      // If the present operation has succeeded, actually wait for it to complete.\n      // Don't bother with it on MAILBOX / IMMEDIATE modes since doing so would\n      // restrict us to the display refresh rate on some platforms (XWayland).\n      if (frame.result >= 0 && (frame.mode == VK_PRESENT_MODE_FIFO_KHR || frame.mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)) {\n        VkResult vr;\n\n        if (m_device->features().khrPresentWait2.presentWait2) {\n          VkPresentWait2InfoKHR waitInfo = { VK_STRUCTURE_TYPE_PRESENT_WAIT_2_INFO_KHR };\n          waitInfo.presentId = frame.frameId;\n          waitInfo.timeout = std::numeric_limits<uint64_t>::max();\n\n          vr = m_vkd->vkWaitForPresent2KHR(m_vkd->device(), m_swapchain, &waitInfo);\n        } else {\n          vr = m_vkd->vkWaitForPresentKHR(m_vkd->device(),\n            m_swapchain, frame.frameId, std::numeric_limits<uint64_t>::max());\n        }\n\n        if (vr < 0 && vr != VK_ERROR_OUT_OF_DATE_KHR && vr != VK_ERROR_SURFACE_LOST_KHR)\n          Logger::err(str::format(\"Presenter: vkWaitForPresentKHR failed: \", vr));\n      }\n\n      // Signal latency tracker right away to get more accurate\n      // measurements if the frame rate limiter is enabled.\n      if (frame.tracker) {\n        frame.tracker->notifyGpuPresentEnd(frame.frameId);\n        frame.tracker = nullptr;\n      }\n\n      // Apply FPS limiter here to align it as closely with scanout as we can,\n      // and delay signaling the frame latency event to emulate behaviour of a\n      // low refresh rate display as closely as we can.\n      m_fpsLimiter.delay();\n\n      // Wake up any thread that may be waiting for the queue to become empty\n      bool canSignal = false;\n\n      { std::unique_lock lock(m_frameMutex);\n\n        m_frameQueue.pop();\n        m_frameDrain.notify_one();\n\n        m_lastCompleted = frame.frameId;\n        canSignal = m_lastSignaled >= frame.frameId;\n      }\n\n      // Always signal even on error, since failures here\n      // are transparent to the front-end.\n      if (canSignal)\n        m_signal->signal(frame.frameId);\n    }\n  }\n\n\n  VkResult Presenter::softError(\n          VkResult                  vr) {\n    // Don't return these as an error state to the caller. The app can't\n    // do much anyway, so just pretend that we don't have a valid swap\n    // chain and move on. An alternative would be to handle errors in a\n    // loop, however this may also not be desireable since it could stall\n    // the app indefinitely in case the surface is in a weird state.\n    if (vr == VK_ERROR_SURFACE_LOST_KHR || vr == VK_ERROR_OUT_OF_DATE_KHR)\n      return VK_NOT_READY;\n\n    return vr;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_presenter.h",
    "content": "#pragma once\n\n#include <functional>\n#include <optional>\n#include <queue>\n#include <vector>\n\n#include \"../util/log/log.h\"\n\n#include \"../util/util_error.h\"\n#include \"../util/util_fps_limiter.h\"\n#include \"../util/util_math.h\"\n#include \"../util/util_string.h\"\n\n#include \"../util/sync/sync_signal.h\"\n\n#include \"../vulkan/vulkan_loader.h\"\n\n#include \"dxvk_format.h\"\n#include \"dxvk_image.h\"\n#include \"dxvk_latency.h\"\n\nnamespace dxvk {\n\n  using PresenterSurfaceProc = std::function<VkResult (VkSurfaceKHR*)>;\n\n  class DxvkDevice;\n\n  /**\n   * \\brief Presenter description\n   * \n   * Contains the desired properties of\n   * the swap chain. This is passed as\n   * an input during swap chain creation.\n   */\n  struct PresenterDesc {\n    bool deferSurfaceCreation = false;\n  };\n\n  /**\n   * \\brief Presenter semaphores\n   * \n   * Pair of semaphores used for acquire and present\n   * operations, including the command buffers used\n   * in between. Also stores a fence to signal on\n   * image acquisition.\n   */\n  struct PresenterSync {\n    VkSemaphore acquire = VK_NULL_HANDLE;\n    VkSemaphore present = VK_NULL_HANDLE;\n    VkFence fence = VK_NULL_HANDLE;\n    VkBool32 fenceSignaled = VK_FALSE;\n  };\n\n  /**\n   * \\brief Queued frame\n   */\n  struct PresenterFrame {\n    uint64_t                frameId   = 0u;\n    Rc<DxvkLatencyTracker>  tracker   = nullptr;\n    VkPresentModeKHR        mode      = VK_PRESENT_MODE_FIFO_KHR;\n    VkResult                result    = VK_NOT_READY;\n  };\n\n  /**\n   * \\brief Format compatibility list\n   */\n  struct PresenterFormatList {\n    VkColorSpaceKHR colorSpace;\n    size_t formatCount;\n    const VkFormat* formats;\n  };\n\n  /**\n   * \\brief Vulkan presenter\n   * \n   * Provides abstractions for some of the\n   * more complicated aspects of Vulkan's\n   * window system integration.\n   */\n  class Presenter : public RcObject {\n\n  public:\n\n    Presenter(\n      const Rc<DxvkDevice>&   device,\n      const Rc<sync::Signal>& signal,\n      const PresenterDesc&    desc,\n            PresenterSurfaceProc&& proc);\n    \n    ~Presenter();\n\n    /**\n     * \\brief Tests swap chain status\n     *\n     * If no swapchain currently exists, this method may create\n     * one so that presentation can subsequently be performed.\n     * \\returns One of the following return codes:\n     *  - \\c VK_SUCCESS if a valid swapchain exists\n     *  - \\c VK_NOT_READY if no swap chain can be created\n     *  - Any other error code if swap chain creation failed.\n     */\n    VkResult checkSwapChainStatus();\n\n    /**\n     * \\brief Acquires next image\n     *\n     * Tries to acquire an image from the underlying Vulkan\n     * swapchain. May recreate the swapchain if any surface\n     * properties or user-specified parameters have changed.\n     * Potentially blocks the calling thread, and must not be\n     * called if any present call is currently in flight.\n     * \\param [out] sync Synchronization semaphores\n     * \\param [out] image Acquired swap chain image\n     * \\returns Status of the operation. May return\n     *    \\c VK_NOT_READY if no swap chain exists.\n     */\n    VkResult acquireNextImage(\n            PresenterSync&  sync,\n            Rc<DxvkImage>&  image);\n    \n    /**\n     * \\brief Presents current image\n     * \n     * Presents the last successfuly acquired image.\n     * \\param [in] frameId Frame number.\n     * \\param [in] tracker Latency tracker\n     * \\returns Status of the operation\n     */\n    VkResult presentImage(\n            uint64_t                frameId,\n      const Rc<DxvkLatencyTracker>& tracker);\n\n    /**\n     * \\brief Signals a given frame\n     *\n     * Waits for the present operation to complete and then signals\n     * the presenter signal with the given frame ID. Must not be\n     * called before GPU work prior to the present submission has\n     * completed in order to maintain consistency.\n     * \\param [in] frameId Frame ID\n     * \\param [in] tracker Latency tracker\n     */\n    void signalFrame(\n            uint64_t                frameId,\n      const Rc<DxvkLatencyTracker>& tracker);\n\n    /**\n     * \\brief Changes sync interval\n     *\n     * Changes the Vulkan present mode as necessary.\n     * \\param [in] syncInterval New sync interval\n     */\n    void setSyncInterval(uint32_t syncInterval);\n\n    /**\n     * \\brief Changes maximum frame rate\n     *\n     * \\param [in] frameRate Target frame rate. Set\n     *    to 0 in order to disable the limiter.\n     */\n    void setFrameRateLimit(double frameRate, uint32_t maxLatency);\n\n    /**\n     * \\brief Sets preferred color space and format\n     *\n     * If the Vulkan surface does not natively support the given\n     * parameter combo, it will try to select a format and color\n     * space with similar properties.\n     * \\param [in] format Preferred surface format\n     */\n    void setSurfaceFormat(VkSurfaceFormatKHR format);\n\n    /**\n     * \\brief Sets preferred surface extent\n     *\n     * The preferred surface extent is only relevant if the Vulkan\n     * surface itself does not have a fixed size. Should match the\n     * back buffer size of the application.\n     * \\param [in] extent Preferred surface extent\n     */\n    void setSurfaceExtent(VkExtent2D extent);\n\n    /**\n     * \\brief Sets HDR metadata\n     *\n     * Updated HDR metadata will be applied on the next \\c acquire.\n     * \\param [in] hdrMetadata HDR Metadata\n     */\n    void setHdrMetadata(VkHdrMetadataEXT hdrMetadata);\n\n    /**\n     * \\brief Checks support for a Vulkan color space\n     *\n     * \\param [in] colorspace The color space to test\n     * \\returns \\c true if the Vulkan surface supports the colorspace\n     */\n    bool supportsColorSpace(VkColorSpaceKHR colorspace);\n\n    /**\n     * \\brief Invalidates Vulkan surface\n     *\n     * This will cause the Vulkan surface to be destroyed and\n     * recreated on the next \\c acquire call. This is a hacky\n     * workaround to support windows with multiple surfaces.\n     */\n    void invalidateSurface();\n\n    /**\n     * \\brief Destroys resources immediately\n     *\n     * Blocks calling thread until pending swapchain operations\n     * have completed, and guarantees that the Vulkan swapchain\n     * and surface get destroyed before the function returns.\n     * This is useful to ensure that the application window can\n     * be reused, even if the presenter object is kept alive.\n     */\n    void destroyResources();\n\n    /**\n     * \\brief Sets latency sleep mode\n     *\n     * Any changes will be applied on the next acquire operation.\n     * \\param [in] sleepMode Latency mode info\n     */\n    void setLatencySleepModeNv(\n      const VkLatencySleepModeInfoNV& sleepMode);\n\n    /**\n     * \\brief Sets latency marker\n     *\n     * Ignored if the current swapchain has not been\n     * created with low latency support.\n     * \\param [in] frameId Frame ID\n     * \\param [in] marker Marker\n     * \\returns CPU timestamp of the marker\n     */\n    dxvk::high_resolution_clock::time_point setLatencyMarkerNv(\n            uint64_t                frameId,\n            VkLatencyMarkerNV       marker);\n\n    /**\n     * \\brief Executes latency sleep\n     *\n     * Ignored if the current swapchain has not been\n     * created with low latency support.\n     * \\returns Sleep duration\n     */\n    dxvk::high_resolution_clock::duration latencySleepNv();\n\n    /**\n     * \\brief Queries latency timings\n     *\n     * \\param [in] timingCount Number of timings to query\n     * \\param [out] timings Latency timings\n     * \\returns Number of frame reports returned\n     */\n    uint32_t getLatencyTimingsNv(\n            uint32_t                timingCount,\n            VkLatencyTimingsFrameReportNV* timings);\n\n  private:\n\n    Rc<DxvkDevice>              m_device;\n    Rc<sync::Signal>            m_signal;\n\n    Rc<vk::InstanceFn>          m_vki;\n    Rc<vk::DeviceFn>            m_vkd;\n\n    PresenterSurfaceProc        m_surfaceProc;\n\n    dxvk::mutex                 m_surfaceMutex;\n    dxvk::condition_variable    m_surfaceCond;\n\n    VkSurfaceKHR                m_surface     = VK_NULL_HANDLE;\n    VkSwapchainKHR              m_swapchain   = VK_NULL_HANDLE;\n\n    VkFullScreenExclusiveEXT    m_fullscreenMode = VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT;\n\n    std::vector<Rc<DxvkImage>>  m_images;\n    std::vector<PresenterSync>  m_semaphores;\n\n    std::vector<VkPresentModeKHR> m_dynamicModes;\n\n    VkExtent2D                  m_preferredExtent = { };\n    VkSurfaceFormatKHR          m_preferredFormat = { };\n    uint32_t                    m_preferredSyncInterval = 1u;\n\n    bool                        m_dirtySwapchain = false;\n    bool                        m_dirtySurface = false;\n\n    bool                        m_hasPresentId = false;\n    bool                        m_hasPresentWait = false;\n    bool                        m_hasSwapchainMaintenance1 = false;\n\n    VkPresentModeKHR            m_presentMode = VK_PRESENT_MODE_FIFO_KHR;\n\n    uint32_t                    m_imageIndex = 0;\n    uint32_t                    m_frameIndex = 0;\n\n    VkResult                    m_acquireStatus = VK_NOT_READY;\n    bool                        m_presentPending = false;\n\n    std::optional<VkHdrMetadataEXT> m_hdrMetadata;\n    bool                        m_hdrMetadataDirty = false;\n\n    std::optional<VkLatencySleepModeInfoNV> m_latencySleepMode;\n    VkSemaphore                 m_latencySemaphore = VK_NULL_HANDLE;\n    uint64_t                    m_latencySleepCounter = 0u;\n\n    bool                        m_latencySleepModeDirty = false;\n    bool                        m_latencySleepSupported = false;\n\n    alignas(CACHE_LINE_SIZE)\n    dxvk::mutex                 m_frameMutex;\n    dxvk::condition_variable    m_frameCond;\n    dxvk::condition_variable    m_frameDrain;\n    dxvk::thread                m_frameThread;\n    std::queue<PresenterFrame>  m_frameQueue;\n\n    uint64_t                    m_lastSignaled = 0u;\n    uint64_t                    m_lastCompleted = 0u;\n\n    alignas(CACHE_LINE_SIZE)\n    FpsLimiter                  m_fpsLimiter;\n\n    bool                        m_hasGamescopeFenceSignalBug = false;\n\n    static const std::array<std::pair<VkColorSpaceKHR, VkColorSpaceKHR>, 2> s_colorSpaceFallbacks;\n\n    void updateSwapChain();\n\n    VkResult recreateSwapChain();\n\n    VkResult createSwapChain();\n\n    VkResult getSupportedFormats(\n            std::vector<VkSurfaceFormatKHR>& formats) const;\n    \n    VkResult getSupportedPresentModes(\n            std::vector<VkPresentModeKHR>& modes) const;\n    \n    VkResult getSwapImages(\n            std::vector<VkImage>&     images);\n    \n    VkSurfaceFormatKHR pickSurfaceFormat(\n            uint32_t                  numSupported,\n      const VkSurfaceFormatKHR*       pSupported,\n      const VkSurfaceFormatKHR&       desired);\n\n    VkColorSpaceKHR pickColorSpace(\n            uint32_t                  numSupported,\n      const VkSurfaceFormatKHR*       pSupported,\n            VkColorSpaceKHR           desired);\n\n    VkFormat pickFormat(\n            uint32_t                  numSupported,\n      const VkSurfaceFormatKHR*       pSupported,\n            VkColorSpaceKHR           colorSpace,\n            VkFormat                  format);\n\n    VkPresentModeKHR pickPresentMode(\n            uint32_t                  numSupported,\n      const VkPresentModeKHR*         pSupported,\n            uint32_t                  syncInterval);\n\n    VkExtent2D pickImageExtent(\n      const VkSurfaceCapabilitiesKHR& caps,\n            VkExtent2D                desired);\n\n    uint32_t pickImageCount(\n            uint32_t                  minImageCount,\n            uint32_t                  maxImageCount);\n\n    VkResult createSurface();\n\n    VkResult createLatencySemaphore();\n\n    void destroySwapchain();\n\n    void destroySurface();\n\n    void destroyLatencySemaphore();\n\n    void waitForSwapchainFence(\n            PresenterSync&            sync);\n\n    void runFrameThread();\n\n    static VkResult softError(\n            VkResult                  vr);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_queue.cpp",
    "content": "#include \"dxvk_device.h\"\n#include \"dxvk_queue.h\"\n\nnamespace dxvk {\n  \n  DxvkSubmissionQueue::DxvkSubmissionQueue(DxvkDevice* device, const DxvkQueueCallback& callback)\n  : m_device(device), m_callback(callback),\n    m_submitThread([this] () { submitCmdLists(); }),\n    m_finishThread([this] () { finishCmdLists(); }) {\n    auto vk = m_device->vkd();\n\n    VkSemaphoreTypeCreateInfo semaphoreType = { VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO };\n    semaphoreType.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;\n\n    VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &semaphoreType };\n\n    VkResult vrGraphics = vk->vkCreateSemaphore(vk->device(), &semaphoreInfo, nullptr, &m_semaphores.graphics);\n    VkResult vrTransfer = vk->vkCreateSemaphore(vk->device(), &semaphoreInfo, nullptr, &m_semaphores.transfer);\n\n    if (vrGraphics || vrTransfer) {\n      throw DxvkError(str::format(\"Failed to create timeline semaphores: \",\n        vrGraphics > vrTransfer ? vrGraphics : vrTransfer));\n    }\n  }\n  \n  \n  DxvkSubmissionQueue::~DxvkSubmissionQueue() {\n    auto vk = m_device->vkd();\n\n    { std::unique_lock<dxvk::mutex> lock(m_mutex);\n      m_stopped.store(true);\n    }\n\n    m_appendCond.notify_all();\n    m_submitCond.notify_all();\n\n    m_submitThread.join();\n    m_finishThread.join();\n\n    vk->vkDestroySemaphore(vk->device(), m_semaphores.graphics, nullptr);\n    vk->vkDestroySemaphore(vk->device(), m_semaphores.transfer, nullptr);\n  }\n  \n  \n  void DxvkSubmissionQueue::submit(\n          DxvkSubmitInfo            submitInfo,\n          DxvkLatencyInfo           latencyInfo,\n          DxvkSubmitStatus*         status) {\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n    m_finishCond.wait(lock, [this] {\n      return m_submitQueue.size() + m_finishQueue.size() <= MaxNumQueuedCommandBuffers;\n    });\n\n    DxvkSubmitEntry entry = { };\n    entry.status = status;\n    entry.submit = std::move(submitInfo);\n    entry.latency = std::move(latencyInfo);\n\n    m_submitQueue.push(std::move(entry));\n    m_appendCond.notify_all();\n  }\n\n\n  void DxvkSubmissionQueue::present(\n          DxvkPresentInfo           presentInfo,\n          DxvkLatencyInfo           latencyInfo,\n          DxvkSubmitStatus*         status) {\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n    DxvkSubmitEntry entry = { };\n    entry.status  = status;\n    entry.present = std::move(presentInfo);\n    entry.latency = std::move(latencyInfo);\n\n    m_submitQueue.push(std::move(entry));\n    m_appendCond.notify_all();\n  }\n\n\n  void DxvkSubmissionQueue::synchronizeSubmission(\n          DxvkSubmitStatus*   status) {\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n    m_submitCond.wait(lock, [status] {\n      return status->result.load() != VK_NOT_READY;\n    });\n  }\n\n\n  void DxvkSubmissionQueue::synchronize() {\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n    m_submitCond.wait(lock, [this] {\n      return m_submitQueue.empty();\n    });\n  }\n\n\n  void DxvkSubmissionQueue::waitForIdle() {\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n    m_submitCond.wait(lock, [this] {\n      return m_submitQueue.empty();\n    });\n\n    m_finishCond.wait(lock, [this] {\n      return m_finishQueue.empty();\n    });\n  }\n\n\n  void DxvkSubmissionQueue::lockDeviceQueue() {\n    m_mutexQueue.lock();\n\n    if (m_callback)\n      m_callback(true);\n  }\n\n\n  void DxvkSubmissionQueue::unlockDeviceQueue() {\n    if (m_callback)\n      m_callback(false);\n\n    m_mutexQueue.unlock();\n  }\n\n\n  void DxvkSubmissionQueue::submitCmdLists() {\n    env::setThreadName(\"dxvk-submit\");\n\n    uint64_t trackedSubmitId = 0u;\n    uint64_t trackedPresentId = 0u;\n\n    while (!m_stopped.load()) {\n      DxvkSubmitEntry entry;\n\n      { std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n        m_appendCond.wait(lock, [this] {\n          return m_stopped.load() || !m_submitQueue.empty();\n        });\n\n        if (m_stopped.load())\n          return;\n\n        entry = std::move(m_submitQueue.front());\n      }\n\n      // Submit command buffer to device\n      if (m_lastError != VK_ERROR_DEVICE_LOST) {\n        std::lock_guard<dxvk::mutex> lock(m_mutexQueue);\n\n        if (m_callback)\n          m_callback(true);\n\n        if (entry.submit.cmdList != nullptr) {\n          if (entry.latency.tracker) {\n            entry.latency.tracker->notifyQueueSubmit(entry.latency.frameId);\n\n            if (!trackedSubmitId && entry.latency.frameId > trackedPresentId)\n              trackedSubmitId = entry.latency.frameId;\n          }\n\n          entry.result = entry.submit.cmdList->submit(\n            m_semaphores, m_timelines, trackedSubmitId);\n          entry.timelines = m_timelines;\n        } else if (entry.present.presenter != nullptr) {\n          if (entry.latency.tracker)\n            entry.latency.tracker->notifyQueuePresentBegin(entry.latency.frameId);\n\n          entry.result = entry.present.presenter->presentImage(\n            entry.present.frameId, entry.latency.tracker);\n\n          if (entry.latency.tracker) {\n            entry.latency.tracker->notifyQueuePresentEnd(\n              entry.latency.frameId, entry.result);\n\n            trackedPresentId = entry.latency.frameId;\n            trackedSubmitId = 0u;\n          }\n        }\n\n        if (m_callback)\n          m_callback(false);\n      } else {\n        // Don't submit anything after device loss\n        // so that drivers get a chance to recover\n        entry.result = VK_ERROR_DEVICE_LOST;\n      }\n\n      if (entry.status)\n        entry.status->result = entry.result;\n      \n      // On success, pass it on to the queue thread\n      { std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n        bool doForward = (entry.result == VK_SUCCESS) ||\n          (entry.present.presenter != nullptr && entry.result != VK_ERROR_DEVICE_LOST);\n\n        if (doForward) {\n          m_finishQueue.push(std::move(entry));\n        } else {\n          Logger::err(str::format(\"DxvkSubmissionQueue: Command submission failed: \", entry.result));\n          m_lastError = entry.result;\n\n          if (m_lastError != VK_ERROR_DEVICE_LOST)\n            m_device->waitForIdle();\n        }\n\n        m_submitQueue.pop();\n        m_submitCond.notify_all();\n      }\n\n      // Good time to invoke allocator tasks now since we\n      // expect this to get called somewhat periodically.\n      m_device->m_objects.memoryManager().performTimedTasks();\n    }\n  }\n  \n  \n  void DxvkSubmissionQueue::finishCmdLists() {\n    env::setThreadName(\"dxvk-queue\");\n\n    auto vk = m_device->vkd();\n\n    while (!m_stopped.load()) {\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n      if (m_finishQueue.empty()) {\n        auto t0 = dxvk::high_resolution_clock::now();\n\n        m_submitCond.wait(lock, [this] {\n          return m_stopped.load() || !m_finishQueue.empty();\n        });\n\n        auto t1 = dxvk::high_resolution_clock::now();\n        m_gpuIdle += std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();\n      }\n\n      if (m_stopped.load())\n        return;\n      \n      DxvkSubmitEntry entry = std::move(m_finishQueue.front());\n      lock.unlock();\n      \n      if (entry.submit.cmdList != nullptr) {\n        VkResult status = m_lastError.load();\n\n        if (status != VK_ERROR_DEVICE_LOST) {\n          std::array<VkSemaphore, 2> semaphores = { m_semaphores.graphics, m_semaphores.transfer };\n          std::array<uint64_t, 2> timelines = { entry.timelines.graphics, entry.timelines.transfer };\n\n          if (entry.latency.tracker)\n            entry.latency.tracker->notifyGpuExecutionBegin(entry.latency.frameId);\n\n          VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };\n          waitInfo.semaphoreCount = semaphores.size();\n          waitInfo.pSemaphores = semaphores.data();\n          waitInfo.pValues = timelines.data();\n\n          status = vk->vkWaitSemaphores(vk->device(), &waitInfo, ~0ull);\n\n          if (entry.latency.tracker && status == VK_SUCCESS)\n            entry.latency.tracker->notifyGpuExecutionEnd(entry.latency.frameId);\n        }\n\n        if (status != VK_SUCCESS) {\n          m_lastError = status;\n\n          if (status != VK_ERROR_DEVICE_LOST)\n            m_device->waitForIdle();\n        }\n      } else if (entry.present.presenter != nullptr) {\n        // Signal the frame and then immediately destroy the reference.\n        // This is necessary since the front-end may want to explicitly\n        // destroy the presenter object. \n        entry.present.presenter->signalFrame(entry.present.frameId, entry.latency.tracker);\n        entry.present.presenter = nullptr;\n      }\n\n      // Release resources and signal events, then immediately wake\n      // up any thread that's currently waiting on a resource in\n      // order to reduce delays as much as possible.\n      if (entry.submit.cmdList != nullptr)\n        entry.submit.cmdList->notifyObjects();\n\n      lock.lock();\n      m_finishQueue.pop();\n      m_finishCond.notify_all();\n      lock.unlock();\n\n      // Free the command list and associated objects now\n      if (entry.submit.cmdList != nullptr) {\n        entry.submit.cmdList->reset();\n        m_device->recycleCommandList(entry.submit.cmdList);\n      }\n    }\n  }\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_queue.h",
    "content": "#pragma once\n\n#include <condition_variable>\n#include <mutex>\n#include <queue>\n\n#include \"../util/thread.h\"\n\n#include \"dxvk_cmdlist.h\"\n#include \"dxvk_latency.h\"\n#include \"dxvk_presenter.h\"\n\nnamespace dxvk {\n  \n  class DxvkDevice;\n\n  /**\n   * \\brief Submission status\n   * \n   * Stores the result of a queue\n   * submission or a present call.\n   */\n  struct DxvkSubmitStatus {\n    std::atomic<VkResult> result = { VK_SUCCESS };\n  };\n\n\n  /**\n   * \\brief Queue submission info\n   * \n   * Stores parameters used to submit\n   * a command buffer to the device.\n   */\n  struct DxvkSubmitInfo {\n    Rc<DxvkCommandList> cmdList;\n  };\n  \n  \n  /**\n   * \\brief Present info\n   *\n   * Stores parameters used to present\n   * a swap chain image on the device.\n   */\n  struct DxvkPresentInfo {\n    Rc<Presenter>       presenter;\n    uint64_t            frameId;\n  };\n\n\n  /**\n   * \\brief Latency info\n   *\n   * Optionally stores a latency tracker\n   * and the associated frame ID.\n   */\n  struct DxvkLatencyInfo {\n    Rc<DxvkLatencyTracker>  tracker;\n    uint64_t                frameId = 0;\n  };\n\n\n  /**\n   * \\brief Submission queue entry\n   */\n  struct DxvkSubmitEntry {\n    VkResult            result;\n    DxvkSubmitStatus*   status;\n    DxvkSubmitInfo      submit;\n    DxvkPresentInfo     present;\n    DxvkLatencyInfo     latency;\n    DxvkTimelineSemaphoreValues timelines;\n  };\n\n\n  /**\n   * \\brief Submission queue\n   */\n  class DxvkSubmissionQueue {\n\n  public:\n    \n    DxvkSubmissionQueue(\n            DxvkDevice*         device,\n      const DxvkQueueCallback&  callback);\n\n    ~DxvkSubmissionQueue();\n\n    /**\n     * \\brief Retrieves estimated GPU idle time\n     *\n     * This is a monotonically increasing counter\n     * which can be evaluated periodically in order\n     * to calculate the GPU load.\n     * \\returns Accumulated GPU idle time, in us\n     */\n    uint64_t gpuIdleTicks() const {\n      return m_gpuIdle.load();\n    }\n\n    /**\n     * \\brief Retrieves last submission error\n     * \n     * In case an error occured during asynchronous command\n     * submission, it will be returned by this function.\n     * \\returns Last error from command submission\n     */\n    VkResult getLastError() const {\n      return m_lastError.load();\n    }\n    \n    /**\n     * \\brief Submits a command list asynchronously\n     * \n     * Queues a command list for submission on the\n     * dedicated submission thread. Use this to take\n     * the submission overhead off the calling thread.\n     * \\param [in] submitInfo Submission parameters\n     * \\param [in] latencyInfo Latency tracker info\n     * \\param [out] status Submission feedback\n     */\n    void submit(\n            DxvkSubmitInfo      submitInfo,\n            DxvkLatencyInfo     latencyInfo,\n            DxvkSubmitStatus*   status);\n    \n    /**\n     * \\brief Presents an image synchronously\n     *\n     * Waits for queued command lists to be submitted\n     * and then presents the current swap chain image\n     * of the presenter. May stall the calling thread.\n     * \\param [in] present Present parameters\n     * \\param [in] latencyInfo Latency tracker info\n     * \\param [out] status Submission feedback\n     */\n    void present(\n            DxvkPresentInfo     presentInfo,\n            DxvkLatencyInfo     latencyInfo,\n            DxvkSubmitStatus*   status);\n    \n    /**\n     * \\brief Synchronizes with one queue submission\n     * \n     * Waits for the result of the given submission\n     * or present operation to become available.\n     * \\param [in,out] status Submission status\n     */\n    void synchronizeSubmission(\n            DxvkSubmitStatus*   status);\n    \n    /**\n     * \\brief Synchronizes with queue submissions\n     * \n     * Waits for all pending command lists to be\n     * submitted to the GPU before returning.\n     */\n    void synchronize();\n\n    /**\n     * \\brief Synchronizes until a given condition becomes true\n     *\n     * Useful to wait for the GPU without busy-waiting.\n     * \\param [in] pred Predicate to check\n     */\n    template<typename Pred>\n    void synchronizeUntil(const Pred& pred) {\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n      m_finishCond.wait(lock, pred);\n    }\n\n    /**\n     * \\brief Waits for all submissions to complete\n     */\n    void waitForIdle();\n\n    /**\n     * \\brief Locks device queue\n     *\n     * Locks the mutex that protects the Vulkan queue\n     * that DXVK uses for command buffer submission.\n     * This is needed when the app submits its own\n     * command buffers to the queue.\n     */\n    void lockDeviceQueue();\n\n    /**\n     * \\brief Unlocks device queue\n     *\n     * Unlocks the mutex that protects the Vulkan\n     * queue used for command buffer submission.\n     */\n    void unlockDeviceQueue();\n    \n  private:\n\n    DxvkDevice*                 m_device;\n    DxvkQueueCallback           m_callback;\n\n    DxvkTimelineSemaphores      m_semaphores;\n    DxvkTimelineSemaphoreValues m_timelines;\n\n    std::atomic<VkResult>       m_lastError = { VK_SUCCESS };\n    \n    std::atomic<bool>           m_stopped = { false };\n    std::atomic<uint64_t>       m_gpuIdle = { 0ull };\n\n    dxvk::mutex                 m_mutex;\n    dxvk::mutex                 m_mutexQueue;\n    \n    dxvk::condition_variable    m_appendCond;\n    dxvk::condition_variable    m_submitCond;\n    dxvk::condition_variable    m_finishCond;\n\n    std::queue<DxvkSubmitEntry> m_submitQueue;\n    std::queue<DxvkSubmitEntry> m_finishQueue;\n\n    dxvk::thread                m_submitThread;\n    dxvk::thread                m_finishThread;\n\n    void submitCmdLists();\n\n    void finishCmdLists();\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_recycler.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <vector>\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Object recycler\n   * \n   * Implements a thread-safe buffer that can store up to\n   * a given number of objects of a certain type. This way,\n   * DXVK can efficiently reuse and reset objects instead\n   * of destroying them and creating them anew.\n   * \\tparam T Type of the objects to store\n   * \\tparam N Maximum number of objects to store\n   */\n  template<typename T, size_t N>\n  class DxvkRecycler {\n    \n  public:\n    \n    /**\n     * \\brief Retrieves an object if possible\n     * \n     * Returns an object that was returned to the recycler\n     * earier. In case no objects are available, this will\n     * return \\c nullptr and a new object has to be created.\n     * \\return An object, or \\c nullptr\n     */\n    Rc<T> retrieveObject() {\n      std::lock_guard<dxvk::mutex> lock(m_mutex);\n      \n      if (m_get == m_put)\n        return nullptr;\n      \n      return std::exchange(m_objects[(m_get++) % N], Rc<T>());\n    }\n    \n    /**\n     * \\brief Returns an object to the recycler\n     * \n     * If the buffer is full, the object will be destroyed\n     * once the last reference runs out of scope. No further\n     * action needs to be taken in this case.\n     * \\param [in] object The object to return\n     */\n    void returnObject(const Rc<T>& object) {\n      std::lock_guard<dxvk::mutex> lock(m_mutex);\n      \n      if (m_put - m_get < N)\n        m_objects[(m_put++) % N] = object;\n    }\n    \n  private:\n    \n    dxvk::mutex           m_mutex;\n    std::array<Rc<T>, N>  m_objects;\n\n    uint64_t              m_get = 0;\n    uint64_t              m_put = 0;\n    \n  };\n  \n}"
  },
  {
    "path": "src/dxvk/dxvk_renderpass.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <vector>\n#include <unordered_map>\n\n#include \"dxvk_hash.h\"\n#include \"dxvk_include.h\"\n#include \"dxvk_limits.h\"\n\nnamespace dxvk {\n\n  class DxvkDevice;\n  \n  /**\n   * \\brief Color attachment transitions\n   * \n   * Stores the load/store ops and the initial\n   * and final layout of a single attachment.\n   */\n  struct DxvkColorAttachmentOps {\n    VkAttachmentLoadOp loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    VkClearColorValue clearValue = VkClearColorValue();\n  };\n  \n  \n  /**\n   * \\brief Depth attachment transitions\n   * \n   * Stores the load/store ops and the initial and\n   * final layout of the depth-stencil attachment.\n   */\n  struct DxvkDepthAttachmentOps {\n    VkAttachmentLoadOp loadOpD = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    VkAttachmentLoadOp loadOpS = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    VkClearDepthStencilValue clearValue = VkClearDepthStencilValue();\n  };\n  \n  \n  /**\n   * \\brief Render pass transitions\n   * \n   * Stores transitions for all depth and color attachments.\n   * This is used to select a specific render pass object\n   * from a group of render passes with the same format.\n   */\n  struct DxvkRenderPassOps {\n    DxvkDepthAttachmentOps depthOps;\n    DxvkColorAttachmentOps colorOps[MaxNumRenderTargets];\n  };\n \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_sampler.cpp",
    "content": "#include <algorithm>\n\n#include \"dxvk_buffer.h\"\n#include \"dxvk_device.h\"\n#include \"dxvk_format.h\"\n#include \"dxvk_sampler.h\"\n\nnamespace dxvk {\n    \n  DxvkSampler::DxvkSampler(\n          DxvkSamplerPool*        pool,\n    const DxvkSamplerKey&         key,\n          uint16_t                index)\n  : m_pool(pool), m_key(key) {\n    auto vk = m_pool->m_device->vkd();\n\n    auto formatInfo = lookupFormatInfo(VkFormat(key.u.p.viewFormat));\n\n    // We generally want to preserve the border color as-is, and only apply the inverse\n    // swizzle if the device applies the image view swizzle to border colors as well.\n    VkSamplerBorderColorComponentMappingCreateInfoEXT borderColorSwizzle = { VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT };\n    borderColorSwizzle.components = { VkComponentSwizzle(key.u.p.viewSwizzleR), VkComponentSwizzle(key.u.p.viewSwizzleG),\n                                      VkComponentSwizzle(key.u.p.viewSwizzleB), VkComponentSwizzle(key.u.p.viewSwizzleA) };\n    borderColorSwizzle.srgb = formatInfo && formatInfo->flags.test(DxvkFormatFlag::ColorSpaceSrgb);\n\n    VkSamplerCustomBorderColorCreateInfoEXT borderColorInfo = { VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT };\n    borderColorInfo.customBorderColor = swizzleBorderColor(key.borderColor, borderColorSwizzle.components);\n\n    if (!m_pool->m_device->features().extCustomBorderColor.customBorderColorWithoutFormat)\n      borderColorInfo.format = VkFormat(key.u.p.viewFormat);\n\n    VkSamplerReductionModeCreateInfo reductionInfo = { VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO };\n    reductionInfo.reductionMode = VkSamplerReductionMode(key.u.p.reduction);\n\n    VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };\n    samplerInfo.magFilter = VkFilter(key.u.p.magFilter);\n    samplerInfo.minFilter = VkFilter(key.u.p.minFilter);\n    samplerInfo.mipmapMode = VkSamplerMipmapMode(key.u.p.mipMode);\n    samplerInfo.addressModeU = VkSamplerAddressMode(key.u.p.addressU);\n    samplerInfo.addressModeV = VkSamplerAddressMode(key.u.p.addressV);\n    samplerInfo.addressModeW = VkSamplerAddressMode(key.u.p.addressW);\n    samplerInfo.mipLodBias = bit::decodeFixed<int32_t, 6, 8>(key.u.p.lodBias);\n    samplerInfo.anisotropyEnable = key.u.p.anisotropy > 0u;\n    samplerInfo.maxAnisotropy = float(key.u.p.anisotropy);\n    samplerInfo.compareEnable = key.u.p.compareEnable != 0u;\n    samplerInfo.compareOp = VkCompareOp(key.u.p.compareOp);\n    samplerInfo.minLod = bit::decodeFixed<uint32_t, 4, 8>(key.u.p.minLod);\n    samplerInfo.maxLod = bit::decodeFixed<uint32_t, 4, 8>(key.u.p.maxLod);\n    samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;\n    samplerInfo.unnormalizedCoordinates = key.u.p.pixelCoord;\n\n    if (key.u.p.legacyCube && m_pool->m_device->features().extNonSeamlessCubeMap.nonSeamlessCubeMap)\n      samplerInfo.flags |= VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT;\n\n    if (!m_pool->m_device->features().core.features.samplerAnisotropy)\n      samplerInfo.anisotropyEnable = VK_FALSE;\n\n    if (key.u.p.hasBorder) {\n      samplerInfo.borderColor = determineBorderColorType(borderColorInfo);\n\n      if (m_pool->m_device->features().extBorderColorSwizzle.borderColorSwizzle\n       && !m_pool->m_device->features().extBorderColorSwizzle.borderColorSwizzleFromImage)\n        borderColorSwizzle.pNext = std::exchange(samplerInfo.pNext, &borderColorSwizzle);\n    }\n\n    if (samplerInfo.borderColor == VK_BORDER_COLOR_FLOAT_CUSTOM_EXT\n     || samplerInfo.borderColor == VK_BORDER_COLOR_INT_CUSTOM_EXT)\n      borderColorInfo.pNext = std::exchange(samplerInfo.pNext, &borderColorInfo);\n\n    if (reductionInfo.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE)\n      reductionInfo.pNext = std::exchange(samplerInfo.pNext, &reductionInfo);\n\n    m_descriptor = m_pool->m_descriptorHeap.createSampler(index, &samplerInfo);\n  }\n\n\n  DxvkSampler::~DxvkSampler() {\n    m_pool->m_descriptorHeap.freeSampler(m_descriptor);\n  }\n\n\n  void DxvkSampler::release() {\n    m_pool->releaseSampler(m_descriptor.samplerIndex);\n  }\n\n\n  VkBorderColor DxvkSampler::determineBorderColorType(const VkSamplerCustomBorderColorCreateInfoEXT& info) const {\n    static const std::array<std::pair<VkClearColorValue, VkBorderColor>, 4> s_borderColors = {{\n      { { { 0.0f, 0.0f, 0.0f, 0.0f } }, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK },\n      { { { 0.0f, 0.0f, 0.0f, 1.0f } }, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK },\n      { { { 1.0f, 1.0f, 1.0f, 1.0f } }, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE },\n    }};\n\n    // Iterate over border colors and try to find an exact match\n    uint32_t componentCount = m_key.u.p.compareEnable ? 1u : 4u;\n\n    for (const auto& e : s_borderColors) {\n      bool allEqual = true;\n\n      for (uint32_t i = 0; i < componentCount; i++)\n        allEqual &= info.customBorderColor.float32[i] == e.first.float32[i];\n\n      if (allEqual)\n        return e.second;\n    }\n\n    // If custom border colors are supported, use that\n    if (m_pool->m_device->features().extCustomBorderColor.customBorderColors\n     && (m_pool->m_device->features().extCustomBorderColor.customBorderColorWithoutFormat || info.format))\n      return VK_BORDER_COLOR_FLOAT_CUSTOM_EXT;\n\n    // Otherwise, use the sum of absolute differences to find the\n    // closest fallback value. Some D3D9 games may rely on this.\n    Logger::warn(\"DXVK: Custom border colors not supported\");\n\n    VkBorderColor result = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;\n\n    float minSad = -1.0f;\n\n    for (const auto& e : s_borderColors) {\n      float sad = 0.0f;\n\n      for (uint32_t i = 0; i < componentCount; i++)\n        sad += std::abs(info.customBorderColor.float32[i] - e.first.float32[i]);\n\n      if (sad < minSad || minSad < 0.0f) {\n        minSad = sad;\n        result = e.second;\n      }\n    }\n\n    return result;\n  }\n\n\n  VkClearColorValue DxvkSampler::swizzleBorderColor(const VkClearColorValue& color, VkComponentMapping mapping) {\n    // Normalize component mapping for inverse look-up\n    if (mapping.r == VK_COMPONENT_SWIZZLE_IDENTITY) mapping.r = VK_COMPONENT_SWIZZLE_R;\n    if (mapping.g == VK_COMPONENT_SWIZZLE_IDENTITY) mapping.g = VK_COMPONENT_SWIZZLE_G;\n    if (mapping.b == VK_COMPONENT_SWIZZLE_IDENTITY) mapping.b = VK_COMPONENT_SWIZZLE_B;\n    if (mapping.a == VK_COMPONENT_SWIZZLE_IDENTITY) mapping.a = VK_COMPONENT_SWIZZLE_A;\n\n    VkClearColorValue result = { };\n    result.float32[0] = mapBorderColorComponent(color, mapping, VK_COMPONENT_SWIZZLE_R);\n    result.float32[1] = mapBorderColorComponent(color, mapping, VK_COMPONENT_SWIZZLE_G);\n    result.float32[2] = mapBorderColorComponent(color, mapping, VK_COMPONENT_SWIZZLE_B);\n    result.float32[3] = mapBorderColorComponent(color, mapping, VK_COMPONENT_SWIZZLE_A);\n    return result;\n  }\n\n\n  float DxvkSampler::mapBorderColorComponent(const VkClearColorValue& color, const VkComponentMapping& mapping, VkComponentSwizzle which) {\n    // Apply inverse swizzle so that applying the view swizzle\n    // returns the intended border color to the extent possible.\n    if (mapping.r == which) return color.float32[0u];\n    if (mapping.g == which) return color.float32[1u];\n    if (mapping.b == which) return color.float32[2u];\n    if (mapping.a == which) return color.float32[3u];\n\n    // The border color component itself isn't used at all,\n    // check whether it is mapped to a special value.\n    VkComponentSwizzle swizzle = which;\n\n    if (which == VK_COMPONENT_SWIZZLE_R) swizzle = mapping.r;\n    if (which == VK_COMPONENT_SWIZZLE_G) swizzle = mapping.g;\n    if (which == VK_COMPONENT_SWIZZLE_B) swizzle = mapping.b;\n    if (which == VK_COMPONENT_SWIZZLE_A) swizzle = mapping.a;\n\n    return swizzle == VK_COMPONENT_SWIZZLE_ONE ? 1.0f : 0.0f;\n  }\n\n\n\n\n  DxvkSamplerDescriptorHeap::DxvkSamplerDescriptorHeap(\n          DxvkDevice*               device,\n          uint32_t                  size)\n  : m_device(device), m_descriptorCount(size) {\n    if (!device->canUseDescriptorHeap())\n      initDescriptorLayout();\n\n    if (device->canUseDescriptorHeap() || device->canUseDescriptorBuffer())\n      initDescriptorHeap();\n    else\n      initDescriptorPool();\n  }\n\n\n  DxvkSamplerDescriptorHeap::~DxvkSamplerDescriptorHeap() {\n    auto vk = m_device->vkd();\n\n    vk->vkDestroyDescriptorPool(vk->device(), m_legacy.pool, nullptr);\n    vk->vkDestroyDescriptorSetLayout(vk->device(), m_legacy.setLayout, nullptr);\n  }\n\n\n  DxvkSamplerDescriptorSet DxvkSamplerDescriptorHeap::getDescriptorSetInfo() const {\n    DxvkSamplerDescriptorSet result = { };\n    result.set = m_legacy.set;\n    result.layout = m_legacy.setLayout;\n    return result;\n  }\n\n\n  DxvkDescriptorHeapBindingInfo DxvkSamplerDescriptorHeap::getDescriptorHeapInfo() const {\n    auto bufferInfo = m_heap.buffer->getSliceInfo();\n\n    DxvkDescriptorHeapBindingInfo result = { };\n    result.buffer = bufferInfo.buffer;\n    result.gpuAddress = bufferInfo.gpuAddress;\n    result.bufferSize = bufferInfo.size;\n\n    if (m_device->canUseDescriptorHeap())\n      result.reservedSize = m_heap.reservedSize;\n\n    return result;\n  }\n\n\n  DxvkSamplerDescriptor DxvkSamplerDescriptorHeap::createSampler(\n          uint16_t              index,\n    const VkSamplerCreateInfo*  createInfo) {\n    auto vk = m_device->vkd();\n\n    DxvkSamplerDescriptor descriptor = { };\n    descriptor.samplerIndex = index;\n\n    if (!m_device->canUseDescriptorHeap() || m_device->hasCudaInterop()) {\n      VkResult vr = vk->vkCreateSampler(vk->device(), createInfo, nullptr, &descriptor.samplerObject);\n\n      if (vr)\n        throw DxvkError(str::format(\"Failed to create sampler object: \", vr));\n    }\n\n    if (m_device->canUseDescriptorHeap()) {\n      auto borderColorInfo = findBorderColorInfo(createInfo->pNext);\n\n      VkSamplerCreateInfo samplerInfo = *createInfo;\n\n      // Find or allocate custom border color, and fall back to\n      // TRANSPARENT_BLACK if this fails.\n      VkSamplerCustomBorderColorIndexCreateInfoEXT borderColorIndex = { VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_INDEX_CREATE_INFO_EXT };\n\n      if (borderColorInfo) {\n        borderColorIndex.index = allocBorderColor(index, borderColorInfo);\n\n        if (borderColorIndex.index != InvalidBorderColor) {\n          borderColorIndex.pNext = std::exchange(samplerInfo.pNext, &borderColorIndex);\n        } else {\n          samplerInfo.borderColor = (samplerInfo.borderColor == VK_BORDER_COLOR_INT_CUSTOM_EXT)\n            ? VK_BORDER_COLOR_INT_TRANSPARENT_BLACK\n            : VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;\n        }\n      }\n\n      VkHostAddressRangeEXT hostRange = { };\n      hostRange.address = m_heap.buffer->mapPtr(m_heap.reservedSize + m_heap.descriptorSize * index);\n      hostRange.size = m_heap.descriptorSize;\n\n      VkResult vr = vk->vkWriteSamplerDescriptorsEXT(vk->device(), 1u, &samplerInfo, &hostRange);\n\n      if (vr) {\n        freeBorderColor(index);\n        throw DxvkError(str::format(\"Failed to write Vulkan sampler descriptor: \", vr));\n      }\n    } else if (m_device->canUseDescriptorBuffer()) {\n      VkDescriptorGetInfoEXT info = { VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT };\n      info.type = VK_DESCRIPTOR_TYPE_SAMPLER;\n      info.data.pSampler = &descriptor.samplerObject;\n\n      vk->vkGetDescriptorEXT(vk->device(), &info, m_heap.descriptorSize,\n        m_heap.buffer->mapPtr(m_heap.descriptorOffset + m_heap.descriptorSize * index));\n    } else {\n      VkDescriptorImageInfo samplerInfo = { };\n      samplerInfo.sampler = descriptor.samplerObject;\n\n      VkWriteDescriptorSet write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };\n      write.dstSet = m_legacy.set;\n      write.dstArrayElement = index;\n      write.descriptorCount = 1u;\n      write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;\n      write.pImageInfo = &samplerInfo;\n\n      vk->vkUpdateDescriptorSets(vk->device(), 1u, &write, 0u, nullptr);\n    }\n\n    return descriptor;\n  }\n\n\n  void DxvkSamplerDescriptorHeap::freeSampler(\n          DxvkSamplerDescriptor sampler) {\n    auto vk = m_device->vkd();\n\n    if (m_device->canUseDescriptorHeap())\n      freeBorderColor(sampler.samplerIndex);\n\n    vk->vkDestroySampler(vk->device(), sampler.samplerObject, nullptr);\n  }\n\n\n  void DxvkSamplerDescriptorHeap::initDescriptorLayout() {\n    auto vk = m_device->vkd();\n\n    VkDescriptorSetLayoutBinding binding = { };\n    binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;\n    binding.descriptorCount = m_descriptorCount;\n    binding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS | VK_SHADER_STAGE_COMPUTE_BIT;\n\n    VkDescriptorBindingFlags bindingFlags = 0u;\n\n    if (!m_device->canUseDescriptorBuffer()) {\n      bindingFlags |= VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT\n                   |  VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT\n                   |  VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT;\n    }\n\n    VkDescriptorSetLayoutBindingFlagsCreateInfo layoutFlags = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO };\n    layoutFlags.bindingCount = 1u;\n    layoutFlags.pBindingFlags = &bindingFlags;\n\n    VkDescriptorSetLayoutCreateInfo layoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, &layoutFlags };\n    layoutInfo.flags = m_device->canUseDescriptorBuffer()\n      ? VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT\n      : VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT;\n    layoutInfo.bindingCount = 1u;\n    layoutInfo.pBindings = &binding;\n\n    VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &layoutInfo, nullptr, &m_legacy.setLayout);\n\n    if (vr)\n      throw DxvkError(str::format(\"Failed to create sampler descriptor set layout: \", vr));\n  }\n\n\n  void DxvkSamplerDescriptorHeap::initDescriptorPool() {\n    auto vk = m_device->vkd();\n\n    VkDescriptorPoolSize poolSize = { };\n    poolSize.type = VK_DESCRIPTOR_TYPE_SAMPLER;\n    poolSize.descriptorCount = m_descriptorCount;\n\n    VkDescriptorPoolCreateInfo poolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };\n    poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT;\n    poolInfo.maxSets = 1u;\n    poolInfo.poolSizeCount = 1u;\n    poolInfo.pPoolSizes = &poolSize;\n\n    VkResult vr = vk->vkCreateDescriptorPool(vk->device(), &poolInfo, nullptr, &m_legacy.pool);\n\n    if (vr)\n      throw DxvkError(str::format(\"Failed to create sampler pool: \", vr));\n\n    VkDescriptorSetAllocateInfo setInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };\n    setInfo.descriptorPool = m_legacy.pool;\n    setInfo.descriptorSetCount = 1u;\n    setInfo.pSetLayouts = &m_legacy.setLayout;\n\n    if ((vr = vk->vkAllocateDescriptorSets(vk->device(), &setInfo, &m_legacy.set)))\n      throw DxvkError(str::format(\"Failed to allocate sampler descriptor set: \", vr));\n  }\n\n\n  void DxvkSamplerDescriptorHeap::initDescriptorHeap() {\n    auto vk = m_device->vkd();\n\n    DxvkBufferCreateInfo bufferInfo = { };\n    bufferInfo.usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;\n    bufferInfo.debugName = \"Sampler heap\";\n\n    if (m_device->canUseDescriptorHeap()) {\n      const auto& properties = m_device->properties().extDescriptorHeap;\n\n      // Descriptor size may be smaller than the required alignment, be sure to pad\n      m_heap.descriptorSize = align(properties.samplerDescriptorSize, properties.samplerDescriptorAlignment);\n      m_heap.reservedSize = properties.minSamplerHeapReservedRange;\n\n      bufferInfo.usage |= VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT;\n      bufferInfo.size = m_heap.reservedSize + m_heap.descriptorSize * m_descriptorCount;\n    } else {\n      const auto& properties = m_device->properties().extDescriptorBuffer;\n      m_heap.descriptorSize = properties.samplerDescriptorSize;\n\n      bufferInfo.usage |= VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;\n\n      vk->vkGetDescriptorSetLayoutSizeEXT(vk->device(), m_legacy.setLayout, &bufferInfo.size);\n      vk->vkGetDescriptorSetLayoutBindingOffsetEXT(vk->device(), m_legacy.setLayout, 0u, &m_heap.descriptorOffset);\n    }\n\n    Logger::info(str::format(\"Creating sampler descriptor heap (\", bufferInfo.size >> 10u, \" kB)\"));\n\n    m_heap.buffer = m_device->createBuffer(bufferInfo,\n      VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |\n      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n      VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n  }\n\n\n  uint32_t DxvkSamplerDescriptorHeap::registerBorderColor(\n    const VkSamplerCustomBorderColorCreateInfoEXT*  borderColor) {\n    // Make sure not to pass any random pNext chains to the driver\n    VkSamplerCustomBorderColorCreateInfoEXT borderColorInfo = *borderColor;\n    borderColorInfo.pNext = nullptr;\n\n    // Try to find matching border color before registering a new one\n    for (uint32_t i = 0u; i < m_borderColors.infos.size(); i++) {\n      auto& color = m_borderColors.infos.at(i);\n\n      if (color.useCount) {\n        bool match = color.format == borderColorInfo.format;\n\n        for (uint32_t i = 0u; i < 4u && match; i++)\n          match = color.color.uint32[i] == borderColorInfo.customBorderColor.uint32[i];\n\n        if (match) {\n          color.useCount++;\n          return i;\n        }\n      }\n    }\n\n    // Try to register a new border color at a free index, but account for\n    // the possibility that an external source may have already registered\n    // that index.\n    auto vk = m_device->vkd();\n\n    for (uint32_t i = 0u; i < m_device->properties().extCustomBorderColor.maxCustomBorderColorSamplers; i++) {\n      if (i < m_borderColors.infos.size() && m_borderColors.infos[i].useCount)\n        continue;\n\n      if (vk->vkRegisterCustomBorderColorEXT(vk->device(), &borderColorInfo, VK_TRUE, &i) == VK_SUCCESS) {\n        if (i >= m_borderColors.infos.size())\n          m_borderColors.infos.resize(i + 1u);\n\n        auto& color = m_borderColors.infos.at(i);\n        color.format = borderColorInfo.format;\n        color.color = borderColorInfo.customBorderColor;\n        color.useCount = 1u;\n        return i;\n      }\n    }\n\n    Logger::err(str::format(\"Failed to register border color\"));\n    return InvalidBorderColor;\n  }\n\n\n  uint32_t DxvkSamplerDescriptorHeap::allocBorderColor(\n          uint16_t                                  sampler,\n    const VkSamplerCustomBorderColorCreateInfoEXT*  borderColor) {\n    std::lock_guard lock(m_borderColors.mutex);\n\n    uint32_t borderColorIndex = registerBorderColor(borderColor);\n\n    if (sampler >= m_borderColors.indexForSampler.size())\n      m_borderColors.indexForSampler.resize(sampler + 1u);\n\n    m_borderColors.indexForSampler.at(sampler) = borderColorIndex;\n    return borderColorIndex;\n  }\n\n\n  void DxvkSamplerDescriptorHeap::freeBorderColor(uint16_t sampler) {\n    std::lock_guard lock(m_borderColors.mutex);\n\n    // Check border color index for the given sampler, if it\n    // is invalid then there will be nothing to do.\n    uint32_t index = InvalidBorderColor;\n\n    if (sampler < m_borderColors.indexForSampler.size())\n      index = m_borderColors.indexForSampler.at(sampler);\n\n    if (index == InvalidBorderColor)\n      return;\n\n    // Decrement use count and free border color if it reaches 0.\n    auto& entry = m_borderColors.infos.at(index);\n\n    if (!(--entry.useCount)) {\n      auto vk = m_device->vkd();\n      vk->vkUnregisterCustomBorderColorEXT(vk->device(), index);\n    }\n  }\n\n\n  const VkSamplerCustomBorderColorCreateInfoEXT* DxvkSamplerDescriptorHeap::findBorderColorInfo(const void* s) {\n    auto chain = reinterpret_cast<const VkBaseInStructure*>(s);\n\n    while (chain && chain->sType != VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT)\n      chain = chain->pNext;\n\n    return reinterpret_cast<const VkSamplerCustomBorderColorCreateInfoEXT*>(chain);\n  }\n\n\n\n\n\n\n  DxvkSamplerPool::DxvkSamplerPool(DxvkDevice* device)\n  : m_device(device), m_descriptorHeap(device, MaxSamplerCount) {\n    // Set up LRU list as a sort-of free list to allocate fresh samplers\n    m_lruHead = 0u;\n    m_lruTail = MaxSamplerCount - 1u;\n\n    for (uint32_t i = 0u; i < MaxSamplerCount; i++) {\n      if (i)\n        m_samplers[i].lruPrev = i - 1u;\n      if (i + 1u < MaxSamplerCount)\n        m_samplers[i].lruNext = i + 1u;\n    }\n\n    // Default sampler, implicitly used for null descriptors or when creating\n    // additional samplers fails for any reason. Keep a persistent reference\n    // so that this sampler does not accidentally get recycled.\n    DxvkSamplerKey defaultKey;\n    defaultKey.setFilter(VK_FILTER_LINEAR, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_LINEAR);\n    defaultKey.setLodRange(-256.0f, 256.0f, 0.0f);\n    defaultKey.setAddressModes(\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);\n    defaultKey.setReduction(VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE);\n\n    m_default = createSampler(defaultKey);\n  }\n\n\n  DxvkSamplerPool::~DxvkSamplerPool() {\n\n  }\n\n\n  Rc<DxvkSampler> DxvkSamplerPool::createSampler(const DxvkSamplerKey& key) {\n    std::unique_lock lock(m_mutex);\n    auto entry = m_samplerLut.find(key);\n\n    if (entry != m_samplerLut.end()) {\n      auto& sampler = m_samplers.at(entry->second);\n\n      // Remove the sampler from the LRU list if it's in there. Due\n      // to the way releasing samplers is implemented upon reaching\n      // a ref count of 0, it is possible that we reach this before\n      // the releasing thread inserted the list into the LRU list.\n      if (!sampler.object->m_refCount.fetch_add(1u, std::memory_order_acquire)) {\n        removeLru(sampler, entry->second);\n\n        m_samplersLive.store(m_samplersLive.load() + 1u, std::memory_order_relaxed);\n      }\n\n      // We already took a reference, forward the pointer as-is\n      return Rc<DxvkSampler>::unsafeCreate(&sampler.object.value());\n    }\n\n    // If there are no samplers we can allocate, fall back to the default\n    if (m_lruHead < 0) {\n      Logger::err(\"Failed to allocate sampler, using default one.\");\n      return m_default;\n    }\n\n    // Use the least recently used sampler entry. This may be a previously\n    // unused sampler, or an object that has not yet been initialized.\n    int32_t samplerIndex = m_lruHead;\n\n    // Destroy existing sampler and remove the corresponding LUT entry\n    auto& sampler = m_samplers.at(samplerIndex);\n\n    if (sampler.object) {\n      m_samplerLut.erase(sampler.object->key());\n      sampler.object.reset();\n    }\n\n    removeLru(sampler, samplerIndex);\n\n    // Create new sampler object and set up the corresponding LUT entry\n    sampler.object.emplace(this, key, uint16_t(samplerIndex));\n    m_samplerLut.insert_or_assign(key, samplerIndex);\n\n    // Update statistics\n    m_samplersLive.store(m_samplersLive.load() + 1u, std::memory_order_relaxed);\n    return &sampler.object.value();\n  }\n\n\n  void DxvkSamplerPool::releaseSampler(int32_t index) {\n    std::unique_lock lock(m_mutex);\n\n    // Always decrement live counter here since it will be incremented\n    // again whenever the sampler is reacquired.\n    m_samplersLive.store(m_samplersLive.load() - 1u);\n\n    // Back off if another thread has re-aquired the sampler. This is\n    // safe since the ref count can only be incremented from zero when\n    // the pool is locked.\n    auto& sampler = m_samplers.at(index);\n\n    if (sampler.object->m_refCount.load(std::memory_order_relaxed))\n      return;\n\n    // It is also possible that two threads end up here while the ref\n    // count is zero. Make sure to not add the sampler to the LRU list\n    // more than once in that case.\n    if (samplerIsInLruList(sampler, index))\n      return;\n\n    // Add sampler to the end of the LRU list, but keep the sampler\n    // object itself as well as the look-up table entry intact in\n    // case the app wants to recreate the same sampler later.\n    appendLru(sampler, index);\n  }\n\n\n  void DxvkSamplerPool::appendLru(SamplerEntry& sampler, int32_t index) {\n    sampler.lruPrev = m_lruTail;\n    sampler.lruNext = -1;\n\n    if (m_lruTail >= 0)\n      m_samplers.at(m_lruTail).lruNext = index;\n    else\n      m_lruHead = index;\n\n    m_lruTail = index;\n  }\n\n\n  void DxvkSamplerPool::removeLru(SamplerEntry& sampler, int32_t index) {\n    if (sampler.lruPrev >= 0)\n      m_samplers.at(sampler.lruPrev).lruNext = sampler.lruNext;\n    else if (m_lruHead == index)\n      m_lruHead = sampler.lruNext;\n\n    if (sampler.lruNext >= 0)\n      m_samplers.at(sampler.lruNext).lruPrev = sampler.lruPrev;\n    else if (m_lruTail == index)\n      m_lruTail = sampler.lruPrev;\n\n    sampler.lruPrev = -1;\n    sampler.lruNext = -1;\n  }\n\n\n  bool DxvkSamplerPool::samplerIsInLruList(SamplerEntry& sampler, int32_t index) const {\n    return sampler.lruPrev >= 0 || m_lruHead == index;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_sampler.h",
    "content": "#pragma once\n\n#include <map>\n#include <memory>\n#include <unordered_map>\n\n#include \"../util/util_bit.h\"\n#include \"../util/thread.h\"\n\n#include \"dxvk_descriptor.h\"\n#include \"dxvk_descriptor_heap.h\"\n#include \"dxvk_hash.h\"\n#include \"dxvk_include.h\"\n\nnamespace dxvk {\n\n  class DxvkBuffer;\n  class DxvkDevice;\n  class DxvkSamplerPool;\n\n  /**\n   * \\brief Sampler key\n   *\n   * Stores packed sampler properties and in a way that\n   * can be reasonably efficiently used with a hash map.\n   */\n  struct DxvkSamplerKey {\n    union {\n      struct {\n        uint32_t minFilter      :  1;\n        uint32_t magFilter      :  1;\n        uint32_t mipMode        :  1;\n        uint32_t anisotropy     :  5;\n\n        uint32_t addressU       :  3;\n        uint32_t addressV       :  3;\n        uint32_t addressW       :  3;\n        uint32_t hasBorder      :  1;\n\n        uint32_t lodBias        : 14;\n\n        uint32_t minLod         : 12;\n        uint32_t maxLod         : 12;\n\n        uint32_t compareEnable  :  1;\n        uint32_t compareOp      :  3;\n        uint32_t reduction      :  2;\n        uint32_t pixelCoord     :  1;\n        uint32_t legacyCube     :  1;\n\n        uint32_t viewSwizzleR   :  4;\n        uint32_t viewSwizzleG   :  4;\n        uint32_t viewSwizzleB   :  4;\n        uint32_t viewSwizzleA   :  4;\n        uint32_t reserved0      : 16;\n\n        uint32_t viewFormat;\n      } p;\n\n      uint32_t properties[4] = { 0u, 0u, 0u, 0u };\n    } u;\n\n    VkClearColorValue borderColor = { };\n\n    void setFilter(VkFilter min, VkFilter mag, VkSamplerMipmapMode mip) {\n      u.p.minFilter = uint32_t(min);\n      u.p.magFilter = uint32_t(mag);\n      u.p.mipMode = uint32_t(mip);\n    }\n\n    void setAniso(uint32_t anisotropy) {\n      u.p.anisotropy = std::min(anisotropy, 16u);\n    }\n\n    void setDepthCompare(bool enable, VkCompareOp op) {\n      u.p.compareEnable = uint32_t(enable);\n      u.p.compareOp = enable ? uint32_t(op) : 0u;\n    }\n\n    void setReduction(VkSamplerReductionMode reduction) {\n      u.p.reduction = uint32_t(reduction);\n    }\n\n    void setUsePixelCoordinates(bool enable) {\n      u.p.pixelCoord = uint32_t(enable);\n    }\n\n    void setLegacyCubeFilter(bool enable) {\n      u.p.legacyCube = uint32_t(enable);\n    }\n\n    void setAddressModes(VkSamplerAddressMode u_, VkSamplerAddressMode v_, VkSamplerAddressMode w_) {\n      u.p.addressU = u_;\n      u.p.addressV = v_;\n      u.p.addressW = w_;\n      u.p.hasBorder = uint32_t(u_ == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER\n                            || v_ == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER\n                            || w_ == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);\n    }\n\n    void setLodRange(float min, float max, float bias) {\n      u.p.minLod = bit::encodeFixed<uint32_t, 4, 8>(min);\n      u.p.maxLod = bit::encodeFixed<uint32_t, 4, 8>(std::max(max, min));\n      u.p.lodBias = bit::encodeFixed<int32_t, 6, 8>(bias);\n    }\n\n    void setBorderColor(VkClearColorValue color) {\n      borderColor = color;\n    }\n\n    void setViewProperties(const VkComponentMapping& mapping, VkFormat format) {\n      u.p.viewSwizzleR = uint32_t(mapping.r);\n      u.p.viewSwizzleG = uint32_t(mapping.g);\n      u.p.viewSwizzleB = uint32_t(mapping.b);\n      u.p.viewSwizzleA = uint32_t(mapping.a);\n      u.p.viewFormat = uint32_t(format);\n    }\n\n    bool eq(const DxvkSamplerKey& other) const {\n      bool eq = u.properties[0] == other.u.properties[0]\n             && u.properties[1] == other.u.properties[1]\n             && u.properties[2] == other.u.properties[2]\n             && u.properties[3] == other.u.properties[3];\n\n      if (eq && u.p.hasBorder) {\n        eq = borderColor.uint32[0] == other.borderColor.uint32[0]\n          && borderColor.uint32[1] == other.borderColor.uint32[1]\n          && borderColor.uint32[2] == other.borderColor.uint32[2]\n          && borderColor.uint32[3] == other.borderColor.uint32[3];\n      }\n\n      return eq;\n    }\n\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(u.properties[0]);\n      hash.add(u.properties[1]);\n      hash.add(u.properties[2]);\n      hash.add(u.properties[3]);\n\n      if (u.p.hasBorder) {\n        hash.add(borderColor.uint32[0]);\n        hash.add(borderColor.uint32[1]);\n        hash.add(borderColor.uint32[2]);\n        hash.add(borderColor.uint32[3]);\n      }\n\n      return hash;\n    }\n\n  };\n\n  static_assert(sizeof(DxvkSamplerKey) == 32u);\n  \n  \n  /**\n   * \\brief Sampler\n   * \n   * Manages a sampler object that can be bound to\n   * a pipeline. Sampler objects provide parameters\n   * for texture lookups within a shader.\n   */\n  class DxvkSampler {\n    friend class DxvkSamplerPool;\n  public:\n    \n    DxvkSampler(\n            DxvkSamplerPool*        pool,\n      const DxvkSamplerKey&         key,\n            uint16_t                index);\n\n    ~DxvkSampler();\n\n    /**\n     * \\brief Increments reference count\n     */\n    force_inline void incRef() {\n      m_refCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    /**\n     * \\brief Decrements reference count\n     *\n     * Recycles the sampler once the ref count reaches zero.\n     */\n    force_inline void decRef() {\n      if (m_refCount.fetch_sub(1u, std::memory_order_relaxed) == 1u)\n        release();\n    }\n\n    /**\n     * \\brief Updates tracking ID for sampler object\n     *\n     * Used when tracking submissions.\n     * \\param [in] trackingID Tracking ID\n     * \\returns \\c true if the tracking ID has been updated,\n     *    \\c false if the sampler was already tracked with this ID.\n     */\n    bool trackId(uint64_t trackingId) {\n      if (trackingId <= m_trackingId)\n        return false;\n\n      m_trackingId = trackingId;\n      return true;\n    }\n\n    /**\n     * \\brief Sampler handle\n     * \\returns Sampler handle\n     */\n    DxvkSamplerDescriptor getDescriptor() const {\n      return m_descriptor;\n    }\n    \n    /**\n     * \\brief Sampler key\n     * \\returns Sampler properties\n     */\n    const DxvkSamplerKey& key() const {\n      return m_key;\n    }\n\n  private:\n    \n    std::atomic<uint64_t> m_refCount  = { 0u };\n    uint64_t              m_trackingId = 0u;\n\n    DxvkSamplerPool*      m_pool      = nullptr;\n    DxvkSamplerKey        m_key       = { };\n\n    DxvkSamplerDescriptor m_descriptor = { };\n\n    void release();\n\n    VkBorderColor determineBorderColorType(const VkSamplerCustomBorderColorCreateInfoEXT& info) const;\n\n    static VkClearColorValue swizzleBorderColor(const VkClearColorValue& color, VkComponentMapping mapping);\n\n    static float mapBorderColorComponent(const VkClearColorValue& color, const VkComponentMapping& mapping, VkComponentSwizzle which);\n\n  };\n\n\n  /**\n   * \\brief Border color registration info\n   */\n  struct DxvkBorderColor {\n    VkFormat          format    = VK_FORMAT_UNDEFINED;\n    VkClearColorValue color     = { };\n    uint32_t          useCount  = 0u;\n  };\n\n\n  /**\n   * \\brief Global sampler set and layout\n   */\n  struct DxvkSamplerDescriptorSet {\n    VkDescriptorSet       set         = VK_NULL_HANDLE;\n    VkDescriptorSetLayout layout      = VK_NULL_HANDLE;\n  };\n\n\n  /**\n   * \\brief Sampler descriptor pool\n   *\n   * Manages a global descriptor pool and set for samplers.\n   */\n  class DxvkSamplerDescriptorHeap {\n    constexpr static uint32_t InvalidBorderColor = -1u;\n  public:\n\n    DxvkSamplerDescriptorHeap(\n            DxvkDevice*               device,\n            uint32_t                  size);\n\n    ~DxvkSamplerDescriptorHeap();\n\n    /**\n     * \\brief Retrieves descriptor set and layout\n     * \\returns Descriptor set and layout handles\n     */\n    DxvkSamplerDescriptorSet getDescriptorSetInfo() const;\n\n    /**\n     * \\brief Retrieves descriptor heap info\n     * \\returns Sampler heap address and size\n     */\n    DxvkDescriptorHeapBindingInfo getDescriptorHeapInfo() const;\n\n    /**\n     * \\brief Writes sampler descriptor to pool\n     *\n     * \\param [in] index Sampler index\n     * \\param [in] createInfo Sampler create info\n     * \\returns Sampler descriptor\n     */\n    DxvkSamplerDescriptor createSampler(\n            uint16_t              index,\n      const VkSamplerCreateInfo*  createInfo);\n\n    /**\n     * \\brief Frees a sampler\n     * \\param [in] sampler Sampler descriptor\n     */\n    void freeSampler(\n            DxvkSamplerDescriptor sampler);\n\n  private:\n\n    DxvkDevice* m_device          = nullptr;\n    uint32_t    m_descriptorCount = 0u;\n\n    struct {\n      VkDescriptorPool      pool      = VK_NULL_HANDLE;\n      VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;\n      VkDescriptorSet       set       = VK_NULL_HANDLE;\n\n    } m_legacy;\n\n    struct {\n      Rc<DxvkBuffer>        buffer    = nullptr;\n\n      VkDeviceSize          descriptorOffset  = 0u;\n      VkDeviceSize          descriptorSize    = 0u;\n\n      VkDeviceSize          reservedSize      = 0u;\n    } m_heap;\n\n    struct {\n      dxvk::mutex                   mutex;\n      std::vector<uint32_t>         indexForSampler;\n      std::vector<DxvkBorderColor>  infos;\n    } m_borderColors;\n\n    void initDescriptorLayout();\n\n    void initDescriptorPool();\n\n    void initDescriptorHeap();\n\n    uint32_t registerBorderColor(\n      const VkSamplerCustomBorderColorCreateInfoEXT*  borderColor);\n\n    uint32_t allocBorderColor(\n            uint16_t                                  sampler,\n      const VkSamplerCustomBorderColorCreateInfoEXT*  borderColor);\n\n    void freeBorderColor(uint16_t sampler);\n\n    static const VkSamplerCustomBorderColorCreateInfoEXT* findBorderColorInfo(const void* s);\n\n  };\n\n\n  /**\n   * \\brief Sampler statistics\n   */\n  struct DxvkSamplerStats {\n    /// Number of samplers currently in use\n    uint32_t liveCount = 0u;\n  };\n\n\n  /**\n   * \\brief Sampler pool\n   *\n   * Manages unique samplers within a device.\n   */\n  class DxvkSamplerPool {\n    friend DxvkSampler;\n  public:\n\n    // Lower limit for sampler counts in Vulkan.\n    constexpr static uint32_t MaxSamplerCount = 2048u;\n\n    DxvkSamplerPool(DxvkDevice* device);\n\n    ~DxvkSamplerPool();\n\n    /**\n     * \\brief Creates sampler\n     *\n     * \\param [in] key Sampler key\n     * \\returns Sampler object\n     */\n    Rc<DxvkSampler> createSampler(const DxvkSamplerKey& key);\n\n    /**\n     * \\brief Queries the global sampler descriptor set\n     *\n     * Required to bind the set, and for pipeline creation.\n     * \\returns Global sampler descriptor set and layout\n     */\n    DxvkSamplerDescriptorSet getDescriptorSetInfo() const {\n      return m_descriptorHeap.getDescriptorSetInfo();\n    }\n\n    /**\n     * \\brief Retrieves descriptor heap info\n     * \\returns Sampler heap address and size\n     */\n    DxvkDescriptorHeapBindingInfo getDescriptorHeapInfo() const {\n      return m_descriptorHeap.getDescriptorHeapInfo();\n    }\n\n    /**\n     * \\brief Retrieves sampler statistics\n     *\n     * Note that these might be out of date immediately.\n     * \\returns Sampler counts\n     */\n    DxvkSamplerStats getStats() const {\n      DxvkSamplerStats stats = { };\n      stats.liveCount = m_samplersLive.load();\n      return stats;\n    }\n\n  private:\n\n    struct SamplerEntry {\n      int32_t lruPrev = -1;\n      int32_t lruNext = -1;\n      std::optional<DxvkSampler> object;\n    };\n\n    DxvkDevice* m_device = nullptr;\n\n    DxvkSamplerDescriptorHeap m_descriptorHeap;\n\n    dxvk::mutex m_mutex;\n\n    std::array<SamplerEntry, MaxSamplerCount> m_samplers;\n\n    std::unordered_map<DxvkSamplerKey, int32_t, DxvkHash, DxvkEq> m_samplerLut;\n\n    int32_t m_lruHead = -1;\n    int32_t m_lruTail = -1;\n\n    std::atomic<uint32_t> m_samplersLive = { 0u };\n\n    Rc<DxvkSampler> m_default = nullptr;\n\n    void releaseSampler(int32_t index);\n\n    void appendLru(SamplerEntry& sampler, int32_t index);\n\n    void removeLru(SamplerEntry& sampler, int32_t index);\n\n    bool samplerIsInLruList(SamplerEntry& sampler, int32_t index) const;\n\n  };\n\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader.cpp",
    "content": "#include \"dxvk_device.h\"\n#include \"dxvk_pipemanager.h\"\n#include \"dxvk_shader.h\"\n#include \"dxvk_shader_io.h\"\n\n#include <dxvk_dummy_frag.h>\n\n#include <algorithm>\n#include <unordered_map>\n#include <unordered_set>\n\nnamespace dxvk {\n\n  std::atomic<uint32_t> DxvkShader::s_cookie = { 0u };\n\n\n  bool DxvkShaderLinkage::eq(const DxvkShaderLinkage& other) const {\n    bool eq = fsDualSrcBlend == other.fsDualSrcBlend\n           && fsFlatShading == other.fsFlatShading;\n\n    if (eq) {\n      eq = prevStageOutputs.getVarCount() == other.prevStageOutputs.getVarCount();\n\n      for (uint32_t i = 0; i < prevStageOutputs.getVarCount() && eq; i++)\n        eq = prevStageOutputs.getVar(i).eq(other.prevStageOutputs.getVar(i));\n    }\n\n    for (uint32_t i = 0; i < rtSwizzles.size() && eq; i++) {\n      eq = rtSwizzles[i].r == other.rtSwizzles[i].r\n        && rtSwizzles[i].g == other.rtSwizzles[i].g\n        && rtSwizzles[i].b == other.rtSwizzles[i].b\n        && rtSwizzles[i].a == other.rtSwizzles[i].a;\n    }\n\n    return eq;\n  }\n\n\n  size_t DxvkShaderLinkage::hash() const {\n    DxvkHashState hash;\n    hash.add(uint32_t(fsDualSrcBlend));\n    hash.add(uint32_t(fsFlatShading));\n\n    for (uint32_t i = 0; i < prevStageOutputs.getVarCount(); i++)\n      hash.add(prevStageOutputs.getVar(i).hash());\n\n    for (uint32_t i = 0; i < rtSwizzles.size(); i++) {\n      hash.add(rtSwizzles[i].r);\n      hash.add(rtSwizzles[i].g);\n      hash.add(rtSwizzles[i].b);\n      hash.add(rtSwizzles[i].a);\n    }\n\n    return hash;\n  }\n\n\n  DxvkShader::DxvkShader()\n  : m_cookie(++s_cookie) {\n\n  }\n\n\n  DxvkShader::~DxvkShader() {\n    \n  }\n\n\n  const std::string& DxvkShader::getShaderDumpPath() {\n    static std::string s_path = env::getEnvVar(\"DXVK_SHADER_DUMP_PATH\");\n    return s_path;\n  }\n\n\n  \n\n  DxvkShaderStageInfo::DxvkShaderStageInfo(const DxvkDevice* device, const DxvkPipelineLayout* layout)\n  : m_device(device) {\n    if (m_device->canUseDescriptorHeap()) {\n      m_mapping = layout->getMappingInfo();\n      m_next = &m_mapping;\n    }\n  }\n\n  void DxvkShaderStageInfo::addStage(\n          VkShaderStageFlagBits   stage,\n          SpirvCodeBuffer&&       code,\n    const VkSpecializationInfo*   specInfo) {\n    // Take ownership of the SPIR-V code buffer\n    auto& codeBuffer = m_codeBuffers[m_stageCount];\n    codeBuffer = std::move(code);\n\n    auto& moduleInfo = m_moduleInfos[m_stageCount].moduleInfo;\n    moduleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, m_next };\n    moduleInfo.codeSize = codeBuffer.size();\n    moduleInfo.pCode = codeBuffer.data();\n\n    auto& stageInfo = m_stageInfos[m_stageCount];\n    stageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, &moduleInfo };\n    stageInfo.stage = stage;\n    stageInfo.pName = \"main\";\n    stageInfo.pSpecializationInfo = specInfo;\n\n    m_stageCount++;\n  }\n  \n\n  void DxvkShaderStageInfo::addStage(\n          VkShaderStageFlagBits   stage,\n    const VkShaderModuleIdentifierEXT& identifier,\n    const VkSpecializationInfo*   specInfo) {\n    // Copy relevant bits of the module identifier\n    uint32_t identifierSize = std::min(identifier.identifierSize, VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT);\n\n    auto& moduleId = m_moduleInfos[m_stageCount].moduleIdentifier;\n    moduleId.createInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_MODULE_IDENTIFIER_CREATE_INFO_EXT };\n    moduleId.createInfo.identifierSize = identifierSize;\n    moduleId.createInfo.pIdentifier = moduleId.data.data();\n    std::memcpy(moduleId.data.data(), identifier.identifier, identifierSize);\n\n    // Set up stage info using the module identifier\n    auto& stageInfo = m_stageInfos[m_stageCount];\n    stageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };\n    stageInfo.pNext = &moduleId.createInfo;\n    stageInfo.stage = stage;\n    stageInfo.pName = \"main\";\n    stageInfo.pSpecializationInfo = specInfo;\n\n    m_stageCount++;\n  }\n\n\n  DxvkShaderStageInfo::~DxvkShaderStageInfo() {\n\n  }\n\n\n  DxvkShaderPipelineLibraryKey::DxvkShaderPipelineLibraryKey() {\n\n  }\n\n\n  DxvkShaderPipelineLibraryKey::~DxvkShaderPipelineLibraryKey() {\n\n  }\n\n\n  void DxvkShaderPipelineLibraryKey::addShader(\n          Rc<DxvkShader>                shader) {\n    m_shaders.push_back(std::move(shader));\n  }\n\n\n  bool DxvkShaderPipelineLibraryKey::eq(\n    const DxvkShaderPipelineLibraryKey& other) const {\n    bool eq = true;\n\n    for (uint32_t i = 0; i < m_shaders.size() && eq; i++)\n      eq = m_shaders[i] == other.m_shaders[i];\n\n    return eq;\n  }\n\n\n  size_t DxvkShaderPipelineLibraryKey::hash() const {\n    DxvkHashState hash;\n\n    for (uint32_t i = 0; i < m_shaders.size(); i++)\n      hash.add(m_shaders[i]->getCookie());\n\n    return hash;\n  }\n\n\n  DxvkShaderPipelineLibrary::DxvkShaderPipelineLibrary(\n          DxvkDevice*               device,\n          DxvkPipelineManager*      manager,\n    const DxvkShaderPipelineLibraryKey& key)\n  : m_device      (device),\n    m_manager     (manager),\n    m_shaders     (key) {\n\n  }\n\n\n  DxvkShaderPipelineLibrary::~DxvkShaderPipelineLibrary() {\n    this->destroyShaderPipelineLocked();\n  }\n\n\n  VkShaderModuleIdentifierEXT DxvkShaderPipelineLibrary::getModuleIdentifier(\n          VkShaderStageFlagBits                 stage) {\n    std::lock_guard lock(m_identifierMutex);\n    auto identifier = getShaderIdentifier(stage);\n\n    if (!identifier->identifierSize) {\n      // Unfortunate, but we'll have to decode the\n      // shader code here to retrieve the identifier\n      SpirvCodeBuffer spirvCode = this->getShaderCode(stage);\n      this->generateModuleIdentifierLocked(identifier, spirvCode);\n    }\n\n    return *identifier;\n  }\n\n\n  DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::acquirePipelineHandle() {\n    std::lock_guard lock(m_mutex);\n\n    if (m_device->mustTrackPipelineLifetime())\n      m_useCount += 1;\n\n    if (m_pipeline)\n      return *m_pipeline;\n\n    m_pipeline = compileShaderPipelineLocked();\n    return *m_pipeline;\n  }\n\n\n  void DxvkShaderPipelineLibrary::releasePipelineHandle() {\n    if (m_device->mustTrackPipelineLifetime()) {\n      std::lock_guard lock(m_mutex);\n\n      if (!(--m_useCount))\n        this->destroyShaderPipelineLocked();\n    }\n  }\n\n\n  void DxvkShaderPipelineLibrary::compilePipeline() {\n    std::lock_guard lock(m_mutex);\n\n    // Skip if a pipeline has already been compiled\n    if (m_compiledOnce)\n      return;\n\n    // Compile the pipeline with default args\n    DxvkShaderPipelineLibraryHandle pipeline = compileShaderPipelineLocked();\n\n    if (!pipeline.handle)\n      return;\n\n    if (m_device->mustTrackPipelineLifetime()) {\n      // On 32-bit, destroy the pipeline immediately in order to\n      // save memory. We should hit the driver's disk cache once\n      // we need to recreate the pipeline.\n      auto vk = m_device->vkd();\n      vk->vkDestroyPipeline(vk->device(), pipeline.handle, nullptr);\n    } else {\n      // Write back pipeline handle for future use\n      m_pipeline = pipeline;\n    }\n  }\n\n\n  void DxvkShaderPipelineLibrary::destroyShaderPipelineLocked() {\n    auto vk = m_device->vkd();\n\n    if (m_pipeline && m_pipeline->handle)\n      vk->vkDestroyPipeline(vk->device(), m_pipeline->handle, nullptr);\n\n    m_pipeline.reset();\n  }\n\n\n  DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipelineLocked() {\n    compileShaders();\n\n    // Increment stat counter the first time this\n    // shader pipeline gets compiled successfully\n    bool compiledBefore = std::exchange(m_compiledOnce, true);\n\n    if (!compiledBefore) {\n      if (m_shaders.findShader(VK_SHADER_STAGE_COMPUTE_BIT))\n        m_manager->m_stats.numComputePipelines += 1;\n      else\n        m_manager->m_stats.numGraphicsLibraries += 1;\n    }\n\n    if (!canCreatePipelineLibrary())\n      return { VK_NULL_HANDLE, 0 };\n\n    this->notifyLibraryCompile();\n\n    // If this is not the first time we're compiling the pipeline,\n    // try to get a cache hit using the shader module identifier\n    // so that we don't have to decompress our SPIR-V shader again.\n    DxvkShaderPipelineLibraryHandle pipeline = { VK_NULL_HANDLE, 0 };\n\n    if (compiledBefore && canUsePipelineCacheControl())\n      pipeline = this->compileShaderPipeline(VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT);\n\n    if (!pipeline.handle)\n      pipeline = this->compileShaderPipeline(0);\n\n    // Well that didn't work\n    if (!pipeline.handle)\n      return { VK_NULL_HANDLE, 0 };\n\n    return pipeline;\n  }\n\n\n  DxvkShaderPipelineLibraryHandle DxvkShaderPipelineLibrary::compileShaderPipeline(\n          VkPipelineCreateFlags2                flags) {\n    DxvkShaderStageInfo stageInfo(m_device, getPipelineLibraryLayout());\n    VkShaderStageFlags stageMask = getShaderStages();\n\n    { std::lock_guard lock(m_identifierMutex);\n      VkShaderStageFlags stages = stageMask;\n\n      while (stages) {\n        auto stage = VkShaderStageFlagBits(stages & -stages);\n        auto identifier = getShaderIdentifier(stage);\n\n        if (flags & VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT) {\n          // Fail if we have no idenfitier for whatever reason, caller\n          // should fall back to the slow path if this happens\n          if (!identifier->identifierSize)\n            return { VK_NULL_HANDLE, 0 };\n\n          stageInfo.addStage(stage, *identifier, nullptr);\n        } else {\n          // Decompress code and generate identifier as needed\n          SpirvCodeBuffer spirvCode = this->getShaderCode(stage);\n\n          if (!identifier->identifierSize)\n            this->generateModuleIdentifierLocked(identifier, spirvCode);\n\n          stageInfo.addStage(stage, std::move(spirvCode), nullptr);\n        }\n\n        stages &= stages - 1;\n      }\n    }\n\n    VkPipeline pipeline = VK_NULL_HANDLE;\n\n    if (stageMask & VK_SHADER_STAGE_VERTEX_BIT)\n      pipeline = compileVertexShaderPipeline(stageInfo, flags);\n    else if (stageMask & VK_SHADER_STAGE_FRAGMENT_BIT)\n      pipeline = compileFragmentShaderPipeline(stageInfo, flags);\n    else if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT)\n      pipeline = compileComputeShaderPipeline(stageInfo, flags);\n\n    // Should be unreachable\n    return { pipeline, flags };\n  }\n\n\n  VkPipeline DxvkShaderPipelineLibrary::compileVertexShaderPipeline(\n    const DxvkShaderStageInfo&          stageInfo,\n          VkPipelineCreateFlags2        flags) {\n    auto vk = m_device->vkd();\n\n    // Set up dynamic state. We do not know any pipeline state\n    // at this time, so make as much state dynamic as we can.\n    small_vector<VkDynamicState, 16> dynamicStates;\n\n    dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_CULL_MODE);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_FRONT_FACE);\n\n    if (m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable)\n      dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT);\n\n    VkPipelineDynamicStateCreateInfo dyInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };\n    dyInfo.dynamicStateCount  = dynamicStates.size();\n    dyInfo.pDynamicStates     = dynamicStates.data();\n\n    // If a tessellation control shader is present, grab the patch vertex count\n    VkPipelineTessellationStateCreateInfo tsInfo = { VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO };\n\n    auto tcs = m_shaders.findShader(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);\n\n    if (tcs)\n      tsInfo.patchControlPoints = tcs->metadata().patchVertexCount;\n\n    // All viewport state is dynamic, so we do not need to initialize this.\n    VkPipelineViewportStateCreateInfo vpInfo = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };\n\n    // Set up rasterizer state. Depth bias, cull mode and front face are\n    // all dynamic. Do not support any polygon modes other than FILL.\n    VkPipelineRasterizationDepthClipStateCreateInfoEXT rsDepthClipInfo = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT };\n\n    VkPipelineRasterizationStateCreateInfo rsInfo = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };\n    rsInfo.depthClampEnable   = VK_TRUE;\n    rsInfo.rasterizerDiscardEnable = VK_FALSE;\n    rsInfo.polygonMode        = VK_POLYGON_MODE_FILL;\n    rsInfo.lineWidth          = 1.0f;\n\n    // Only use the fixed depth clip state if we can't make it dynamic\n    if (!m_device->features().extExtendedDynamicState3.extendedDynamicState3DepthClipEnable) {\n      rsDepthClipInfo.pNext = std::exchange(rsInfo.pNext, &rsDepthClipInfo);\n      rsDepthClipInfo.depthClipEnable = VK_TRUE;\n    }\n\n    // Only the view mask is used as input, and since we do not use MultiView, it is always 0\n    VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };\n\n    VkPipelineCreateFlags2CreateInfo flagsInfo = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO, &rtInfo };\n    flagsInfo.flags = VK_PIPELINE_CREATE_2_LIBRARY_BIT_KHR | flags;\n\n    if (m_device->canUseDescriptorHeap())\n      flagsInfo.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (m_device->canUseDescriptorBuffer())\n      flagsInfo.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkGraphicsPipelineLibraryCreateInfoEXT libInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, &flagsInfo };\n    libInfo.flags             = VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT;\n\n    VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo };\n    info.stageCount           = stageInfo.getStageCount();\n    info.pStages              = stageInfo.getStageInfos();\n    info.pTessellationState   = m_shaders.findShader(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ? &tsInfo : nullptr;\n    info.pViewportState       = &vpInfo;\n    info.pRasterizationState  = &rsInfo;\n    info.pDynamicState        = &dyInfo;\n    info.layout               = getPipelineLibraryLayout()->getPipelineLayout();\n    info.basePipelineIndex    = -1;\n\n    VkPipeline pipeline = VK_NULL_HANDLE;\n    VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);\n\n    if (vr && vr != VK_PIPELINE_COMPILE_REQUIRED_EXT)\n      Logger::err(str::format(\"DxvkShaderPipelineLibrary: Failed to create vertex shader pipeline: \", vr));\n\n    return vr ? VK_NULL_HANDLE : pipeline;\n  }\n\n\n  VkPipeline DxvkShaderPipelineLibrary::compileFragmentShaderPipeline(\n    const DxvkShaderStageInfo&          stageInfo,\n          VkPipelineCreateFlags2        flags) {\n    auto vk = m_device->vkd();\n\n    // Set up dynamic state. We do not know any pipeline state\n    // at this time, so make as much state dynamic as we can.\n    small_vector<VkDynamicState, 16> dynamicStates;\n\n    dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_COMPARE_OP);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE);\n    dynamicStates.push_back(VK_DYNAMIC_STATE_STENCIL_OP);\n\n    if (m_device->features().core.features.depthBounds) {\n      dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE);\n      dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS);\n    }\n\n    auto fs = m_shaders.findShader(VK_SHADER_STAGE_FRAGMENT_BIT);\n\n    bool hasSampleRateShading = fs && fs->metadata().flags.test(DxvkShaderFlag::HasSampleRateShading);\n    bool hasDynamicMultisampleState = hasSampleRateShading\n      && m_device->features().extExtendedDynamicState3.extendedDynamicState3RasterizationSamples\n      && m_device->features().extExtendedDynamicState3.extendedDynamicState3SampleMask;\n    bool hasDynamicSampleLocations = m_device->canUseSampleLocations(0u);\n\n    if (hasDynamicMultisampleState) {\n      dynamicStates.push_back(VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT);\n      dynamicStates.push_back(VK_DYNAMIC_STATE_SAMPLE_MASK_EXT);\n\n      if (!fs || !fs->metadata().flags.test(DxvkShaderFlag::ExportsSampleMask)) {\n        if (m_device->features().extExtendedDynamicState3.extendedDynamicState3AlphaToCoverageEnable)\n          dynamicStates.push_back(VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT);\n      }\n    }\n\n    if (hasDynamicSampleLocations) {\n      dynamicStates.push_back(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_ENABLE_EXT);\n      dynamicStates.push_back(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT);\n    }\n\n    VkPipelineDynamicStateCreateInfo dyInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };\n    dyInfo.dynamicStateCount  = dynamicStates.size();\n    dyInfo.pDynamicStates     = dynamicStates.data();\n\n    // Set up multisample state. If sample shading is enabled, assume that\n    // we only have one sample enabled, with a non-zero sample mask and no\n    // alpha-to-coverage.\n    VkSampleMask msSampleMask = 0x1;\n\n    VkPipelineMultisampleStateCreateInfo msInfo = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };\n    msInfo.sampleShadingEnable  = VK_TRUE;\n    msInfo.minSampleShading     = 1.0f;\n\n    if (!hasDynamicMultisampleState) {\n      msInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;\n      msInfo.pSampleMask          = &msSampleMask;\n    }\n\n    // All depth-stencil state is dynamic, so no need to initialize this.\n    // Depth bounds testing is disabled on devices which don't support it.\n    VkPipelineDepthStencilStateCreateInfo dsInfo = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };\n\n    // Only the view mask is used as input, and since we do not use MultiView, it is always 0\n    VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };\n\n    VkPipelineCreateFlags2CreateInfo flagsInfo = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO, &rtInfo };\n    flagsInfo.flags = VK_PIPELINE_CREATE_2_LIBRARY_BIT_KHR | flags;\n\n    if (m_device->canUseDescriptorHeap())\n      flagsInfo.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (m_device->canUseDescriptorBuffer())\n      flagsInfo.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkGraphicsPipelineLibraryCreateInfoEXT libInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, &flagsInfo };\n    libInfo.flags             = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT;\n\n    VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo };\n    info.stageCount           = stageInfo.getStageCount();\n    info.pStages              = stageInfo.getStageInfos();\n    info.pDepthStencilState   = &dsInfo;\n    info.pDynamicState        = &dyInfo;\n    info.layout               = m_layout->getLayout(DxvkPipelineLayoutType::Independent)->getPipelineLayout();\n    info.basePipelineIndex    = -1;\n\n    if (hasSampleRateShading)\n      info.pMultisampleState  = &msInfo;\n\n    VkPipeline pipeline = VK_NULL_HANDLE;\n    VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);\n\n    if (vr && !(flags & VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT))\n      Logger::err(str::format(\"DxvkShaderPipelineLibrary: Failed to create fragment shader pipeline: \", vr));\n\n    return vr ? VK_NULL_HANDLE : pipeline;\n  }\n\n\n  VkPipeline DxvkShaderPipelineLibrary::compileComputeShaderPipeline(\n    const DxvkShaderStageInfo&          stageInfo,\n          VkPipelineCreateFlags2        flags) {\n    auto vk = m_device->vkd();\n\n    // Compile the compute pipeline as normal\n    VkPipelineCreateFlags2CreateInfo flagsInfo = { VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO };\n    flagsInfo.flags = flags;\n\n    if (m_device->canUseDescriptorHeap())\n      flagsInfo.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;\n\n    if (m_device->canUseDescriptorBuffer())\n      flagsInfo.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;\n\n    VkComputePipelineCreateInfo info = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };\n    info.stage        = *stageInfo.getStageInfos();\n    info.layout       = getPipelineLibraryLayout()->getPipelineLayout();\n    info.basePipelineIndex = -1;\n\n    if (flagsInfo.flags)\n      flagsInfo.pNext = std::exchange(info.pNext, &flagsInfo);\n\n    VkPipeline pipeline = VK_NULL_HANDLE;\n    VkResult vr = vk->vkCreateComputePipelines(vk->device(), VK_NULL_HANDLE, 1, &info, nullptr, &pipeline);\n\n    if (vr && vr != VK_PIPELINE_COMPILE_REQUIRED_EXT)\n      Logger::err(str::format(\"DxvkShaderPipelineLibrary: Failed to create compute shader pipeline: \", vr));\n\n    return vr ? VK_NULL_HANDLE : pipeline;\n  }\n\n\n  SpirvCodeBuffer DxvkShaderPipelineLibrary::getShaderCode(VkShaderStageFlagBits stage) const {\n    // As a special case, it is possible that we have to deal with\n    // a null shader, but the pipeline library extension requires\n    // us to always specify a fragment shader for fragment stages,\n    // so we need to return a dummy shader in that case.\n    DxvkShader* shader = getShader(stage);\n\n    if (!shader)\n      return SpirvCodeBuffer(dxvk_dummy_frag);\n\n    DxvkPipelineLayoutType layoutType = stage == VK_SHADER_STAGE_COMPUTE_BIT\n      ? DxvkPipelineLayoutType::Merged\n      : DxvkPipelineLayoutType::Independent;\n\n    return shader->getCode(m_layout->getBindingMap(layoutType), nullptr);\n  }\n\n\n  void DxvkShaderPipelineLibrary::generateModuleIdentifierLocked(\n          VkShaderModuleIdentifierEXT*  identifier,\n    const SpirvCodeBuffer&              spirvCode) {\n    auto vk = m_device->vkd();\n\n    if (!canUsePipelineCacheControl())\n      return;\n\n    VkShaderModuleCreateInfo info = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };\n    info.codeSize = spirvCode.size();\n    info.pCode = spirvCode.data();\n\n    vk->vkGetShaderModuleCreateInfoIdentifierEXT(\n      vk->device(), &info, identifier);\n  }\n\n\n  VkShaderStageFlags DxvkShaderPipelineLibrary::getShaderStages() const {\n    VkShaderStageFlags result = 0u;\n\n    for (uint32_t i = 0u; i < m_shaders.getShaderCount(); i++)\n      result |= m_shaders.getShader(i)->metadata().stage;\n\n    // Must be a fragment shader even if fs is null\n    if (!result)\n      result = VK_SHADER_STAGE_FRAGMENT_BIT;\n\n    return result;\n  }\n\n\n  DxvkShader* DxvkShaderPipelineLibrary::getShader(\n          VkShaderStageFlagBits         stage) const {\n    return m_shaders.findShader(stage);\n  }\n\n\n  VkShaderModuleIdentifierEXT* DxvkShaderPipelineLibrary::getShaderIdentifier(\n          VkShaderStageFlagBits         stage) {\n    switch (stage) {\n      case VK_SHADER_STAGE_VERTEX_BIT:\n        return &m_identifiers.vs;\n\n      case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:\n        return &m_identifiers.tcs;\n\n      case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:\n        return &m_identifiers.tes;\n\n      case VK_SHADER_STAGE_GEOMETRY_BIT:\n        return &m_identifiers.gs;\n\n      case VK_SHADER_STAGE_FRAGMENT_BIT:\n        return &m_identifiers.fs;\n\n      case VK_SHADER_STAGE_COMPUTE_BIT:\n        return &m_identifiers.cs;\n\n      default:\n        return nullptr;\n    }\n  }\n\n\n  void DxvkShaderPipelineLibrary::compileShaders() {\n    if (m_layout)\n      return;\n\n    VkShaderStageFlags stages = 0u;\n\n    for (uint32_t i = 0u; i < m_shaders.getShaderCount(); i++) {\n      auto shader = m_shaders.getShader(i);\n      shader->compile();\n      stages |= shader->metadata().stage;\n    }\n\n    if (!stages)\n      stages = VK_SHADER_STAGE_FRAGMENT_BIT;\n\n    DxvkPipelineLayoutBuilder layoutBuilder(stages);\n\n    for (uint32_t i = 0u; i < m_shaders.getShaderCount(); i++) {\n      auto shader = m_shaders.getShader(i);\n      layoutBuilder.addLayout(shader->getLayout());\n    }\n\n    m_layout.emplace(m_device, m_manager, layoutBuilder);\n  }\n\n\n  bool DxvkShaderPipelineLibrary::canCreatePipelineLibrary() const {\n    // Check whether device supports GPL at all\n    if (!m_device->canUseGraphicsPipelineLibrary())\n      return false;\n\n    // Can only create pre-raster pipelines if all shaders are present\n    if ((m_shaders.findShader(VK_SHADER_STAGE_GEOMETRY_BIT)\n      || m_shaders.findShader(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)\n      || m_shaders.findShader(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))\n     && !m_shaders.findShader(VK_SHADER_STAGE_VERTEX_BIT))\n      return false;\n\n    // The final geometry stage must export position\n    DxvkShader* lastPreRasterStage = m_shaders.findShader(VK_SHADER_STAGE_GEOMETRY_BIT);\n\n    if (!lastPreRasterStage)\n      lastPreRasterStage = m_shaders.findShader(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);\n\n    if (!lastPreRasterStage)\n      lastPreRasterStage = m_shaders.findShader(VK_SHADER_STAGE_VERTEX_BIT);\n\n    for (uint32_t i = 0u; i < m_shaders.getShaderCount(); i++) {\n      auto currShader = m_shaders.getShader(i);\n\n      if (!canCreatePipelineLibraryForShader(*currShader, currShader == lastPreRasterStage))\n        return false;\n\n      if (i) {\n        // Ensure that stage I/O is compatible between stages\n        auto prevShader = m_shaders.getShader(i - 1u);\n\n        const auto& prevShaderMeta = prevShader->metadata();\n        const auto& currShaderMeta = currShader->metadata();\n\n        bool semanticIo = currShaderMeta.flags.test(DxvkShaderFlag::SemanticIo)\n                       && prevShaderMeta.flags.test(DxvkShaderFlag::SemanticIo);\n\n        if (!DxvkShaderIo::checkStageCompatibility(\n            currShaderMeta.stage, currShaderMeta.inputs,\n            prevShaderMeta.stage, prevShaderMeta.outputs, semanticIo))\n          return false;\n      }\n    }\n\n    return true;\n  }\n\n\n  bool DxvkShaderPipelineLibrary::canCreatePipelineLibraryForShader(DxvkShader& shader, bool needsPosition) const {\n    const auto& metadata = shader.metadata();\n\n    // Tessellation control shaders must define a valid vertex count\n    if (metadata.stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT\n      && (metadata.patchVertexCount < 1 || metadata.patchVertexCount > 32))\n      return false;\n\n    // We don't support GPL with transform feedback right now\n    if (metadata.flags.test(DxvkShaderFlag::HasTransformFeedback))\n      return false;\n\n    // Pre-raster pipelines must export vertex position to be useful. This\n    // stops us from compiling vertex shader libraries that are only used\n    // as input for tessellation or geometry shaers.\n    if (needsPosition && !metadata.flags.test(DxvkShaderFlag::ExportsPosition))\n      return false;\n\n    // Spec constant selectors are only supported in graphics\n    if (metadata.specConstantMask & (1u << MaxNumSpecConstants))\n      return metadata.stage != VK_SHADER_STAGE_COMPUTE_BIT;\n\n    // Always late-compile shaders with spec constants\n    // that don't use the spec constant selector\n    return !metadata.specConstantMask;\n  }\n\n\n  void DxvkShaderPipelineLibrary::notifyLibraryCompile() const {\n    // Only notify the shader itself if we're actually\n    // building the shader's standalone pipeline library\n    if (m_shaders.getShaderCount() == 1u)\n      m_shaders.getShader(0u)->notifyCompile();\n  }\n\n\n  bool DxvkShaderPipelineLibrary::canUsePipelineCacheControl() const {\n    const auto& features = m_device->features();\n\n    return features.vk13.pipelineCreationCacheControl\n        && features.extShaderModuleIdentifier.shaderModuleIdentifier;\n  }\n\n\n  const DxvkPipelineLayout* DxvkShaderPipelineLibrary::getPipelineLibraryLayout() const {\n    DxvkPipelineLayoutType type = m_shaders.findShader(VK_SHADER_STAGE_COMPUTE_BIT)\n      ? DxvkPipelineLayoutType::Merged\n      : DxvkPipelineLayoutType::Independent;\n\n    return m_layout->getLayout(type);\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader.h",
    "content": "#pragma once\n\n#include <optional>\n#include <vector>\n\n#include \"dxvk_include.h\"\n#include \"dxvk_limits.h\"\n#include \"dxvk_pipelayout.h\"\n#include \"dxvk_shader_io.h\"\n\n#include \"../spirv/spirv_code_buffer.h\"\n#include \"../spirv/spirv_compression.h\"\n#include \"../spirv/spirv_module.h\"\n\nnamespace dxvk {\n  \n  class DxvkShader;\n  class DxvkShaderModule;\n  class DxvkPipelineManager;\n  struct DxvkPipelineStats;\n\n  /**\n   * \\brief Push data to handle built-ins\n   */\n  struct DxvkBuiltInPushData {\n    static constexpr uint32_t SampleCountOffset = 0u;\n    static constexpr uint32_t SampleCountBits = 8u;\n\n    static constexpr uint32_t MaxTessFactorOffset = SampleCountOffset + SampleCountBits;\n    static constexpr uint32_t MaxTessFactorBits = 8u;\n\n    DxvkBuiltInPushData() = default;\n\n    explicit DxvkBuiltInPushData(uint32_t sampleCount, uint32_t maxTessFactor)\n    : builtIns((sampleCount   << SampleCountOffset)\n             | (maxTessFactor << MaxTessFactorOffset)) { }\n\n    uint32_t builtIns = 0u;\n  };\n\n\n  /**\n   * \\brief Shader compile flags\n   */\n  enum class DxvkShaderCompileFlag : uint32_t {\n    /// Whether to detect and resolve cases of missing\n    /// shared memory barriers in compute shaders\n    InsertSharedMemoryBarriers  = 0u,\n    /// Whether to detect and resolve cases of missing\n    /// resource memory barriers in compute shaders\n    InsertResourceBarriers      = 1u,\n    /// Whether loads from typed read-write resources\n    /// require the format of the resource to be R32.\n    TypedR32LoadRequiresFormat  = 2u,\n    /// Whether to replace all multisampled image\n    /// resource bindings with single-sampled variants.\n    DisableMsaa                 = 3u,\n    /// Whether to enable sample interpolation for all\n    /// interpolated shader inputs.\n    EnableSampleRateShading     = 4u,\n    /// Whether the device supports 16-bit int and float\n    /// arithmetic. Effectively enables min16 lowering.\n    Supports16BitArithmetic     = 5u,\n    /// Whether 16-bit push data is supported. Used to\n    /// pack sampler indices in the binding model\n    Supports16BitPushData       = 6u,\n    /// Whether to lower unsigned int to float conversions.\n    /// Needed to work around an Nvidia driver bug.\n    LowerItoF                   = 7u,\n    /// Whether to manualy clamp the input for float-to-integer\n    /// conversions to avoid overflow and get correct NaN behaviour\n    LowerFtoI                   = 8u,\n    /// Whether to lower sin/cos to a custom approximation.\n    /// Used on hardware where the built-in intrinsics are\n    /// not accurate enough.\n    LowerSinCos                 = 9u,\n    /// Whether to clamp non-infinite inputs to f32tof16 in\n    /// order to work around issues on drivers that use RTE.\n    LowerF32toF16               = 10u,\n    /// Whether to lower built-in constant arrays to a regular\n    /// constant buffer. The register space and index for this\n    /// buffer are defined in the compile options.\n    LowerConstantArrays         = 11u,\n    /// Whether to enable semantic-based I/O interface matching\n    SemanticIo                  = 12u,\n  };\n\n  using DxvkShaderCompileFlags = Flags<DxvkShaderCompileFlag>;\n\n\n  /**\n   * \\brief Shader lowering flags\n   *\n   * These flags do not affect the internal IR.\n   */\n  enum class DxvkShaderSpirvFlag : uint32_t {\n    /// Whether to export point size.\n    ExportPointSize             = 0u,\n    /// Whether raw access chains are supported.\n    SupportsNvRawAccessChains   = 1u,\n    /// Whether signed zero / inf / nan preserve is\n    /// supported for the given bit width\n    SupportsSzInfNanPreserve16  = 2u,\n    SupportsSzInfNanPreserve32  = 3u,\n    SupportsSzInfNanPreserve64  = 4u,\n    /// Whether rounding to nearest even is supported\n    /// for the given bit width\n    SupportsRte16               = 5u,\n    SupportsRte32               = 6u,\n    SupportsRte64               = 7u,\n    /// Whether rounding towards zero is supported\n    /// for the given bit width\n    SupportsRtz16               = 8u,\n    SupportsRtz32               = 9u,\n    SupportsRtz64               = 10u,\n    /// Whether flushing denorms is supported for the\n    /// given bit width\n    SupportsDenormFlush16       = 11u,\n    SupportsDenormFlush32       = 12u,\n    SupportsDenormFlush64       = 13u,\n    /// Whether preserving denorms is supported for the\n    /// given bit width\n    SupportsDenormPreserve16    = 14u,\n    SupportsDenormPreserve32    = 15u,\n    SupportsDenormPreserve64    = 16u,\n    /// Whether 16/64-bit rounding and denorm modes can be\n    /// set independently of the corresponding 32-bit mode\n    IndependentRoundMode        = 17u,\n    IndependentDenormMode       = 18u,\n    /// Whether float control 2 features are supported\n    SupportsFloatControls2      = 19u,\n    /// Whether dynamic non-uniform resource indexing is\n    /// supported. Only applies to typed resources.\n    SupportsResourceIndexing    = 20u,\n  };\n\n  using DxvkShaderSpirvFlags = Flags<DxvkShaderSpirvFlag>;\n\n\n  /**\n   * \\brief Shader compile options\n   *\n   * Device-level options to enable certain\n   * features or behaviours.\n   */\n  struct DxvkShaderOptions {\n    /// Compile flags\n    DxvkShaderCompileFlags flags = 0u;\n    /// SPIR-V lowering flags\n    DxvkShaderSpirvFlags spirv = 0u;\n    /// Maximum uniform buffer size, in bytes. Constant buffer bindings\n    /// larger than this will be lowered to a storage buffer.\n    uint32_t maxUniformBufferSize = 0u;\n    /// Maximum uniform buffer count per shader. Constant buffer\n    /// bindigs beyond this will be lowered to a storage buffer.\n    /// Negative numbers impose no limit on the number of buffers.\n    int32_t maxUniformBufferCount = -1;\n    /// Global push data offset for emulated built-ins\n    uint32_t builtInPushDataOffset = 0u;\n    /// Minimum required storage buffer alignment. Buffers\n    /// with a smaller guaranteed alignment must be demoted\n    /// to typed buffers.\n    uint32_t minStorageBufferAlignment = 0u;\n  };\n\n\n  /**\n   * \\brief Shader flags\n   *\n   * Provides extra information about the features\n   * used by a shader.\n   */\n  enum DxvkShaderFlag : uint64_t {\n    HasSampleRateShading,\n    HasTransformFeedback,\n    ExportsPosition,\n    ExportsStencilRef,\n    ExportsViewportIndexLayerFromVertexStage,\n    ExportsSampleMask,\n    UsesFragmentCoverage,\n    UsesSparseResidency,\n    TessellationPoints,\n    SemanticIo,\n  };\n\n  using DxvkShaderFlags = Flags<DxvkShaderFlag>;\n  \n  /**\n   * \\brief Shader metadata\n   */\n  struct DxvkShaderMetadata {\n    /// Shader stage\n    VkShaderStageFlagBits stage = VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;\n    /// Shader property flags\n    DxvkShaderFlags flags = { };\n    /// Specialization constant IDs used by the shader\n    uint32_t specConstantMask = 0u;\n    /// Input variables consumed by the shader\n    DxvkShaderIo inputs = { };\n    /// Output variables produced by the shader\n    DxvkShaderIo outputs = { };\n    /// Input primitive topology (for geometry shaders only)\n    VkPrimitiveTopology inputTopology = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;\n    /// Output primitive topology for geometry or tessellation shaders\n    VkPrimitiveTopology outputTopology = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;\n    /// Fragment shader input locations to consider for flat shading\n    uint32_t flatShadingInputs = 0;\n    /// Rasterized stream for geometry shaders, or -1\n    int32_t rasterizedStream = 0;\n    /// Tess control patch vertex count\n    uint32_t patchVertexCount = 0;\n    /// Transform feedback vertex strides\n    std::array<uint32_t, MaxNumXfbBuffers> xfbStrides = { };\n  };\n\n\n  /**\n   * \\brief Shader module create info\n   */\n  struct DxvkShaderLinkage {\n    bool fsDualSrcBlend  = false;\n    bool fsFlatShading   = false;\n    bool semanticIo      = false;\n\n    VkPrimitiveTopology inputTopology = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;\n\n    VkShaderStageFlagBits prevStage = VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;\n    DxvkShaderIo prevStageOutputs = { };\n\n    std::array<VkComponentMapping, MaxNumRenderTargets> rtSwizzles = { };\n\n    bool eq(const DxvkShaderLinkage& other) const;\n\n    size_t hash() const;\n  };\n  \n  \n  /**\n   * \\brief Shader object\n   * \n   * Stores a SPIR-V shader and information on the\n   * bindings that the shader uses. In order to use\n   * the shader with a pipeline, a shader module\n   * needs to be created from he shader object.\n   */\n  class DxvkShader {\n    \n  public:\n    \n    DxvkShader();\n\n    virtual ~DxvkShader();\n\n    force_inline void incRef() {\n      m_refCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    force_inline void decRef() {\n      if (!(m_refCount.fetch_sub(1u, std::memory_order_acquire) - 1u))\n        delete this;\n    }\n\n    /**\n     * \\brief Retrieves shader cookie\n     *\n     * Unique value identifying the shader object that\n     * can be used for look-up purposes.\n     * \\returns Unique shader cookie\n     */\n    size_t getCookie() const {\n      return m_cookie;\n    }\n\n    /**\n     * \\brief Shader metadata\n     * \\returns Shader metadata\n     */\n    const DxvkShaderMetadata& metadata() {\n      if (unlikely(!m_metadata))\n        m_metadata = getShaderMetadata();\n\n      return *m_metadata;\n    }\n\n    /**\n     * \\brief Tests whether this shader needs to be compiled\n     *\n     * If pipeline libraries are supported, this will return\n     * \\c false once the pipeline library is being compiled.\n     * \\returns \\c true if compilation is still needed\n     */\n    bool needsCompile() const {\n      return m_needsCompile.load();\n    }\n\n    /**\n     * \\brief Notifies library compile\n     *\n     * Called automatically when pipeline compilation begins. Returns\n     * the previous state of the compile flag, which will be \\c true\n     * if compilation is still required, and \\c false otherwise.\n     */\n    bool notifyCompile() {\n      return m_needsCompile.exchange(false);\n    }\n\n    /**\n     * \\brief Queries shader binding layout\n     * \\returns Pipeline layout builder\n     */\n    virtual DxvkPipelineLayoutBuilder getLayout() = 0;\n\n    /**\n     * \\brief Queries uncached shader metadata\n     *\n     * Compiles the shader as necessary.\n     * \\returns Shader metadata\n     */\n    virtual DxvkShaderMetadata getShaderMetadata() = 0;\n\n    /**\n     * \\brief Compiles shader\n     *\n     * Performs any IR conversions and lowering that may be necessary.\n     * Shader metadata will be immediately available afterwards.\n     */\n    virtual void compile() = 0;\n\n    /**\n     * \\brief Retrieves SPIR-V code for the given shader\n     *\n     * Creates the final shader binary with the given binding\n     * mapping and pipeline state information.\n     * \\param [in] bindings Biding map\n     * \\param [in] linkage Pipeline state info\n     * \\returns Uncompressed SPIR-V code\n     */\n    virtual SpirvCodeBuffer getCode(\n      const DxvkShaderBindingMap*       bindings,\n      const DxvkShaderLinkage*          linkage) = 0;\n\n    /**\n     * \\brief Dumps SPIR-V shader\n     * \n     * Can be used to store the SPIR-V code in a file.\n     * \\param [in] outputStream Stream to write to \n     */\n    virtual void dump(std::ostream& outputStream) = 0;\n\n    /**\n     * \\brief Retrieves debug name\n     * \\returns The shader's name\n     */\n    virtual std::string debugName() = 0;\n\n    /**\n     * \\brief Get lookup hash for a shader\n     *\n     * Convenience method that returns \\c 0 for a null\n     * pointer, and the shader's lookup hash otherwise.\n     * \\param [in] shader The shader\n     * \\returns The shader's lookup hash, or 0\n     */\n    static uint32_t getCookie(const Rc<DxvkShader>& shader) {\n      return shader != nullptr ? shader->getCookie() : 0;\n    }\n\n    /**\n     * \\brief Queries shader dump path\n     * \\returns Shader dump path, or empty string\n     */\n    static const std::string& getShaderDumpPath();\n\n  private:\n\n    static std::atomic<uint32_t>  s_cookie;\n\n    std::atomic<uint32_t>         m_refCount = { 0u };\n    uint32_t                      m_cookie = 0;\n\n    std::atomic<bool>             m_needsCompile = { true };\n\n    std::optional<DxvkShaderMetadata> m_metadata;\n\n  };\n  \n\n  /**\n   * \\brief Shader code collection\n   * \n   * Manages shader stage and shader module create structures that can\n   * be passed to pipeline creation. Vulkan shader modules are not used,\n   * instead we rely on maintenance5 functionality.\n   */\n  class DxvkShaderStageInfo {\n    \n  public:\n\n    DxvkShaderStageInfo(const DxvkDevice* device, const DxvkPipelineLayout* layout);\n\n    DxvkShaderStageInfo             (DxvkShaderStageInfo&& other) = delete;\n    DxvkShaderStageInfo& operator = (DxvkShaderStageInfo&& other) = delete;\n\n    ~DxvkShaderStageInfo();\n\n    /**\n     * \\brief Counts shader stages\n     * \\returns Shader stage count\n     */\n    uint32_t getStageCount() const {\n      return m_stageCount;\n    }\n\n    /**\n     * \\brief Queries shader stage infos\n     * \\returns Pointer to shader stage infos\n     */\n    const VkPipelineShaderStageCreateInfo* getStageInfos() const {\n      return m_stageInfos.data();\n    }\n\n    /**\n     * \\brief Adds a shader stage with specialization info\n     *\n     * \\param [in] stage Shader stage\n     * \\param [in] code SPIR-V code\n     * \\param [in] specinfo Specialization info\n     */\n    void addStage(\n            VkShaderStageFlagBits   stage,\n            SpirvCodeBuffer&&       code,\n      const VkSpecializationInfo*   specInfo);\n\n    /**\n     * \\brief Adds stage using a module identifier\n     *\n     * \\param [in] stage Shader stage\n     * \\param [in] identifier Shader module identifier\n     * \\param [in] specinfo Specialization info\n     */\n    void addStage(\n            VkShaderStageFlagBits   stage,\n      const VkShaderModuleIdentifierEXT& identifier,\n      const VkSpecializationInfo*   specInfo);\n\n  private:\n\n    const DxvkDevice* m_device;\n\n    struct ShaderModuleIdentifier {\n      VkPipelineShaderStageModuleIdentifierCreateInfoEXT createInfo;\n      std::array<uint8_t, VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT> data;\n    };\n\n    union ShaderModuleInfo {\n      ShaderModuleIdentifier    moduleIdentifier;\n      VkShaderModuleCreateInfo  moduleInfo;\n    };\n\n    VkShaderDescriptorSetAndBindingMappingInfoEXT   m_mapping = { };\n    void*                                           m_next = nullptr;\n\n    std::array<SpirvCodeBuffer,                 5>  m_codeBuffers;\n    std::array<ShaderModuleInfo,                5>  m_moduleInfos = { };\n    std::array<VkPipelineShaderStageCreateInfo, 5>  m_stageInfos  = { };\n    uint32_t                                        m_stageCount  = 0;\n\n  };\n\n\n  /**\n   * \\brief Shader set\n   *\n   * Stores a set of shader pointers\n   * for use in a pipeline library.\n   */\n  struct DxvkShaderSet {\n    DxvkShader* vs = nullptr;\n    DxvkShader* tcs = nullptr;\n    DxvkShader* tes = nullptr;\n    DxvkShader* gs = nullptr;\n    DxvkShader* fs = nullptr;\n    DxvkShader* cs = nullptr;\n  };\n\n\n  /**\n   * \\brief Shader identifer set\n   *\n   * Stores a set of shader module identifiers\n   * for use in a pipeline library.\n   */\n  struct DxvkShaderIdentifierSet {\n    VkShaderModuleIdentifierEXT vs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };\n    VkShaderModuleIdentifierEXT tcs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };\n    VkShaderModuleIdentifierEXT tes = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };\n    VkShaderModuleIdentifierEXT gs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };\n    VkShaderModuleIdentifierEXT fs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };\n    VkShaderModuleIdentifierEXT cs = { VK_STRUCTURE_TYPE_SHADER_MODULE_IDENTIFIER_EXT };\n  };\n\n\n  /**\n   * \\brief Shader pipeline library key\n   */\n  class DxvkShaderPipelineLibraryKey {\n\n  public:\n\n    DxvkShaderPipelineLibraryKey();\n\n    ~DxvkShaderPipelineLibraryKey();\n\n    /**\n     * \\brief Queries number of shaders in the set\n     * \\returns Shader count\n     */\n    uint32_t getShaderCount() const {\n      return m_shaders.size();\n    }\n\n    /**\n     * \\brief Queries shader by index\n     */\n    DxvkShader* getShader(uint32_t index) const {\n      return m_shaders[index].ptr();\n    }\n\n    /**\n     * \\brief Queries number of shaders in the set\n     * \\returns Shader count\n     */\n    DxvkShader* findShader(VkShaderStageFlagBits stage) const {\n      for (const auto& shader : m_shaders) {\n        if (shader->metadata().stage == stage)\n          return shader.ptr();\n      }\n\n      return nullptr;\n    }\n\n    /**\n     * \\brief Adds a shader to the key\n     *\n     * Shaders must be added in stage order.\n     * \\param [in] shader Shader to add\n     */\n    void addShader(Rc<DxvkShader> shader);\n\n    /**\n     * \\brief Checks for equality\n     *\n     * \\param [in] other Key to compare to\n     * \\returns \\c true if the keys are equal\n     */\n    bool eq(const DxvkShaderPipelineLibraryKey& other) const;\n\n    /**\n     * \\brief Computes key hash\n     * \\returns Key hash\n     */\n    size_t hash() const;\n\n  private:\n\n    small_vector<Rc<DxvkShader>, 4> m_shaders;\n\n  };\n\n\n  /**\n   * \\brief Pipeline library handle\n   *\n   * Stores a pipeline library handle and the necessary link flags.\n   */\n  struct DxvkShaderPipelineLibraryHandle {\n    VkPipeline              handle;\n    VkPipelineCreateFlags2  linkFlags;\n  };\n\n\n  /**\n   * \\brief Shader pipeline library\n   *\n   * Stores a pipeline object for either a complete compute\n   * pipeline, a pre-rasterization pipeline library consisting\n   * of a single vertex shader, or a fragment shader pipeline\n   * library. All state unknown at shader compile time will\n   * be made dynamic.\n   */\n  class DxvkShaderPipelineLibrary {\n\n  public:\n\n    DxvkShaderPipelineLibrary(\n            DxvkDevice*               device,\n            DxvkPipelineManager*      manager,\n      const DxvkShaderPipelineLibraryKey& key);\n\n    ~DxvkShaderPipelineLibrary();\n\n    /**\n     * \\brief Queries shader module identifier\n     *\n     * Can be used to compile an optimized pipeline using the same\n     * shader code, but without having to wait for the pipeline\n     * library for this shader shader to compile first.\n     * \\param [in] stage Shader stage to query\n     * \\returns Shader module identifier\n     */\n    VkShaderModuleIdentifierEXT getModuleIdentifier(\n            VkShaderStageFlagBits                 stage);\n\n    /**\n     * \\brief Acquires pipeline handle for the given set of arguments\n     *\n     * Either returns an already compiled pipeline library object, or\n     * performs the compilation step if that has not happened yet.\n     * Increments the use count by one.\n     * \\returns Vulkan pipeline handle\n     */\n    DxvkShaderPipelineLibraryHandle acquirePipelineHandle();\n\n    /**\n     * \\brief Releases pipeline\n     *\n     * Decrements the use count by 1. If the use count reaches 0,\n     * any previously compiled pipeline library object may be\n     * destroyed in order to save memory.\n     */\n    void releasePipelineHandle();\n\n    /**\n     * \\brief Compiles the pipeline with default arguments\n     *\n     * This is meant to be called from a worker thread in\n     * order to reduce the amount of work done on the app's\n     * main thread.\n     */\n    void compilePipeline();\n\n  private:\n\n    DxvkDevice*                     m_device = nullptr;\n    DxvkPipelineManager*            m_manager = nullptr;\n\n    DxvkShaderPipelineLibraryKey    m_shaders;\n\n    std::optional<DxvkPipelineBindings> m_layout;\n\n    dxvk::mutex                     m_mutex;\n    uint32_t                        m_useCount      = 0u;\n    bool                            m_compiledOnce  = false;\n\n    std::optional<DxvkShaderPipelineLibraryHandle> m_pipeline;\n\n    dxvk::mutex                     m_identifierMutex;\n    DxvkShaderIdentifierSet         m_identifiers;\n\n    void destroyShaderPipelineLocked();\n\n    DxvkShaderPipelineLibraryHandle compileShaderPipelineLocked();\n\n    DxvkShaderPipelineLibraryHandle compileShaderPipeline(\n            VkPipelineCreateFlags2        flags);\n\n    VkPipeline compileVertexShaderPipeline(\n      const DxvkShaderStageInfo&          stageInfo,\n            VkPipelineCreateFlags2        flags);\n\n    VkPipeline compileFragmentShaderPipeline(\n      const DxvkShaderStageInfo&          stageInfo,\n            VkPipelineCreateFlags2        flags);\n\n    VkPipeline compileComputeShaderPipeline(\n      const DxvkShaderStageInfo&          stageInfo,\n            VkPipelineCreateFlags2        flags);\n\n    SpirvCodeBuffer getShaderCode(\n            VkShaderStageFlagBits         stage) const;\n\n    void generateModuleIdentifierLocked(\n            VkShaderModuleIdentifierEXT*  identifier,\n      const SpirvCodeBuffer&              spirvCode);\n\n    VkShaderStageFlags getShaderStages() const;\n\n    DxvkShader* getShader(\n            VkShaderStageFlagBits         stage) const;\n\n    VkShaderModuleIdentifierEXT* getShaderIdentifier(\n            VkShaderStageFlagBits         stage);\n\n    void notifyLibraryCompile() const;\n\n    void compileShaders();\n\n    bool canCreatePipelineLibrary() const;\n\n    bool canCreatePipelineLibraryForShader(DxvkShader& shader, bool needsPosition) const;\n\n    bool canUsePipelineCacheControl() const;\n\n    const DxvkPipelineLayout* getPipelineLibraryLayout() const;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_cache.cpp",
    "content": "#include <iomanip>\n#include <version.h>\n\n#include \"dxvk_shader_cache.h\"\n\n#include \"../util/util_time.h\"\n\nnamespace dxvk {\n\n  DxvkShaderCache::Instance DxvkShaderCache::s_instance;\n\n  DxvkShaderCache::DxvkShaderCache()\n  : m_filePaths(getDefaultFilePaths()) {\n\n  }\n\n\n  DxvkShaderCache::~DxvkShaderCache() {\n    if (m_writer.joinable()) {\n      { std::unique_lock lock(m_writeMutex);\n        m_writeQueue.push(nullptr);\n        m_writeCond.notify_one();\n      }\n\n      m_writer.join();\n    }\n  }\n\n\n  Rc<DxvkIrShader> DxvkShaderCache::lookupShader(\n    const std::string&                name,\n    const DxvkIrShaderCreateInfo&     options) {\n    if (!ensureStatus(Status::OpenReadWrite))\n      return nullptr;\n\n    LutKey k = { };\n    k.name = name;\n    k.createInfo = options;\n\n    auto entry = m_lut.find(k);\n\n    if (entry == m_lut.end()) {\n      if (Logger::logLevel() <= LogLevel::Debug)\n        Logger::debug(str::format(\"Shader cache miss: \", name));\n\n      return nullptr;\n    }\n\n    if (Logger::logLevel() <= LogLevel::Debug) {\n      Logger::debug(str::format(\"Shader cache hit: \", name,\n        \" (offset: \", entry->second.offset,\n        \", size: \", entry->second.binarySize,\n        \", metadata: \", entry->second.metadataSize, \")\"));\n    }\n\n    std::unique_lock lock(m_fileMutex);\n    auto shader = loadCachedShaderLocked(entry->first, entry->second);\n\n    if (!shader) {\n      Logger::warn(str::format(\"Failed to load cached shader \", name));\n\n      if (!openWriteOnlyLocked())\n        Logger::warn(str::format(\"Failed to re-initialize shader cache \", name));\n\n      m_status.store(Status::OpenWriteOnly, std::memory_order_release);\n    }\n\n    return shader;\n  }\n\n\n  void DxvkShaderCache::addShader(Rc<DxvkIrShader> shader) {\n    if (!ensureStatus(Status::OpenReadWrite))\n      return;\n\n    LutKey k = { };\n    k.name = shader->debugName();\n    k.createInfo = shader->getShaderCreateInfo();\n\n    if (m_lut.find(k) == m_lut.end()) {\n      std::unique_lock lock(m_writeMutex);\n      m_writeQueue.push(std::move(shader));\n      m_writeCond.notify_one();\n\n      if (!m_writer.joinable())\n        m_writer = dxvk::thread([this] { runWriter(); });\n    }\n  }\n\n\n  bool DxvkShaderCache::ensureStatus(Status status) {\n    auto currentStatus = m_status.load(std::memory_order_acquire);\n\n    if (currentStatus == Status::Uninitialized)\n      currentStatus = initialize();\n\n    return currentStatus >= status;\n  }\n\n\n  DxvkShaderCache::Status DxvkShaderCache::initialize() {\n    std::unique_lock lock(m_fileMutex);\n    auto status = m_status.load(std::memory_order_relaxed);\n\n    if (status != Status::Uninitialized)\n      return status;\n\n    status = tryInitializeLocked();\n\n    m_status.store(status, std::memory_order_release);\n    return status;\n  }\n\n\n  DxvkShaderCache::Status DxvkShaderCache::tryInitializeLocked() {\n    if (m_filePaths.directory.empty() || m_filePaths.binFile.empty() || m_filePaths.lutFile.empty()) {\n      Logger::warn(\"No path found for shader cache, consider setting DXVK_SHADER_CACHE_PATH.\");\n      return Status::CacheDisabled;\n    }\n\n    if (openReadWriteLocked()) {\n      if (parseLut())\n        return Status::OpenReadWrite;\n    }\n\n    if (openWriteOnlyLocked())\n      return Status::OpenReadWrite;\n\n    return Status::CacheDisabled;\n  }\n\n\n  bool DxvkShaderCache::openReadWriteLocked() {\n    // Try to open both files in read-only mode for now, re-open\n    // in read-write mode when we actually add new cache entries.\n    auto path = m_filePaths.directory + env::PlatformDirSlash;\n\n    auto flags = util::FileFlags(\n      util::FileFlag::AllowRead,\n      util::FileFlag::AllowWrite,\n      util::FileFlag::Exclusive);\n\n    m_binFile.open(path + m_filePaths.binFile, flags);\n    m_lutFile.open(path + m_filePaths.lutFile, flags);\n\n    if (!m_binFile || !m_lutFile)\n      return false;\n\n    Logger::info(str::format(\"Found cache file: \", path + m_filePaths.binFile));\n    return true;\n  }\n\n\n  bool DxvkShaderCache::openWriteOnlyLocked() {\n    // Didn't have a lot of success so far, nuke the files and retry.\n    auto path = m_filePaths.directory + env::PlatformDirSlash;\n\n    auto flags = util::FileFlags(\n      util::FileFlag::AllowWrite,\n      util::FileFlag::Truncate,\n      util::FileFlag::Exclusive);\n\n    m_binFile.open(path + m_filePaths.binFile, flags);\n    m_lutFile.open(path + m_filePaths.lutFile, flags);\n\n    if (!m_binFile || !m_lutFile) {\n      if (!env::createDirectory(m_filePaths.directory)) {\n        Logger::warn(str::format(\"Failed to create directory: \", m_filePaths.directory));\n        return false;\n      }\n\n      m_binFile.open(path + m_filePaths.binFile, flags);\n      m_lutFile.open(path + m_filePaths.lutFile, flags);\n    }\n\n    if (!m_binFile)\n      Logger::warn(str::format(\"Failed to create \", path + m_filePaths.binFile, \", disabling cache\"));\n\n    if (!m_lutFile)\n      Logger::warn(str::format(\"Failed to create \", path + m_filePaths.lutFile, \", disabling cache\"));\n\n    if (!m_binFile || !m_lutFile)\n      return false;\n\n    Logger::info(str::format(\"Created cache file: \", path + m_filePaths.binFile));\n\n    LutHeader header = { };\n    header.magic = { 'D', 'X', 'V', 'K' };\n    header.versionString = DXVK_VERSION;\n\n    if (!writeHeader(m_lutFile, header)) {\n      Logger::warn(str::format(\"Failed to write cache header: \", path + m_filePaths.lutFile));\n      return false;\n    }\n\n    return true;\n  }\n\n\n  bool DxvkShaderCache::parseLut() {\n    LutHeader header;\n\n    size_t size = m_lutFile.size();\n    size_t offset = 0u;\n\n    if (!readBytes(m_lutFile, header.magic.data(), offset, header.magic.size())\n     || !readString(m_lutFile, offset, header.versionString)) {\n      Logger::warn(\"Failed to parse cache file header.\");\n      return false;\n    }\n\n    if (header.versionString != DXVK_VERSION) {\n      Logger::warn(str::format(\"Cache was created with DXVK version \", header.versionString,\n        \", but current version is \", DXVK_VERSION, \". Discarding old cache.\"));\n      return false;\n    }\n\n    while (offset < size) {\n      LutKey k;\n      LutEntry e;\n\n      if (!readShaderLutEntry(k, e, offset)) {\n        Logger::warn(\"Failed to parse cache look-up table.\");\n        return false;\n      }\n\n      m_lut.insert_or_assign(k, e);\n    }\n\n    return true;\n  }\n\n\n  bool DxvkShaderCache::writeShaderXfbInfo(util::File& stream, const dxbc_spv::ir::IoXfbInfo& xfb) {\n    return writeString(stream, xfb.semanticName)\n        && write(stream, xfb.semanticIndex)\n        && write(stream, xfb.componentMask)\n        && write(stream, xfb.stream)\n        && write(stream, xfb.buffer)\n        && write(stream, xfb.offset)\n        && write(stream, xfb.stride);\n  }\n\n\n  bool DxvkShaderCache::writeShaderCreateInfo(util::File& stream, const DxvkIrShaderCreateInfo& createInfo) {\n    bool status = write(stream, createInfo.options)\n               && write(stream, createInfo.flatShadingInputs)\n               && write(stream, createInfo.rasterizedStream);\n\n    status = status && write(stream, uint32_t(createInfo.xfbEntries.size()));\n\n    for (const auto& xfb : createInfo.xfbEntries)\n      status = status && writeShaderXfbInfo(stream, xfb);\n\n    return status;\n  }\n\n\n  Rc<DxvkIrShader> DxvkShaderCache::loadCachedShaderLocked(const LutKey& key, const LutEntry& entry) {\n    std::vector<uint8_t> ir(entry.binarySize);\n\n    size_t offset = entry.offset;\n\n    if (!readBytes(m_binFile, ir.data(), offset, entry.binarySize)) {\n      Logger::warn(\"Failed to read cached shader binary\");\n      return nullptr;\n    }\n\n    if (entry.checksum != bit::fnv1a_hash(ir.data(), ir.size())) {\n      Logger::warn(\"Checksum mismatch for cached shader\");\n      return nullptr;\n    }\n\n    DxvkShaderMetadata metadata;\n\n    if (!readShaderMetadata(m_binFile, offset, metadata)) {\n      Logger::warn(\"Failed to read cached shader metadata\");\n      return nullptr;\n    }\n\n    DxvkPipelineLayoutBuilder layout;\n\n    if (!readShaderLayout(m_binFile, offset, layout)) {\n      Logger::warn(\"Failed to read cached shader binding layout\");\n      return nullptr;\n    }\n\n    return new DxvkIrShader(key.name, key.createInfo, std::move(metadata), std::move(layout), std::move(ir));\n  }\n\n\n  bool DxvkShaderCache::writeShaderLutEntry(DxvkIrShader& shader, const LutEntry& entry) {\n    return writeString(m_lutFile, shader.debugName())\n        && writeShaderCreateInfo(m_lutFile, shader.getShaderCreateInfo())\n        && write(m_lutFile, entry);\n  }\n\n\n  bool DxvkShaderCache::writeShaderToCache(DxvkIrShader& shader) {\n    auto entry = writeShaderBinary(m_binFile, shader);\n\n    if (!entry)\n      return false;\n\n    return writeShaderLutEntry(shader, *entry);\n  }\n\n\n  bool DxvkShaderCache::readShaderIo(util::File& stream, size_t& offset, DxvkShaderIo& io) {\n    uint8_t varCount = 0u;\n\n    if (!read(stream, offset, varCount))\n      return false;\n\n    for (uint32_t i = 0u; i < varCount; i++) {\n      DxvkShaderIoVar var = { };\n\n      if (!read(stream, offset, var.builtIn)\n       || !read(stream, offset, var.location)\n       || !read(stream, offset, var.componentIndex)\n       || !read(stream, offset, var.componentCount)\n       || !read(stream, offset, var.isPatchConstant)\n       || !read(stream, offset, var.semanticIndex)\n       || !readString(stream, offset, var.semanticName))\n        return false;\n\n      io.add(std::move(var));\n    }\n\n    return true;\n  }\n\n\n  bool DxvkShaderCache::readShaderMetadata(util::File& stream, size_t& offset, DxvkShaderMetadata& metadata) {\n    bool status = read(stream, offset, metadata.stage)\n               && read(stream, offset, metadata.flags)\n               && read(stream, offset, metadata.specConstantMask)\n               && readShaderIo(stream, offset, metadata.inputs)\n               && readShaderIo(stream, offset, metadata.outputs)\n               && read(stream, offset, metadata.inputTopology)\n               && read(stream, offset, metadata.outputTopology)\n               && read(stream, offset, metadata.flatShadingInputs)\n               && read(stream, offset, metadata.rasterizedStream)\n               && read(stream, offset, metadata.patchVertexCount);\n\n    for (auto& xfb : metadata.xfbStrides)\n      status = status && read(stream, offset, xfb);\n\n    return status;\n  }\n\n\n  bool DxvkShaderCache::readShaderLayout(util::File& stream, size_t& offset, DxvkPipelineLayoutBuilder& layout) {\n    VkShaderStageFlags stageMask = { };\n\n    if (!read(stream, offset, stageMask))\n      return false;\n\n    layout = DxvkPipelineLayoutBuilder(stageMask);\n\n    // Read push data blocks\n    uint32_t pushDataMask = 0u;\n\n    if (!read(stream, offset, pushDataMask))\n      return false;\n\n    for (uint32_t i = 0u; i < bit::popcnt(pushDataMask); i++) {\n      DxvkPushDataBlock block = { };\n\n      if (!read(stream, offset, block))\n        return false;\n\n      layout.addPushData(block);\n    }\n\n    // Read shader binding info\n    uint32_t bindingCount = 0u;\n\n    if (!read(stream, offset, bindingCount))\n      return false;\n\n    for (uint32_t i = 0u; i < bindingCount; i++) {\n      DxvkShaderDescriptor binding = { };\n\n      if (!read(stream, offset, binding))\n        return false;\n\n      layout.addBindings(1u, &binding);\n    }\n\n    // Read sampler heap mappings\n    uint32_t samplerHeapCount = 0u;\n\n    if (!read(stream, offset, samplerHeapCount))\n      return false;\n\n    for (uint32_t i = 0u; i < samplerHeapCount; i++) {\n      DxvkShaderBinding binding = { };\n\n      if (!read(stream, offset, binding))\n        return false;\n\n      layout.addSamplerHeap(binding);\n    }\n\n    return true;\n  }\n\n\n  bool DxvkShaderCache::readShaderXfbInfo(util::File& stream, size_t& offset, dxbc_spv::ir::IoXfbInfo& xfb) {\n    return readString(stream, offset, xfb.semanticName)\n        && read(stream, offset, xfb.semanticIndex)\n        && read(stream, offset, xfb.componentMask)\n        && read(stream, offset, xfb.stream)\n        && read(stream, offset, xfb.buffer)\n        && read(stream, offset, xfb.offset)\n        && read(stream, offset, xfb.stride);\n }\n\n\n  bool DxvkShaderCache::readShaderLutKey(util::File& stream, size_t& offset, LutKey& key) {\n    bool status = readString(stream, offset, key.name)\n               && read(stream, offset, key.createInfo.options)\n               && read(stream, offset, key.createInfo.flatShadingInputs)\n               && read(stream, offset, key.createInfo.rasterizedStream);\n\n    uint32_t xfbCount = 0u;\n    status = status && read(stream, offset, xfbCount);\n\n    key.createInfo.xfbEntries.resize(xfbCount);\n\n    for (uint32_t i = 0u; i < xfbCount; i++)\n      status = status && readShaderXfbInfo(stream, offset, key.createInfo.xfbEntries[i]);\n\n    return status;\n  }\n\n\n  bool DxvkShaderCache::readShaderLutEntry(LutKey& key, LutEntry& entry, size_t& offset) {\n    return readShaderLutKey(m_lutFile, offset, key) && read(m_lutFile, offset, entry);\n  }\n\n\n  void DxvkShaderCache::runWriter() {\n    small_vector<Rc<DxvkIrShader>, 128u> localQueue;\n\n    env::setThreadName(\"dxvk-cache\");\n\n    bool stop = false;\n\n    while (!stop) {\n      std::unique_lock lock(m_writeMutex);\n\n      m_writeCond.wait(lock, [this] {\n        return !m_writeQueue.empty();\n      });\n\n      auto entry = std::move(m_writeQueue.front());\n      m_writeQueue.pop();\n\n      lock.unlock();\n\n      stop = !entry;\n      bool drain = stop;\n\n      if (entry) {\n        localQueue.push_back(std::move(entry));\n        drain = localQueue.size() == localQueue.capacity();\n      }\n\n      if (drain) {\n        std::unique_lock fileLock(m_fileMutex);\n\n        for (const auto& shader : localQueue) {\n          if (!writeShaderToCache(*shader)) {\n            Logger::err(\"Failed to write cache file.\");\n            m_status = Status::CacheDisabled;\n            return;\n          }\n        }\n\n        localQueue.clear();\n\n        m_binFile.flush();\n        m_lutFile.flush();\n      }\n    }\n  }\n\n\n  bool DxvkShaderCache::writeShaderLayout(util::File& stream, const DxvkPipelineLayoutBuilder& layout) {\n    bool status = write(stream, layout.getStageMask())\n               && write(stream, layout.getPushDataMask());\n\n    for (auto pushIndex : bit::BitMask(layout.getPushDataMask()))\n      status = status && write(stream, layout.getPushDataBlock(pushIndex));\n\n    auto bindings = layout.getBindings();\n    status = status && write(stream, uint32_t(bindings.bindingCount));\n\n    for (size_t i = 0u; i < bindings.bindingCount; i++)\n      status = status && write(stream, bindings.bindings[i]);\n\n    status = status && write(stream, uint32_t(layout.getSamplerHeapBindingCount()));\n\n    for (size_t i = 0u; i < layout.getSamplerHeapBindingCount(); i++)\n      status = status && write(stream, layout.getSamplerHeapBinding(i));\n\n    return status;\n  }\n\n\n  bool DxvkShaderCache::writeShaderIo(util::File& stream, const DxvkShaderIo& io) {\n    bool status = write(stream, uint8_t(io.getVarCount()));\n\n    for (uint32_t i = 0u; i < io.getVarCount(); i++) {\n      const auto& var = io.getVar(i);\n\n      status = status && write(stream, var.builtIn)\n                      && write(stream, var.location)\n                      && write(stream, var.componentIndex)\n                      && write(stream, var.componentCount)\n                      && write(stream, var.isPatchConstant)\n                      && write(stream, var.semanticIndex)\n                      && writeString(stream, var.semanticName);\n    }\n\n    return status;\n  }\n\n\n  bool DxvkShaderCache::writeShaderMetadata(util::File& stream, const DxvkShaderMetadata& metadata) {\n    bool status = write(stream, metadata.stage)\n               && write(stream, metadata.flags)\n               && write(stream, metadata.specConstantMask)\n               && writeShaderIo(stream, metadata.inputs)\n               && writeShaderIo(stream, metadata.outputs)\n               && write(stream, metadata.inputTopology)\n               && write(stream, metadata.outputTopology)\n               && write(stream, metadata.flatShadingInputs)\n               && write(stream, metadata.rasterizedStream)\n               && write(stream, metadata.patchVertexCount);\n\n    for (const auto& xfb : metadata.xfbStrides)\n      status = status && write(stream, xfb);\n\n    return status;\n  }\n\n\n  std::optional<DxvkShaderCache::LutEntry> DxvkShaderCache::writeShaderBinary(util::File& stream, DxvkIrShader& shader) {\n    auto [data, size] = shader.getSerializedIr();\n\n    LutEntry entry = { };\n    entry.offset = stream.size();\n    entry.binarySize = size;\n\n    if (!writeBytes(stream, data, size)\n     || !writeShaderMetadata(stream, shader.getShaderMetadata())\n     || !writeShaderLayout(stream, shader.getLayout()))\n      return std::nullopt;\n\n\n    entry.metadataSize = uint32_t(uint64_t(stream.size()) - (entry.offset + entry.binarySize));\n    entry.checksum = bit::fnv1a_hash(data, size);\n    return std::make_optional(entry);\n  }\n\n\n  bool DxvkShaderCache::writeHeader(util::File& stream, const LutHeader& header) {\n    return writeBytes(stream, header.magic.data(), header.magic.size())\n        && writeString(stream, header.versionString);\n  }\n\n\n  DxvkShaderCache::FilePaths DxvkShaderCache::getDefaultFilePaths() {\n    std::string cachePath = env::getEnvVar(\"DXVK_SHADER_CACHE_PATH\");\n\n    if (cachePath.empty()) {\n      #ifdef _WIN32\n      cachePath = env::getEnvVar(\"LOCALAPPDATA\");\n      #endif\n\n      if (cachePath.empty())\n        cachePath = env::getEnvVar(\"XDG_CACHE_HOME\");\n\n      if (cachePath.empty()) {\n        cachePath = env::getEnvVar(\"HOME\");\n\n        if (!cachePath.empty()) {\n          cachePath += env::PlatformDirSlash;\n          cachePath += \".cache\";\n        }\n      }\n\n      if (!cachePath.empty()) {\n        cachePath += env::PlatformDirSlash;\n        cachePath += \"dxvk\";\n      }\n    }\n\n    if (cachePath.empty())\n      return FilePaths();\n\n    // Determine file name based on the actual executable,\n    // including the containing directory.\n    std::string exePath = env::getExePath();\n\n    if (exePath.empty())\n      return FilePaths();\n\n    size_t pathStart = exePath.find_last_of(env::PlatformDirSlash);\n\n    if (pathStart != std::string::npos)\n      pathStart = exePath.find_last_of(env::PlatformDirSlash, pathStart);\n\n    if (pathStart == std::string::npos)\n      pathStart = 0u;\n\n    uint64_t hash = bit::fnv1a_init();\n\n    for (size_t i = pathStart; i < exePath.size(); i++)\n      hash = bit::fnv1a_iter(hash, uint8_t(exePath[i]));\n\n    std::string baseName = str::format(std::hex, std::setw(16u), std::setfill('0'), hash);\n\n    FilePaths paths;\n    paths.directory = cachePath;\n    paths.lutFile = baseName + \".dxvk.lut\";\n    paths.binFile = baseName + \".dxvk.bin\";\n    return paths;\n  }\n\n\n  Rc<DxvkShaderCache> DxvkShaderCache::getInstance() {\n    std::lock_guard lock(s_instance.mutex);\n\n    if (!s_instance.instance)\n      s_instance.instance = new DxvkShaderCache();\n\n    return s_instance.instance;\n  }\n\n\n  void DxvkShaderCache::freeInstance() {\n    std::lock_guard lock(s_instance.mutex);\n\n    // The ref count can only be incremented from 0 to 1 inside a locked\n    // context, so this check is safe. Don't destroy the object if another\n    // thread has essentially revived it.\n    if (m_useCount.load(std::memory_order_relaxed) || s_instance.instance != this) {\n      if (s_instance.instance == this)\n        s_instance.instance = nullptr;\n\n      delete this;\n    }\n  }\n\n\n  size_t DxvkShaderCache::LutKey::hash() const {\n    DxvkHashState hash;\n    hash.add(bit::fnv1a_hash(name.data(), name.size()));\n    hash.add(createInfo.hash());\n    return hash;\n  }\n\n\n  bool DxvkShaderCache::LutKey::eq(const LutKey& k) const {\n    return name == k.name && createInfo.eq(k.createInfo);\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_cache.h",
    "content": "#pragma once\n\n#include <array>\n#include <optional>\n#include <string>\n#include <queue>\n#include <type_traits>\n#include <unordered_map>\n#include <vector>\n\n#include \"../util/thread.h\"\n#include \"../util/util_env.h\"\n#include \"../util/util_file.h\"\n\n#include \"dxvk_shader_ir.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Shader cache\n   *\n   * On-disk cache for shaders using the internal IR.\n   *\n   * The implementation creates two files that can trivially grow by appending\n   * data to them: A binary blob that contains the actual serialized IR as well\n   * as shader metadata, and a look-up table\n   */\n  class DxvkShaderCache {\n\n  public:\n\n    struct FilePaths {\n      std::string directory;\n      std::string lutFile;\n      std::string binFile;\n    };\n\n    ~DxvkShaderCache();\n\n    void incRef() {\n      m_useCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    void decRef() {\n      if (m_useCount.fetch_sub(1u, std::memory_order_release) == 1u)\n        freeInstance();\n    }\n\n    /**\n     * \\brief Looks up shader with matching name and options\n     *\n     * \\param [in] name Shader name\n     * \\param [in] options Shader properties and compile options\n     * \\returns Shader object, or \\c nullptr if the shader in\n     *    question could not be found in the cache.\n     */\n    Rc<DxvkIrShader> lookupShader(\n      const std::string&                name,\n      const DxvkIrShaderCreateInfo&     options);\n\n    /**\n     * \\brief Writes shader to cache file\n     *\n     * The shader binary will be written asynchronously.\n     * \\param [in] shader Shader to write to cache\n     */\n    void addShader(Rc<DxvkIrShader> shader);\n\n    /**\n     * \\brief Determines cache file path based on current environment and executable\n     * \\returns File paths and file names for cache files\n     */\n    static FilePaths getDefaultFilePaths();\n\n    /**\n     * \\brief Initializes shader cache\n     * \\returns Shader cache instance\n     */\n    static Rc<DxvkShaderCache> getInstance();\n\n  private:\n\n    struct Instance {\n      dxvk::mutex       mutex;\n      DxvkShaderCache*  instance = nullptr;\n    };\n\n    static Instance s_instance;\n\n    struct LutHeader {\n      std::array<char, 4u>  magic = { };\n      std::string           versionString = { };\n    };\n\n    struct LutKey {\n      std::string name;\n      DxvkIrShaderCreateInfo createInfo;\n\n      size_t hash() const;\n\n      bool eq(const LutKey& k) const;\n    };\n\n    struct LutEntry {\n      uint64_t offset = 0u;\n      uint32_t binarySize = 0u;\n      uint32_t metadataSize = 0u;\n      uint64_t checksum = 0u;\n    };\n\n    enum class Status : uint32_t {\n      Uninitialized   = 0u,\n      CacheDisabled   = 1u,\n      OpenWriteOnly   = 2u,\n      OpenReadWrite   = 3u,\n    };\n\n    std::atomic<uint32_t>         m_useCount = { 0u };\n\n    FilePaths                     m_filePaths;\n    dxvk::mutex                   m_fileMutex;\n\n    util::File                    m_lutFile;\n    util::File                    m_binFile;\n\n    std::atomic<Status>           m_status = { Status::Uninitialized };\n\n    std::unordered_map<LutKey, LutEntry, DxvkHash, DxvkEq> m_lut;\n\n    dxvk::mutex                   m_writeMutex;\n    dxvk::condition_variable      m_writeCond;\n    std::queue<Rc<DxvkIrShader>>  m_writeQueue;\n\n    dxvk::thread                  m_writer;\n\n    DxvkShaderCache();\n\n    bool ensureStatus(Status status);\n\n    Status initialize();\n\n    Status tryInitializeLocked();\n\n    bool openReadWriteLocked();\n\n    bool openWriteOnlyLocked();\n\n    bool parseLut();\n\n    Rc<DxvkIrShader> loadCachedShaderLocked(const LutKey& key, const LutEntry& entry);\n\n    bool writeShaderLutEntry(DxvkIrShader& shader, const LutEntry& entry);\n\n    bool writeShaderToCache(DxvkIrShader& shader);\n\n    bool readShaderLutEntry(LutKey& key, LutEntry& entry, size_t& offset);\n\n    void runWriter();\n\n    void freeInstance();\n\n    static bool writeShaderXfbInfo(util::File& stream, const dxbc_spv::ir::IoXfbInfo& xfb);\n\n    static bool writeShaderCreateInfo(util::File& stream, const DxvkIrShaderCreateInfo& createInfo);\n\n    static bool writeShaderLayout(util::File& stream, const DxvkPipelineLayoutBuilder& layout);\n\n    static bool writeShaderIo(util::File& stream, const DxvkShaderIo& io);\n\n    static bool writeShaderMetadata(util::File& stream, const DxvkShaderMetadata& metadata);\n\n    static std::optional<LutEntry> writeShaderBinary(util::File& stream, DxvkIrShader& shader);\n\n    static bool writeHeader(util::File& stream, const LutHeader& header);\n\n    static bool readShaderIo(util::File& stream, size_t& offset, DxvkShaderIo& io);\n\n    static bool readShaderXfbInfo(util::File& stream, size_t& offset, dxbc_spv::ir::IoXfbInfo& xfb);\n\n    static bool readShaderLutKey(util::File& stream, size_t& offset, LutKey& key);\n\n    static bool readShaderMetadata(util::File& stream, size_t& offset, DxvkShaderMetadata& metadata);\n\n    static bool readShaderLayout(util::File& stream, size_t& offset, DxvkPipelineLayoutBuilder& layout);\n\n    static bool writeBytes(util::File& stream, const char* data, size_t size) {\n      return stream.append(size, data);\n    }\n\n    static bool writeBytes(util::File& stream, const uint8_t* data, size_t size) {\n      return writeBytes(stream, reinterpret_cast<const char*>(data), size);\n    }\n\n    static bool writeString(util::File& stream, const std::string& string) {\n      return write(stream, uint16_t(string.size())) && writeBytes(stream, string.data(), string.size());\n    }\n\n    template<typename T, std::enable_if_t<std::is_trivially_copyable_v<T>, bool> = true>\n    static bool write(util::File& stream, const T& data) {\n      return writeBytes(stream, reinterpret_cast<const char*>(&data), sizeof(data));\n    }\n\n    static bool readBytes(util::File& stream, char* data, size_t& offset, size_t size) {\n      bool result = stream.read(offset, size, data);\n      offset += size;\n      return result;\n    }\n\n    static bool readBytes(util::File& stream, uint8_t* data, size_t& offset, size_t size) {\n      return readBytes(stream, reinterpret_cast<char*>(data), offset, size);\n    }\n\n    static bool readString(util::File& stream, size_t& offset, std::string& string) {\n      uint16_t len = 0u;\n\n      if (!read(stream, offset, len))\n        return false;\n\n      string.resize(len);\n      return readBytes(stream, string.data(), offset, len);\n    }\n\n    template<typename T, std::enable_if_t<std::is_trivially_copyable_v<T>, bool> = true>\n    static bool read(util::File& stream, size_t& offset, T& data) {\n      return readBytes(stream, reinterpret_cast<char*>(&data), offset, sizeof(data));\n    }\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_io.cpp",
    "content": "#include \"dxvk_shader_io.h\"\n\nnamespace dxvk {\n\n  DxvkShaderIo::DxvkShaderIo() {\n\n  }\n\n\n  DxvkShaderIo::~DxvkShaderIo() {\n\n  }\n\n\n  void DxvkShaderIo::add(DxvkShaderIoVar var) {\n    size_t size = m_vars.size();\n    size_t index = 0u;\n\n    while (index < size && orderBefore(m_vars[index], var))\n      index++;\n\n    m_vars.resize(size + 1u);\n\n    for (size_t i = size; i > index; i--)\n      m_vars[i] = m_vars[i - 1u];\n\n    m_vars[index] = std::move(var);\n  }\n\n\n  uint32_t DxvkShaderIo::computeMask() const {\n    uint32_t result = 0u;\n\n    for (size_t i = 0u; i < m_vars.size(); i++) {\n      if (m_vars[i].builtIn == spv::BuiltInMax)\n        result |= 1u << m_vars[i].location;\n    }\n\n    return result;\n  }\n\n\n  bool DxvkShaderIo::checkStageCompatibility(\n          VkShaderStageFlagBits stage,\n    const DxvkShaderIo&         inputs,\n          VkShaderStageFlagBits prevStage,\n    const DxvkShaderIo&         outputs,\n          bool                  matchSemantics) {\n    for (uint32_t i = 0, j = 0; i < inputs.getVarCount(); i++) {\n      // Ignore built-ins that don't need to be written by previous stage\n      auto input = inputs.getVar(i);\n\n      if (input.builtIn != spv::BuiltInMax) {\n        if (isBuiltInInputGenerated(stage, prevStage, input.builtIn))\n          continue;\n      }\n\n      // Find corresponding output variable\n      if (j >= outputs.getVarCount())\n        return false;\n\n      while (orderBefore(outputs.getVar(j), input)) {\n        if (++j >= outputs.getVarCount())\n          return false;\n      }\n\n      auto output = outputs.getVar(j);\n\n      if (input.builtIn != spv::BuiltInMax) {\n        // Require a full match for built-ins\n        if (input.builtIn != output.builtIn || input.componentCount != output.componentCount)\n          return false;\n      } else {\n        // The only legal mismatch is output stage writing more components\n        // than the input stage consumes, everything else has to match.\n        if (input.isPatchConstant != output.isPatchConstant ||\n            input.location        != output.location ||\n            input.componentIndex  != output.componentIndex ||\n            input.componentCount  >  output.componentCount)\n          return false;\n\n        // Only match semantics if explicitly requested, and ignore built-ins\n        // since they are inherently matched via the actual built-in anyway.\n        if (matchSemantics) {\n          if (!str::compareCaseInsensitive(input.semanticName.c_str(), output.semanticName.c_str())\n           || input.semanticIndex != output.semanticIndex)\n            return false;\n        }\n      }\n    }\n\n    return true;\n  }\n\n\n  DxvkShaderIo DxvkShaderIo::forVertexBindings(uint32_t bindingMask) {\n    DxvkShaderIo result;\n\n    for (auto location : bit::BitMask(bindingMask)) {\n      DxvkShaderIoVar var = { };\n      var.location = location;\n      var.componentCount = 4u;\n\n      result.add(std::move(var));\n    }\n\n    return result;\n  }\n\n\n  bool DxvkShaderIo::isBuiltInInputGenerated(\n        VkShaderStageFlagBits stage,\n        VkShaderStageFlagBits prevStage,\n        spv::BuiltIn          builtIn) {\n    switch (builtIn) {\n      case spv::BuiltInPrimitiveId: {\n        // Must be exported by DS / GS when read in subsequent stage\n        return prevStage == VK_SHADER_STAGE_VERTEX_BIT ||\n               prevStage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;\n      }\n\n      case spv::BuiltInPosition:\n        return stage == VK_SHADER_STAGE_FRAGMENT_BIT;\n\n      case spv::BuiltInClipDistance:\n      case spv::BuiltInCullDistance:\n      case spv::BuiltInTessLevelInner:\n      case spv::BuiltInTessLevelOuter:\n        return false;\n\n      default:\n        return true;\n    }\n  }\n\n\n  bool DxvkShaderIo::orderBefore(\n    const DxvkShaderIoVar&      a,\n    const DxvkShaderIoVar&      b) {\n    if (a.builtIn != b.builtIn)\n      return a.builtIn < b.builtIn;\n\n    if (a.location != b.location)\n      return a.location < b.location;\n\n    return a.componentIndex < b.componentIndex;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_io.h",
    "content": "#pragma once\n\n#include \"dxvk_include.h\"\n#include \"dxvk_hash.h\"\n\n#include \"../spirv/spirv_module.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Shader I/O variable\n   *\n   * Stores metadata about a shader-declared I/O var.\n   */\n  struct DxvkShaderIoVar {\n    /// Built-in. If the variable represents a user\n    /// varying instead, this will be BuiltInMax.\n    spv::BuiltIn builtIn = spv::BuiltInMax;\n    /// User varying location\n    uint8_t location = 0u;\n    /// User varying component index\n    uint8_t componentIndex = 0u;\n    /// Component count or array size\n    uint8_t componentCount = 0u;\n    /// Whether the declaration is a patch constant.\n    /// Only used in tessellation shaders.\n    bool isPatchConstant = false;\n    /// Semantic name and index\n    uint32_t semanticIndex = 0u;\n    std::string semanticName = { };\n\n    bool eq(const DxvkShaderIoVar& other) const {\n      return builtIn         == other.builtIn &&\n             location        == other.location &&\n             componentIndex  == other.componentIndex &&\n             componentCount  == other.componentCount &&\n             isPatchConstant == other.isPatchConstant &&\n             semanticIndex   == other.semanticIndex &&\n             semanticName    == other.semanticName;\n    }\n\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(uint32_t(builtIn));\n      hash.add(uint32_t(location));\n      hash.add(uint32_t(componentIndex));\n      hash.add(uint32_t(componentCount));\n      hash.add(uint32_t(isPatchConstant));\n      hash.add(uint32_t(semanticIndex));\n      hash.add(bit::fnv1a_hash(semanticName.data(), semanticName.size()));\n      return hash;\n    }\n  };\n\n\n  /**\n   * \\brief Shader I/O metadata\n   *\n   * Collection of all I/O variables declared in a shader.\n   */\n  class DxvkShaderIo {\n\n  public:\n\n    DxvkShaderIo();\n\n    ~DxvkShaderIo();\n\n    /**\n     * \\brief Number of I/O variables in collection\n     * \\returns Number of declared I/O variables\n     */\n    uint32_t getVarCount() const {\n      return uint32_t(m_vars.size());\n    }\n\n    /**\n     * \\brief Queries I/O variable metadata\n     *\n     * \\param [in] index Variable index\n     * \\returns Info for the given variable\n     */\n    DxvkShaderIoVar getVar(uint32_t index) const {\n      return m_vars[index];\n    }\n\n    /**\n     * \\brief Adds an I/O variable\n     *\n     * Ensures that variables are ordered for faster,\n     * linear-time compatibility checking later.\n     * \\param [in] var Variable declaration to add\n     */\n    void add(DxvkShaderIoVar var);\n\n    /**\n     * \\brief Computes used location mask\n     *\n     * Useful when determining which render targets or vertex buffer\n     * bindings are written or consumed by a shader.\n     * \\returns Mask of user I/O locations\n     */\n    uint32_t computeMask() const;\n\n    /**\n     * \\brief Checks I/O compatibility between shaders\n     *\n     * \\param [in] stage Shader stage that consumes inputs\n     * \\param [in] inputs Input variables consumed by the shader\n     * \\param [in] prevStage Previous stage. Ignored for vertex shaders.\n     * \\param [in] outputs Output variables written by the previous\n     *    stage, or vertex buffer bindings in case of vertex shaders.\n     * \\param [in] matchSemantics Whether to compare shader semantics.\n     *    If not set, semantic names will be ignored completely.\n     * \\returns \\c true if all input variables consumed by the given\n     *    shader are written by the previous stage, or \\c false if any\n     *    fix-up is required.\n     */\n    static bool checkStageCompatibility(\n            VkShaderStageFlagBits stage,\n      const DxvkShaderIo&         inputs,\n            VkShaderStageFlagBits prevStage,\n      const DxvkShaderIo&         outputs,\n            bool                  matchSemantics);\n\n    /**\n     * \\brief Computes I/O object for vertex bindings.\n     *\n     * \\param [in] bindingMask Vertex binding mask\n     * \\returns I/O object corresponding to the given mask\n     */\n    static DxvkShaderIo forVertexBindings(uint32_t bindingMask);\n\n  private:\n\n    small_vector<DxvkShaderIoVar, 32> m_vars;\n\n    static bool isBuiltInInputGenerated(\n            VkShaderStageFlagBits stage,\n            VkShaderStageFlagBits prevStage,\n            spv::BuiltIn          builtIn);\n\n    static bool orderBefore(\n      const DxvkShaderIoVar&      a,\n      const DxvkShaderIoVar&      b);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_ir.cpp",
    "content": "#include <ir/ir_serialize.h>\n\n#include <spirv/spirv_builder.h>\n\n#include <util/util_log.h>\n\n#include \"dxvk_shader_ir.h\"\n\nnamespace dxvk {\n\n  size_t DxvkIrShaderCreateInfo::hash() const {\n    static_assert(std::is_trivially_copyable_v<DxvkShaderOptions>);\n\n    DxvkHashState hash;\n    hash.add(bit::fnv1a_hash(reinterpret_cast<const char*>(&options), sizeof(options)));\n    hash.add(flatShadingInputs);\n    hash.add(rasterizedStream);\n\n    for (const auto& xfb : xfbEntries)\n      hash.add(std::hash<dxbc_spv::ir::IoXfbInfo>()(xfb));\n\n    return hash;\n  }\n\n\n  bool DxvkIrShaderCreateInfo::eq(const DxvkIrShaderCreateInfo& other) const {\n    static_assert(std::is_trivially_copyable_v<DxvkShaderOptions>);\n\n    if (std::memcmp(&options, &other.options, sizeof(options)))\n      return false;\n\n    if (flatShadingInputs != other.flatShadingInputs\n     || rasterizedStream != other.rasterizedStream)\n      return false;\n\n    if (xfbEntries.size() != other.xfbEntries.size())\n      return false;\n\n    for (size_t i = 0u; i != xfbEntries.size(); i++) {\n      if (xfbEntries[i] != other.xfbEntries[i])\n        return false;\n    }\n\n    return true;\n  }\n\n\n  /**\n   * \\brief DXVK-specific logger for dxbc-spirv\n   */\n  class DxvkDxbcSpirvLogger : public dxbc_spv::util::Logger {\n\n  public:\n\n    DxvkDxbcSpirvLogger(std::string shaderName)\n    : m_debugName(std::move(shaderName)) { }\n\n\n    void message(dxbc_spv::util::LogLevel severity, const char* message) override {\n      dxvk::Logger::log(convertLogLevel(severity), m_debugName + \": \" + message);\n    }\n\n    dxbc_spv::util::LogLevel getMinimumSeverity() override {\n      switch (dxvk::Logger::logLevel()) {\n        case LogLevel::Debug:\n          return dxbc_spv::util::LogLevel::eDebug;\n        case LogLevel::Info:\n          return dxbc_spv::util::LogLevel::eInfo;\n        case LogLevel::Warn:\n          return dxbc_spv::util::LogLevel::eWarn;\n        default:\n          return dxbc_spv::util::LogLevel::eError;\n      }\n    }\n\n  private:\n\n    std::string m_debugName;\n\n    static LogLevel convertLogLevel(dxbc_spv::util::LogLevel severity) {\n      switch (severity) {\n        case dxbc_spv::util::LogLevel::eDebug:\n          return LogLevel::Debug;\n        case dxbc_spv::util::LogLevel::eInfo:\n          return LogLevel::Info;\n        case dxbc_spv::util::LogLevel::eWarn:\n          return LogLevel::Warn;\n        case dxbc_spv::util::LogLevel::eError:\n          return LogLevel::Error;\n      }\n\n      return LogLevel::Info;\n    }\n\n  };\n\n\n  /**\n   * \\brief DXVK-specific resource mapping for dxbc-spirv shaders\n   *\n   * Uses the pre-computed pipeline layout to map\n   */\n  class DxvkShaderResourceMapping : public dxbc_spv::spirv::ResourceMapping {\n\n  public:\n\n    explicit DxvkShaderResourceMapping(VkShaderStageFlagBits stage, const DxvkShaderBindingMap* bindings)\n    : m_stage(stage), m_bindings(bindings) { }\n\n    ~DxvkShaderResourceMapping() {\n\n    }\n\n    dxbc_spv::spirv::DescriptorBinding mapDescriptor(\n          dxbc_spv::ir::ScalarType type,\n          uint32_t                regSpace,\n          uint32_t                regIndex) {\n      DxvkShaderBinding binding(m_stage, setIndexForType(type), regIndex);\n\n      if (m_bindings) {\n        auto dstBinding = m_bindings->mapBinding(binding);\n\n        if (dstBinding)\n          binding = *dstBinding;\n      }\n\n      dxbc_spv::spirv::DescriptorBinding result = { };\n      result.set = binding.getSet();\n      result.binding = binding.getBinding();\n      return result;\n    }\n\n    uint32_t mapPushData(dxbc_spv::ir::ShaderStageMask stages) {\n      // Must be consistent with the lowering pass\n      uint32_t offset = 0u;\n\n      if (stages && stages == stages.first())\n        offset = uint32_t(DxvkLimits::MaxSharedPushDataSize);\n\n      if (m_bindings)\n        offset = m_bindings->mapPushData(m_stage, offset);\n\n      return offset;\n    }\n\n    static uint32_t setIndexForType(dxbc_spv::ir::ScalarType type) {\n      switch (type) {\n        case dxbc_spv::ir::ScalarType::eSampler: return 0u;\n        case dxbc_spv::ir::ScalarType::eCbv: return 1u;\n        case dxbc_spv::ir::ScalarType::eSrv: return 2u;\n        case dxbc_spv::ir::ScalarType::eUav: return 3u;\n        case dxbc_spv::ir::ScalarType::eUavCounter: return 4u;\n        default: return -1u;\n      }\n    }\n\n  private:\n\n    VkShaderStageFlagBits       m_stage;\n    const DxvkShaderBindingMap* m_bindings;\n\n  };\n\n\n\n\n  /**\n   * \\brief DXVK-specific pass to lower resource bindings\n   *\n   * Maps individual sampler bindings to the global sampler heap, promotes\n   * UAV counters to BDA if available push data space allows it, and handles\n   * built-ins that cannot be directly lowered to SPIR-V.\n   *\n   * Also generates pipeline layout information from lowered resources.\n   */\n  class DxvkIrLowerBindingModelPass {\n\n  public:\n\n    DxvkIrLowerBindingModelPass(\n            dxbc_spv::ir::Builder&    builder,\n      const DxvkIrShaderConverter&    shader,\n      const DxvkIrShaderCreateInfo&   info)\n    : m_builder (builder),\n      m_shader  (shader),\n      m_info    (info) {\n\n    }\n\n    /**\n     * \\brief Runs lowering pass\n     */\n    void run() {\n      gatherAliasedResourceBindings();\n\n      auto iter = m_builder.begin();\n\n      while (iter != m_builder.getDeclarations().second) {\n        switch (iter->getOpCode()) {\n          case dxbc_spv::ir::OpCode::eEntryPoint: {\n            iter = handleEntryPoint(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclSampler: {\n            iter = handleSampler(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclUavCounter: {\n            iter = handleUavCounter(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclUav: {\n            iter = handleUav(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclSrv: {\n            iter = handleSrv(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclCbv: {\n            iter = handleCbv(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclXfb: {\n            iter = handleXfb(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclInput: {\n            iter = handleUserInput(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclInputBuiltIn: {\n            iter = handleBuiltInInput(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclOutputBuiltIn: {\n            iter = handleBuiltInOutput(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclPushData: {\n            iter = handlePushData(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eDclSpecConstant: {\n            iter = handleSpecConstant(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eSetGsInputPrimitive: {\n            iter = handleInputTopology(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eSetGsOutputPrimitive:\n          case dxbc_spv::ir::OpCode::eSetTessDomain: {\n            iter = handleOutputTopology(iter);\n          } break;\n\n          case dxbc_spv::ir::OpCode::eSetTessPrimitive: {\n            iter = handleTessPrimitive(iter);\n          } break;\n\n          default:\n            ++iter;\n        }\n      }\n\n      rewriteSamplers();\n      rewriteUavCounters();\n\n      if (m_sharedPushDataOffset) {\n        auto stageMask = (m_metadata.stage & VK_SHADER_STAGE_ALL_GRAPHICS)\n          ? VK_SHADER_STAGE_ALL_GRAPHICS : VK_SHADER_STAGE_COMPUTE_BIT;\n\n        m_layout.addPushData(DxvkPushDataBlock(stageMask,\n          0u, m_sharedPushDataOffset, sizeof(uint32_t), 0u));\n      }\n\n      if (m_localPushDataOffset) {\n        m_layout.addPushData(DxvkPushDataBlock(m_metadata.stage, DxvkLimits::MaxSharedPushDataSize,\n          m_localPushDataOffset, m_localPushDataAlign, m_localPushDataResourceMask));\n      }\n\n      m_metadata.inputs = convertIoMap(dxbc_spv::ir::IoMap::forInputs(m_builder));\n\n      int32_t rasterizedStream = 0u;\n\n      if (m_stage == dxbc_spv::ir::ShaderStage::eGeometry)\n        rasterizedStream = m_info.rasterizedStream;\n\n      m_metadata.outputs = convertIoMap(dxbc_spv::ir::IoMap::forOutputs(m_builder, uint32_t(rasterizedStream)));\n\n      if (m_info.options.flags.test(DxvkShaderCompileFlag::SemanticIo))\n        m_metadata.flags.set(DxvkShaderFlag::SemanticIo);\n    }\n\n\n    /**\n     * \\brief Extracts layout info\n     * \\returns Binding layout\n     */\n    DxvkPipelineLayoutBuilder getLayout() {\n      return std::move(m_layout);\n    }\n\n    /**\n     * \\brief Queries shader metadata\n     * \\returns Shader metadata\n     */\n    DxvkShaderMetadata getMetadata() const {\n      return m_metadata;\n    }\n\n  private:\n\n    struct SamplerInfo {\n      dxbc_spv::ir::SsaDef sampler = { };\n      dxbc_spv::ir::SsaDef indexFn = { };\n      uint32_t samplerIndex = 0u;\n      uint32_t samplerCount = 0u;\n    };\n\n    struct UavCounterInfo {\n      dxbc_spv::ir::SsaDef dcl = { };\n    };\n\n    struct ResourceKey {\n      dxbc_spv::ir::OpCode opCode = { };\n      uint32_t registerSpace = 0u;\n      uint32_t registerIndex = 0u;\n      VkDescriptorType descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM;\n\n      bool eq(const ResourceKey& other) const {\n        return opCode         == other.opCode\n            && registerSpace  == other.registerSpace\n            && registerIndex  == other.registerIndex\n            && descriptorType == other.descriptorType;\n      }\n\n      size_t hash() const {\n        DxvkHashState hash;\n        hash.add(uint32_t(opCode));\n        hash.add(registerSpace);\n        hash.add(registerIndex);\n        hash.add(uint32_t(descriptorType));\n        return hash;\n      }\n    };\n\n    struct ResourceAlias {\n      bool hasAlias = false;\n      bool hasBinding = false;\n    };\n\n    dxbc_spv::ir::Builder&        m_builder;\n    const DxvkIrShaderConverter&  m_shader;\n    DxvkIrShaderCreateInfo        m_info = { };\n\n    DxvkShaderMetadata            m_metadata = { };\n    DxvkPipelineLayoutBuilder     m_layout;\n\n    dxbc_spv::ir::SsaDef          m_entryPoint = { };\n    dxbc_spv::ir::ShaderStage     m_stage = { };\n\n    dxbc_spv::ir::SsaDef          m_incUavCounterFunction = { };\n    dxbc_spv::ir::SsaDef          m_decUavCounterFunction = { };\n\n    dxbc_spv::ir::SsaDef          m_builtInPushData = { };\n\n    uint32_t                      m_localPushDataAlign = 4u;\n    uint32_t                      m_localPushDataOffset = 0u;\n    uint32_t                      m_localPushDataResourceMask = 0u;\n\n    uint32_t                      m_sharedPushDataOffset = 0u;\n\n    small_vector<SamplerInfo,     16u>  m_samplers;\n    small_vector<UavCounterInfo,  64u>  m_uavCounters;\n\n    std::unordered_map<ResourceKey, ResourceAlias, DxvkHash, DxvkEq> m_resources;\n\n    ResourceAlias& getResourceAlias(dxbc_spv::ir::OpCode opCode, uint32_t space, uint32_t index, VkDescriptorType descriptorType) {\n      ResourceKey k = { };\n      k.opCode = opCode;\n      k.registerSpace = space;\n      k.registerIndex = index;\n      k.descriptorType = descriptorType;\n\n      return m_resources.at(k);\n    }\n\n\n    VkDescriptorType determineDescriptorType(const dxbc_spv::ir::Op& op) const {\n      switch (op.getOpCode()) {\n        case dxbc_spv::ir::OpCode::eDclSampler:\n          return VK_DESCRIPTOR_TYPE_SAMPLER;\n\n        case dxbc_spv::ir::OpCode::eDclCbv: {\n          return op.getType().byteSize() <= m_info.options.maxUniformBufferSize\n            ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER\n            : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n        }\n\n        case dxbc_spv::ir::OpCode::eDclSrv: {\n          auto resourceKind = dxbc_spv::ir::ResourceKind(op.getOperand(4u));\n\n          if (dxbc_spv::ir::resourceIsBuffer(resourceKind)) {\n            return dxbc_spv::ir::resourceIsTyped(resourceKind)\n              ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER\n              : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n          } else {\n            return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n          }\n        }\n\n        case dxbc_spv::ir::OpCode::eDclUav: {\n          auto resourceKind = dxbc_spv::ir::ResourceKind(op.getOperand(4u));\n\n          if (dxbc_spv::ir::resourceIsBuffer(resourceKind)) {\n            return dxbc_spv::ir::resourceIsTyped(resourceKind)\n              ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER\n              : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n          } else {\n            return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n          }\n        }\n\n        case dxbc_spv::ir::OpCode::eDclUavCounter:\n          return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n\n        default:\n          throw DxvkError(str::format(\"Unhandled resource declaration: \", op.getOpCode()));\n      }\n    }\n\n\n    void gatherAliasedResourceBindings() {\n      auto iter = m_builder.begin();\n\n      while (iter != m_builder.getDeclarations().second) {\n        switch (iter->getOpCode()) {\n          case dxbc_spv::ir::OpCode::eDclSrv:\n          case dxbc_spv::ir::OpCode::eDclUav: {\n            ResourceKey k = { };\n            k.opCode = iter->getOpCode();\n            k.registerSpace = uint32_t(iter->getOperand(1u));\n            k.registerIndex = uint32_t(iter->getOperand(2u));\n            k.descriptorType = determineDescriptorType(*iter);\n\n            auto e = m_resources.emplace(std::piecewise_construct, std::tuple(k), std::tuple());\n\n            if (!e.second)\n              e.first->second.hasAlias = true;\n          } break;\n\n          default:\n            break;\n        }\n\n        iter++;\n      }\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleEntryPoint(dxbc_spv::ir::Builder::iterator op) {\n      m_entryPoint = op->getDef();\n      m_stage = dxbc_spv::ir::ShaderStage(op->getOperand(op->getFirstLiteralOperandIndex()));\n      m_metadata.stage = convertShaderStage(m_stage);\n      m_layout = DxvkPipelineLayoutBuilder(m_metadata.stage);\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleSampler(dxbc_spv::ir::Builder::iterator op) {\n      // Emit global sampler heap later, we can't do much here yet\n      auto& e = m_samplers.emplace_back();\n      e.sampler = op->getDef();\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleCbv(dxbc_spv::ir::Builder::iterator op) {\n      auto regSpace = uint32_t(op->getOperand(1u));\n      auto regIndex = uint32_t(op->getOperand(2u));\n      auto regCount = uint32_t(op->getOperand(3u));\n\n      DxvkBindingInfo binding = { };\n      binding.set = DxvkShaderResourceMapping::setIndexForType(dxbc_spv::ir::ScalarType::eCbv);\n      binding.binding = regIndex;\n      binding.resourceIndex = m_shader.determineResourceIndex(m_stage,\n        dxbc_spv::ir::ScalarType::eCbv, regSpace, regIndex);\n      binding.descriptorType = determineDescriptorType(*op);\n      binding.descriptorCount = regCount;\n      binding.access = VK_ACCESS_UNIFORM_READ_BIT;\n\n      if (binding.descriptorType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)\n        binding.access = VK_ACCESS_SHADER_READ_BIT;\n\n      binding.flags.set(DxvkDescriptorFlag::UniformBuffer);\n\n      addBinding(binding);\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleSrv(dxbc_spv::ir::Builder::iterator op) {\n      auto resourceKind = dxbc_spv::ir::ResourceKind(op->getOperand(4u));\n\n      auto regSpace = uint32_t(op->getOperand(1u));\n      auto regIndex = uint32_t(op->getOperand(2u));\n      auto regCount = uint32_t(op->getOperand(3u));\n\n      DxvkBindingInfo binding = { };\n      binding.set = DxvkShaderResourceMapping::setIndexForType(dxbc_spv::ir::ScalarType::eSrv);\n      binding.binding = regIndex;\n      binding.resourceIndex = m_shader.determineResourceIndex(m_stage,\n        dxbc_spv::ir::ScalarType::eSrv, regSpace, regIndex);\n      binding.descriptorType = determineDescriptorType(*op);\n      binding.descriptorCount = regCount;\n      binding.access = VK_ACCESS_SHADER_READ_BIT;\n      binding.viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n\n      if (dxbc_spv::ir::resourceIsMultisampled(resourceKind))\n        binding.flags.set(DxvkDescriptorFlag::Multisampled);\n\n      auto& resourceAlias = getResourceAlias(op->getOpCode(), regSpace, regIndex, binding.descriptorType);\n\n      if (!resourceAlias.hasAlias)\n        binding.viewType = determineViewType(resourceKind);\n\n      if (resourceHasSparseFeedbackLoads(op))\n        m_metadata.flags.set(DxvkShaderFlag::UsesSparseResidency);\n\n      if (!std::exchange(resourceAlias.hasBinding, true))\n        addBinding(binding);\n\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleUav(dxbc_spv::ir::Builder::iterator op) {\n      auto regSpace = uint32_t(op->getOperand(1u));\n      auto regIndex = uint32_t(op->getOperand(2u));\n      auto regCount = uint32_t(op->getOperand(3u));\n\n      auto resourceKind = dxbc_spv::ir::ResourceKind(op->getOperand(4u));\n      auto uavFlags = dxbc_spv::ir::UavFlags(op->getOperand(5u));\n\n      DxvkBindingInfo binding = { };\n      binding.set = DxvkShaderResourceMapping::setIndexForType(dxbc_spv::ir::ScalarType::eUav);\n      binding.binding = regIndex;\n      binding.resourceIndex = m_shader.determineResourceIndex(m_stage,\n        dxbc_spv::ir::ScalarType::eUav, regSpace, regIndex);\n      binding.descriptorType = determineDescriptorType(*op);\n      binding.descriptorCount = regCount;\n      binding.viewType = VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n\n      if (!(uavFlags & dxbc_spv::ir::UavFlag::eWriteOnly))\n        binding.access |= VK_ACCESS_SHADER_READ_BIT;\n\n      if (!(uavFlags & dxbc_spv::ir::UavFlag::eReadOnly)) {\n        binding.access |= VK_ACCESS_SHADER_WRITE_BIT;\n        binding.accessOp = determineAccessOpForUav(op);\n      }\n\n      auto& resourceAlias = getResourceAlias(op->getOpCode(), regSpace, regIndex, binding.descriptorType);\n\n      if (!resourceAlias.hasAlias)\n        binding.viewType = determineViewType(resourceKind);\n\n      if (resourceHasSparseFeedbackLoads(op))\n        m_metadata.flags.set(DxvkShaderFlag::UsesSparseResidency);\n\n      if (!std::exchange(resourceAlias.hasBinding, true))\n        addBinding(binding);\n\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleUavCounter(dxbc_spv::ir::Builder::iterator op) {\n      auto& e = m_uavCounters.emplace_back();\n      e.dcl = op->getDef();\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleXfb(dxbc_spv::ir::Builder::iterator op) {\n      m_metadata.flags.set(DxvkShaderFlag::HasTransformFeedback);\n\n      auto xfbBuffer = uint32_t(op->getOperand(1u));\n      auto xfbStride = uint32_t(op->getOperand(2u));\n\n      m_metadata.xfbStrides.at(xfbBuffer) = xfbStride;\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleUserInput(dxbc_spv::ir::Builder::iterator op) {\n      if (m_stage == dxbc_spv::ir::ShaderStage::ePixel)\n        handleInputInterpolation(op);\n\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleBuiltInInput(dxbc_spv::ir::Builder::iterator op) {\n      if (m_stage == dxbc_spv::ir::ShaderStage::ePixel)\n        handleInputInterpolation(op);\n\n      auto builtIn = dxbc_spv::ir::BuiltIn(op->getOperand(op->getFirstLiteralOperandIndex()));\n\n      if (builtIn == dxbc_spv::ir::BuiltIn::eSampleCount\n       || builtIn == dxbc_spv::ir::BuiltIn::eTessFactorLimit)\n        return rewriteBuiltIn(op, builtIn);\n\n      if (builtIn == dxbc_spv::ir::BuiltIn::eIsFullyCovered)\n        m_metadata.flags.set(DxvkShaderFlag::UsesFragmentCoverage);\n\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleBuiltInOutput(dxbc_spv::ir::Builder::iterator op) {\n      auto builtIn = dxbc_spv::ir::BuiltIn(op->getOperand(op->getFirstLiteralOperandIndex()));\n\n      switch (builtIn) {\n        case dxbc_spv::ir::BuiltIn::ePosition: {\n          m_metadata.flags.set(DxvkShaderFlag::ExportsPosition);\n        } break;\n\n        case dxbc_spv::ir::BuiltIn::eLayerIndex:\n        case dxbc_spv::ir::BuiltIn::eViewportIndex: {\n          if (m_stage != dxbc_spv::ir::ShaderStage::eGeometry)\n            m_metadata.flags.set(DxvkShaderFlag::ExportsViewportIndexLayerFromVertexStage);\n        } break;\n\n        case dxbc_spv::ir::BuiltIn::eSampleMask: {\n          m_metadata.flags.set(DxvkShaderFlag::ExportsSampleMask);\n        } break;\n\n        case dxbc_spv::ir::BuiltIn::eStencilRef: {\n          m_metadata.flags.set(DxvkShaderFlag::ExportsStencilRef);\n        } break;\n\n        default:\n          break;\n      }\n\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handlePushData(dxbc_spv::ir::Builder::iterator op) {\n      auto offset = uint32_t(op->getOperand(op->getFirstLiteralOperandIndex() + 0u));\n      auto stages = dxbc_spv::ir::ShaderStageMask(op->getOperand(op->getFirstLiteralOperandIndex() + 1u));\n\n      // Adjust local offset if this is a local declaration\n      if (stages == m_stage)\n        m_localPushDataOffset = std::max(m_localPushDataOffset, offset + op->getType().byteSize());\n      else\n        m_sharedPushDataOffset = std::max(m_sharedPushDataOffset, offset + op->getType().byteSize());\n\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleSpecConstant(dxbc_spv::ir::Builder::iterator op) {\n      auto specId = uint32_t(op->getOperand(op->getFirstLiteralOperandIndex()));\n      m_metadata.specConstantMask |= 1u << specId;\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleInputTopology(dxbc_spv::ir::Builder::iterator op) {\n      auto type = dxbc_spv::ir::PrimitiveType(op->getOperand(1u));\n\n      switch (type) {\n        case dxbc_spv::ir::PrimitiveType::ePoints: {\n          m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;\n        } break;\n\n        case dxbc_spv::ir::PrimitiveType::eLines: {\n          m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;\n        } break;\n\n        case dxbc_spv::ir::PrimitiveType::eLinesAdj: {\n          m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;\n        } break;\n\n        case dxbc_spv::ir::PrimitiveType::eTriangles: {\n          m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n        } break;\n\n        case dxbc_spv::ir::PrimitiveType::eTrianglesAdj: {\n          m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;\n        } break;\n\n        case dxbc_spv::ir::PrimitiveType::eQuads:\n        case dxbc_spv::ir::PrimitiveType::ePatch: {\n          Logger::err(str::format(\"Unhandled input topology: \", type));\n        } break;\n      }\n\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleOutputTopology(dxbc_spv::ir::Builder::iterator op) {\n      auto type = dxbc_spv::ir::PrimitiveType(op->getOperand(1u));\n\n      switch (type) {\n        case dxbc_spv::ir::PrimitiveType::ePoints: {\n          m_metadata.outputTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;\n        } break;\n\n        case dxbc_spv::ir::PrimitiveType::eLines: {\n          m_metadata.outputTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;\n        } break;\n\n        case dxbc_spv::ir::PrimitiveType::eTriangles:\n        case dxbc_spv::ir::PrimitiveType::eQuads: {\n          m_metadata.outputTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n        } break;\n\n        case dxbc_spv::ir::PrimitiveType::eLinesAdj:\n        case dxbc_spv::ir::PrimitiveType::eTrianglesAdj:\n        case dxbc_spv::ir::PrimitiveType::ePatch: {\n          Logger::err(str::format(\"Unhandled output topology: \", type));\n        } break;\n      }\n\n      return ++op;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator handleTessPrimitive(dxbc_spv::ir::Builder::iterator op) {\n      auto type = dxbc_spv::ir::PrimitiveType(op->getOperand(1u));\n\n      if (type == dxbc_spv::ir::PrimitiveType::ePoints)\n        m_metadata.flags.set(DxvkShaderFlag::TessellationPoints);\n\n      return ++op;\n    }\n\n\n    void handleInputInterpolation(dxbc_spv::ir::Builder::iterator op) {\n      auto interpolation = dxbc_spv::ir::InterpolationModes(op->getOperand(op->getOperandCount() - 1u));\n\n      if (interpolation & dxbc_spv::ir::InterpolationMode::eSample)\n        m_metadata.flags.set(DxvkShaderFlag::HasSampleRateShading);\n    }\n\n\n    void addDebugMemberName(dxbc_spv::ir::SsaDef def, uint32_t member, const std::string& name) {\n      if (!name.empty()) {\n        if (m_builder.getOp(def).getType().isStructType())\n          m_builder.add(dxbc_spv::ir::Op::DebugMemberName(def, member, name.c_str()));\n        else\n          m_builder.add(dxbc_spv::ir::Op::DebugName(def, name.c_str()));\n      }\n    }\n\n\n    dxbc_spv::ir::SsaDef declareSamplerHeap() {\n      // Declare sampler heap with unknown size since it may vary by device\n      uint32_t set = DxvkShaderResourceMapping::setIndexForType(dxbc_spv::ir::ScalarType::eSampler);\n\n      m_layout.addSamplerHeap(DxvkShaderBinding(m_metadata.stage, set, 0u));\n      auto var = m_builder.add(dxbc_spv::ir::Op::DclSampler(m_entryPoint, 0u, 0u, 0u));\n\n      m_builder.add(dxbc_spv::ir::Op::DebugName(var, \"sampler_heap\"));\n      return var;\n    }\n\n\n    dxbc_spv::ir::SsaDef declareSamplerPushData() {\n      dxbc_spv::ir::Type pushDataType = { };\n\n      // Align to dword boundary, we need it for push data processing\n      m_localPushDataOffset = align(m_localPushDataOffset, sizeof(uint32_t));\n\n      // Compute index offsets for each sampler\n      uint32_t wordCount = 0u;\n\n      for (auto& e : m_samplers) {\n        e.samplerIndex = wordCount;\n        e.samplerCount = uint32_t(m_builder.getOp(e.sampler).getOperand(3u));\n\n        wordCount += e.samplerCount;\n      }\n\n      // Mark corresponding dwords as resources\n      uint32_t dwordIndex = m_localPushDataOffset / sizeof(uint32_t);\n      uint32_t dwordCount = (wordCount + 1u) / 2u;\n\n      m_localPushDataResourceMask |= ((1ull << dwordCount) - 1ull) << dwordIndex;\n\n      if (m_info.options.flags.test(DxvkShaderCompileFlag::Supports16BitPushData)) {\n        // Add each word separately and pad with a dummy entry if unaligned\n        for (uint32_t i = 0u; i < wordCount; i++)\n          pushDataType.addStructMember(dxbc_spv::ir::ScalarType::eU16);\n\n        if (wordCount & 1u)\n          pushDataType.addStructMember(dxbc_spv::ir::ScalarType::eU16);\n      } else {\n        // Add dword member for each pair fo samplers\n        for (uint32_t i = 0u; i < dwordCount; i++)\n          pushDataType.addStructMember(dxbc_spv::ir::ScalarType::eU32);\n      }\n\n      // Declare actual push data structure\n      auto def = m_builder.add(dxbc_spv::ir::Op::DclPushData(\n        pushDataType, m_entryPoint, m_localPushDataOffset, m_stage));\n\n      m_localPushDataOffset += pushDataType.byteSize();\n\n      // Add debug names for sampler indices\n      for (const auto& e : m_samplers) {\n        if (m_info.options.flags.test(DxvkShaderCompileFlag::Supports16BitPushData)) {\n          addDebugMemberName(def, e.samplerIndex, getDebugName(e.sampler));\n        } else if (!(e.samplerIndex % 2u)) {\n          std::string debugName = getDebugName(e.sampler);\n\n          for (const auto& eHi : m_samplers) {\n            if (eHi.samplerIndex == e.samplerIndex + 1u) {\n              debugName += \"_\";\n              debugName += getDebugName(eHi.sampler);\n              break;\n            }\n          }\n\n          addDebugMemberName(def, e.samplerIndex / 2u, debugName);\n        }\n      }\n\n      return def;\n    }\n\n\n    dxbc_spv::ir::SsaDef defineBuiltInPushData() {\n      if (!m_builtInPushData) {\n        dxbc_spv::ir::Type pushDataType = { };\n\n        m_builtInPushData = m_builder.add(dxbc_spv::ir::Op::DclPushData(dxbc_spv::ir::ScalarType::eU32,\n          m_entryPoint, m_info.options.builtInPushDataOffset, dxbc_spv::ir::ShaderStageMask()));\n\n        m_builder.add(dxbc_spv::ir::Op::DebugName(m_builtInPushData, \"builtins\"));\n\n        m_sharedPushDataOffset = std::max<uint32_t>(m_sharedPushDataOffset,\n          m_info.options.builtInPushDataOffset + sizeof(uint32_t));\n      }\n\n      return m_builtInPushData;\n    }\n\n\n    dxbc_spv::ir::Builder::iterator rewriteBuiltIn(dxbc_spv::ir::Builder::iterator op, dxbc_spv::ir::BuiltIn builtIn) {\n      // Map built-in to bit range in the built-in push data dword\n      static const std::array<std::tuple<dxbc_spv::ir::BuiltIn, uint16_t, uint16_t>, 2u> s_builtins = {{\n        { dxbc_spv::ir::BuiltIn::eSampleCount,     DxvkBuiltInPushData::SampleCountOffset,    DxvkBuiltInPushData::SampleCountBits },\n        { dxbc_spv::ir::BuiltIn::eTessFactorLimit, DxvkBuiltInPushData::MaxTessFactorOffset,  DxvkBuiltInPushData::MaxTessFactorBits },\n      }};\n\n      uint32_t bitIndex = 0u;\n      uint32_t bitCount = 32u;\n\n      for (const auto& e : s_builtins) {\n        auto [which, index, count] = e;\n\n        if (which == builtIn) {\n          bitIndex = index;\n          bitCount = count;\n          break;\n        }\n      }\n\n      // Emit helper function to actually load the push data dword\n      auto ref = m_builder.getCode().first->getDef();\n\n      auto helper = m_builder.addBefore(ref, dxbc_spv::ir::Op::Function(op->getType()));\n      auto cursor = m_builder.setCursor(helper);\n\n      m_builder.add(dxbc_spv::ir::Op::Label());\n\n      auto value = m_builder.add(dxbc_spv::ir::Op::PushDataLoad(\n        dxbc_spv::ir::ScalarType::eU32, defineBuiltInPushData(), dxbc_spv::ir::SsaDef()));\n      value = m_builder.add(dxbc_spv::ir::Op::UBitExtract(dxbc_spv::ir::ScalarType::eU32,\n        value, m_builder.makeConstant(bitIndex), m_builder.makeConstant(bitCount)));\n\n      if (op->getType() != dxbc_spv::ir::ScalarType::eU32) {\n        value = m_builder.add(op->getType().getBaseType(0u).isFloatType()\n          ? dxbc_spv::ir::Op::ConvertItoF(op->getType(), value)\n          : dxbc_spv::ir::Op::ConvertItoI(op->getType(), value));\n      }\n\n      m_builder.add(dxbc_spv::ir::Op::Return(op->getType(), value));\n      m_builder.add(dxbc_spv::ir::Op::FunctionEnd());\n      m_builder.setCursor(cursor);\n\n      auto debugName = getDebugName(op->getDef());\n\n      if (!debugName.empty())\n        m_builder.add(dxbc_spv::ir::Op::DebugName(helper, debugName.c_str()));\n\n      // Replace all input loads with a call to the helper function and remove\n      // any debug instructions, as well as the input declaration itself.\n      small_vector<dxbc_spv::ir::SsaDef, 64u> uses;\n      m_builder.getUses(op->getDef(), uses);\n\n      for (auto use : uses) {\n        const auto& useOp = m_builder.getOp(use);\n\n        if (useOp.getOpCode() == dxbc_spv::ir::OpCode::eInputLoad) {\n          m_builder.rewriteOp(useOp.getDef(),\n            dxbc_spv::ir::Op::FunctionCall(op->getType(), helper));\n        } else {\n          m_builder.remove(use);\n        }\n      }\n\n      return m_builder.iter(m_builder.remove(op->getDef()));\n    }\n\n\n    dxbc_spv::ir::SsaDef loadConstantSamplerIndex(dxbc_spv::ir::SsaDef ref, dxbc_spv::ir::SsaDef pushDataDef, const SamplerInfo& info, uint32_t index) {\n      uint32_t wordIndex = info.samplerIndex + index;\n\n      if (m_info.options.flags.test(DxvkShaderCompileFlag::Supports16BitPushData)) {\n        dxbc_spv::ir::SsaDef memberIndex = { };\n\n        if (m_builder.getOp(pushDataDef).getType().isStructType())\n          memberIndex = m_builder.makeConstant(wordIndex);\n\n        dxbc_spv::ir::SsaDef samplerIndex = m_builder.addBefore(ref,\n          dxbc_spv::ir::Op::PushDataLoad(dxbc_spv::ir::ScalarType::eU16, pushDataDef, memberIndex));\n        samplerIndex = m_builder.addBefore(ref, dxbc_spv::ir::Op::ConvertItoI(\n          dxbc_spv::ir::ScalarType::eU32, samplerIndex));\n        return samplerIndex;\n      } else {\n        dxbc_spv::ir::SsaDef bitIndex = m_builder.makeConstant(uint32_t(16u * (wordIndex % 2u)));\n        dxbc_spv::ir::SsaDef memberIndex = { };\n\n        if (m_builder.getOp(pushDataDef).getType().isStructType())\n          memberIndex = m_builder.makeConstant(uint32_t(wordIndex / 2u));\n\n        dxbc_spv::ir::SsaDef samplerIndex = m_builder.addBefore(ref,\n          dxbc_spv::ir::Op::PushDataLoad(dxbc_spv::ir::ScalarType::eU32, pushDataDef, memberIndex));\n        samplerIndex = m_builder.addBefore(ref, dxbc_spv::ir::Op::UBitExtract(\n          dxbc_spv::ir::ScalarType::eU32, samplerIndex, bitIndex, m_builder.makeConstant(16u)));\n        return samplerIndex;\n      }\n    }\n\n\n    dxbc_spv::ir::SsaDef buildSamplerIndexFn(const SamplerInfo& info, dxbc_spv::ir::SsaDef pushDataDef) {\n      /* Declare function parameter and function */\n      auto indexParam = m_builder.add(dxbc_spv::ir::Op::DclParam(dxbc_spv::ir::ScalarType::eU32));\n      m_builder.add(dxbc_spv::ir::Op::DebugName(indexParam, \"index\"));\n\n      auto fn = m_builder.addBefore(m_builder.getCode().first->getDef(),\n        dxbc_spv::ir::Op::Function(dxbc_spv::ir::ScalarType::eU32).addParam(indexParam));\n      m_builder.add(dxbc_spv::ir::Op::DebugName(fn, (getDebugName(info.sampler) + \"_load\").c_str()));\n\n      auto fnEnd = m_builder.addAfter(fn, dxbc_spv::ir::Op::FunctionEnd());\n      m_builder.addBefore(fnEnd, dxbc_spv::ir::Op::Label());\n\n      /* Load each sampler index and pick the correct one for the requested index */\n      auto index = m_builder.addBefore(fnEnd, dxbc_spv::ir::Op::ParamLoad(\n        dxbc_spv::ir::ScalarType::eU32, fn, indexParam));\n      auto result = m_builder.makeConstant(0u);\n\n      for (uint32_t i = 0u; i < info.samplerCount; i++) {\n        auto sampler = loadConstantSamplerIndex(fnEnd, pushDataDef, info, i);\n\n        auto cond = m_builder.addBefore(fnEnd, dxbc_spv::ir::Op::IEq(\n          dxbc_spv::ir::ScalarType::eBool, index, m_builder.makeConstant(i)));\n\n        result = m_builder.addBefore(fnEnd, dxbc_spv::ir::Op::Select(\n          dxbc_spv::ir::ScalarType::eU32, cond, sampler, result));\n      }\n\n      m_builder.addBefore(fnEnd, dxbc_spv::ir::Op::Return(dxbc_spv::ir::ScalarType::eU32, result));\n      return fn;\n    }\n\n\n    dxbc_spv::ir::SsaDef loadSamplerIndex(dxbc_spv::ir::SsaDef ref, dxbc_spv::ir::SsaDef pushDataDef, SamplerInfo& info, const dxbc_spv::ir::Op& index) {\n      if (index.isConstant())\n        return loadConstantSamplerIndex(ref, pushDataDef, info, uint32_t(index.getOperand(0u)));\n\n      if (!info.indexFn)\n        info.indexFn = buildSamplerIndexFn(info, pushDataDef);\n\n      return m_builder.addBefore(ref, dxbc_spv::ir::Op::FunctionCall(\n        dxbc_spv::ir::ScalarType::eU32, info.indexFn).addParam(index.getDef()));\n    }\n\n\n    dxbc_spv::ir::Builder::iterator rewriteSampler(dxbc_spv::ir::Builder::iterator sampler, dxbc_spv::ir::SsaDef heapDef, dxbc_spv::ir::SsaDef pushDataDef) {\n      small_vector<dxbc_spv::ir::SsaDef, 64u> uses;\n      m_builder.getUses(sampler->getDef(), uses);\n\n      // Find sampler entry\n      SamplerInfo info = { };\n\n      for (const auto& e : m_samplers) {\n        if (e.sampler == sampler->getDef()) {\n          info = e;\n          break;\n        }\n      }\n\n      // Rewrite descriptor load to fetch the index from the push data block,\n      // and the sampler descriptor itself from the sampler heap\n      for (size_t i = 0u; i < uses.size(); i++) {\n        const auto& op = m_builder.getOp(uses[i]);\n\n        if (op.getOpCode() == dxbc_spv::ir::OpCode::eDescriptorLoad) {\n          dxbc_spv::ir::SsaDef samplerIndex = loadSamplerIndex(op.getDef(),\n            pushDataDef, info, m_builder.getOpForOperand(op, 1u));\n\n          m_builder.rewriteOp(op.getDef(), dxbc_spv::ir::Op::DescriptorLoad(\n            op.getType(), heapDef, samplerIndex));\n        } else if (op.isDeclarative()) {\n          m_builder.removeOp(op);\n        }\n      }\n\n      // Infer push data offset from member index and word index\n      uint32_t localPushDataOffset = m_localPushDataOffset\n        - m_builder.getOp(pushDataDef).getType().byteSize();\n\n      // Add sampler info to the descriptor layout\n      auto regSpace = uint32_t(sampler->getOperand(1u));\n      auto regIndex = uint32_t(sampler->getOperand(2u));\n\n      for (uint32_t i = 0u; i < info.samplerCount; i++) {\n        DxvkBindingInfo binding = { };\n        binding.resourceIndex = m_shader.determineResourceIndex(m_stage,\n          dxbc_spv::ir::ScalarType::eSampler, regSpace, regIndex + i);\n        binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;\n        binding.blockOffset = MaxSharedPushDataSize + localPushDataOffset\n          + sizeof(uint16_t) * (info.samplerIndex + i);\n        binding.flags.set(DxvkDescriptorFlag::PushData);\n\n        addBinding(binding);\n      }\n\n      return m_builder.iter(m_builder.remove(sampler->getDef()));\n    }\n\n\n    void sortSamplers() {\n      // Sort samplers by binding index for consistency\n      std::sort(m_samplers.begin(), m_samplers.end(), [&] (const SamplerInfo& a, const SamplerInfo& b) {\n        const auto& aOp = m_builder.getOp(a.sampler);\n        const auto& bOp = m_builder.getOp(b.sampler);\n\n        return uint32_t(aOp.getOperand(2u)) < uint32_t(bOp.getOperand(2u));\n      });\n    }\n\n\n    void rewriteSamplers() {\n      if (m_samplers.empty())\n        return;\n\n      sortSamplers();\n\n      auto samplerIndices = declareSamplerPushData();\n      auto samplerHeap = declareSamplerHeap();\n\n      auto iter = m_builder.begin();\n\n      while (iter != m_builder.getDeclarations().second) {\n        if (iter->getOpCode() == dxbc_spv::ir::OpCode::eDclSampler && iter->getDef() != samplerHeap)\n          iter = rewriteSampler(iter, samplerHeap, samplerIndices);\n        else\n          ++iter;\n      }\n    }\n\n\n    void sortUavCounters() {\n      // Sort samplers by the corresponding UAV binding index for consistency\n      std::sort(m_uavCounters.begin(), m_uavCounters.end(),\n        [this] (const UavCounterInfo& a, const UavCounterInfo& b) {\n          const auto& aUav = m_builder.getOpForOperand(a.dcl, 1u);\n          const auto& bUav = m_builder.getOpForOperand(b.dcl, 1u);\n\n          return uint32_t(aUav.getOperand(2u)) < uint32_t(bUav.getOperand(2u));\n        });\n    }\n\n\n    dxbc_spv::ir::SsaDef getUavCounterFunction(dxbc_spv::ir::AtomicOp atomicOp) {\n      auto& def = atomicOp == dxbc_spv::ir::AtomicOp::eInc\n        ? m_incUavCounterFunction\n        : m_decUavCounterFunction;\n\n      if (def)\n        return def;\n\n      auto mainFunc = m_builder.getOpForOperand(m_entryPoint, 0u).getDef();\n\n      // Declare counter address parameter and function\n      auto param = m_builder.add(dxbc_spv::ir::Op::DclParam(dxbc_spv::ir::ScalarType::eU64));\n      m_builder.add(dxbc_spv::ir::Op::DebugName(param, \"va\"));\n\n      def = m_builder.addBefore(mainFunc, dxbc_spv::ir::Op::Function(dxbc_spv::ir::ScalarType::eU32).addParam(param));\n      m_builder.add(dxbc_spv::ir::Op::DebugName(def, atomicOp == dxbc_spv::ir::AtomicOp::eInc ? \"uav_ctr_inc\" : \"uav_ctr_dec\"));\n\n      // Insert labels\n      auto execBlock = m_builder.addBefore(mainFunc, dxbc_spv::ir::Op::Label());\n      auto mergeBlock = m_builder.addBefore(mainFunc, dxbc_spv::ir::Op::Label());\n      auto entryBlock = m_builder.addAfter(def, dxbc_spv::ir::Op::LabelSelection(mergeBlock));\n\n      // Insert check whether the counter address is null\n      auto address = m_builder.addBefore(execBlock, dxbc_spv::ir::Op::ParamLoad(dxbc_spv::ir::ScalarType::eU64, def, param));\n      auto execCond = m_builder.addBefore(execBlock, dxbc_spv::ir::Op::INe(dxbc_spv::ir::ScalarType::eBool, address, m_builder.makeConstant(uint64_t(0u))));\n      m_builder.addBefore(execBlock, dxbc_spv::ir::Op::BranchConditional(execCond, execBlock, mergeBlock));\n\n      // Insert actual atomic op\n      auto pointer = m_builder.addBefore(mergeBlock, dxbc_spv::ir::Op::Pointer(dxbc_spv::ir::ScalarType::eU32, address, dxbc_spv::ir::UavFlags()));\n      auto value = m_builder.addBefore(mergeBlock, dxbc_spv::ir::Op::MemoryAtomic(atomicOp,\n        dxbc_spv::ir::ScalarType::eU32, pointer, dxbc_spv::ir::SsaDef(), dxbc_spv::ir::SsaDef()));\n\n      if (atomicOp == dxbc_spv::ir::AtomicOp::eDec) {\n        value = m_builder.addBefore(mergeBlock, dxbc_spv::ir::Op::ISub(\n          dxbc_spv::ir::ScalarType::eU32, value, m_builder.makeConstant(1u)));\n      }\n\n      m_builder.addBefore(mergeBlock, dxbc_spv::ir::Op::Branch(mergeBlock));\n\n      // Insert phi and function return\n      value = m_builder.addBefore(mainFunc, dxbc_spv::ir::Op::Phi(dxbc_spv::ir::ScalarType::eU32)\n        .addPhi(execBlock, value)\n        .addPhi(entryBlock, m_builder.makeConstant(0u)));\n\n      m_builder.addBefore(mainFunc, dxbc_spv::ir::Op::Return(dxbc_spv::ir::ScalarType::eU32, value));\n      m_builder.addBefore(mainFunc, dxbc_spv::ir::Op::FunctionEnd());\n      return def;\n    }\n\n\n    void rewriteUavCounterUsesAsBda(dxbc_spv::ir::SsaDef descriptor, dxbc_spv::ir::SsaDef pushData, uint32_t pushMember) {\n      small_vector<dxbc_spv::ir::SsaDef, 64u> uses;\n      m_builder.getUses(descriptor, uses);\n\n      // Rewrite descriptor load to load the raw pointer from push data\n      dxbc_spv::ir::SsaDef memberIndex = { };\n\n      if (m_builder.getOp(pushData).getType().isStructType())\n        memberIndex = m_builder.makeConstant(pushMember);\n\n      m_builder.rewriteOp(descriptor, dxbc_spv::ir::Op::PushDataLoad(\n        dxbc_spv::ir::ScalarType::eU64, pushData, memberIndex));\n\n      // Rewrite counter atomics as raw memory atomics. Counter decrement semantics differ\n      // from regular decrement, so take that into account and subtract 1 from the result.\n      for (auto use : uses) {\n        const auto& useOp = m_builder.getOp(use);\n\n        if (useOp.getOpCode() == dxbc_spv::ir::OpCode::eCounterAtomic) {\n          auto atomicOp = dxbc_spv::ir::AtomicOp(useOp.getOperand(1u));\n          auto func = getUavCounterFunction(atomicOp);\n\n          m_builder.rewriteOp(use, dxbc_spv::ir::Op::FunctionCall(\n            dxbc_spv::ir::ScalarType::eU32, func).addParam(descriptor));\n        }\n      }\n    }\n\n\n    void rewriteUavCounterAsBda(dxbc_spv::ir::SsaDef uavCounter, dxbc_spv::ir::SsaDef pushData, uint32_t pushMember) {\n      small_vector<dxbc_spv::ir::SsaDef, 64u> uses;\n      m_builder.getUses(uavCounter, uses);\n\n      for (auto use : uses) {\n        if (m_builder.getOp(use).getOpCode() == dxbc_spv::ir::OpCode::eDescriptorLoad)\n          rewriteUavCounterUsesAsBda(use, pushData, pushMember);\n        else\n          m_builder.remove(use);\n      }\n\n      m_builder.remove(uavCounter);\n    }\n\n\n    bool hasUavCounterArray() const {\n      for (const auto& e : m_uavCounters) {\n        const auto& uav = m_builder.getOpForOperand(e.dcl, 1u);\n        auto regCount = uint32_t(uav.getOperand(3u));\n\n        if (regCount != 1u)\n          return true;\n      }\n\n      return false;\n    }\n\n\n    void rewriteUavCounters() {\n      if (m_uavCounters.empty())\n        return;\n\n      sortUavCounters();\n\n      // In compute shaders, we can freely use push data space\n      auto ssboAlignment = m_info.options.minStorageBufferAlignment;\n\n      size_t maxPushDataSize = m_stage == dxbc_spv::ir::ShaderStage::eCompute\n        ? MaxTotalPushDataSize - MaxReservedPushDataSize\n        : MaxPerStagePushDataSize;\n\n      size_t uavCounterIndex = 0u;\n\n      if (m_localPushDataOffset + sizeof(uint64_t) <= maxPushDataSize && ssboAlignment <= 4u && !hasUavCounterArray()) {\n        // Align push data to a multiple of 8 bytes before emitting counters\n        m_localPushDataAlign = std::max<uint32_t>(m_localPushDataAlign, sizeof(uint64_t));\n        m_localPushDataOffset = align(m_localPushDataOffset, m_localPushDataAlign);\n\n        // Declare push data variable and type\n        dxbc_spv::ir::Type pushDataType = { };\n\n        size_t maxUavCounters = std::min<size_t>(m_uavCounters.size(),\n          (maxPushDataSize - m_localPushDataOffset) / sizeof(uint64_t));\n\n        for (uint32_t i = 0u; i < maxUavCounters; i++)\n          pushDataType.addStructMember(dxbc_spv::ir::ScalarType::eU64);\n\n        auto pushDataVar = m_builder.add(dxbc_spv::ir::Op::DclPushData(\n          pushDataType, m_entryPoint, m_localPushDataOffset, m_stage));\n\n        while (uavCounterIndex < m_uavCounters.size() && m_localPushDataOffset + sizeof(uint64_t) <= maxPushDataSize) {\n          const auto& uavCounter = m_uavCounters[uavCounterIndex];\n          const auto& uavOp = m_builder.getOpForOperand(uavCounter.dcl, 1u);\n\n          auto regSpace = uint32_t(uavOp.getOperand(1u));\n          auto regIndex = uint32_t(uavOp.getOperand(2u));\n\n          DxvkBindingInfo binding = { };\n          binding.resourceIndex = m_shader.determineResourceIndex(m_stage,\n            dxbc_spv::ir::ScalarType::eUavCounter, regSpace, regIndex);\n          binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n          binding.access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;\n          binding.blockOffset = MaxSharedPushDataSize + m_localPushDataOffset;\n          binding.flags.set(DxvkDescriptorFlag::PushData);\n\n          addBinding(binding);\n\n          m_localPushDataResourceMask |= 3ull << (m_localPushDataOffset / sizeof(uint32_t));\n          m_localPushDataOffset += sizeof(uint64_t);\n\n          addDebugMemberName(pushDataVar, uavCounterIndex, getDebugName(uavCounter.dcl));\n\n          rewriteUavCounterAsBda(uavCounter.dcl, pushDataVar, uavCounterIndex++);\n        }\n      }\n\n      // Emit remaining UAV counters as regular descriptors\n      while (uavCounterIndex < m_uavCounters.size()) {\n        const auto& uavCounter = m_uavCounters[uavCounterIndex++];\n        const auto& uavOp = m_builder.getOpForOperand(uavCounter.dcl, 1u);\n\n        auto regSpace = uint32_t(uavOp.getOperand(1u));\n        auto regIndex = uint32_t(uavOp.getOperand(2u));\n        auto regCount = uint32_t(uavOp.getOperand(3u));\n\n        DxvkBindingInfo binding = { };\n        binding.set = DxvkShaderResourceMapping::setIndexForType(dxbc_spv::ir::ScalarType::eUavCounter);\n        binding.binding = regIndex;\n        binding.resourceIndex = m_shader.determineResourceIndex(m_stage,\n          dxbc_spv::ir::ScalarType::eUavCounter, regSpace, regIndex);\n        binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n        binding.descriptorCount = regCount;\n        binding.access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;\n\n        addBinding(binding);\n      }\n    }\n\n\n    void addBinding(const DxvkBindingInfo& binding) {\n      DxvkShaderDescriptor descriptor(binding, m_metadata.stage);\n      m_layout.addBindings(1u, &descriptor);\n    }\n\n\n    DxvkAccessOp determineAccessOpForStore(const dxbc_spv::ir::Op& op) const {\n      if (!op.isConstant() || !op.getType().isBasicType())\n        return DxvkAccessOp::None;\n\n      // If the constant is a vector, all scalars must be the same since we can\n      // only encode one scalar value, and if values written to the same location\n      // differ then the execution order matters.\n      auto type = op.getType().getBaseType(0u);\n\n      if (byteSize(type.getBaseType()) > 4u)\n        return DxvkAccessOp::None;\n\n      uint32_t value = uint32_t(op.getOperand(0u));\n\n      for (uint32_t i = 1u; i < type.getVectorSize(); i++) {\n        if (uint32_t(op.getOperand(i)) != value)\n          return DxvkAccessOp::None;\n      }\n\n      constexpr uint32_t IMaxValue = 1u << DxvkAccessOp::StoreValueBits;\n      constexpr uint32_t FBitShift = 32u - DxvkAccessOp::StoreValueBits;\n      constexpr uint32_t FBitMask = (1u << FBitShift) - 1u;\n\n      if (value < IMaxValue) {\n        // Trivial case, represent as unsigned int\n        return DxvkAccessOp(DxvkAccessOp::StoreUi, value);\n      } else if (~value < IMaxValue) {\n        // 'Signed' integer, use one's complement instead of the\n        // usual two's here to gain an extra value we can encode\n        return DxvkAccessOp(DxvkAccessOp::StoreSi, ~value);\n      } else if (!(value & FBitMask)) {\n        // Potential float bit pattern, need to ignore mantissa\n        return DxvkAccessOp(DxvkAccessOp::StoreF, value >>FBitShift);\n      }\n\n      return DxvkAccessOp::None;\n    }\n\n\n    std::optional<DxvkAccessOp> determineAccessOpForAccess(const dxbc_spv::ir::Op& op) const {\n      switch (op.getOpCode()) {\n        case dxbc_spv::ir::OpCode::eBufferLoad:\n        case dxbc_spv::ir::OpCode::eImageLoad:\n          return DxvkAccessOp::Load;\n\n        case dxbc_spv::ir::OpCode::eBufferStore:\n        case dxbc_spv::ir::OpCode::eImageStore: {\n          return determineAccessOpForStore(m_builder.getOpForOperand(op,\n            op.getFirstLiteralOperandIndex() - 1u));\n        }\n\n        case dxbc_spv::ir::OpCode::eBufferAtomic:\n        case dxbc_spv::ir::OpCode::eImageAtomic: {\n          // Order matters if the result is used\n          if (!op.getType().isVoidType())\n            return DxvkAccessOp::None;\n\n          auto atomicOp = dxbc_spv::ir::AtomicOp(op.getOperand(op.getFirstLiteralOperandIndex()));\n\n          switch (atomicOp) {\n            case dxbc_spv::ir::AtomicOp::eInc:\n            case dxbc_spv::ir::AtomicOp::eDec:\n            case dxbc_spv::ir::AtomicOp::eAdd:\n            case dxbc_spv::ir::AtomicOp::eSub:\n              return DxvkAccessOp::Add;\n\n            case dxbc_spv::ir::AtomicOp::eOr:\n              return DxvkAccessOp::Or;\n\n            case dxbc_spv::ir::AtomicOp::eAnd:\n              return DxvkAccessOp::And;\n\n            case dxbc_spv::ir::AtomicOp::eXor:\n              return DxvkAccessOp::Xor;\n\n            case dxbc_spv::ir::AtomicOp::eSMin:\n              return DxvkAccessOp::IMin;\n\n            case dxbc_spv::ir::AtomicOp::eSMax:\n              return DxvkAccessOp::IMax;\n\n            case dxbc_spv::ir::AtomicOp::eUMin:\n              return DxvkAccessOp::UMin;\n\n            case dxbc_spv::ir::AtomicOp::eUMax:\n              return DxvkAccessOp::UMax;\n\n            case dxbc_spv::ir::AtomicOp::eLoad:\n              return DxvkAccessOp::Load;\n\n            case dxbc_spv::ir::AtomicOp::eStore: {\n              return determineAccessOpForStore(m_builder.getOpForOperand(op,\n                op.getFirstLiteralOperandIndex() - 1u));\n            }\n\n            default:\n              return DxvkAccessOp::None;\n          }\n        }\n\n        default:\n          // Resource queries etc don't access resource memory,\n          // so they must not affect the result\n          return std::nullopt;\n      }\n    }\n\n\n    DxvkAccessOp determineAccessOpForUav(dxbc_spv::ir::Builder::iterator op) const {\n      std::optional<DxvkAccessOp> accessOp;\n\n      auto [a, b] = m_builder.getUses(op->getDef());\n\n      for (auto iter = a; iter != b; iter++) {\n        if (iter->getOpCode() == dxbc_spv::ir::OpCode::eDescriptorLoad) {\n          auto [aDesc, bDesc] = m_builder.getUses(iter->getDef());\n\n          for (auto use = aDesc; use != bDesc; use++) {\n            auto access = determineAccessOpForAccess(*use);\n\n            if (!access)\n              continue;\n\n            if (access == DxvkAccessOp::None) {\n              // Can't optimize the access\n              return DxvkAccessOp::None;\n            }\n\n            if (!accessOp) {\n              // First order-invariant access\n              accessOp = access;\n            } else if (accessOp != access) {\n              // Different access type, can't merge\n              return DxvkAccessOp::None;\n            }\n          }\n        }\n      }\n\n      if (accessOp)\n        return *accessOp;\n\n      return DxvkAccessOp::None;\n    }\n\n\n    bool descriptorHasSparseFeedbackLoads(const dxbc_spv::ir::Op& op) const {\n      auto [a, b] = m_builder.getUses(op.getDef());\n\n      for (auto iter = a; iter != b; iter++) {\n        if (iter->getFlags() & dxbc_spv::ir::OpFlag::eSparseFeedback)\n          return true;\n      }\n\n      return false;\n    }\n\n\n    bool resourceHasSparseFeedbackLoads(dxbc_spv::ir::Builder::iterator op) const {\n      auto [a, b] = m_builder.getUses(op->getDef());\n\n      for (auto iter = a; iter != b; iter++) {\n        if (iter->getOpCode() == dxbc_spv::ir::OpCode::eDescriptorLoad) {\n          if (descriptorHasSparseFeedbackLoads(*iter))\n            return true;\n        }\n      }\n\n      return false;\n    }\n\n\n    DxvkShaderIo convertIoMap(const dxbc_spv::ir::IoMap& io) const {\n      DxvkShaderIo map;\n\n      for (const auto& e : io) {\n        DxvkShaderIoVar var = { };\n\n        if (e.getType() == dxbc_spv::ir::IoEntryType::eBuiltIn) {\n          auto builtIn = convertBuiltIn(e.getBuiltIn());\n\n          if (!builtIn)\n            continue;\n\n          var.builtIn = *builtIn;\n          var.location = 0u;\n          var.componentIndex = 0u;\n          var.componentCount = e.computeComponentCount();\n          var.isPatchConstant = builtIn == spv::BuiltInTessLevelInner ||\n                                builtIn == spv::BuiltInTessLevelOuter;\n        } else {\n          var.builtIn = spv::BuiltInMax;\n          var.location = e.getLocationIndex();\n          var.componentIndex = e.getFirstComponentIndex();\n          var.componentCount = e.computeComponentCount();\n          var.isPatchConstant = e.getType() == dxbc_spv::ir::IoEntryType::ePerPatch;\n\n          if (m_info.options.flags.test(DxvkShaderCompileFlag::SemanticIo)) {\n            auto semantic = io.getSemanticForEntry(e);\n            var.semanticName = semantic.name;\n            var.semanticIndex = semantic.index;\n          }\n        }\n\n        map.add(std::move(var));\n      }\n\n      return map;\n    }\n\n\n    std::optional<spv::BuiltIn> convertBuiltIn(dxbc_spv::ir::BuiltIn builtIn) const {\n      switch (builtIn) {\n        case dxbc_spv::ir::BuiltIn::ePosition:\n          return m_stage == dxbc_spv::ir::ShaderStage::ePixel\n            ? spv::BuiltInFragCoord\n            : spv::BuiltInPosition;\n        case dxbc_spv::ir::BuiltIn::eClipDistance:\n          return spv::BuiltInClipDistance;\n        case dxbc_spv::ir::BuiltIn::eCullDistance:\n          return spv::BuiltInCullDistance;\n        case dxbc_spv::ir::BuiltIn::eVertexId:\n          return spv::BuiltInVertexIndex;\n        case dxbc_spv::ir::BuiltIn::eInstanceId:\n          return spv::BuiltInInstanceIndex;\n        case dxbc_spv::ir::BuiltIn::ePrimitiveId:\n          return spv::BuiltInPrimitiveId;\n        case dxbc_spv::ir::BuiltIn::eLayerIndex:\n          return spv::BuiltInLayer;\n        case dxbc_spv::ir::BuiltIn::eViewportIndex:\n          return spv::BuiltInViewportIndex;\n        case dxbc_spv::ir::BuiltIn::eGsVertexCountIn:\n          return std::nullopt;\n        case dxbc_spv::ir::BuiltIn::eGsInstanceId:\n          return spv::BuiltInInvocationId;\n        case dxbc_spv::ir::BuiltIn::eTessControlPointCountIn:\n          return spv::BuiltInPatchVertices;\n        case dxbc_spv::ir::BuiltIn::eTessControlPointId:\n          return spv::BuiltInInvocationId;\n        case dxbc_spv::ir::BuiltIn::eTessCoord:\n          return spv::BuiltInTessCoord;\n        case dxbc_spv::ir::BuiltIn::eTessFactorInner:\n          return spv::BuiltInTessLevelInner;\n        case dxbc_spv::ir::BuiltIn::eTessFactorOuter:\n          return spv::BuiltInTessLevelOuter;\n        case dxbc_spv::ir::BuiltIn::eSampleCount:\n          return std::nullopt;\n        case dxbc_spv::ir::BuiltIn::eSampleId:\n          return spv::BuiltInSampleId;\n        case dxbc_spv::ir::BuiltIn::eSamplePosition:\n          return spv::BuiltInSamplePosition;\n        case dxbc_spv::ir::BuiltIn::eSampleMask:\n          return spv::BuiltInSampleMask;\n        case dxbc_spv::ir::BuiltIn::eIsFrontFace:\n          return spv::BuiltInFrontFacing;\n        case dxbc_spv::ir::BuiltIn::eDepth:\n          return spv::BuiltInFragDepth;\n        case dxbc_spv::ir::BuiltIn::eStencilRef:\n          return spv::BuiltInFragStencilRefEXT;\n        case dxbc_spv::ir::BuiltIn::eIsFullyCovered:\n          return spv::BuiltInFullyCoveredEXT;\n        case dxbc_spv::ir::BuiltIn::eWorkgroupId:\n          return spv::BuiltInWorkgroupId;\n        case dxbc_spv::ir::BuiltIn::eGlobalThreadId:\n          return spv::BuiltInGlobalInvocationId;\n        case dxbc_spv::ir::BuiltIn::eLocalThreadId:\n          return spv::BuiltInLocalInvocationId;\n        case dxbc_spv::ir::BuiltIn::eLocalThreadIndex:\n          return spv::BuiltInLocalInvocationIndex;\n        case dxbc_spv::ir::BuiltIn::ePointSize:\n          return spv::BuiltInPointSize;\n        case dxbc_spv::ir::BuiltIn::eTessFactorLimit:\n          return std::nullopt;\n      }\n\n      return std::nullopt;\n    }\n\n\n    static VkShaderStageFlagBits convertShaderStage(dxbc_spv::ir::ShaderStage stage) {\n      switch (stage) {\n        case dxbc_spv::ir::ShaderStage::eVertex:\n          return VK_SHADER_STAGE_VERTEX_BIT;\n        case dxbc_spv::ir::ShaderStage::eHull:\n          return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;\n        case dxbc_spv::ir::ShaderStage::eDomain:\n          return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;\n        case dxbc_spv::ir::ShaderStage::eGeometry:\n          return VK_SHADER_STAGE_GEOMETRY_BIT;\n        case dxbc_spv::ir::ShaderStage::ePixel:\n          return VK_SHADER_STAGE_FRAGMENT_BIT;\n        case dxbc_spv::ir::ShaderStage::eCompute:\n          return VK_SHADER_STAGE_COMPUTE_BIT;\n        case dxbc_spv::ir::ShaderStage::eFlagEnum:\n          break;\n      }\n\n      return VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;\n    }\n\n\n    static VkImageViewType determineViewType(dxbc_spv::ir::ResourceKind kind) {\n      switch (kind) {\n        case dxbc_spv::ir::ResourceKind::eImage1D:\n          return VK_IMAGE_VIEW_TYPE_1D;\n        case dxbc_spv::ir::ResourceKind::eImage1DArray:\n          return VK_IMAGE_VIEW_TYPE_1D_ARRAY;\n        case dxbc_spv::ir::ResourceKind::eImage2D:\n        case dxbc_spv::ir::ResourceKind::eImage2DMS:\n          return VK_IMAGE_VIEW_TYPE_2D;\n        case dxbc_spv::ir::ResourceKind::eImage2DArray:\n        case dxbc_spv::ir::ResourceKind::eImage2DMSArray:\n          return VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n        case dxbc_spv::ir::ResourceKind::eImageCube:\n          return VK_IMAGE_VIEW_TYPE_CUBE;\n        case dxbc_spv::ir::ResourceKind::eImageCubeArray:\n          return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;\n        case dxbc_spv::ir::ResourceKind::eImage3D:\n          return VK_IMAGE_VIEW_TYPE_3D;\n        default:\n          return VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n      }\n    }\n\n\n    std::string getDebugName(dxbc_spv::ir::SsaDef def) {\n      auto [a, b] = m_builder.getUses(def);\n\n      for (auto iter = a; iter != b; iter++) {\n        if (iter->getOpCode() == dxbc_spv::ir::OpCode::eDebugName)\n          return iter->getLiteralString(iter->getFirstLiteralOperandIndex());\n      }\n\n      return std::to_string(def.getId());\n    }\n\n  };\n\n\n\n  DxvkIrShaderConverter::~DxvkIrShaderConverter() {\n\n  }\n\n\n\n  DxvkIrShader::DxvkIrShader(\n    const DxvkIrShaderCreateInfo&   info,\n          Rc<DxvkIrShaderConverter> shader)\n  : m_baseIr(std::move(shader)), m_debugName(m_baseIr->getDebugName()), m_info(info) {\n\n  }\n\n\n  DxvkIrShader::DxvkIrShader(\n          std::string               name,\n    const DxvkIrShaderCreateInfo&   info,\n          DxvkShaderMetadata        metadata,\n          DxvkPipelineLayoutBuilder layout,\n          std::vector<uint8_t>      ir)\n  : m_debugName   (std::move(name)), m_info(info),\n    m_layout      (std::move(layout)),\n    m_ir          (std::move(ir)),\n    m_convertedIr (true),\n    m_metadata    (std::move(metadata)) {\n\n  }\n\n\n  DxvkIrShader::~DxvkIrShader() {\n\n  }\n\n\n  DxvkShaderMetadata DxvkIrShader::getShaderMetadata() {\n    convertIr(\"getShaderMetadata()\");\n\n    return m_metadata;\n  }\n\n\n  void DxvkIrShader::compile() {\n    convertIr(nullptr);\n  }\n\n\n  SpirvCodeBuffer DxvkIrShader::getCode(\n    const DxvkShaderBindingMap*       bindings,\n    const DxvkShaderLinkage*          linkage) {\n    convertIr(\"getCode()\");\n\n    DxvkDxbcSpirvLogger logger(debugName());\n\n    dxbc_spv::ir::Builder irBuilder;\n    deserializeIr(irBuilder);\n\n    // Fix up shader I/O based on shader linkage\n    { dxbc_spv::ir::LowerIoPass ioPass(irBuilder);\n      if (linkage) {\n        if (m_metadata.stage == VK_SHADER_STAGE_FRAGMENT_BIT && linkage->fsFlatShading && m_info.flatShadingInputs)\n          ioPass.enableFlatInterpolation(m_info.flatShadingInputs);\n\n        if (m_metadata.stage == VK_SHADER_STAGE_GEOMETRY_BIT && linkage->inputTopology != m_metadata.inputTopology)\n          ioPass.changeGsInputPrimitiveType(convertPrimitiveType(linkage->inputTopology));\n\n        if (m_metadata.stage == VK_SHADER_STAGE_FRAGMENT_BIT && linkage->fsDualSrcBlend) {\n          dxbc_spv::ir::IoMap io = { };\n          io.add(dxbc_spv::ir::IoLocation(dxbc_spv::ir::IoEntryType::ePerVertex, 0u, 0xfu), dxbc_spv::ir::IoSemantic());\n          io.add(dxbc_spv::ir::IoLocation(dxbc_spv::ir::IoEntryType::ePerVertex, 1u, 0xfu), dxbc_spv::ir::IoSemantic());\n\n          ioPass.resolveUnusedOutputs(io);\n        }\n\n        if (m_metadata.stage == VK_SHADER_STAGE_FRAGMENT_BIT) {\n          std::array<dxbc_spv::ir::IoOutputSwizzle, 8u> swizzles = { };\n          uint32_t outputMask = m_metadata.outputs.computeMask();\n\n          for (auto i : bit::BitMask(outputMask))\n            swizzles.at(i) = convertOutputSwizzle(linkage->rtSwizzles.at(i));\n\n          ioPass.swizzleOutputs(swizzles.size(), swizzles.data());\n        }\n\n        bool matchSemantics = linkage->semanticIo && m_metadata.flags.test(DxvkShaderFlag::SemanticIo);\n\n        if (m_metadata.stage != VK_SHADER_STAGE_COMPUTE_BIT && !DxvkShaderIo::checkStageCompatibility(\n            m_metadata.stage, m_metadata.inputs, linkage->prevStage, linkage->prevStageOutputs, matchSemantics)) {\n          auto prevStageIoMap = convertIoMap(linkage->prevStageOutputs, linkage->prevStage);\n\n          if (matchSemantics)\n            ioPass.resolveSemanticIo(prevStageIoMap);\n\n          ioPass.resolveMismatchedIo(convertShaderStage(linkage->prevStage), prevStageIoMap);\n        }\n\n        if (m_metadata.stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)\n          ioPass.resolvePatchConstantLocations(convertIoMap(m_metadata.outputs, m_metadata.stage));\n\n        if (m_metadata.stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)\n          ioPass.resolvePatchConstantLocations(convertIoMap(linkage->prevStageOutputs, linkage->prevStage));\n      }\n\n      if (m_metadata.stage == VK_SHADER_STAGE_FRAGMENT_BIT && m_info.options.flags.test(DxvkShaderCompileFlag::EnableSampleRateShading))\n        ioPass.enableSampleInterpolation();\n    }\n\n    // Set up SPIR-V options. Only enable float controls if a sufficient subset\n    // of features is supported; this avoids running into performance issues on\n    // Nvidia where just enabling RTE on FP32 causes a ~20% performance drop.\n    dxbc_spv::spirv::SpirvBuilder::Options options = { };\n    options.includeDebugNames = true;\n    options.nvRawAccessChains = m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsNvRawAccessChains);\n    options.dualSourceBlending = linkage && linkage->fsDualSrcBlend;\n\n    if (m_info.options.spirv.all(DxvkShaderSpirvFlag::IndependentDenormMode,\n                               DxvkShaderSpirvFlag::SupportsRte32,\n                               DxvkShaderSpirvFlag::SupportsDenormFlush32)) {\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsRte16))\n        options.supportedRoundModesF16 |= dxbc_spv::ir::RoundMode::eNearestEven;\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsRte32))\n        options.supportedRoundModesF32 |= dxbc_spv::ir::RoundMode::eNearestEven;\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsRte64))\n        options.supportedRoundModesF64 |= dxbc_spv::ir::RoundMode::eNearestEven;\n\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsRtz16))\n        options.supportedRoundModesF16 |= dxbc_spv::ir::RoundMode::eZero;\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsRtz32))\n        options.supportedRoundModesF32 |= dxbc_spv::ir::RoundMode::eZero;\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsRtz64))\n        options.supportedRoundModesF64 |= dxbc_spv::ir::RoundMode::eZero;\n\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsDenormFlush16))\n        options.supportedDenormModesF16 |= dxbc_spv::ir::DenormMode::eFlush;\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsDenormFlush32))\n        options.supportedDenormModesF32 |= dxbc_spv::ir::DenormMode::eFlush;\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsDenormFlush64))\n        options.supportedDenormModesF64 |= dxbc_spv::ir::DenormMode::eFlush;\n\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsDenormPreserve16))\n        options.supportedDenormModesF16 |= dxbc_spv::ir::DenormMode::ePreserve;\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsDenormPreserve32))\n        options.supportedDenormModesF32 |= dxbc_spv::ir::DenormMode::ePreserve;\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsDenormPreserve64))\n        options.supportedDenormModesF64 |= dxbc_spv::ir::DenormMode::ePreserve;\n\n      if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsSzInfNanPreserve32))\n        options.floatControls2 = m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsFloatControls2);\n    }\n\n    options.supportsZeroInfNanPreserveF16 = m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsSzInfNanPreserve16);\n    options.supportsZeroInfNanPreserveF32 = m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsSzInfNanPreserve32);\n    options.supportsZeroInfNanPreserveF64 = m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsSzInfNanPreserve64);\n\n    options.maxCbvSize = m_info.options.maxUniformBufferSize;\n    options.maxCbvCount = m_info.options.maxUniformBufferCount;\n\n    // Build final SPIR-V binary\n    DxvkShaderResourceMapping mapping(m_metadata.stage, bindings);\n\n    dxbc_spv::spirv::SpirvBuilder spirvBuilder(irBuilder, mapping, options);\n    spirvBuilder.buildSpirvBinary();\n\n    return SpirvCodeBuffer(spirvBuilder.getSpirvBinary());\n  }\n\n\n  DxvkPipelineLayoutBuilder DxvkIrShader::getLayout() {\n    convertIr(\"getLayout()\");\n\n    return m_layout;\n  }\n\n\n  void DxvkIrShader::dump(std::ostream& outputStream) {\n    auto code = getCode(nullptr, nullptr);\n    outputStream.write(reinterpret_cast<const char*>(code.data()), code.size());\n  }\n\n\n  std::pair<const uint8_t*, size_t> DxvkIrShader::getSerializedIr() {\n    convertIr(\"getSerializedIr()\");\n\n    return std::make_pair(m_ir.data(), m_ir.size());\n  }\n\n\n  std::string DxvkIrShader::debugName() {\n    return m_debugName;\n  }\n\n\n  void DxvkIrShader::convertIr(const char* reason) {\n    if (m_convertedIr.load(std::memory_order_acquire))\n      return;\n\n    std::lock_guard lock(m_mutex);\n\n    if (m_convertedIr.load(std::memory_order_relaxed))\n      return;\n\n    if (reason && Logger::logLevel() <= LogLevel::Debug)\n      Logger::debug(str::format(m_debugName, \": Early compile: \", reason));\n\n    const auto& dumpPath = getShaderDumpPath();\n\n    if (!dumpPath.empty())\n      dumpSource(dumpPath);\n\n    convertShader();\n\n    // Destroy original converter, we no longer need it\n    m_baseIr = nullptr;\n\n    m_convertedIr.store(true, std::memory_order_release);\n\n    // Need to do this *after* marking the conversion as done since lowering\n    // to SPIR-V itself will otherwise call into this method again\n    if (!dumpPath.empty())\n      dumpSpv(dumpPath);\n  }\n\n\n  void DxvkIrShader::convertShader() {\n    DxvkDxbcSpirvLogger logger(m_debugName);\n\n    dxbc_spv::ir::Builder builder;\n    m_baseIr->convertShader(builder);\n\n    if (!m_info.xfbEntries.empty()) {\n      dxbc_spv::ir::LowerIoPass ioPass(builder);\n\n      ioPass.resolveXfbOutputs(\n        m_info.xfbEntries.size(),\n        m_info.xfbEntries.data(),\n        m_info.rasterizedStream);\n    }\n\n    if (m_info.options.flags.test(DxvkShaderCompileFlag::DisableMsaa)) {\n      dxbc_spv::ir::LowerIoPass ioPass(builder);\n      ioPass.demoteMultisampledSrv();\n    }\n\n    dxbc_spv::ir::CompileOptions options;\n    options.arithmeticOptions.lowerDot = true;\n    options.arithmeticOptions.lowerSinCos = m_info.options.flags.test(DxvkShaderCompileFlag::LowerSinCos);\n    options.arithmeticOptions.lowerMsad = true;\n    options.arithmeticOptions.lowerF32toF16 = m_info.options.flags.test(DxvkShaderCompileFlag::LowerF32toF16);\n    options.arithmeticOptions.lowerConvertFtoI = m_info.options.flags.test(DxvkShaderCompileFlag::LowerFtoI);\n    options.arithmeticOptions.lowerGsVertexCountIn = false;\n    options.arithmeticOptions.hasNvUnsignedItoFBug = m_info.options.flags.test(DxvkShaderCompileFlag::LowerItoF);\n\n    options.min16Options.enableFloat16 = m_info.options.flags.test(DxvkShaderCompileFlag::Supports16BitArithmetic);\n    options.min16Options.enableInt16 = m_info.options.flags.test(DxvkShaderCompileFlag::Supports16BitArithmetic);\n\n    options.resourceOptions.allowSubDwordScratchAndLds = true;\n    options.resourceOptions.flattenLds = false;\n    options.resourceOptions.flattenScratch = false;\n    options.resourceOptions.structuredCbv = true;\n    options.resourceOptions.structuredSrvUav = true;\n\n    auto ssboAlignment = m_info.options.minStorageBufferAlignment;\n    options.bufferOptions.useTypedForRaw = ssboAlignment > 16u;\n    options.bufferOptions.useTypedForStructured = ssboAlignment > 4u;\n    options.bufferOptions.useTypedForSparseFeedback = true;\n    options.bufferOptions.useRawForTypedAtomic = ssboAlignment <= 4u;\n    options.bufferOptions.forceFormatForTypedUavRead = m_info.options.flags.test(DxvkShaderCompileFlag::TypedR32LoadRequiresFormat);\n    options.bufferOptions.minStructureAlignment = ssboAlignment;\n\n    options.scalarizeOptions.subDwordVectors = true;\n\n    options.syncOptions.insertRovLocks = true;\n    options.syncOptions.insertLdsBarriers = m_info.options.flags.test(DxvkShaderCompileFlag::InsertSharedMemoryBarriers);\n    options.syncOptions.insertUavBarriers = m_info.options.flags.test(DxvkShaderCompileFlag::InsertResourceBarriers);\n\n    options.derivativeOptions.hoistNontrivialDerivativeOps = true;\n    options.derivativeOptions.hoistNontrivialImplicitLodOps = false;\n    options.derivativeOptions.hoistDescriptorLoads = true;\n\n    options.cseOptions.relocateDescriptorLoad = true;\n\n    if (m_info.options.spirv.test(DxvkShaderSpirvFlag::SupportsResourceIndexing))\n      options.descriptorIndexing.optimizeDescriptorIndexing = true;\n\n    dxbc_spv::ir::legalizeIr(builder, options);\n\n    // Generate shader metadata based on the final code\n    DxvkIrLowerBindingModelPass lowerBindingModelPass(builder, *m_baseIr, m_info);\n    lowerBindingModelPass.run();\n\n    m_metadata = lowerBindingModelPass.getMetadata();\n    m_layout = lowerBindingModelPass.getLayout();\n\n    serializeIr(builder);\n  }\n\n\n  void DxvkIrShader::serializeIr(const dxbc_spv::ir::Builder& builder) {\n    dxbc_spv::ir::Serializer serializer(builder);\n\n    std::vector<uint8_t> data(serializer.computeSerializedSize());\n    serializer.serialize(data.data(), data.size());\n\n    m_ir = std::move(data);\n  }\n\n\n  void DxvkIrShader::deserializeIr(dxbc_spv::ir::Builder& builder) const {\n    dxbc_spv::ir::Deserializer deserializer(m_ir.data(), m_ir.size());\n\n    if (!deserializer.deserialize(builder))\n      throw DxvkError(\"Failed to deserialize shader\");\n  }\n\n\n  void DxvkIrShader::dumpSource(const std::string& path) {\n    if (m_baseIr)\n      m_baseIr->dumpSource(path);\n  }\n\n\n  void DxvkIrShader::dumpSpv(const std::string& path) {\n    std::ofstream file(str::topath(str::format(path, \"/\", m_debugName, \".spv\").c_str()).c_str(), std::ios_base::trunc | std::ios_base::binary);\n\n    auto code = getCode(nullptr, nullptr);\n    file.write(reinterpret_cast<const char*>(code.data()), code.size());\n  }\n\n\n  dxbc_spv::ir::PrimitiveType DxvkIrShader::convertPrimitiveType(VkPrimitiveTopology topology) {\n    switch (topology) {\n      case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:\n        return dxbc_spv::ir::PrimitiveType::ePoints;\n\n      case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:\n      case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:\n        return dxbc_spv::ir::PrimitiveType::eLines;\n\n      case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:\n      case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:\n        return dxbc_spv::ir::PrimitiveType::eLinesAdj;\n\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:\n        return dxbc_spv::ir::PrimitiveType::eTriangles;\n\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:\n      case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:\n        return dxbc_spv::ir::PrimitiveType::eTrianglesAdj;\n\n      default:\n        return dxbc_spv::ir::PrimitiveType();\n    }\n  }\n\n\n  dxbc_spv::ir::IoOutputSwizzle DxvkIrShader::convertOutputSwizzle(VkComponentMapping mapping) {\n    dxbc_spv::ir::IoOutputSwizzle result;\n    result.x = convertOutputComponent(mapping.r, dxbc_spv::ir::IoOutputComponent::eX);\n    result.y = convertOutputComponent(mapping.g, dxbc_spv::ir::IoOutputComponent::eY);\n    result.z = convertOutputComponent(mapping.b, dxbc_spv::ir::IoOutputComponent::eZ);\n    result.w = convertOutputComponent(mapping.a, dxbc_spv::ir::IoOutputComponent::eW);\n    return result;\n  }\n\n\n  dxbc_spv::ir::IoOutputComponent DxvkIrShader::convertOutputComponent(VkComponentSwizzle swizzle, dxbc_spv::ir::IoOutputComponent identity) {\n    switch (swizzle) {\n      case VK_COMPONENT_SWIZZLE_R: return dxbc_spv::ir::IoOutputComponent::eX;\n      case VK_COMPONENT_SWIZZLE_G: return dxbc_spv::ir::IoOutputComponent::eY;\n      case VK_COMPONENT_SWIZZLE_B: return dxbc_spv::ir::IoOutputComponent::eZ;\n      case VK_COMPONENT_SWIZZLE_A: return dxbc_spv::ir::IoOutputComponent::eW;\n      default: return identity;\n    }\n  }\n\n\n  dxbc_spv::ir::ShaderStage DxvkIrShader::convertShaderStage(VkShaderStageFlagBits stage) {\n    switch (stage) {\n      case VK_SHADER_STAGE_VERTEX_BIT:\n        return dxbc_spv::ir::ShaderStage::eVertex;\n      case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:\n        return dxbc_spv::ir::ShaderStage::eHull;\n      case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:\n        return dxbc_spv::ir::ShaderStage::eDomain;\n      case VK_SHADER_STAGE_GEOMETRY_BIT:\n        return dxbc_spv::ir::ShaderStage::eGeometry;\n      case VK_SHADER_STAGE_FRAGMENT_BIT:\n        return dxbc_spv::ir::ShaderStage::ePixel;\n      case VK_SHADER_STAGE_COMPUTE_BIT:\n        return dxbc_spv::ir::ShaderStage::eCompute;\n      default:\n        return dxbc_spv::ir::ShaderStage();\n    }\n  }\n\n\n  dxbc_spv::ir::IoMap DxvkIrShader::convertIoMap(const DxvkShaderIo& io, VkShaderStageFlagBits stage) {\n    dxbc_spv::ir::IoMap map = { };\n\n    for (uint32_t i = 0u; i < io.getVarCount(); i++) {\n      const auto& var = io.getVar(i);\n\n      if (var.builtIn != spv::BuiltInMax) {\n        auto builtIn = convertBuiltIn(var.builtIn, stage);\n\n        if (builtIn) {\n          map.add(\n            dxbc_spv::ir::IoLocation(*builtIn, uint8_t((1u << var.componentCount) - 1u)),\n            dxbc_spv::ir::IoSemantic());\n        }\n      } else {\n        auto type = var.isPatchConstant\n          ? dxbc_spv::ir::IoEntryType::ePerPatch\n          : dxbc_spv::ir::IoEntryType::ePerVertex;\n\n        dxbc_spv::ir::IoSemantic semantic = { };\n        semantic.name = var.semanticName;\n        semantic.index = var.semanticIndex;\n\n        map.add(dxbc_spv::ir::IoLocation(type, var.location,\n          ((1u << var.componentCount) - 1u) << var.componentIndex),\n          std::move(semantic));\n      }\n    }\n\n    return map;\n  }\n\n\n  std::optional<dxbc_spv::ir::BuiltIn> DxvkIrShader::convertBuiltIn(spv::BuiltIn builtIn, VkShaderStageFlagBits stage) {\n    switch (builtIn) {\n      case spv::BuiltInFragCoord:\n      case spv::BuiltInPosition:\n        return dxbc_spv::ir::BuiltIn::ePosition;\n      case spv::BuiltInClipDistance:\n        return dxbc_spv::ir::BuiltIn::eClipDistance;\n      case spv::BuiltInCullDistance:\n        return dxbc_spv::ir::BuiltIn::eCullDistance;\n      case spv::BuiltInVertexId:\n      case spv::BuiltInVertexIndex:\n        return dxbc_spv::ir::BuiltIn::eVertexId;\n      case spv::BuiltInInstanceId:\n      case spv::BuiltInInstanceIndex:\n        return dxbc_spv::ir::BuiltIn::eInstanceId;\n      case spv::BuiltInPrimitiveId:\n        return dxbc_spv::ir::BuiltIn::ePrimitiveId;\n      case spv::BuiltInLayer:\n        return dxbc_spv::ir::BuiltIn::eLayerIndex;\n      case spv::BuiltInViewportIndex:\n        return dxbc_spv::ir::BuiltIn::eViewportIndex;\n      case spv::BuiltInInvocationId: {\n        if (stage == VK_SHADER_STAGE_GEOMETRY_BIT)\n          return dxbc_spv::ir::BuiltIn::eGsInstanceId;\n        if (stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)\n          return dxbc_spv::ir::BuiltIn::eTessControlPointId;\n      } return std::nullopt;\n      case spv::BuiltInPatchVertices:\n        return dxbc_spv::ir::BuiltIn::eTessControlPointCountIn;\n      case spv::BuiltInTessCoord:\n        return dxbc_spv::ir::BuiltIn::eTessCoord;\n      case spv::BuiltInTessLevelInner:\n        return dxbc_spv::ir::BuiltIn::eTessFactorInner;\n      case spv::BuiltInTessLevelOuter:\n        return dxbc_spv::ir::BuiltIn::eTessFactorOuter;\n      case spv::BuiltInSampleId:\n        return dxbc_spv::ir::BuiltIn::eSampleId;\n      case spv::BuiltInSamplePosition:\n        return dxbc_spv::ir::BuiltIn::eSamplePosition;\n      case spv::BuiltInSampleMask:\n        return dxbc_spv::ir::BuiltIn::eSampleMask;\n      case spv::BuiltInFrontFacing:\n        return dxbc_spv::ir::BuiltIn::eIsFrontFace;\n      case spv::BuiltInFragDepth:\n        return dxbc_spv::ir::BuiltIn::eDepth;\n      case spv::BuiltInFragStencilRefEXT:\n        return dxbc_spv::ir::BuiltIn::eStencilRef;\n      case spv::BuiltInFullyCoveredEXT:\n        return dxbc_spv::ir::BuiltIn::eIsFullyCovered;\n      case spv::BuiltInWorkgroupId:\n        return dxbc_spv::ir::BuiltIn::eWorkgroupId;\n      case spv::BuiltInGlobalInvocationId:\n        return dxbc_spv::ir::BuiltIn::eGlobalThreadId;\n      case spv::BuiltInLocalInvocationId:\n        return dxbc_spv::ir::BuiltIn::eLocalThreadId;\n      case spv::BuiltInLocalInvocationIndex:\n        return dxbc_spv::ir::BuiltIn::eLocalThreadIndex;\n      default:\n        return std::nullopt;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_ir.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <string>\n#include <vector>\n\n#include <dxbc/dxbc_api.h>\n\n#include <ir/ir_builder.h>\n\n#include <ir/passes/ir_pass_lower_io.h>\n\n#include \"dxvk_shader.h\"\n\n#include \"../util/thread.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief IR shader properties\n   *\n   * Stores some metadata that cannot be inferred from\n   * the IR, as well as some binding model mappings.\n   */\n  struct DxvkIrShaderCreateInfo {\n    /// Shader compile options\n    DxvkShaderOptions options;\n    /// Mask of user input locations to enable flat shading for\n    uint32_t flatShadingInputs = 0u;\n    /// Rasterized geometry stream\n    int32_t rasterizedStream = 0;\n    /// Streamout parameters\n    small_vector<dxbc_spv::ir::IoXfbInfo, 8u> xfbEntries = { };\n\n    size_t hash() const;\n\n    bool eq(const DxvkIrShaderCreateInfo& other) const;\n  };\n\n\n  /**\n   * \\brief Raw shader binary for dxbc-spirv\n   *\n   * Performs the initial shader conversion and provides a method for\n   * the shader implementation to map resource registers to DXVK bindings.\n   */\n  class DxvkIrShaderConverter {\n\n  public:\n\n    void incRef() {\n      m_useCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    void decRef() {\n      if (m_useCount.fetch_sub(1u, std::memory_order_release) == 1u)\n        delete this;\n    }\n\n    virtual ~DxvkIrShaderConverter();\n\n    /**\n     * \\brief Performs initial shader conversion\n     * \\param [out] builder IR builder\n     */\n    virtual void convertShader(\n            dxbc_spv::ir::Builder&    builder) = 0;\n\n    /**\n     * \\brief Maps IR binding to internal resource index\n     *\n     * \\param [in] stage Shader stage\n     * \\param [in] type Descriptor type\n     * \\param [in] regSpace Register space\n     * \\param [in] regIndex Register index\n     */\n    virtual uint32_t determineResourceIndex(\n            dxbc_spv::ir::ShaderStage stage,\n            dxbc_spv::ir::ScalarType  type,\n            uint32_t                  regSpace,\n            uint32_t                  regIndex) const = 0;\n\n    /**\n     * \\brief Dumps source to the given output file\n     * \\param [in] file Output stream\n     */\n    virtual void dumpSource(const std::string& path) const = 0;\n\n    /**\n     * \\brief Queries shader debug name\n     * \\returns Shader debug name\n     */\n    virtual std::string getDebugName() const = 0;\n\n  private:\n\n    std::atomic<uint32_t> m_useCount = { };\n\n  };\n\n\n  /**\n   * \\brief DXBC-SPIRV IR shader\n   */\n  class DxvkIrShader : public DxvkShader {\n\n  public:\n\n    DxvkIrShader(\n      const DxvkIrShaderCreateInfo&   info,\n            Rc<DxvkIrShaderConverter> shader);\n\n    DxvkIrShader(\n            std::string               name,\n      const DxvkIrShaderCreateInfo&   info,\n            DxvkShaderMetadata        metadata,\n            DxvkPipelineLayoutBuilder layout,\n            std::vector<uint8_t>      ir);\n\n    ~DxvkIrShader();\n\n    /**\n     * \\brief Queries shader create info\n     * \\returns Shader create info\n     */\n    DxvkIrShaderCreateInfo getShaderCreateInfo() const {\n      return m_info;\n    }\n\n    /**\n     * \\brief Queries shader metadata\n     *\n     * Compiles the shader on demand.\n     * \\returns Shader metadata\n     */\n    DxvkShaderMetadata getShaderMetadata();\n\n    /**\n     * \\brief Compiles shader to internal IR\n     */\n    void compile();\n\n    /**\n     * \\brief Patches code using given info\n     *\n     * Rewrites binding IDs and potentially fixes up other\n     * parts of the code depending on pipeline state.\n     * \\param [in] bindings Biding map\n     * \\param [in] state Pipeline state info\n     * \\returns Uncompressed SPIR-V code buffer\n     */\n    SpirvCodeBuffer getCode(\n      const DxvkShaderBindingMap*       bindings,\n      const DxvkShaderLinkage*          linkage);\n\n    /**\n     * \\brief Queries shader binding layout\n     * \\returns Pipeline layout builder\n     */\n    DxvkPipelineLayoutBuilder getLayout();\n\n    /**\n     * \\brief Dumps SPIR-V binary to a stream\n     * \\param [in] outputStream Stream to write to\n     */\n    void dump(std::ostream& outputStream);\n\n    /**\n     * \\brief Queries serialized IR\n     */\n    std::pair<const uint8_t*, size_t> getSerializedIr();\n\n    /**\n     * \\brief Retrieves debug name for this shader\n     * \\returns Shader debug name\n     */\n    std::string debugName();\n\n  private:\n\n    Rc<DxvkIrShaderConverter>     m_baseIr;\n    std::string                   m_debugName;\n\n    DxvkIrShaderCreateInfo        m_info;\n    DxvkPipelineLayoutBuilder     m_layout;\n\n    dxvk::mutex                   m_mutex;\n\n    std::vector<uint8_t>          m_ir;\n    std::atomic<bool>             m_convertedIr = { false };\n\n    DxvkShaderMetadata            m_metadata = { };\n\n    void convertIr(const char* reason);\n\n    void convertShader();\n\n    void serializeIr(const dxbc_spv::ir::Builder& builder);\n\n    void deserializeIr(dxbc_spv::ir::Builder& builder) const;\n\n    void dumpSource(const std::string& dumpPath);\n\n    void dumpSpv(const std::string& dumpPath);\n\n    static dxbc_spv::ir::PrimitiveType convertPrimitiveType(VkPrimitiveTopology topology);\n\n    static dxbc_spv::ir::IoOutputSwizzle convertOutputSwizzle(VkComponentMapping mapping);\n\n    static dxbc_spv::ir::IoOutputComponent convertOutputComponent(VkComponentSwizzle swizzle, dxbc_spv::ir::IoOutputComponent identity);\n\n    static dxbc_spv::ir::ShaderStage convertShaderStage(VkShaderStageFlagBits stage);\n\n    static dxbc_spv::ir::IoMap convertIoMap(const DxvkShaderIo& io, VkShaderStageFlagBits stage);\n\n    static std::optional<dxbc_spv::ir::BuiltIn> convertBuiltIn(spv::BuiltIn builtIn, VkShaderStageFlagBits stage);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_key.cpp",
    "content": "#include \"dxvk_shader_key.h\"\n\nnamespace dxvk {\n\n  DxvkShaderHash::DxvkShaderHash() {\n\n  }\n\n\n  DxvkShaderHash::DxvkShaderHash(\n          VkShaderStageFlagBits stage,\n          uint32_t              codeSize,\n    const uint8_t*              hash,\n          size_t                hashSize)\n  : DxvkShaderHash(stage, codeSize, hash, hashSize, nullptr, 0u) { }\n\n\n  DxvkShaderHash::DxvkShaderHash(\n          VkShaderStageFlagBits stage,\n          uint32_t              codeSize,\n    const uint8_t*              hash,\n          size_t                hashSize,\n    const uint8_t*              metaHash,\n          size_t                metaSize)\n  : m_stage(uint16_t(stage)), m_xfb(metaSize ? 1u : 0u), m_size(codeSize) {\n    size_t index = 0u;\n\n    for (size_t i = 0u; i < hashSize; i += 4u) {\n      m_hash[index] ^= getDword(&hash[i]);\n      index = (index + 1u) % m_hash.size();\n    }\n\n    for (size_t i = 0u; i < metaSize; i += 4u) {\n      m_hash[index] ^= getDword(&metaHash[i]);\n      index = (index + 1u) % m_hash.size();\n    }\n  }\n\n\n  std::string DxvkShaderHash::toString() const {\n    std::string name;\n    name.reserve(48u);\n\n    if (m_xfb) {\n      name = \"xfb\";\n    } else {\n      switch (m_stage) {\n        case VK_SHADER_STAGE_VERTEX_BIT: name = \"vs\"; break;\n        case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: name = \"tcs\"; break;\n        case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: name = \"tes\"; break;\n        case VK_SHADER_STAGE_GEOMETRY_BIT: name = \"gs\"; break;\n        case VK_SHADER_STAGE_FRAGMENT_BIT: name = \"fs\"; break;\n        case VK_SHADER_STAGE_COMPUTE_BIT: name = \"cs\"; break;\n        default: name = \"shdr\";\n      }\n    }\n\n    name += \".\";\n\n    for (size_t i = 0u; i < m_hash.size(); i++) {\n      for (uint32_t j = 0u; j < 4u; j++) {\n        name += toHex(m_hash[i] >> (8u * j + 4u));\n        name += toHex(m_hash[i] >> (8u * j));\n      }\n    }\n\n    return name;\n  }\n\n\n  bool DxvkShaderHash::eq(const DxvkShaderHash& other) const {\n    bool eq = m_stage == other.m_stage\n           && m_xfb == other.m_xfb\n           && m_size == other.m_size;\n\n    for (size_t i = 0u; i < m_hash.size(); i++)\n      eq = eq && m_hash[i] == other.m_hash[i];\n\n    return eq;\n  }\n\n\n  size_t DxvkShaderHash::hash() const {\n    DxvkHashState hash = { };\n    hash.add(m_stage);\n    hash.add(m_xfb);\n    hash.add(m_size);\n\n    for (auto dw : m_hash)\n      hash.add(dw);\n\n    return hash;\n  }\n\n\n  size_t DxvkShaderHash::getDword(const uint8_t* dw) {\n    return  (uint32_t(dw[0u]))\n          | (uint32_t(dw[1u]) << 8u)\n          | (uint32_t(dw[2u]) << 16u)\n          | (uint32_t(dw[3u]) << 24u);\n  }\n\n\n  char DxvkShaderHash::toHex(uint8_t nibble) {\n    static const std::array<char, 16u> ch = {\n      '0', '1', '2', '3', '4', '5', '6', '7',\n      '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',\n    };\n\n    return ch[nibble & 0xfu];\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_key.h",
    "content": "#pragma once\n\n#include \"dxvk_hash.h\"\n#include \"dxvk_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Shader look-up key\n   *\n   * Stores the shader hash itself, as well as some basic\n   * metadata like the shader type or hashed xfb metadata.\n   */\n  class DxvkShaderHash {\n\n  public:\n\n    DxvkShaderHash();\n\n    /**\n     * \\brief Initializes shader hash\n     *\n     * \\param [in] stage Shader stage\n     * \\param [in] codeSize Shader code size, in bytes\n     * \\param [in] hash Pointer to shader hash\n     * \\param [in] hashSize Shader hash size, in bytes\n     */\n    DxvkShaderHash(\n            VkShaderStageFlagBits stage,\n            uint32_t              codeSize,\n      const uint8_t*              hash,\n            size_t                hashSize);\n\n    /**\n     * \\brief Initializes shader hash\n     *\n     * \\param [in] stage Shader stage\n     * \\param [in] codeSize Shader code size, in bytes\n     * \\param [in] hash Pointer to shader hash\n     * \\param [in] hashSize Shader hash size, in bytes\n     * \\param [in] metaHash Metadata hash\n     * \\param [in] metaSize Metadata hash size, in bytes\n     */\n    DxvkShaderHash(\n            VkShaderStageFlagBits stage,\n            uint32_t              codeSize,\n      const uint8_t*              hash,\n            size_t                hashSize,\n      const uint8_t*              metaHash,\n            size_t                metaSize);\n\n    /**\n     * \\brief Shader stage\n     * \\returns Shader stage\n     */\n    VkShaderStageFlagBits stage() const {\n      return VkShaderStageFlagBits(m_stage);\n    }\n\n    /**\n     * \\brief Whether shader was created using streamout metadata\n     * \\returns \\c true if the shader has transform feedback metadata\n     */\n    bool hasXfb() const {\n      return m_xfb;\n    }\n\n    /**\n     * \\brief Generates shader name for the given hash\n     * \\returns Shader name to use for tooling etc.\n     */\n    std::string toString() const;\n\n    /**\n     * \\brief Compares two shader hashes\n     *\n     * \\param [in] other Other hash to compare to\n     * \\returns \\c true if the hashes are equal\n     */\n    bool eq(const DxvkShaderHash& other) const;\n\n    /**\n     * \\brief Computes look-up hash for shader\n     * \\returns Hash for internal hash tables\n     */\n    size_t hash() const;\n\n  private:\n\n    uint16_t                  m_stage = -1;\n    uint16_t                  m_xfb   = 0u;\n    uint32_t                  m_size  = 0u;\n    std::array<uint32_t, 4u>  m_hash  = { };\n\n    static size_t getDword(const uint8_t* dw);\n\n    static char toHex(uint8_t nibble);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_spirv.cpp",
    "content": "#include <algorithm>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"dxvk_device.h\"\n#include \"dxvk_shader_spirv.h\"\n\nnamespace dxvk {\n\n  DxvkSpirvShader::DxvkSpirvShader(\n    const DxvkSpirvShaderCreateInfo&  info,\n          SpirvCodeBuffer&&           spirv)\n  : m_info(info), m_layout(getShaderStage(spirv)) {\n    m_info.bindings = nullptr;\n\n    SpirvCodeBuffer code = std::move(spirv);\n    m_metadata.stage = VkShaderStageFlagBits(m_layout.getStageMask());\n    m_metadata.flatShadingInputs = info.flatShadingInputs;\n    m_metadata.rasterizedStream = info.xfbRasterizedStream;\n    m_metadata.patchVertexCount = info.patchVertexCount;\n\n    // Copy resource binding slot infos\n    for (uint32_t i = 0; i < info.bindingCount; i++) {\n      DxvkShaderDescriptor descriptor(info.bindings[i], m_metadata.stage);\n      m_layout.addBindings(1, &descriptor);\n    }\n\n    // Run an analysis pass over the SPIR-V code to gather some\n    // info that we may need during pipeline compilation.\n    gatherIdOffsets(code);\n    gatherMetadata(code);\n\n    if (m_info.samplerHeap.getStageMask() & m_metadata.stage) {\n      m_layout.addSamplerHeap(DxvkShaderBinding(m_metadata.stage,\n        info.samplerHeap.getSet(),\n        info.samplerHeap.getBinding()));\n    }\n\n    if (!info.debugName.empty())\n      m_debugName = info.debugName;\n    else if (m_debugName.empty())\n      m_debugName = std::to_string(getCookie());\n\n    m_code = SpirvCompressedBuffer(code);\n  }\n\n\n  DxvkSpirvShader::~DxvkSpirvShader() {\n\n  }\n\n\n  DxvkShaderMetadata DxvkSpirvShader::getShaderMetadata() {\n    return m_metadata;\n  }\n\n\n  void DxvkSpirvShader::compile() {\n    // No-op sice SPIR-V is already compiled\n  }\n\n\n  SpirvCodeBuffer DxvkSpirvShader::getCode(\n    const DxvkShaderBindingMap*       bindings,\n    const DxvkShaderLinkage*          linkage) {\n    SpirvCodeBuffer spirvCode = m_code.decompress();\n    patchResourceBindingsAndIoLocations(spirvCode, bindings, linkage);\n\n    // Undefined I/O handling is coarse, and not supported for tessellation shaders.\n    if (linkage) {\n      uint32_t undefinedInputs = 0u;\n\n      if (m_metadata.stage != VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {\n        auto producedMask = linkage->prevStageOutputs.computeMask();\n        auto consumedMask = m_metadata.inputs.computeMask();\n\n        auto definedMask = producedMask & consumedMask;\n        undefinedInputs = definedMask ^ consumedMask;\n      }\n\n      // Replace undefined input variables with zero\n      for (uint32_t u : bit::BitMask(undefinedInputs))\n        eliminateInput(spirvCode, u);\n\n      // Patch primitive topology as necessary\n      if (m_metadata.stage == VK_SHADER_STAGE_GEOMETRY_BIT\n       && linkage->inputTopology != m_metadata.inputTopology\n       && linkage->inputTopology != VK_PRIMITIVE_TOPOLOGY_MAX_ENUM)\n        patchInputTopology(spirvCode, linkage->inputTopology);\n\n      // Emit fragment shader swizzles as necessary\n      if (m_metadata.stage == VK_SHADER_STAGE_FRAGMENT_BIT)\n        emitOutputSwizzles(spirvCode, m_metadata.outputs.computeMask(), linkage->rtSwizzles.data());\n\n      // Emit input decorations for flat shading as necessary\n      if (m_metadata.stage == VK_SHADER_STAGE_FRAGMENT_BIT && linkage->fsFlatShading)\n        emitFlatShadingDeclarations(spirvCode, m_info.flatShadingInputs);\n    }\n\n    return spirvCode;\n  }\n\n\n  DxvkPipelineLayoutBuilder DxvkSpirvShader::getLayout() {\n    return m_layout;\n  }\n\n\n  void DxvkSpirvShader::dump(std::ostream& outputStream) {\n    m_code.decompress().store(outputStream);\n  }\n\n\n  std::string DxvkSpirvShader::debugName() {\n    return m_debugName;\n  }\n\n\n  void DxvkSpirvShader::gatherIdOffsets(\n          SpirvCodeBuffer&          code) {\n    for (auto i = code.begin(); i != code.end(); i++) {\n      auto ins = *i;\n\n      switch (ins.opCode()) {\n        case spv::OpString:\n        case spv::OpTypeVoid:\n        case spv::OpTypeInt:\n        case spv::OpTypeFloat:\n        case spv::OpTypeBool:\n        case spv::OpTypeVector:\n        case spv::OpTypeMatrix:\n        case spv::OpTypeImage:\n        case spv::OpTypeSampler:\n        case spv::OpTypeSampledImage:\n        case spv::OpTypeArray:\n        case spv::OpTypeRuntimeArray:\n        case spv::OpTypeStruct:\n        case spv::OpTypeOpaque:\n        case spv::OpTypePointer:\n        case spv::OpTypeFunction: {\n          m_idToOffset.insert({ ins.arg(1u), ins.offset() });\n        } break;\n\n        case spv::OpConstant:\n        case spv::OpVariable: {\n          m_idToOffset.insert({ ins.arg(2u), ins.offset() });\n        } break;\n\n        case spv::OpFunction:\n          return;\n\n        default:\n          break;\n      }\n    }\n  }\n\n\n  void DxvkSpirvShader::gatherMetadata(\n          SpirvCodeBuffer&          code) {\n    for (auto i = code.begin(); i != code.end(); i++) {\n      auto ins = *i;\n\n      switch (ins.opCode()) {\n        case spv::OpCapability: {\n          switch (spv::Capability(ins.arg(1u))) {\n            case spv::CapabilitySampleRateShading: {\n              m_metadata.flags.set(DxvkShaderFlag::HasSampleRateShading);\n            } break;\n\n            case spv::CapabilityShaderLayer:\n            case spv::CapabilityShaderViewportIndex: {\n              if (m_metadata.stage != VK_SHADER_STAGE_FRAGMENT_BIT\n               && m_metadata.stage != VK_SHADER_STAGE_GEOMETRY_BIT)\n                m_metadata.flags.set(DxvkShaderFlag::ExportsViewportIndexLayerFromVertexStage);\n            } break;\n\n            case spv::CapabilitySparseResidency: {\n              m_metadata.flags.set(DxvkShaderFlag::UsesSparseResidency);\n            } break;\n\n            case spv::CapabilityFragmentFullyCoveredEXT: {\n              m_metadata.flags.set(DxvkShaderFlag::UsesFragmentCoverage);\n            } break;\n\n            default:\n              break;\n          }\n        } break;\n\n        case spv::OpExecutionMode: {\n          switch (spv::ExecutionMode(ins.arg(2u))) {\n            case spv::ExecutionModeStencilRefReplacingEXT: {\n              m_metadata.flags.set(DxvkShaderFlag::ExportsStencilRef);\n            } break;\n\n            case spv::ExecutionModeXfb: {\n              m_metadata.flags.set(DxvkShaderFlag::HasTransformFeedback);\n            } break;\n\n            case spv::ExecutionModePointMode: {\n              m_metadata.flags.set(DxvkShaderFlag::TessellationPoints);\n            } break;\n\n            case spv::ExecutionModeInputPoints: {\n              m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;\n            } break;\n\n            case spv::ExecutionModeInputLines: {\n              m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;\n            } break;\n\n            case spv::ExecutionModeInputLinesAdjacency: {\n              m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;\n            } break;\n\n            case spv::ExecutionModeInputTrianglesAdjacency: {\n              m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;\n            } break;\n\n            case spv::ExecutionModeIsolines: {\n              m_metadata.outputTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;\n            } break;\n\n            case spv::ExecutionModeTriangles: {\n              if (m_metadata.stage == VK_SHADER_STAGE_GEOMETRY_BIT)\n                m_metadata.inputTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n              else if (m_metadata.stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT\n                    || m_metadata.stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)\n                m_metadata.outputTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n            } break;\n\n            case spv::ExecutionModeQuads: {\n              // Tess domain only, this will produce triangles\n              m_metadata.outputTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n            } break;\n\n            case spv::ExecutionModeOutputPoints: {\n              m_metadata.outputTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;\n            } break;\n\n            case spv::ExecutionModeOutputLineStrip: {\n              // For metadata purposes we use list topologies everywhere\n              m_metadata.outputTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;\n            } break;\n\n            case spv::ExecutionModeOutputTriangleStrip: {\n              // For metadata purposes we use list topologies everywhere\n              m_metadata.outputTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n            } break;\n\n            default:\n              break;\n          }\n        } break;\n\n        case spv::OpDecorate: {\n          handleDecoration(ins, ins.arg(1u), -1, 2u);\n        } break;\n\n        case spv::OpMemberDecorate: {\n          handleDecoration(ins, ins.arg(1u), ins.arg(2u), 3u);\n        } break;\n\n        case spv::OpSource: {\n          if (ins.length() > 3u)\n            handleDebugName(code, ins.arg(3u));\n        } break;\n\n        case spv::OpVariable: {\n          auto varId = ins.arg(2u);\n          auto storage = spv::StorageClass(ins.arg(3u));\n\n          SpirvInstruction ptrType(code.data(), m_idToOffset.at(ins.arg(1u)), code.dwords());\n          SpirvInstruction varType(code.data(), m_idToOffset.at(ptrType.arg(3u)), code.dwords());\n\n          switch (storage) {\n            case spv::StorageClassInput:\n            case spv::StorageClassOutput: {\n              // Tess control outputs as well as inputs, tess eval inputs and geometry inputs\n              // must use array types for non-patch constant variables. Unwrap the outer array.\n              if (varType.opCode() == spv::OpTypeArray && !getDecoration(varId, -1).patch) {\n                bool isArrayInput = (storage == spv::StorageClassInput)\n                  && (m_metadata.stage == VK_SHADER_STAGE_GEOMETRY_BIT\n                   || m_metadata.stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT\n                   || m_metadata.stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);\n\n                bool isArrayOutput = (storage == spv::StorageClassOutput) &&\n                  (m_metadata.stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);\n\n                if (isArrayInput || isArrayOutput)\n                  varType = SpirvInstruction(code.data(), m_idToOffset.at(varType.arg(2u)), code.dwords());\n              }\n\n              if (varType.opCode() == spv::OpTypeStruct) {\n                auto structId = varType.arg(1u);\n\n                for (uint32_t i = 2u; i < varType.length(); i++) {\n                  SpirvInstruction memberType(code.data(), m_idToOffset.at(varType.arg(i)), code.dwords());\n                  handleIoVariable(code, memberType, storage, structId, int32_t(i - 2u));\n                }\n              } else {\n                handleIoVariable(code, varType, storage, varId, -1);\n              }\n           } break;\n\n            case spv::StorageClassPushConstant: {\n              m_pushConstantStructId = varType.arg(1u);\n\n              if (!m_info.sharedPushData.isEmpty()) {\n                auto stageMask = (m_metadata.stage & VK_SHADER_STAGE_ALL_GRAPHICS)\n                  ? VK_SHADER_STAGE_ALL_GRAPHICS : VK_SHADER_STAGE_COMPUTE_BIT;\n\n                m_layout.addPushData(DxvkPushDataBlock(stageMask,\n                  m_info.sharedPushData.getOffset(),\n                  m_info.sharedPushData.getSize(),\n                  m_info.sharedPushData.getAlignment(),\n                  m_info.sharedPushData.getResourceDwordMask()));\n              }\n\n              if (!m_info.localPushData.isEmpty()) {\n                m_layout.addPushData(DxvkPushDataBlock(m_metadata.stage,\n                  m_info.localPushData.getOffset(),\n                  m_info.localPushData.getSize(),\n                  m_info.localPushData.getAlignment(),\n                  m_info.localPushData.getResourceDwordMask()));\n              }\n            } break;\n\n            default:\n              break;\n          }\n        } break;\n\n        case spv::OpFunction:\n          return;\n\n        default:\n          break;\n      }\n    }\n  }\n\n\n  void DxvkSpirvShader::handleIoVariable(\n          SpirvCodeBuffer&          code,\n    const SpirvInstruction&         type,\n          spv::StorageClass         storage,\n          uint32_t                  varId,\n          int32_t                   member) {\n    const auto& decoration = getDecoration(varId, member);\n\n    if (storage == spv::StorageClassOutput) {\n      if (m_metadata.stage == VK_SHADER_STAGE_GEOMETRY_BIT) {\n        int32_t stream = 0;\n\n        if (decoration.stream)\n          stream = int32_t(*decoration.stream);\n\n        if (stream != m_metadata.rasterizedStream)\n          return;\n\n        if (decoration.xfbBuffer && decoration.xfbStride)\n          m_metadata.xfbStrides.at(*decoration.xfbBuffer) = *decoration.xfbStride;\n      }\n\n      if (decoration.builtIn == spv::BuiltInPosition)\n        m_metadata.flags.set(DxvkShaderFlag::ExportsPosition);\n\n      if (decoration.builtIn == spv::BuiltInSampleMask)\n        m_metadata.flags.set(DxvkShaderFlag::ExportsSampleMask);\n    }\n\n    DxvkShaderIoVar varInfo = { };\n\n    if (decoration.builtIn)\n      varInfo.builtIn = *decoration.builtIn;\n\n    if (decoration.location)\n      varInfo.location = *decoration.location;\n\n    if (decoration.index)\n      varInfo.location |= (*decoration.index) << 5u;\n\n    if (decoration.component)\n      varInfo.componentIndex = *decoration.component;\n\n    auto componentInfo = getComponentCountForType(code, type, varInfo.builtIn);\n\n    varInfo.componentCount = componentInfo.first;\n    varInfo.isPatchConstant = decoration.patch;\n\n    for (uint32_t i = 0u; i < componentInfo.second; i++) {\n      if (storage == spv::StorageClassOutput)\n        m_metadata.outputs.add(varInfo);\n      else\n        m_metadata.inputs.add(varInfo);\n\n      varInfo.location += 1u;\n    }\n  }\n\n\n  void DxvkSpirvShader::handleDecoration(\n    const SpirvInstruction&         ins,\n          uint32_t                  id,\n          int32_t                   member,\n          uint32_t                  baseArg) {\n    auto range = m_decorations.equal_range(id);\n    auto entry = std::find_if(range.first, range.second,\n      [member] (const std::pair<uint32_t, DxvkSpirvDecorations>& decoration) {\n        return decoration.second.memberIndex == member;\n      });\n\n    if (entry == range.second) {\n      DxvkSpirvDecorations decoration = { };\n      decoration.memberIndex = member;\n\n      entry = m_decorations.insert({ id, decoration });\n    }\n\n    switch (spv::Decoration(ins.arg(baseArg))) {\n      case spv::DecorationLocation: {\n        entry->second.location = ins.arg(baseArg + 1u);\n      } break;\n\n      case spv::DecorationIndex: {\n        entry->second.index = ins.arg(baseArg + 1u);\n      } break;\n\n      case spv::DecorationComponent: {\n        entry->second.component = ins.arg(baseArg + 1u);\n      } break;\n\n      case spv::DecorationDescriptorSet: {\n        entry->second.set = ins.arg(baseArg + 1u);\n      } break;\n\n      case spv::DecorationBinding: {\n        entry->second.binding = ins.arg(baseArg + 1u);\n      } break;\n\n      case spv::DecorationOffset: {\n        entry->second.offset = ins.arg(baseArg + 1u);\n      } break;\n\n      case spv::DecorationArrayStride: {\n        entry->second.stride = ins.arg(baseArg + 1u);\n      } break;\n\n      case spv::DecorationBuiltIn: {\n        entry->second.builtIn = spv::BuiltIn(ins.arg(baseArg + 1u));\n      } break;\n\n      case spv::DecorationSpecId: {\n        uint32_t specId = ins.arg(baseArg + 1u);\n        m_metadata.specConstantMask |= 1u << specId;\n      } break;\n\n      case spv::DecorationPatch: {\n        entry->second.patch = true;\n      } break;\n\n      case spv::DecorationStream: {\n        entry->second.stream = ins.arg(baseArg + 1u);\n      } break;\n\n      case spv::DecorationXfbBuffer: {\n        entry->second.xfbBuffer = ins.arg(baseArg + 1u);\n      } break;\n\n      case spv::DecorationXfbStride: {\n        entry->second.xfbStride = ins.arg(baseArg + 1u);\n      } break;\n\n      default:\n        break;\n    }\n  }\n\n\n  void DxvkSpirvShader::handleDebugName(\n          SpirvCodeBuffer&          code,\n          uint32_t                  stringId) {\n    auto e = m_idToOffset.find(stringId);\n\n    if (e == m_idToOffset.end())\n      return;\n\n    SpirvInstruction str(code.data(), e->second, code.dwords());\n\n    for (uint32_t i = 2u; i < str.length(); i++) {\n      uint32_t arg = str.arg(i);\n\n      for (uint32_t j = 0u; j < 4u; j++) {\n        auto ch = char(arg >> (8u * j));\n\n        if (!ch)\n          return;\n\n        m_debugName.push_back(ch);\n      }\n    }\n  }\n\n\n  const DxvkSpirvDecorations& DxvkSpirvShader::getDecoration(\n          uint32_t                  id,\n          int32_t                   member) const {\n    static const DxvkSpirvDecorations s_defaultDecorations = { };\n\n    auto range = m_decorations.equal_range(id);\n    auto entry = std::find_if(range.first, range.second,\n      [member] (const std::pair<uint32_t, DxvkSpirvDecorations>& decoration) {\n        return decoration.second.memberIndex == member;\n      });\n\n    if (entry == range.second)\n      return s_defaultDecorations;\n\n    return entry->second;\n  }\n\n\n  std::pair<uint32_t, uint32_t> DxvkSpirvShader::getComponentCountForType(\n          SpirvCodeBuffer&          code,\n    const SpirvInstruction&         type,\n          spv::BuiltIn              builtIn) const {\n    switch (type.opCode()) {\n      case spv::OpTypeVoid:\n      case spv::OpTypeBool:\n      case spv::OpTypeInt:\n      case spv::OpTypeFloat:\n        return std::make_pair(1u, 1u);\n\n      case spv::OpTypeVector:\n        return std::make_pair(type.arg(3u), 1u);\n\n      case spv::OpTypeArray: {\n        auto nested = SpirvInstruction(code.data(), m_idToOffset.at(type.arg(2u)), code.dwords());\n        auto length = SpirvInstruction(code.data(), m_idToOffset.at(type.arg(3u)), code.dwords());\n\n        if (builtIn == spv::BuiltInClipDistance\n         || builtIn == spv::BuiltInCullDistance\n         || builtIn == spv::BuiltInTessLevelInner\n         || builtIn == spv::BuiltInTessLevelOuter\n         || builtIn == spv::BuiltInSampleMask) {\n          // Treat built-in arrays as a single 'location' of sorts\n          return std::make_pair(length.arg(3u), 1u);\n        }\n\n        return std::make_pair(getComponentCountForType(code, nested, builtIn).first, length.arg(3u));\n      }\n\n      default:\n        throw DxvkError(str::format(\"Unexpected I/O type: \", type.opCode()));\n    }\n  }\n\n\n  void DxvkSpirvShader::patchResourceBindingsAndIoLocations(\n          SpirvCodeBuffer&            code,\n    const DxvkShaderBindingMap*       bindings,\n    const DxvkShaderLinkage*          linkage) const {\n    for (auto i = code.begin(); i != code.end(); i++) {\n      auto ins = *i;\n\n      switch (ins.opCode()) {\n        case spv::OpDecorate: {\n          auto objectId = ins.arg(1u);\n          auto decorationType = spv::Decoration(ins.arg(2u));\n          const auto& decoration = getDecoration(objectId, -1);\n\n          switch (decorationType) {\n            case spv::DecorationDescriptorSet:\n            case spv::DecorationBinding: {\n              uint32_t set = 0u;\n              uint32_t binding = 0u;\n\n              if (decoration.set)\n                set = *decoration.set;\n\n              if (decoration.binding)\n                binding = *decoration.binding;\n\n              auto mappedBinding = bindings->mapBinding(\n                DxvkShaderBinding(m_metadata.stage, set, binding));\n\n              if (mappedBinding) {\n                if (decorationType == spv::DecorationDescriptorSet)\n                  ins.setArg(3u, mappedBinding->getSet());\n\n                if (decorationType == spv::DecorationBinding)\n                  ins.setArg(3u, mappedBinding->getBinding());\n              }\n            } break;\n\n            case spv::DecorationLocation:\n            case spv::DecorationIndex: {\n              if (!linkage || !linkage->fsDualSrcBlend)\n                break;\n\n              // Ensure that what we're patching is actually an output variable\n              SpirvInstruction var(code.data(), m_idToOffset.at(ins.arg(1u)), code.dwords());\n\n              if (spv::StorageClass(var.arg(3u)) != spv::StorageClassOutput)\n                break;\n\n              // Set location to 0 or index to 1 for location 1\n              if (decoration.location == 1u)\n                ins.setArg(3u, decorationType == spv::DecorationIndex ? 1u : 0u);\n            } break;\n\n            default:\n              break;\n          }\n        } break;\n\n        case spv::OpMemberDecorate: {\n          auto objectId = ins.arg(1u);\n          auto decorationType = spv::Decoration(ins.arg(3u));\n\n          switch (decorationType) {\n            case spv::DecorationOffset: {\n              if (objectId != m_pushConstantStructId)\n                break;\n\n              uint32_t offset = bindings->mapPushData(m_metadata.stage, ins.arg(4u));\n\n              if (offset < MaxTotalPushDataSize)\n                ins.setArg(4u, offset);\n            } break;\n\n            default:\n              break;\n          }\n        } break;\n\n        case spv::OpFunction:\n          return;\n\n        default:\n          break;\n      }\n    }\n  }\n\n\n  VkShaderStageFlagBits DxvkSpirvShader::getShaderStage(\n          SpirvCodeBuffer&          code) {\n    for (auto i = code.begin(); i != code.end(); i++) {\n      auto ins = *i;\n\n      if (ins.opCode() == spv::OpEntryPoint) {\n        auto model = spv::ExecutionModel(ins.arg(1u));\n\n        switch (model) {\n          case spv::ExecutionModelVertex:\n            return VK_SHADER_STAGE_VERTEX_BIT;\n          case spv::ExecutionModelTessellationControl:\n            return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;\n          case spv::ExecutionModelTessellationEvaluation:\n            return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;\n          case spv::ExecutionModelGeometry:\n            return VK_SHADER_STAGE_GEOMETRY_BIT;\n          case spv::ExecutionModelFragment:\n            return VK_SHADER_STAGE_FRAGMENT_BIT;\n          case spv::ExecutionModelGLCompute:\n            return VK_SHADER_STAGE_COMPUTE_BIT;\n          default:\n            throw DxvkError(str::format(\"Invalid execution model: \", model));\n        }\n      }\n    }\n\n    throw DxvkError(\"No OpEntryPoint found in SPIR-V shader\");\n  }\n\n\n  void DxvkSpirvShader::eliminateInput(\n          SpirvCodeBuffer&          code,\n          uint32_t                  location) {\n    struct SpirvTypeInfo {\n      spv::Op           op            = spv::OpNop;\n      uint32_t          baseTypeId    = 0;\n      uint32_t          compositeSize = 0;\n      spv::StorageClass storageClass  = spv::StorageClassMax;\n    };\n\n    uint32_t spirvVersion = code.data()[1];\n\n    std::unordered_map<uint32_t, SpirvTypeInfo> types;\n    std::unordered_map<uint32_t, uint32_t>      constants;\n    std::unordered_set<uint32_t>                candidates;\n\n    // Find the input variable in question\n    size_t   inputVarOffset = 0;\n    uint32_t inputVarTypeId = 0;\n    uint32_t inputVarId     = 0;\n\n    for (auto ins : code) {\n      if (ins.opCode() == spv::OpDecorate) {\n        if (ins.arg(2) == spv::DecorationLocation\n         && ins.arg(3) == location)\n          candidates.insert(ins.arg(1));\n      }\n\n      if (ins.opCode() == spv::OpConstant)\n        constants.insert({ ins.arg(2), ins.arg(3) });\n\n      if (ins.opCode() == spv::OpTypeFloat || ins.opCode() == spv::OpTypeInt)\n        types.insert({ ins.arg(1), { ins.opCode(), 0, ins.arg(2), spv::StorageClassMax }});\n\n      if (ins.opCode() == spv::OpTypeVector)\n        types.insert({ ins.arg(1), { ins.opCode(), ins.arg(2), ins.arg(3), spv::StorageClassMax }});\n\n      if (ins.opCode() == spv::OpTypeArray) {\n        auto constant = constants.find(ins.arg(3));\n        if (constant == constants.end())\n          continue;\n        types.insert({ ins.arg(1), { ins.opCode(), ins.arg(2), constant->second, spv::StorageClassMax }});\n      }\n\n      if (ins.opCode() == spv::OpTypePointer)\n        types.insert({ ins.arg(1), { ins.opCode(), ins.arg(3), 0, spv::StorageClass(ins.arg(2)) }});\n\n      if (ins.opCode() == spv::OpVariable && spv::StorageClass(ins.arg(3)) == spv::StorageClassInput) {\n        if (candidates.find(ins.arg(2)) != candidates.end()) {\n          inputVarOffset = ins.offset();\n          inputVarTypeId = ins.arg(1);\n          inputVarId     = ins.arg(2);\n          break;\n        }\n      }\n\n      if (ins.opCode() == spv::OpFunction)\n        break;\n    }\n\n    if (!inputVarId)\n      return;\n\n    // Declare private pointer types\n    auto pointerType = types.find(inputVarTypeId);\n    if (pointerType == types.end())\n      return;\n\n    code.beginInsertion(inputVarOffset);\n    std::vector<std::pair<uint32_t, SpirvTypeInfo>> privateTypes;\n\n    for (auto p  = types.find(pointerType->second.baseTypeId);\n              p != types.end();\n              p  = types.find(p->second.baseTypeId)) {\n      std::pair<uint32_t, SpirvTypeInfo> info = *p;\n      info.first = 0;\n      info.second.baseTypeId = p->first;\n      info.second.storageClass = spv::StorageClassPrivate;\n\n      for (auto t : types) {\n        if (t.second.op           == info.second.op\n         && t.second.baseTypeId   == info.second.baseTypeId\n         && t.second.storageClass == info.second.storageClass)\n          info.first = t.first;\n      }\n\n      if (!info.first) {\n        info.first = code.allocId();\n\n        code.putIns(spv::OpTypePointer, 4);\n        code.putWord(info.first);\n        code.putWord(info.second.storageClass);\n        code.putWord(info.second.baseTypeId);\n      }\n\n      privateTypes.push_back(info);\n    }\n\n    // Define zero constants\n    uint32_t constantId = 0;\n\n    for (auto i = privateTypes.rbegin(); i != privateTypes.rend(); i++) {\n      if (constantId) {\n        uint32_t compositeSize = i->second.compositeSize;\n        uint32_t compositeId   = code.allocId();\n\n        code.putIns(spv::OpConstantComposite, 3 + compositeSize);\n        code.putWord(i->second.baseTypeId);\n        code.putWord(compositeId);\n\n        for (uint32_t i = 0; i < compositeSize; i++)\n          code.putWord(constantId);\n\n        constantId = compositeId;\n      } else {\n        constantId = code.allocId();\n\n        code.putIns(spv::OpConstant, 4);\n        code.putWord(i->second.baseTypeId);\n        code.putWord(constantId);\n        code.putWord(0);\n      }\n    }\n\n    // Erase and re-declare variable\n    code.erase(4);\n\n    code.putIns(spv::OpVariable, 5);\n    code.putWord(privateTypes[0].first);\n    code.putWord(inputVarId);\n    code.putWord(spv::StorageClassPrivate);\n    code.putWord(constantId);\n\n    code.endInsertion();\n\n    // Remove variable from interface list\n    if (spirvVersion < spvVersion(1, 4)) {\n      for (auto ins : code) {\n        if (ins.opCode() == spv::OpEntryPoint) {\n          uint32_t argIdx = 2 + code.strLen(ins.chr(2));\n\n          while (argIdx < ins.length()) {\n            if (ins.arg(argIdx) == inputVarId) {\n              ins.setArg(0, spv::OpEntryPoint | ((ins.length() - 1) << spv::WordCountShift));\n\n              code.beginInsertion(ins.offset() + argIdx);\n              code.erase(1);\n              code.endInsertion();\n              break;\n            }\n\n            argIdx += 1;\n          }\n\n          break;\n        }\n      }\n    }\n\n    // Remove location and other declarations\n    for (auto iter = code.begin(); iter != code.end(); ) {\n      auto ins = *(iter++);\n\n      if (ins.opCode() == spv::OpDecorate && ins.arg(1) == inputVarId) {\n        uint32_t numWords;\n\n        switch (ins.arg(2)) {\n          case spv::DecorationLocation:\n          case spv::DecorationFlat:\n          case spv::DecorationNoPerspective:\n          case spv::DecorationCentroid:\n          case spv::DecorationPatch:\n          case spv::DecorationSample:\n            numWords = ins.length();\n            break;\n\n          default:\n            numWords = 0;\n        }\n\n        if (numWords) {\n          code.beginInsertion(ins.offset());\n          code.erase(numWords);\n\n          iter = SpirvInstructionIterator(code.data(), code.endInsertion(), code.dwords());\n        }\n      }\n\n      if (ins.opCode() == spv::OpFunction)\n        break;\n    }\n\n    // Fix up pointer types used in access chain instructions\n    std::unordered_map<uint32_t, uint32_t> accessChainIds;\n\n    for (auto ins : code) {\n      if (ins.opCode() == spv::OpAccessChain\n       || ins.opCode() == spv::OpInBoundsAccessChain) {\n        uint32_t depth = ins.length() - 4;\n\n        if (ins.arg(3) == inputVarId) {\n          // Access chains accessing the variable directly\n          ins.setArg(1, privateTypes.at(depth).first);\n          accessChainIds.insert({ ins.arg(2), depth });\n        } else {\n          // Access chains derived from the variable\n          auto entry = accessChainIds.find(ins.arg(2));\n          if (entry != accessChainIds.end()) {\n            depth += entry->second;\n            ins.setArg(1, privateTypes.at(depth).first);\n            accessChainIds.insert({ ins.arg(2), depth });\n          }\n        }\n      }\n    }\n  }\n\n\n  void DxvkSpirvShader::emitOutputSwizzles(\n          SpirvCodeBuffer&          code,\n          uint32_t                  outputMask,\n          const VkComponentMapping* swizzles) {\n    // Skip this step entirely if all relevant\n    // outputs use the identity swizzle\n    bool requiresEpilogue = false;\n\n    for (auto index : bit::BitMask(outputMask))\n      requiresEpilogue |= !util::isIdentityMapping(swizzles[index]);\n\n    if (!requiresEpilogue)\n      return;\n\n    // Gather some information. We need to scan pointer types with\n    // the output storage class to find the base vector type, and\n    // we need to scan vector types to find the component count.\n    uint32_t entryPointId = 0;\n    uint32_t functionId = 0;\n\n    size_t epilogueOffset = 0;\n    size_t variableOffset = 0;\n\n    struct VarInfo {\n      uint32_t varId;\n      uint32_t typeId;\n      uint32_t location;\n      uint32_t componentCount;\n      uint32_t componentTypeId;\n    };\n\n    struct VarIdInfo {\n      uint32_t location;\n    };\n\n    struct TypeIdInfo {\n      uint32_t componentCount;\n      uint32_t baseTypeId;\n    };\n\n    union IdInfo {\n      VarIdInfo     var;\n      TypeIdInfo    type;\n    };\n\n    // Stores type information depending on type category:\n    // OpTypePointer:   type id -> base type id\n    // OpTypeVector:    type id -> component count\n    // OpTypeFloat/Int: type id -> 1\n    std::unordered_map<uint32_t, IdInfo> idInfo;\n    std::vector<VarInfo> varInfos;\n\n    SpirvInstruction prev;\n\n    for (auto ins : code) {\n      switch (ins.opCode()) {\n        case spv::OpEntryPoint: {\n          entryPointId = ins.arg(2);\n        } break;\n\n        case spv::OpDecorate: {\n          if (ins.arg(2) == spv::DecorationLocation) {\n            IdInfo info;\n            info.var.location = ins.arg(3);\n            idInfo.insert({ ins.arg(1), info });\n          }\n        } break;\n\n        case spv::OpTypeVector: {\n          IdInfo info;\n          info.type.componentCount = ins.arg(3);\n          info.type.baseTypeId = ins.arg(2);\n          idInfo.insert({ ins.arg(1), info });\n        } break;\n\n        case spv::OpTypeInt:\n        case spv::OpTypeFloat: {\n          IdInfo info;\n          info.type.componentCount = 1;\n          info.type.baseTypeId = 0;\n          idInfo.insert({ ins.arg(1), info });\n        } break;\n\n        case spv::OpTypePointer: {\n          if (ins.arg(2) == spv::StorageClassOutput) {\n            IdInfo info;\n            info.type.componentCount = 0;\n            info.type.baseTypeId = ins.arg(3);\n            idInfo.insert({ ins.arg(1), info });\n          }\n        } break;\n\n        case spv::OpVariable: {\n          if (!variableOffset)\n            variableOffset = ins.offset();\n\n          if (ins.arg(3) == spv::StorageClassOutput) {\n            uint32_t ptrId = ins.arg(1);\n            uint32_t varId = ins.arg(2);\n\n            auto ptrEntry = idInfo.find(ptrId);\n            auto varEntry = idInfo.find(varId);\n\n            if (ptrEntry != idInfo.end()\n             && varEntry != idInfo.end()) {\n              uint32_t typeId = ptrEntry->second.type.baseTypeId;\n\n              auto typeEntry = idInfo.find(typeId);\n              if (typeEntry != idInfo.end()) {\n                VarInfo info;\n                info.varId = varId;\n                info.typeId = typeId;\n                info.location = varEntry->second.var.location;\n                info.componentCount = typeEntry->second.type.componentCount;\n                info.componentTypeId = (info.componentCount == 1)\n                  ? typeId : typeEntry->second.type.baseTypeId;\n\n                varInfos.push_back(info);\n              }\n            }\n          }\n        } break;\n\n        case spv::OpFunction: {\n          functionId = ins.arg(2);\n        } break;\n\n        case spv::OpFunctionEnd: {\n          if (entryPointId == functionId)\n            epilogueOffset = prev.offset();\n        } break;\n\n        default:\n          prev = ins;\n      }\n\n      if (epilogueOffset)\n        break;\n    }\n\n    // Oops, this shouldn't happen\n    if (!epilogueOffset)\n      return;\n\n    code.beginInsertion(epilogueOffset);\n\n    struct ConstInfo {\n      uint32_t constId;\n      uint32_t typeId;\n      uint32_t value;\n    };\n\n    std::vector<ConstInfo> consts;\n\n    for (const auto& var : varInfos) {\n      uint32_t storeId = 0;\n\n      if (var.componentCount == 1) {\n        if (util::getComponentIndex(swizzles[var.location].r, 0) != 0) {\n          storeId = code.allocId();\n\n          ConstInfo constInfo;\n          constInfo.constId = storeId;\n          constInfo.typeId = var.componentTypeId;\n          constInfo.value = 0;\n          consts.push_back(constInfo);\n        }\n      } else {\n        uint32_t constId = 0;\n\n        std::array<uint32_t, 4> indices = {{\n          util::getComponentIndex(swizzles[var.location].r, 0),\n          util::getComponentIndex(swizzles[var.location].g, 1),\n          util::getComponentIndex(swizzles[var.location].b, 2),\n          util::getComponentIndex(swizzles[var.location].a, 3),\n        }};\n\n        bool needsSwizzle = false;\n\n        for (uint32_t i = 0; i < var.componentCount && !constId; i++) {\n          needsSwizzle |= indices[i] != i;\n\n          if (indices[i] >= var.componentCount)\n            constId = code.allocId();\n        }\n\n        if (needsSwizzle) {\n          uint32_t loadId = code.allocId();\n          code.putIns(spv::OpLoad, 4);\n          code.putWord(var.typeId);\n          code.putWord(loadId);\n          code.putWord(var.varId);\n\n          if (!constId) {\n            storeId = code.allocId();\n            code.putIns(spv::OpVectorShuffle, 5 + var.componentCount);\n            code.putWord(var.typeId);\n            code.putWord(storeId);\n            code.putWord(loadId);\n            code.putWord(loadId);\n\n            for (uint32_t i = 0; i < var.componentCount; i++)\n              code.putWord(indices[i]);\n          } else {\n            std::array<uint32_t, 4> ids = { };\n\n            ConstInfo constInfo;\n            constInfo.constId = constId;\n            constInfo.typeId = var.componentTypeId;\n            constInfo.value = 0;\n            consts.push_back(constInfo);\n\n            for (uint32_t i = 0; i < var.componentCount; i++) {\n              if (indices[i] < var.componentCount) {\n                ids[i] = code.allocId();\n\n                code.putIns(spv::OpCompositeExtract, 5);\n                code.putWord(var.componentTypeId);\n                code.putWord(ids[i]);\n                code.putWord(loadId);\n                code.putWord(indices[i]);\n              } else {\n                ids[i] = constId;\n              }\n            }\n\n            storeId = code.allocId();\n            code.putIns(spv::OpCompositeConstruct, 3 + var.componentCount);\n            code.putWord(var.typeId);\n            code.putWord(storeId);\n\n            for (uint32_t i = 0; i < var.componentCount; i++)\n              code.putWord(ids[i]);\n          }\n        }\n      }\n\n      if (storeId) {\n        code.putIns(spv::OpStore, 3);\n        code.putWord(var.varId);\n        code.putWord(storeId);\n      }\n    }\n\n    code.endInsertion();\n\n    // If necessary, insert constants\n    if (!consts.empty()) {\n      code.beginInsertion(variableOffset);\n\n      for (const auto& c : consts) {\n        code.putIns(spv::OpConstant, 4);\n        code.putWord(c.typeId);\n        code.putWord(c.constId);\n        code.putWord(c.value);\n      }\n\n      code.endInsertion();\n    }\n  }\n\n\n  void DxvkSpirvShader::emitFlatShadingDeclarations(\n          SpirvCodeBuffer&          code,\n          uint32_t                  inputMask) {\n    if (!inputMask)\n      return;\n\n    struct VarInfo {\n      uint32_t varId;\n      size_t decorationOffset;\n    };\n\n    std::unordered_set<uint32_t> candidates;\n    std::unordered_map<uint32_t, size_t> decorations;\n    std::vector<VarInfo> flatVars;\n\n    size_t decorateOffset = 0;\n\n    for (auto ins : code) {\n      switch (ins.opCode()) {\n        case spv::OpDecorate: {\n          decorateOffset = ins.offset() + ins.length();\n          uint32_t varId = ins.arg(1);\n\n          switch (ins.arg(2)) {\n            case spv::DecorationLocation: {\n              uint32_t location = ins.arg(3);\n\n              if (inputMask & (1u << location))\n                candidates.insert(varId);\n            } break;\n\n            case spv::DecorationFlat:\n            case spv::DecorationCentroid:\n            case spv::DecorationSample:\n            case spv::DecorationNoPerspective: {\n              decorations.insert({ varId, ins.offset() + 2 });\n            } break;\n\n            default: ;\n          }\n        } break;\n\n        case spv::OpVariable: {\n          if (ins.arg(3) == spv::StorageClassInput) {\n            uint32_t varId = ins.arg(2);\n\n            // Only consider variables that have a desired location\n            if (candidates.find(varId) != candidates.end()) {\n              VarInfo varInfo;\n              varInfo.varId = varId;\n              varInfo.decorationOffset = 0;\n\n              auto decoration = decorations.find(varId);\n              if (decoration != decorations.end())\n                varInfo.decorationOffset = decoration->second;\n\n              flatVars.push_back(varInfo);\n            }\n          }\n        } break;\n\n        default:\n          break;\n      }\n    }\n\n    // Change existing decorations as necessary\n    for (const auto& var : flatVars) {\n      if (var.decorationOffset) {\n        uint32_t* rawCode = code.data();\n        rawCode[var.decorationOffset] = spv::DecorationFlat;\n      }\n    }\n\n    // Insert new decorations for remaining variables\n    code.beginInsertion(decorateOffset);\n\n    for (const auto& var : flatVars) {\n      if (!var.decorationOffset) {\n        code.putIns(spv::OpDecorate, 3);\n        code.putWord(var.varId);\n        code.putWord(spv::DecorationFlat);\n      }\n    }\n\n    code.endInsertion();\n  }\n\n\n  void DxvkSpirvShader::patchInputTopology(\n          SpirvCodeBuffer&          code,\n          VkPrimitiveTopology       topology) {\n    struct TopologyInfo {\n      VkPrimitiveTopology topology;\n      spv::ExecutionMode  mode;\n      uint32_t            vertexCount;\n    };\n\n    static const std::array<TopologyInfo, 5> s_topologies = {{\n      { VK_PRIMITIVE_TOPOLOGY_POINT_LIST,                   spv::ExecutionModeInputPoints,              1u },\n      { VK_PRIMITIVE_TOPOLOGY_LINE_LIST,                    spv::ExecutionModeInputLines,               2u },\n      { VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,     spv::ExecutionModeInputLinesAdjacency,      4u },\n      { VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,                spv::ExecutionModeTriangles,                3u },\n      { VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, spv::ExecutionModeInputTrianglesAdjacency,  6u },\n    }};\n\n    const TopologyInfo* topologyInfo = nullptr;\n\n    for (const auto& top : s_topologies) {\n      if (top.topology == topology) {\n        topologyInfo = &top;\n        break;\n      }\n    }\n\n    if (!topologyInfo)\n      return;\n\n    uint32_t typeUint32Id = 0u;\n    uint32_t typeSint32Id = 0u;\n\n    struct ConstantInfo {\n      uint32_t typeId;\n      uint32_t value;\n    };\n\n    struct ArrayTypeInfo {\n      uint32_t arrayLengthId;\n      uint32_t scalarTypeId;\n      uint32_t replaceTypeId;\n    };\n\n    struct PointerTypeInfo {\n      uint32_t objectTypeId;\n    };\n\n    std::unordered_map<uint32_t, uint32_t> nullConstantsByType;\n    std::unordered_map<uint32_t, ConstantInfo> constants;\n    std::unordered_map<uint32_t, uint32_t> uintConstantValueToId;\n    std::unordered_map<uint32_t, ArrayTypeInfo> arrayTypes;\n    std::unordered_map<uint32_t, PointerTypeInfo> pointerTypes;\n    std::unordered_map<uint32_t, uint32_t> variableTypes;\n    std::unordered_set<uint32_t> nullAccessChains;\n    std::unordered_map<uint32_t, uint32_t> nullVarsByType;\n    std::vector<std::pair<uint32_t, uint32_t>> newNullVars;\n\n    uint32_t functionOffset = 0u;\n\n    for (auto iter = code.begin(); iter != code.end(); ) {\n      auto ins = *iter;\n\n      switch (ins.opCode()) {\n        case spv::OpExecutionMode: {\n          bool isTopology = false;\n\n          for (const auto& top : s_topologies)\n            isTopology |= spv::ExecutionMode(ins.arg(2)) == top.mode;\n\n          if (isTopology)\n            ins.setArg(2, uint32_t(topologyInfo->mode));\n        } break;\n\n        case spv::OpConstant: {\n          if (ins.arg(1) == typeUint32Id || ins.arg(1) == typeSint32Id) {\n            ConstantInfo c = { };\n            c.typeId = ins.arg(1);\n            c.value = ins.arg(3);\n\n            constants.insert({ ins.arg(2), c });\n            uintConstantValueToId.insert({ ins.arg(3), ins.arg(2) });\n          }\n        } break;\n\n        case spv::OpConstantNull: {\n          nullConstantsByType.insert({ ins.arg(1), ins.arg(2) });\n        } break;\n\n        case spv::OpTypeInt: {\n          if (ins.arg(2u) == 32u) {\n            if (ins.arg(3u))\n              typeSint32Id = ins.arg(1u);\n            else\n              typeUint32Id = ins.arg(1u);\n          }\n        } break;\n\n        case spv::OpTypeArray: {\n          ArrayTypeInfo t = { };\n          t.arrayLengthId = ins.arg(3);\n          t.scalarTypeId = ins.arg(2);\n          t.replaceTypeId = 0u;\n\n          arrayTypes.insert({ ins.arg(1), t });\n        } break;\n\n        case spv::OpTypePointer: {\n          // We know that all input arrays use the vertex count as their outer\n          // array size, so it is safe for us to simply replace the array type\n          // of any pointer type declaration with an appropriately sized array.\n          auto storageClass = spv::StorageClass(ins.arg(2));\n\n          if (storageClass == spv::StorageClassInput) {\n            uint32_t len = ins.length();\n\n            uint32_t arrayTypeId = 0u;\n            uint32_t scalarTypeId = 0u;\n\n            PointerTypeInfo t = { };\n            t.objectTypeId = ins.arg(3);\n\n            auto entry = arrayTypes.find(t.objectTypeId);\n\n            if (entry != arrayTypes.end()) {\n              if (!entry->second.replaceTypeId) {\n                arrayTypeId = code.allocId();\n                scalarTypeId = entry->second.scalarTypeId;\n\n                entry->second.replaceTypeId = arrayTypeId;\n              }\n\n              t.objectTypeId = entry->second.replaceTypeId;\n              ins.setArg(3, t.objectTypeId);\n            }\n\n            pointerTypes.insert({ ins.arg(1), t });\n\n            // If we replaced the array type, emit it before the pointer type\n            // decoration as necessary. It is legal to delcare identical array\n            // types multiple times.\n            if (arrayTypeId) {\n              code.beginInsertion(ins.offset());\n\n              auto lengthId = uintConstantValueToId.find(topologyInfo->vertexCount);\n\n              if (lengthId == uintConstantValueToId.end()) {\n                if (!typeUint32Id) {\n                  typeUint32Id = code.allocId();\n\n                  code.putIns  (spv::OpTypeInt, 4);\n                  code.putWord (typeUint32Id);\n                  code.putWord (32);\n                  code.putWord (0);\n                }\n\n                ConstantInfo c;\n                c.typeId = typeUint32Id;\n                c.value = topologyInfo->vertexCount;\n\n                uint32_t arrayLengthId = code.allocId();\n\n                code.putIns  (spv::OpConstant, 4);\n                code.putWord (c.typeId);\n                code.putWord (arrayLengthId);\n                code.putWord (c.value);\n\n                lengthId = uintConstantValueToId.insert({ c.value, arrayLengthId }).first;\n                constants.insert({ arrayLengthId, c });\n              }\n\n              ArrayTypeInfo t = { };\n              t.scalarTypeId = scalarTypeId;\n              t.arrayLengthId = lengthId->second;\n\n              arrayTypes.insert({ arrayTypeId, t });\n\n              code.putIns   (spv::OpTypeArray, 4);\n              code.putWord  (arrayTypeId);\n              code.putWord  (t.scalarTypeId);\n              code.putWord  (t.arrayLengthId);\n\n              iter = SpirvInstructionIterator(code.data(), code.endInsertion() + len, code.dwords());\n              continue;\n            }\n          }\n        } break;\n\n        case spv::OpVariable: {\n          auto storageClass = spv::StorageClass(ins.arg(3));\n\n          if (storageClass == spv::StorageClassInput)\n            variableTypes.insert({ ins.arg(2), ins.arg(1) });\n        } break;\n\n        case spv::OpFunction: {\n          if (!functionOffset)\n            functionOffset = ins.offset();\n        } break;\n\n        case spv::OpAccessChain:\n        case spv::OpInBoundsAccessChain: {\n          bool nullChain = false;\n          auto var = variableTypes.find(ins.arg(3));\n\n          if (var == variableTypes.end()) {\n            // If we're recursively loading from a null access chain, skip\n            auto chain = nullAccessChains.find(ins.arg(3));\n            nullChain = chain != nullAccessChains.end();\n          } else {\n            // If the index is out of bounds, mark the access chain as\n            // dead so we can replace all loads with a null constant.\n            auto c = constants.find(ins.arg(4u));\n\n            if (c == constants.end())\n              break;\n\n            nullChain = c->second.value >= topologyInfo->vertexCount;\n          }\n\n          if (nullChain) {\n            nullAccessChains.insert(ins.arg(2));\n\n            code.beginInsertion(ins.offset());\n            code.erase(ins.length());\n\n            iter = SpirvInstructionIterator(code.data(), code.endInsertion(), code.dwords());\n            continue;\n          }\n        } break;\n\n        case spv::OpLoad: {\n          // If we're loading from a null access chain, replace with null constant load.\n          // We should never load the entire array at once, so ignore that case.\n          if (nullAccessChains.find(ins.arg(3)) != nullAccessChains.end()) {\n            auto var = nullVarsByType.find(ins.arg(1));\n\n            if (var == nullVarsByType.end()) {\n              var = nullVarsByType.insert({ ins.arg(1), code.allocId() }).first;\n              newNullVars.push_back(std::make_pair(var->second, ins.arg(1)));\n            }\n\n            ins.setArg(3, var->second);\n          }\n        } break;\n\n        default:;\n      }\n\n      iter++;\n    }\n\n    // Insert new null variables\n    code.beginInsertion(functionOffset);\n\n    for (auto v : newNullVars) {\n      auto nullConst = nullConstantsByType.find(v.second);\n\n      if (nullConst == nullConstantsByType.end()) {\n        uint32_t nullConstId = code.allocId();\n\n        code.putIns   (spv::OpConstantNull, 3u);\n        code.putWord  (v.second);\n        code.putWord  (nullConstId);\n\n        nullConst = nullConstantsByType.insert({ v.second, nullConstId }).first;\n      }\n\n      uint32_t pointerTypeId = code.allocId();\n\n      code.putIns   (spv::OpTypePointer, 4u);\n      code.putWord  (pointerTypeId);\n      code.putWord  (spv::StorageClassPrivate);\n      code.putWord  (v.second);\n\n      code.putIns   (spv::OpVariable, 5u);\n      code.putWord  (pointerTypeId);\n      code.putWord  (v.first);\n      code.putWord  (spv::StorageClassPrivate);\n      code.putWord  (nullConst->second);\n    }\n\n    code.endInsertion();\n\n    // Add newly declared null variables to entry point\n    for (auto ins : code) {\n      if (ins.opCode() == spv::OpEntryPoint) {\n        uint32_t len = ins.length();\n        uint32_t token = ins.opCode() | ((len + newNullVars.size()) << 16);\n        ins.setArg(0, token);\n\n        code.beginInsertion(ins.offset() + len);\n\n        for (auto v : newNullVars)\n          code.putWord(v.first);\n\n        code.endInsertion();\n        break;\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_shader_spirv.h",
    "content": "#pragma once\n\n#include <optional>\n\n#include \"dxvk_shader.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief SPIR-V shader create info\n   */\n  struct DxvkSpirvShaderCreateInfo {\n    /// Descriptor info\n    uint32_t bindingCount = 0;\n    const DxvkBindingInfo* bindings = nullptr;\n    /// Flat shading input mask\n    uint32_t flatShadingInputs = 0;\n    /// Push data blocks\n    DxvkPushDataBlock sharedPushData;\n    DxvkPushDataBlock localPushData;\n    /// Descriptor set and binding of global sampler heap\n    DxvkShaderBinding samplerHeap;\n    /// Rasterized stream, or -1\n    int32_t xfbRasterizedStream = 0;\n    /// Tess control patch vertex count\n    uint32_t patchVertexCount = 0;\n    /// Manually assigned debug name\n    std::string debugName;\n  };\n\n\n  /**\n   * \\brief Decorations for a SPIR-V ID\n   */\n  struct DxvkSpirvDecorations {\n    int32_t memberIndex = -1;\n    std::optional<uint32_t> location;\n    std::optional<uint32_t> index;\n    std::optional<uint32_t> component;\n    std::optional<uint32_t> set;\n    std::optional<uint32_t> binding;\n    std::optional<uint32_t> offset;\n    std::optional<uint32_t> stride;\n    std::optional<uint32_t> stream;\n    std::optional<uint32_t> xfbBuffer;\n    std::optional<uint32_t> xfbStride;\n    bool patch = false;\n    std::optional<spv::BuiltIn> builtIn;\n  };\n\n\n  /**\n   * \\brief SPIR-V shader\n   */\n  class DxvkSpirvShader : public DxvkShader {\n\n  public:\n\n    DxvkSpirvShader(\n      const DxvkSpirvShaderCreateInfo&  info,\n            SpirvCodeBuffer&&           spirv);\n\n    template<size_t N>\n    DxvkSpirvShader(\n      const DxvkSpirvShaderCreateInfo&  info,\n      const uint32_t(&code)[N])\n    : DxvkSpirvShader(info, SpirvCodeBuffer(N, code)) { }\n\n    ~DxvkSpirvShader();\n\n    /**\n     * \\brief Queries shader metadata\n     * \\returns Shader metadata\n     */\n    DxvkShaderMetadata getShaderMetadata();\n\n    /**\n     * \\brief Called when the shader itself needs to be compiled\n     */\n    void compile();\n\n    /**\n     * \\brief Patches code using given info\n     *\n     * Rewrites binding IDs and potentially fixes up other\n     * parts of the code depending on pipeline state.\n     * \\param [in] bindings Biding map\n     * \\param [in] state Pipeline state info\n     * \\returns Uncompressed SPIR-V code buffer\n     */\n    SpirvCodeBuffer getCode(\n      const DxvkShaderBindingMap*       bindings,\n      const DxvkShaderLinkage*          linkage);\n\n    /**\n     * \\brief Queries shader binding layout\n     * \\returns Pipeline layout builder\n     */\n    DxvkPipelineLayoutBuilder getLayout();\n\n    /**\n     * \\brief Dumps SPIR-V binary to a stream\n     * \\param [in] outputStream Stream to write to\n     */\n    void dump(std::ostream& outputStream);\n\n    /**\n     * \\brief Retrieves debug name for this shader\n     * \\returns Shader debug name\n     */\n    std::string debugName();\n\n  private:\n\n    DxvkSpirvShaderCreateInfo     m_info  = { };\n\n    SpirvCompressedBuffer         m_code;\n    DxvkPipelineLayoutBuilder     m_layout;\n\n    std::string                   m_debugName;\n    uint32_t                      m_pushConstantStructId = 0u;\n\n    DxvkShaderMetadata            m_metadata = { };\n\n    std::unordered_multimap<uint32_t, DxvkSpirvDecorations> m_decorations = { };\n    std::unordered_map<uint32_t, uint32_t> m_idToOffset = { };\n\n    void gatherIdOffsets(\n            SpirvCodeBuffer&          code);\n\n    void gatherMetadata(\n            SpirvCodeBuffer&          code);\n\n    void handleIoVariable(\n            SpirvCodeBuffer&          code,\n      const SpirvInstruction&         type,\n            spv::StorageClass         storage,\n            uint32_t                  varId,\n            int32_t                   member);\n\n    void handleDecoration(\n      const SpirvInstruction&         ins,\n            uint32_t                  id,\n            int32_t                   member,\n            uint32_t                  baseArg);\n\n    void handleDebugName(\n            SpirvCodeBuffer&          code,\n            uint32_t                  stringId);\n\n    const DxvkSpirvDecorations& getDecoration(\n            uint32_t                  id,\n            int32_t                   member) const;\n\n    std::pair<uint32_t, uint32_t> getComponentCountForType(\n            SpirvCodeBuffer&          code,\n      const SpirvInstruction&         type,\n            spv::BuiltIn              builtIn) const;\n\n    void patchResourceBindingsAndIoLocations(\n            SpirvCodeBuffer&          code,\n      const DxvkShaderBindingMap*     bindings,\n      const DxvkShaderLinkage*        linkage) const;\n\n    static VkShaderStageFlagBits getShaderStage(\n            SpirvCodeBuffer&          code);\n\n    static void eliminateInput(\n            SpirvCodeBuffer&          code,\n            uint32_t                  location);\n\n    static void emitOutputSwizzles(\n            SpirvCodeBuffer&          code,\n            uint32_t                  outputMask,\n      const VkComponentMapping*       swizzles);\n\n    static void emitFlatShadingDeclarations(\n            SpirvCodeBuffer&          code,\n            uint32_t                  inputMask);\n\n    static void patchInputTopology(\n            SpirvCodeBuffer&          code,\n            VkPrimitiveTopology       topology);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_signal.cpp",
    "content": "#include \"dxvk_signal.h\"\n\nnamespace dxvk {\n  \n  DxvkSignalTracker::DxvkSignalTracker() {\n\n  }\n\n\n  DxvkSignalTracker::~DxvkSignalTracker() {\n\n  }\n    \n  \n  void DxvkSignalTracker::add(const Rc<sync::Signal>& signal, uint64_t value) {\n    m_signals.push_back({ signal, value });\n  }\n\n\n  void DxvkSignalTracker::notify() {\n    for (const auto& pair : m_signals)\n      pair.first->signal(pair.second);\n  }\n\n\n  void DxvkSignalTracker::reset() {\n    m_signals.clear();\n  }\n  \n}"
  },
  {
    "path": "src/dxvk/dxvk_signal.h",
    "content": "#pragma once\n\n#include \"dxvk_include.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Signal tracker\n   */\n  class DxvkSignalTracker {\n    \n  public:\n    \n    DxvkSignalTracker();\n    ~DxvkSignalTracker();\n    \n    /**\n     * \\brief Adds a signal to track\n     *\n     * \\param [in] signal The signal\n     * \\param [in] value Target value\n     */\n    void add(const Rc<sync::Signal>& signal, uint64_t value);\n    \n    /**\n     * \\brief Notifies tracked signals\n     */\n    void notify();\n    \n    /**\n     * \\brief Resets signal tracker\n     */\n    void reset();\n    \n  private:\n    \n    std::vector<std::pair<Rc<sync::Signal>, uint64_t>> m_signals;\n    \n  };\n  \n}"
  },
  {
    "path": "src/dxvk/dxvk_sparse.cpp",
    "content": "#include <algorithm>\n#include <sstream>\n\n#include \"dxvk_buffer.h\"\n#include \"dxvk_device.h\"\n#include \"dxvk_image.h\"\n#include \"dxvk_sparse.h\"\n\nnamespace dxvk {\n\n  std::atomic<uint64_t> DxvkPagedResource::s_cookie = { 0u };\n\n\n  DxvkPagedResource::~DxvkPagedResource() {\n\n  }\n\n\n  void DxvkPagedResource::makeResourceResident() {\n    m_allocator->requestMakeResident(this);\n  }\n\n\n  DxvkResourceRef::~DxvkResourceRef() {\n    auto resource = reinterpret_cast<DxvkPagedResource*>(m_ptr & ~AccessMask);\n    auto access = DxvkAccess(m_ptr & AccessMask);\n\n    if (access != DxvkAccess::Move)\n      resource->requestResidency();\n\n    resource->release(access);\n  }\n\n\n  DxvkSparseMapping::DxvkSparseMapping()\n  : m_pool(nullptr),\n    m_page(nullptr) {\n\n  }\n\n\n  DxvkSparseMapping::DxvkSparseMapping(\n          Rc<DxvkSparsePageAllocator> allocator,\n          Rc<DxvkResourceAllocation>  page)\n  : m_pool(std::move(allocator)),\n    m_page(std::move(page)) {\n\n  }\n\n\n  DxvkSparseMapping::DxvkSparseMapping(\n          DxvkSparseMapping&&         other)\n  : m_pool(std::move(other.m_pool)),\n    m_page(std::move(other.m_page)) {\n    // No need to acquire here. The only place from which\n    // this constructor can be called does this atomically.\n  }\n\n\n  DxvkSparseMapping::DxvkSparseMapping(\n    const DxvkSparseMapping&          other)\n  : m_pool(other.m_pool),\n    m_page(other.m_page) {\n    this->acquire();\n  }\n\n\n  DxvkSparseMapping& DxvkSparseMapping::operator = (\n          DxvkSparseMapping&&         other) {\n    this->release();\n\n    m_pool = std::move(other.m_pool);\n    m_page = std::move(other.m_page);\n    return *this;\n  }\n\n\n  DxvkSparseMapping& DxvkSparseMapping::operator = (\n    const DxvkSparseMapping&          other) {\n    other.acquire();\n    this->release();\n\n    m_pool = other.m_pool;\n    m_page = other.m_page;\n    return *this;\n  }\n\n\n  DxvkSparseMapping::~DxvkSparseMapping() {\n    this->release();\n  }\n\n\n  void DxvkSparseMapping::acquire() const {\n    if (m_page != nullptr)\n      m_pool->acquirePage(m_page);\n  }\n\n\n  void DxvkSparseMapping::release() const {\n    if (m_page != nullptr)\n      m_pool->releasePage(m_page);\n  }\n\n\n  DxvkSparsePageAllocator::DxvkSparsePageAllocator(\n          DxvkMemoryAllocator&  memoryAllocator)\n  : m_memory(&memoryAllocator) {\n\n  }\n\n\n  DxvkSparsePageAllocator::~DxvkSparsePageAllocator() {\n\n  }\n\n\n  DxvkSparseMapping DxvkSparsePageAllocator::acquirePage(\n          uint32_t              page) {\n    std::lock_guard lock(m_mutex);\n\n    if (unlikely(page >= m_pageCount))\n      return DxvkSparseMapping();\n\n    m_useCount += 1;\n    return DxvkSparseMapping(this, m_pages[page]);\n  }\n\n\n  void DxvkSparsePageAllocator::setCapacity(\n          uint32_t              pageCount) {\n    std::lock_guard lock(m_mutex);\n\n    if (pageCount < m_pageCount) {\n      if (!m_useCount)\n        m_pages.resize(pageCount);\n    } else if (pageCount > m_pageCount) {\n      std::vector<Rc<DxvkResourceAllocation>> newPages;\n      newPages.reserve(pageCount - m_pageCount);\n\n      for (size_t i = 0; i < pageCount - m_pageCount; i++)\n        newPages.push_back(m_memory->createSparsePage());\n\n      // Sort page by memory and offset to enable more\n      // batching opportunities during page table updates\n      std::sort(newPages.begin(), newPages.end(),\n        [] (const Rc<DxvkResourceAllocation>& a, const Rc<DxvkResourceAllocation>& b) {\n          auto aHandle = a->getMemoryInfo();\n          auto bHandle = b->getMemoryInfo();\n\n          // Ignore length here, the offsets cannot be the same anyway.\n          if (aHandle.memory < bHandle.memory) return true;\n          if (aHandle.memory > bHandle.memory) return false;\n\n          return aHandle.offset < bHandle.offset;\n        });\n\n      for (auto& page : newPages)\n        m_pages.push_back(std::move(page));\n    }\n\n    m_pageCount = pageCount;\n  }\n\n\n  void DxvkSparsePageAllocator::acquirePage(\n    const Rc<DxvkResourceAllocation>& page) {\n    std::lock_guard lock(m_mutex);\n    m_useCount += 1;\n  }\n\n\n  void DxvkSparsePageAllocator::releasePage(\n    const Rc<DxvkResourceAllocation>& page) {\n    std::lock_guard lock(m_mutex);\n    m_useCount -= 1;\n\n    if (!m_useCount)\n      m_pages.resize(m_pageCount);\n  }\n\n\n  DxvkSparsePageTable::DxvkSparsePageTable() {\n\n  }\n\n\n  DxvkSparsePageTable::DxvkSparsePageTable(\n          DxvkDevice*             device,\n    const VkBufferCreateInfo&     bufferInfo,\n          VkBuffer                bufferHandle)\n  : m_buffer(bufferHandle) {\n    VkDeviceSize bufferSize = bufferInfo.size;\n\n    // For linear buffers, the mapping is very simple\n    // and consists of consecutive 64k pages\n    size_t pageCount = align(bufferSize, SparseMemoryPageSize) / SparseMemoryPageSize;\n    m_metadata.resize(pageCount);\n    m_mappings.resize(pageCount);\n\n    for (size_t i = 0; i < pageCount; i++) {\n      VkDeviceSize pageOffset = SparseMemoryPageSize * i;\n      m_metadata[i].type = DxvkSparsePageType::Buffer;\n      m_metadata[i].buffer.offset = pageOffset;\n      m_metadata[i].buffer.length = std::min(SparseMemoryPageSize, bufferSize - pageOffset);\n    }\n\n    // Initialize properties and subresource info so that we can\n    // easily query this without having to know the resource type\n    m_subresources.resize(1);\n    m_subresources[0].pageCount = { uint32_t(pageCount), 1u, 1u };\n    m_subresources[0].pageIndex = 0;\n\n    m_properties.pageRegionExtent = { uint32_t(SparseMemoryPageSize), 1u, 1u };\n  }\n\n\n  DxvkSparsePageTable::DxvkSparsePageTable(\n          DxvkDevice*             device,\n    const VkImageCreateInfo&      imageInfo,\n          VkImage                 imageHandle)\n  : m_image(imageHandle) {\n    auto vk = device->vkd();\n\n    // Query sparse memory requirements\n    uint32_t reqCount = 0;\n    vk->vkGetImageSparseMemoryRequirements(vk->device(), imageHandle, &reqCount, nullptr);\n\n    std::vector<VkSparseImageMemoryRequirements> req(reqCount);\n    vk->vkGetImageSparseMemoryRequirements(vk->device(), imageHandle, &reqCount, req.data());\n\n    // Find first non-metadata struct and use it to fill in the image properties\n    bool foundMainAspect = false;\n\n    for (const auto& r : req) {\n      if (r.formatProperties.aspectMask & VK_IMAGE_ASPECT_METADATA_BIT) {\n        VkDeviceSize metadataSize = r.imageMipTailSize;\n\n        if (!(r.formatProperties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT))\n          metadataSize *= imageInfo.arrayLayers;\n\n        m_properties.metadataPageCount += uint32_t(metadataSize / SparseMemoryPageSize);\n      } else if (!foundMainAspect) {\n        m_properties.flags = r.formatProperties.flags;\n        m_properties.pageRegionExtent = r.formatProperties.imageGranularity;\n\n        if (r.imageMipTailFirstLod < imageInfo.mipLevels && r.imageMipTailSize) {\n          m_properties.pagedMipCount = r.imageMipTailFirstLod;\n          m_properties.mipTailOffset = r.imageMipTailOffset;\n          m_properties.mipTailSize = r.imageMipTailSize;\n          m_properties.mipTailStride = r.imageMipTailStride;\n        } else {\n          m_properties.pagedMipCount = imageInfo.mipLevels;\n        }\n\n        foundMainAspect = true;\n      } else {\n        Logger::err(str::format(\"Found multiple aspects for sparse image:\"\n          \"\\n  Type:            \", imageInfo.imageType,\n          \"\\n  Format:          \", imageInfo.format,\n          \"\\n  Flags:           \", imageInfo.flags,\n          \"\\n  Extent:          \", \"(\", imageInfo.extent.width,\n                                  \",\", imageInfo.extent.height,\n                                  \",\", imageInfo.extent.depth, \")\",\n          \"\\n  Mip levels:      \", imageInfo.mipLevels,\n          \"\\n  Array layers:    \", imageInfo.arrayLayers,\n          \"\\n  Samples:         \", imageInfo.samples,\n          \"\\n  Usage:           \", imageInfo.usage,\n          \"\\n  Tiling:          \", imageInfo.tiling));\n      }\n    }\n\n    // Fill in subresource metadata and compute page count\n    uint32_t totalPageCount = 0;\n    uint32_t subresourceCount = imageInfo.arrayLayers * imageInfo.mipLevels;\n    m_subresources.reserve(subresourceCount);\n\n    for (uint32_t l = 0; l < imageInfo.arrayLayers; l++) {\n      for (uint32_t m = 0; m < imageInfo.mipLevels; m++) {\n        if (m < m_properties.pagedMipCount) {\n          // Compute block count for current mip based on image properties\n          VkExtent3D mipExtent = util::computeMipLevelExtent(imageInfo.extent, m);\n\n          DxvkSparseImageSubresourceProperties subresourceInfo;\n          subresourceInfo.isMipTail = VK_FALSE;\n          subresourceInfo.pageCount = util::computeBlockCount(mipExtent, m_properties.pageRegionExtent);\n\n          // Advance total page count by number of pages in the subresource\n          subresourceInfo.pageIndex = totalPageCount;\n          totalPageCount += util::flattenImageExtent(subresourceInfo.pageCount);\n\n          m_subresources.push_back(subresourceInfo);\n        } else {\n          DxvkSparseImageSubresourceProperties subresourceInfo = { };\n          subresourceInfo.isMipTail = VK_TRUE;\n          subresourceInfo.pageCount = { 0u, 0u, 0u };\n          subresourceInfo.pageIndex = 0u;\n          m_subresources.push_back(subresourceInfo);\n        }\n      }\n    }\n\n    if (m_properties.mipTailSize) {\n      m_properties.mipTailPageIndex = totalPageCount;\n\n      // We may need multiple mip tails for the image\n      uint32_t mipTailPageCount = m_properties.mipTailSize / SparseMemoryPageSize;\n\n      if (!(m_properties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT))\n        mipTailPageCount *= imageInfo.arrayLayers;\n\n      totalPageCount += mipTailPageCount;\n    }\n\n    // Fill in page metadata\n    m_metadata.reserve(totalPageCount);\n    m_mappings.resize(totalPageCount);\n\n    const DxvkFormatInfo* formatInfo = lookupFormatInfo(imageInfo.format);\n\n    for (uint32_t l = 0; l < imageInfo.arrayLayers; l++) {\n      for (uint32_t m = 0; m < m_properties.pagedMipCount; m++) {\n        VkExtent3D mipExtent = util::computeMipLevelExtent(imageInfo.extent, m);\n        VkExtent3D pageCount = util::computeBlockCount(mipExtent, m_properties.pageRegionExtent);\n\n        for (uint32_t z = 0; z < pageCount.depth; z++) {\n          for (uint32_t y = 0; y < pageCount.height; y++) {\n            for (uint32_t x = 0; x < pageCount.width; x++) {\n              DxvkSparsePageInfo pageInfo;\n              pageInfo.type = DxvkSparsePageType::Image;\n              pageInfo.image.subresource.aspectMask = formatInfo->aspectMask;\n              pageInfo.image.subresource.mipLevel = m;\n              pageInfo.image.subresource.arrayLayer = l;\n              pageInfo.image.offset.x = x * m_properties.pageRegionExtent.width;\n              pageInfo.image.offset.y = y * m_properties.pageRegionExtent.height;\n              pageInfo.image.offset.z = z * m_properties.pageRegionExtent.depth;\n              pageInfo.image.extent.width  = std::min(m_properties.pageRegionExtent.width,  mipExtent.width  - pageInfo.image.offset.x);\n              pageInfo.image.extent.height = std::min(m_properties.pageRegionExtent.height, mipExtent.height - pageInfo.image.offset.y);\n              pageInfo.image.extent.depth  = std::min(m_properties.pageRegionExtent.depth,  mipExtent.depth  - pageInfo.image.offset.z);\n              m_metadata.push_back(pageInfo);\n            }\n          }\n        }\n      }\n    }\n\n    if (m_properties.mipTailSize) {\n      uint32_t pageCount = m_properties.mipTailSize / SparseMemoryPageSize;\n      uint32_t layerCount = 1;\n\n      if (!(m_properties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT))\n        layerCount = imageInfo.arrayLayers;\n\n      for (uint32_t i = 0; i < layerCount; i++) {\n        for (uint32_t j = 0; j < pageCount; j++) {\n          DxvkSparsePageInfo pageInfo;\n          pageInfo.type = DxvkSparsePageType::ImageMipTail;\n          pageInfo.mipTail.resourceOffset = m_properties.mipTailOffset\n            + i * m_properties.mipTailStride\n            + j * SparseMemoryPageSize;\n          pageInfo.mipTail.resourceLength = SparseMemoryPageSize;\n          m_metadata.push_back(pageInfo);\n        }\n      }\n    }\n  }\n\n\n  VkBuffer DxvkSparsePageTable::getBufferHandle() const {\n    return m_buffer;\n  }\n\n\n  VkImage DxvkSparsePageTable::getImageHandle() const {\n    return m_image;\n  }\n\n\n  uint32_t DxvkSparsePageTable::computePageIndex(\n          uint32_t                subresource,\n          VkOffset3D              regionOffset,\n          VkExtent3D              regionExtent,\n          VkBool32                regionIsLinear,\n          uint32_t                pageIndex) const {\n    auto subresourceInfo = getSubresourceProperties(subresource);\n\n    // The mip tail is always linear\n    if (subresourceInfo.isMipTail)\n      return m_properties.mipTailPageIndex + pageIndex;\n\n    // Compute offset into the given subresource\n    VkOffset3D pageOffset = regionOffset;\n\n    if (!regionIsLinear) {\n      pageOffset.x += (pageIndex % regionExtent.width);\n      pageOffset.y += (pageIndex / regionExtent.width) % regionExtent.height;\n      pageOffset.z += (pageIndex / regionExtent.width) / regionExtent.height;\n      pageIndex = 0;\n    }\n\n    uint32_t result = subresourceInfo.pageIndex + pageOffset.x\n      + subresourceInfo.pageCount.width * (pageOffset.y\n      + subresourceInfo.pageCount.height * pageOffset.z);\n\n    return result + pageIndex;\n  }\n\n\n  DxvkSparseMapping DxvkSparsePageTable::getMapping(\n          uint32_t                page) {\n    return page < m_mappings.size()\n      ? m_mappings[page]\n      : DxvkSparseMapping();\n  }\n\n\n  void DxvkSparsePageTable::updateMapping(\n          DxvkCommandList*        cmd,\n          uint32_t                page,\n          DxvkSparseMapping&&     mapping) {\n    if (m_mappings[page] != mapping) {\n      if (m_mappings[page])\n        cmd->track(m_mappings[page].m_page);\n\n      m_mappings[page] = std::move(mapping);\n    }\n  }\n\n\n  DxvkSparseBindSubmission::DxvkSparseBindSubmission() {\n\n  }\n\n\n  DxvkSparseBindSubmission::~DxvkSparseBindSubmission() {\n\n  }\n\n\n  void DxvkSparseBindSubmission::waitSemaphore(\n          VkSemaphore             semaphore,\n          uint64_t                value) {\n    m_waitSemaphores.push_back(semaphore);\n    m_waitSemaphoreValues.push_back(value);\n  }\n\n\n  void DxvkSparseBindSubmission::signalSemaphore(\n          VkSemaphore             semaphore,\n          uint64_t                value) {\n    m_signalSemaphores.push_back(semaphore);\n    m_signalSemaphoreValues.push_back(value);\n  }\n\n\n  void DxvkSparseBindSubmission::bindBufferMemory(\n    const DxvkSparseBufferBindKey& key,\n    const DxvkResourceMemoryInfo& memory) {\n    m_bufferBinds.insert_or_assign(key, memory);\n  }\n\n\n  void DxvkSparseBindSubmission::bindImageMemory(\n    const DxvkSparseImageBindKey& key,\n    const DxvkResourceMemoryInfo& memory) {\n    m_imageBinds.insert_or_assign(key, memory);\n  }\n\n\n  void DxvkSparseBindSubmission::bindImageOpaqueMemory(\n    const DxvkSparseImageOpaqueBindKey& key,\n    const DxvkResourceMemoryInfo& memory) {\n    m_imageOpaqueBinds.insert_or_assign(key, memory);\n  }\n\n\n  VkResult DxvkSparseBindSubmission::submit(\n          DxvkDevice*             device,\n          VkQueue                 queue) {\n    auto vk = device->vkd();\n\n    DxvkSparseBufferBindArrays buffer;\n    this->processBufferBinds(buffer);\n\n    DxvkSparseImageBindArrays image;\n    this->processImageBinds(image);\n\n    DxvkSparseImageOpaqueBindArrays opaque;\n    this->processOpaqueBinds(opaque);\n\n    // The sparse binding API has never been updated to take the new\n    // semaphore submission info structs, so we have to do this instead\n    VkTimelineSemaphoreSubmitInfo timelineInfo = { VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO };\n    timelineInfo.waitSemaphoreValueCount = m_waitSemaphoreValues.size();\n    timelineInfo.pWaitSemaphoreValues = m_waitSemaphoreValues.data();\n    timelineInfo.signalSemaphoreValueCount = m_signalSemaphoreValues.size();\n    timelineInfo.pSignalSemaphoreValues = m_signalSemaphoreValues.data();\n\n    VkBindSparseInfo bindInfo = { VK_STRUCTURE_TYPE_BIND_SPARSE_INFO };\n\n    if (!m_waitSemaphores.empty()) {\n      bindInfo.pNext = &timelineInfo;\n      bindInfo.waitSemaphoreCount = m_waitSemaphores.size();\n      bindInfo.pWaitSemaphores = m_waitSemaphores.data();\n    }\n\n    if (!buffer.infos.empty()) {\n      bindInfo.bufferBindCount = buffer.infos.size();\n      bindInfo.pBufferBinds = buffer.infos.data();\n    }\n\n    if (!opaque.infos.empty()) {\n      bindInfo.imageOpaqueBindCount = opaque.infos.size();\n      bindInfo.pImageOpaqueBinds = opaque.infos.data();\n    }\n\n    if (!image.infos.empty()) {\n      bindInfo.imageBindCount = image.infos.size();\n      bindInfo.pImageBinds = image.infos.data();\n    }\n\n    if (!m_signalSemaphores.empty()) {\n      bindInfo.pNext = &timelineInfo;\n      bindInfo.signalSemaphoreCount = m_signalSemaphores.size();\n      bindInfo.pSignalSemaphores = m_signalSemaphores.data();\n    }\n\n    VkResult vr = vk->vkQueueBindSparse(queue, 1, &bindInfo, VK_NULL_HANDLE);\n\n    if (vr) {\n      Logger::err(str::format(\"Sparse binding failed: \", vr));\n      this->logSparseBindingInfo(LogLevel::Error, &bindInfo);\n    }\n\n    this->reset();\n    return vr;\n  }\n\n\n  void DxvkSparseBindSubmission::reset() {\n    m_waitSemaphoreValues.clear();\n    m_waitSemaphores.clear();\n    m_signalSemaphoreValues.clear();\n    m_signalSemaphores.clear();\n\n    m_bufferBinds.clear();\n    m_imageBinds.clear();\n    m_imageOpaqueBinds.clear();\n  }\n\n\n  bool DxvkSparseBindSubmission::tryMergeMemoryBind(\n          VkSparseMemoryBind&               oldBind,\n    const VkSparseMemoryBind&               newBind) {\n    if (newBind.memory != oldBind.memory || newBind.flags != oldBind.flags)\n      return false;\n\n    // The resource region must be consistent\n    if (newBind.resourceOffset != oldBind.resourceOffset + oldBind.size)\n      return false;\n\n    // If memory is not null, the memory range must also be consistent\n    if (newBind.memory && newBind.memoryOffset != oldBind.memoryOffset + oldBind.size)\n      return false;\n\n    oldBind.size += newBind.size;\n    return true;\n  }\n\n\n  bool DxvkSparseBindSubmission::tryMergeImageBind(\n          std::pair<DxvkSparseImageBindKey, DxvkResourceMemoryInfo>& oldBind,\n    const std::pair<DxvkSparseImageBindKey, DxvkResourceMemoryInfo>& newBind) {\n    if (oldBind.first.image != newBind.first.image\n     || oldBind.first.subresource.aspectMask != newBind.first.subresource.aspectMask\n     || oldBind.first.subresource.mipLevel != newBind.first.subresource.mipLevel\n     || oldBind.first.subresource.arrayLayer != newBind.first.subresource.arrayLayer)\n      return false;\n\n    if (oldBind.second.memory != newBind.second.memory)\n      return false;\n\n    if (oldBind.second.memory) {\n      if (oldBind.second.offset + oldBind.second.size != newBind.second.offset)\n        return false;\n    }\n\n    bool canMerge = false;\n\n    VkOffset3D oldOffset = oldBind.first.offset;\n    VkExtent3D oldExtent = oldBind.first.extent;\n    VkOffset3D newOffset = newBind.first.offset;\n    VkExtent3D newExtent = newBind.first.extent;\n    VkExtent3D delta = { };\n\n    if (uint32_t(oldOffset.x + oldExtent.width) == uint32_t(newOffset.x)) {\n      canMerge = oldOffset.y == newOffset.y && oldExtent.height == newExtent.height\n              && oldOffset.z == newOffset.z && oldExtent.depth == newExtent.depth;\n      delta.width = newExtent.width;\n    } else if (uint32_t(oldOffset.y + oldExtent.height) == uint32_t(newOffset.y)) {\n      canMerge = oldOffset.x == newOffset.x && oldExtent.width == newExtent.width\n              && oldOffset.z == newOffset.z && oldExtent.depth == newExtent.depth;\n      delta.height = newExtent.height;\n    } else if (uint32_t(oldOffset.z + oldExtent.depth) == uint32_t(newOffset.z)) {\n      canMerge = oldOffset.x == newOffset.x && oldExtent.width == newExtent.width\n              && oldOffset.y == newOffset.y && oldExtent.height == newExtent.height;\n      delta.depth = newExtent.depth;\n    }\n\n    if (canMerge) {\n      oldBind.first.extent.width  += delta.width;\n      oldBind.first.extent.height += delta.height;\n      oldBind.first.extent.depth  += delta.depth;\n\n      if (oldBind.second.memory)\n        oldBind.second.size += newBind.second.size;\n    }\n\n    return canMerge;\n  }\n\n\n  void DxvkSparseBindSubmission::processBufferBinds(\n          DxvkSparseBufferBindArrays&       buffer) {\n    std::vector<std::pair<VkBuffer, VkSparseMemoryBind>> ranges;\n    ranges.reserve(m_bufferBinds.size());\n\n    for (const auto& e : m_bufferBinds) {\n      const auto& key = e.first;\n      const auto& handle = e.second;\n\n      VkSparseMemoryBind bind = { };\n      bind.resourceOffset = key.offset;\n      bind.size           = key.size;\n      bind.memory         = handle.memory;\n      bind.memoryOffset   = handle.offset;\n\n      bool merged = false;\n\n      if (!ranges.empty() && ranges.back().first == key.buffer)\n        merged = tryMergeMemoryBind(ranges.back().second, bind);\n\n      if (!merged)\n        ranges.push_back({ key.buffer, bind });\n    }\n\n    populateOutputArrays(buffer.binds, buffer.infos, ranges);\n  }\n\n\n  void DxvkSparseBindSubmission::processImageBinds(\n          DxvkSparseImageBindArrays&        image) {\n    std::vector<std::pair<DxvkSparseImageBindKey, DxvkResourceMemoryInfo>> binds;\n    binds.reserve(m_imageBinds.size());\n\n    for (const auto& e : m_imageBinds) {\n      std::pair<DxvkSparseImageBindKey, DxvkResourceMemoryInfo> newBind = e;\n\n      while (!binds.empty()) {\n        std::pair<DxvkSparseImageBindKey, DxvkResourceMemoryInfo> oldBind = binds.back();\n\n        if (!tryMergeImageBind(oldBind, newBind))\n          break;\n\n        newBind = oldBind;\n        binds.pop_back();\n      }\n\n      binds.push_back(newBind);\n    }\n\n    std::vector<std::pair<VkImage, VkSparseImageMemoryBind>> ranges;\n    ranges.reserve(m_imageBinds.size());\n\n    for (const auto& e : binds) {\n      const auto& key = e.first;\n      const auto& handle = e.second;\n\n      VkSparseImageMemoryBind bind = { };\n      bind.subresource    = key.subresource;\n      bind.offset         = key.offset;\n      bind.extent         = key.extent;\n      bind.memory         = handle.memory;\n      bind.memoryOffset   = handle.offset;\n\n      ranges.push_back({ key.image, bind });\n    }\n\n    populateOutputArrays(image.binds, image.infos, ranges);\n  }\n\n\n  void DxvkSparseBindSubmission::processOpaqueBinds(\n          DxvkSparseImageOpaqueBindArrays&  opaque) {\n    std::vector<std::pair<VkImage, VkSparseMemoryBind>> ranges;\n    ranges.reserve(m_imageOpaqueBinds.size());\n\n    for (const auto& e : m_imageOpaqueBinds) {\n      const auto& key = e.first;\n      const auto& handle = e.second;\n\n      VkSparseMemoryBind bind = { };\n      bind.resourceOffset = key.offset;\n      bind.size           = key.size;\n      bind.memory         = handle.memory;\n      bind.memoryOffset   = handle.offset;\n      bind.flags          = key.flags;\n\n      bool merged = false;\n\n      if (!ranges.empty() && ranges.back().first == key.image)\n        merged = tryMergeMemoryBind(ranges.back().second, bind);\n\n      if (!merged)\n        ranges.push_back({ key.image, bind });\n    }\n\n    populateOutputArrays(opaque.binds, opaque.infos, ranges);\n  }\n\n\n  template<typename HandleType, typename BindType, typename InfoType>\n  void DxvkSparseBindSubmission::populateOutputArrays(\n          std::vector<BindType>&            binds,\n          std::vector<InfoType>&            infos,\n    const std::vector<std::pair<HandleType, BindType>>& input) {\n    HandleType handle = VK_NULL_HANDLE;\n\n    // Resize bind array so that pointers remain\n    // valid as we iterate over the input array\n    binds.resize(input.size());\n\n    for (size_t i = 0; i < input.size(); i++) {\n      binds[i] = input[i].second;\n\n      if (handle != input[i].first) {\n        // Create new info entry if the handle\n        // differs from that of the previous entry\n        handle = input[i].first;\n        infos.push_back({ handle, 1u, &binds[i] });\n      } else {\n        // Otherwise just increment the bind count\n        infos.back().bindCount += 1;\n      }\n    }\n  }\n\n\n  void DxvkSparseBindSubmission::logSparseBindingInfo(\n          LogLevel                          level,\n    const VkBindSparseInfo*                 info) {\n    std::stringstream str;\n    str << \"VkBindSparseInfo:\" << std::endl;\n\n    auto timelineInfo = static_cast<const VkTimelineSemaphoreSubmitInfo*>(info->pNext);\n\n    if (info->waitSemaphoreCount) {\n      str << \"  Wait semaphores (\" << std::dec << info->waitSemaphoreCount << \"):\" << std::endl;\n\n      for (uint32_t i = 0; i < info->waitSemaphoreCount; i++)\n        str << \"    \" << info->pWaitSemaphores[i] << \" (\" << timelineInfo->pWaitSemaphoreValues[i] << \")\" << std::endl;\n    }\n\n    if (info->bufferBindCount) {\n      str << \"  Buffer binds (\" << std::dec << info->bufferBindCount << \"):\" << std::endl;\n\n      for (uint32_t i = 0; i < info->bufferBindCount; i++) {\n        const auto* bindInfo = &info->pBufferBinds[i];\n        str << \"    VkBuffer \" << bindInfo->buffer << \" (\" << bindInfo->bindCount << \"):\" << std::endl;\n\n        for (uint32_t j = 0; j < bindInfo->bindCount; j++) {\n          const auto* bind = &bindInfo->pBinds[j];\n          str << \"        \" << bind->resourceOffset << \" -> \" << bind->memory\n              << \" (\" << bind->memoryOffset << \",\" << bind->size << \")\" << std::endl;\n        }\n      }\n    }\n\n    if (info->imageOpaqueBindCount) {\n      str << \"  Opaque image binds (\" << std::dec << info->imageOpaqueBindCount << \"):\" << std::endl;\n\n      for (uint32_t i = 0; i < info->imageOpaqueBindCount; i++) {\n        const auto* bindInfo = &info->pImageOpaqueBinds[i];\n        str << \"    VkImage \" << bindInfo->image << \" (\" << bindInfo->bindCount << \"):\" << std::endl;\n\n        for (uint32_t j = 0; j < bindInfo->bindCount; j++) {\n          const auto* bind = &bindInfo->pBinds[j];\n          str << \"        \" << bind->resourceOffset << \" -> \" << bind->memory\n              << \" (\" << bind->memoryOffset << \",\" << bind->size << \")\" << std::endl;\n        }\n      }\n    }\n\n    if (info->imageBindCount) {\n      str << \"  Opaque image binds (\" << std::dec << info->imageOpaqueBindCount << \"):\" << std::endl;\n\n      for (uint32_t i = 0; i < info->imageBindCount; i++) {\n        const auto* bindInfo = &info->pImageBinds[i];\n        str << \"    VkImage \" << bindInfo->image << \" (\" << bindInfo->bindCount << \"):\" << std::endl;\n\n        for (uint32_t j = 0; j < bindInfo->bindCount; j++) {\n          const auto* bind = &bindInfo->pBinds[j];\n\n          str << \"        Aspect 0x\" << std::hex << bind->subresource.aspectMask\n              << \", Mip \" << std::dec << bind->subresource.mipLevel\n              << \", Layer \" << bind->subresource.arrayLayer\n              << \":\" << std::endl;\n\n          str << \"        \" << bind->offset.x << \",\" << bind->offset.y << \",\" << bind->offset.z << \":\"\n              << bind->extent.width << \"x\" << bind->extent.height << \"x\" << bind->extent.depth\n              << \" -> \" << bind->memory << \" (\" << bind->memoryOffset << \")\" << std::endl;\n        }\n      }\n    }\n\n    if (info->signalSemaphoreCount) {\n      str << \"  Signal semaphores (\" << std::dec << info->signalSemaphoreCount << \"):\" << std::endl;\n\n      for (uint32_t i = 0; i < info->signalSemaphoreCount; i++)\n        str << \"    \" << info->pSignalSemaphores[i] << \" (\" << timelineInfo->pSignalSemaphoreValues[i] << \")\" << std::endl;\n    }\n\n    Logger::log(level, str.str());\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_sparse.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <map>\n\n#include \"dxvk_access.h\"\n#include \"dxvk_memory.h\"\n\nnamespace dxvk {\n\n  class DxvkCommandList;\n  class DxvkDevice;\n  class DxvkBuffer;\n  class DxvkImage;\n  class DxvkPagedResource;\n  class DxvkSparsePage;\n  class DxvkSparsePageAllocator;\n  class DxvkSparsePageTable;\n\n  constexpr static VkDeviceSize SparseMemoryPageSize = 1ull << 16;\n\n  /**\n   * \\brief Buffer info for sparse page\n   *\n   * Stores the buffer region backed by\n   * any given page.\n   */\n  struct DxvkSparsePageBufferInfo {\n    VkDeviceSize              offset;\n    VkDeviceSize              length;\n  };\n\n\n  /**\n   * \\brief Image info for sparse page\n   *\n   * Stores the image region backed by\n   * any given page.\n   */\n  struct DxvkSparsePageImageInfo {\n    VkImageSubresource        subresource;\n    VkOffset3D                offset;\n    VkExtent3D                extent;\n  };\n\n\n  /**\n   * \\brief Image mip tail info for sparse page\n   *\n   * Stores the virtual resource offset and size\n   * within the mip tail backed by any given page.\n   */\n  struct DxvkSparsePageMipTailInfo {\n    VkDeviceSize              resourceOffset;\n    VkDeviceSize              resourceLength;\n  };\n\n\n  /**\n   * \\brief Page type\n   */\n  enum class DxvkSparsePageType : uint32_t {\n    None            = 0,\n    Buffer          = 1,\n    Image           = 2,\n    ImageMipTail    = 3,\n  };\n\n\n  /**\n   * \\brief Sparse page table metadata\n   *\n   * Stores the resource region backed by any given page.\n   */\n  struct DxvkSparsePageInfo {\n    DxvkSparsePageType type;\n    union {\n      DxvkSparsePageBufferInfo  buffer;\n      DxvkSparsePageImageInfo   image;\n      DxvkSparsePageMipTailInfo mipTail;\n    };\n  };\n\n\n  /**\n   * \\brief Image tiling info\n   */\n  struct DxvkSparseImageProperties {\n    VkSparseImageFormatFlags  flags;\n    VkExtent3D                pageRegionExtent;\n    uint32_t                  pagedMipCount;\n    uint32_t                  metadataPageCount;\n    uint32_t                  mipTailPageIndex;\n    VkDeviceSize              mipTailOffset;\n    VkDeviceSize              mipTailSize;\n    VkDeviceSize              mipTailStride;\n  };\n\n\n  /**\n   * \\brief Image subresource tiling info\n   */\n  struct DxvkSparseImageSubresourceProperties {\n    VkBool32                  isMipTail;\n    VkExtent3D                pageCount;\n    uint32_t                  pageIndex;\n  };\n\n\n  /**\n   * \\brief Sparse binding flags\n   */\n  enum class DxvkSparseBindFlag : uint32_t {\n    SkipSynchronization,\n  };\n\n  using DxvkSparseBindFlags = Flags<DxvkSparseBindFlag>;\n\n\n  /**\n   * \\brief Sparse page binding mode\n   */\n  enum class DxvkSparseBindMode : uint32_t {\n    Null, ///< Unbind the given resource page\n    Bind, ///< Bind to given allocator page\n    Copy, ///< Copy bindig from source resource\n  };\n\n\n  /**\n   * \\brief Sparse page binding info for a given page\n   *\n   * Stores the resource page index as well as the index\n   * of the allocator page that should be bound to that\n   * resource page.\n   */\n  struct DxvkSparseBind {\n    DxvkSparseBindMode        mode;\n    uint32_t                  dstPage;\n    uint32_t                  srcPage;\n  };\n\n\n  /**\n   * \\brief Sparse binding info\n   *\n   * Stores the resource to change page bindings for, the\n   * allocator from which pages will be allocated, and\n   * a list of page bidnings\n   */\n  struct DxvkSparseBindInfo {\n    Rc<DxvkPagedResource>       dstResource;\n    Rc<DxvkPagedResource>       srcResource;\n    Rc<DxvkSparsePageAllocator> srcAllocator;\n    std::vector<DxvkSparseBind> binds;\n  };\n\n\n  /**\n   * \\brief Sparse page mapping\n   *\n   * Stores a reference to a page as well as the pool that the page\n   * was allocated from, and automatically manages the use counter\n   * of the pool as the reference is being moved or copied around.\n   */\n  class DxvkSparseMapping {\n    friend DxvkSparsePageAllocator;\n    friend DxvkSparsePageTable;\n  public:\n\n    DxvkSparseMapping();\n\n    DxvkSparseMapping(DxvkSparseMapping&& other);\n    DxvkSparseMapping(const DxvkSparseMapping& other);\n\n    DxvkSparseMapping& operator = (DxvkSparseMapping&& other);\n    DxvkSparseMapping& operator = (const DxvkSparseMapping& other);\n\n    ~DxvkSparseMapping();\n\n    /**\n     * \\brief Queries memory handle\n     * \\returns Memory information\n     */\n    DxvkResourceMemoryInfo getMemoryInfo() const {\n      if (!m_page)\n        return DxvkResourceMemoryInfo();\n\n      return m_page->getMemoryInfo();\n    }\n\n    bool operator == (const DxvkSparseMapping& other) const {\n      // Pool is a function of the page, so no need to check both\n      return m_page == other.m_page;\n    }\n\n    bool operator != (const DxvkSparseMapping& other) const {\n      return m_page != other.m_page;\n    }\n\n    explicit operator bool () const {\n      return m_page != nullptr;\n    }\n\n  private:\n\n    Rc<DxvkSparsePageAllocator> m_pool;\n    Rc<DxvkResourceAllocation>  m_page;\n\n    DxvkSparseMapping(\n            Rc<DxvkSparsePageAllocator> allocator,\n            Rc<DxvkResourceAllocation>  page);\n\n    void acquire() const;\n\n    void release() const;\n\n  };\n\n\n  /**\n   * \\brief Sparse memory allocator\n   *\n   * Provides an allocator for sparse pages with variable capacity.\n   * Pages are use-counted to make sure they are not removed from\n   * the allocator too early.\n   */\n  class DxvkSparsePageAllocator : public RcObject {\n    friend DxvkSparseMapping;\n  public:\n\n    DxvkSparsePageAllocator(\n            DxvkMemoryAllocator&  memoryAllocator);\n\n    ~DxvkSparsePageAllocator();\n\n    /**\n     * \\brief Acquires page at the given offset\n     *\n     * If the offset is valid, this will atomically\n     * increment the allocator's use count and return\n     * a reference to the page.\n     * \\param [in] page Page index\n     * \\returns Page mapping object\n     */\n    DxvkSparseMapping acquirePage(\n            uint32_t              page);\n\n    /**\n     * \\brief Changes the allocator's maximum capacity\n     *\n     * Allocates new pages as necessary, and frees existing\n     * pages if none of the pages are currently in use.\n     * \\param [in] pageCount New capacity, in pages\n     */\n    void setCapacity(\n            uint32_t              pageCount);\n\n  private:\n\n    DxvkMemoryAllocator*              m_memory;\n\n    dxvk::mutex                       m_mutex;\n    uint32_t                          m_pageCount = 0u;\n    uint32_t                          m_useCount = 0u;\n    std::vector<Rc<DxvkResourceAllocation>> m_pages;\n\n    void acquirePage(\n      const Rc<DxvkResourceAllocation>& page);\n\n    void releasePage(\n      const Rc<DxvkResourceAllocation>& page);\n\n  };\n\n\n  /**\n   * \\brief Sparse page table\n   *\n   * Stores mappings from a resource region to a given memory page,\n   * as well as mapping tile indices to the given resource region.\n   */\n  class DxvkSparsePageTable {\n\n  public:\n\n    DxvkSparsePageTable();\n\n    DxvkSparsePageTable(\n            DxvkDevice*             device,\n      const VkBufferCreateInfo&     bufferInfo,\n            VkBuffer                bufferHandle);\n\n    DxvkSparsePageTable(\n            DxvkDevice*             device,\n      const VkImageCreateInfo&      imageInfo,\n            VkImage                 imageHandle);\n\n    /**\n     * \\brief Checks whether page table is defined\n     * \\returns \\c true if the page table is defined\n     */\n    explicit operator bool () const {\n      return m_buffer || m_image;\n    }\n\n    /**\n     * \\brief Queries buffer handle\n     * \\returns Buffer handle\n     */\n    VkBuffer getBufferHandle() const;\n\n    /**\n     * \\brief Queries image handle\n     * \\returns Image handle\n     */\n    VkImage getImageHandle() const;\n\n    /**\n     * \\brief Counts total number of pages in the resources\n     *\n     * Counts the number of pages for the entire resource, both\n     * for paged subresources as well as the mip tail.\n     * \\returns Total number of pages\n     */\n    uint32_t getPageCount() const {\n      return uint32_t(m_metadata.size());\n    }\n\n    /**\n     * \\brief Counts number of subresource infos\n     * \\returns Subresource info count\n     */\n    uint32_t getSubresourceCount() const {\n      return uint32_t(m_subresources.size());\n    }\n\n    /**\n     * \\brief Retrieves image properties\n     *\n     * Only contains meaningful info if the page\n     * table object was created for an image.\n     * \\returns Image properties\n     */\n    DxvkSparseImageProperties getProperties() const {\n      return m_properties;\n    }\n\n    /**\n     * \\brief Retrieves image subresource properties\n     *\n     * \\param [in] subresource The subresource to query\n     * \\returns Properties of the given subresource\n     */\n    DxvkSparseImageSubresourceProperties getSubresourceProperties(uint32_t subresource) const {\n      return subresource < getSubresourceCount()\n        ? m_subresources[subresource]\n        : DxvkSparseImageSubresourceProperties();\n    }\n\n    /**\n     * \\brief Queries info for a given page\n     *\n     * \\param [in] page Page index\n     * \\returns Page info\n     */\n    DxvkSparsePageInfo getPageInfo(uint32_t page) const {\n      return page < getPageCount()\n        ? m_metadata[page]\n        : DxvkSparsePageInfo();\n    }\n\n    /**\n     * \\brief Computes page index within a given image region\n     *\n     * \\param [in] subresource Subresource index\n     * \\param [in] regionOffset Region offset, in pages\n     * \\param [in] regionExtent Region extent, in pages\n     * \\param [in] regionIsLinear Whether to use the region extent\n     * \\param [in] pageIndex Page within the given region\n     * \\returns Page index. The returned number may be out\n     *    of bounds if the given region is invalid.\n     */\n    uint32_t computePageIndex(\n            uint32_t                subresource,\n            VkOffset3D              regionOffset,\n            VkExtent3D              regionExtent,\n            VkBool32                regionIsLinear,\n            uint32_t                pageIndex) const;\n\n    /**\n     * \\brief Queries page mapping\n     *\n     * \\param [in] page Page index\n     * \\returns Current page mapping\n     */\n    DxvkSparseMapping getMapping(\n            uint32_t                page);\n\n    /**\n     * \\brief Changes a page mapping\n     *\n     * Updates the given page mapping in the table, and ensures\n     * that the previously mapped page does not get destroyed\n     * prematurely by tracking it in the given command list.\n     * \\param [in] cmd Command list\n     * \\param [in] page Page index\n     * \\param [in] mapping New mapping\n     */\n    void updateMapping(\n            DxvkCommandList*        cmd,\n            uint32_t                page,\n            DxvkSparseMapping&&     mapping);\n\n  private:\n\n    VkBuffer m_buffer = VK_NULL_HANDLE;\n    VkImage m_image = VK_NULL_HANDLE;\n\n    DxvkSparseImageProperties                         m_properties    = { };\n    std::vector<DxvkSparseImageSubresourceProperties> m_subresources;\n    std::vector<DxvkSparsePageInfo>                   m_metadata;\n    std::vector<DxvkSparseMapping>                    m_mappings;\n\n  };\n\n\n  /**\n   * \\brief Resource residency status\n   */\n  enum class DxvkResourceResidency : uint32_t {\n    Resident  = 0u, ///< Resource is resident in desired memory type\n    Demoted   = 1u, ///< Resource is likely unused and may get evicted\n    Evicted   = 2u, ///< Resource got evicted to system memory\n  };\n\n\n  /**\n   * \\brief Paged resource\n   *\n   * Base class for memory-backed resources that may\n   * or may not also have a sparse page table.\n   */\n  class DxvkPagedResource {\n\n  public:\n\n    DxvkPagedResource(DxvkMemoryAllocator& allocator)\n    : m_allocator(&allocator), m_cookie(++s_cookie) { }\n\n    virtual ~DxvkPagedResource();\n\n    /**\n     * \\brief Queries resource cookie\n     * \\returns Resource cookie\n     */\n    uint64_t cookie() const {\n      return m_cookie;\n    }\n\n    /**\n     * \\brief Increments reference count\n     */\n    force_inline void incRef() {\n      acquire(DxvkAccess::None);\n    }\n\n    /**\n     * \\brief Decrements reference count\n     */\n    force_inline void decRef() {\n      release(DxvkAccess::None);\n    }\n\n    /**\n     * \\brief Acquires resource with given access\n     *\n     * Atomically increments both the reference count\n     * as well as the use count for the given access.\n     */\n    force_inline void acquire(DxvkAccess access) {\n      m_useCount.fetch_add(getIncrement(access), std::memory_order_acquire);\n    }\n\n    /**\n     * \\brief Releases resource with given access\n     *\n     * Atomically decrements both the reference count\n     * as well as the use count for the given access.\n     */\n    force_inline void release(DxvkAccess access) {\n      uint64_t increment = getIncrement(access);\n      uint64_t remaining = m_useCount.fetch_sub(increment, std::memory_order_release);\n\n      if (unlikely(remaining == increment))\n        delete this;\n    }\n\n    /**\n     * \\brief Converts reference type\n     *\n     * \\param [in] from Old access type\n     * \\param [in] to New access type\n     */\n    force_inline void convertRef(DxvkAccess from, DxvkAccess to) {\n      uint64_t increment = getIncrement(to) - getIncrement(from);\n\n      if (increment)\n        m_useCount.fetch_add(increment, std::memory_order_acq_rel);\n    }\n\n    /**\n     * \\brief Checks whether resource is in use\n     * \n     * Returns \\c true if there are pending accesses to\n     * the resource by the GPU matching the given access\n     * type. Note that checking for reads will also return\n     * \\c true if the resource is being written to.\n     * \\param [in] access Access type to check for\n     * \\returns \\c true if the resource is in use\n     */\n    force_inline bool isInUse(DxvkAccess access) const {\n      return m_useCount.load(std::memory_order_acquire) >= getIncrement(access);\n    }\n\n    /**\n     * \\brief Tries to acquire reference\n     *\n     * If the reference count is zero at the time this is called,\n     * the method will fail, otherwise the reference count will\n     * be incremented by one. This is useful to safely obtain a\n     * pointer from a look-up table that does not own references.\n     * \\returns \\c true on success\n     */\n    Rc<DxvkPagedResource> tryAcquire() {\n      uint64_t increment = getIncrement(DxvkAccess::None);\n      uint64_t refCount = m_useCount.load(std::memory_order_acquire);\n\n      do {\n        if (!refCount)\n          return nullptr;\n      } while (!m_useCount.compare_exchange_strong( refCount,\n        refCount + increment, std::memory_order_relaxed));\n\n      return Rc<DxvkPagedResource>::unsafeCreate(this);\n    }\n\n    /**\n     * \\brief Queries tracking ID\n     *\n     * Used to determine when a resource has last been used.\n     * \\returns Tracking ID\n     */\n    uint64_t getTrackId() const {\n      return m_trackId >> 1u;\n    }\n\n    /**\n     * \\brief Sets tracked command list ID\n     *\n     * Used to work out if a resource has been used in the current\n     * command list and optimize certain transfer operations.\n     * \\param [in] trackingId Tracking ID\n     * \\param [in] access Tracked access\n     * \\returns \\c true if the tracking ID was updated, or \\c false\n     *    if the resource was already tracked with the same ID.\n     */\n    bool trackId(uint64_t trackingId, DxvkAccess access) {\n      // Encode write access in the least significant bit\n      uint64_t trackId = (trackingId << 1u) + uint64_t(access == DxvkAccess::Write);\n\n      if (trackId <= m_trackId)\n        return false;\n\n      m_trackId = trackId;\n      return true;\n    }\n\n    /**\n     * \\brief Checks whether a resource has been tracked\n     *\n     * \\param [in] trackingId Current tracking ID\n     * \\param [in] access Destination access\n     * \\returns \\c true if the resource has been used in a way that\n     *    prevents recordering commands with the given resource access.\n     */\n    bool isTracked(uint64_t trackingId, DxvkAccess access) const {\n      // We actually want to check for read access here so that this check only\n      // fails if the resource hasn't been used or if both accesses are read-only.\n      return m_trackId >= (trackingId << 1u) + uint64_t(access != DxvkAccess::Write);\n    }\n\n    /**\n     * \\brief Resets tracking\n     *\n     * Marks the resource as unused in the current command list.\n     * Should be done when assigning new backing storage.\n     */\n    void resetTracking() {\n      m_trackId = 0u;\n    }\n\n    /**\n     * \\brief Checks whether the buffer has been used for gfx stores\n     *\n     * \\returns \\c true if any graphics pipeline has written this\n     *    resource via transform feedback or a storage descriptor.\n     */\n    bool hasGfxStores() const {\n      return m_hasGfxStores;\n    }\n\n    /**\n     * \\brief Tracks graphics pipeline side effects\n     *\n     * Must be called whenever the resource is written via graphics\n     * pipeline storage descriptors or transform feedback.\n     * \\returns \\c true if side effects were already tracked.\n     */\n    bool trackGfxStores() {\n      return std::exchange(m_hasGfxStores, true);\n    }\n\n    /**\n     * \\brief Requests eviction\n     *\n     * If the resource is currently resident, this will update its\n     * status to \\c Demoted and return \\c false. If the status is\n     * already \\c Demoted, then this will return \\c true, indicating\n     * that the resource has not been used since the last call.\n     * \\returns \\c true if the resource can be evicted\n     */\n    bool requestEviction() {\n      DxvkResourceResidency status = m_residency.load(std::memory_order_acquire);\n\n      if (status == DxvkResourceResidency::Resident && m_residency.compare_exchange_strong(\n          status, DxvkResourceResidency::Demoted, std::memory_order_release))\n        return false;\n\n      return status == DxvkResourceResidency::Demoted;\n    }\n\n    /**\n     * \\brief Requests the resource to be made resident\n     *\n     * If the resource has been demoted, its status will be changed back to\n     * \\c Resident so that it will not be evicted. Otherwise, if the resource\n     * has already been evicted, it will be queued up to be streamed back into\n     * video memory.\n     */\n    void requestResidency() {\n      DxvkResourceResidency status = m_residency.load(std::memory_order_acquire);\n\n      if (likely(status == DxvkResourceResidency::Resident))\n        return;\n\n      if (status == DxvkResourceResidency::Demoted && m_residency.compare_exchange_strong(\n          status, DxvkResourceResidency::Resident, std::memory_order_release))\n        return;\n\n      if (status == DxvkResourceResidency::Evicted)\n        makeResourceResident();\n    }\n\n    /**\n     * \\brief Queries sparse page table\n     *\n     * Should be removed once storage objects can\n     * be retrieved from resources diectly.\n     * \\returns Sparse page table, if defined\n     */\n    virtual DxvkSparsePageTable* getSparsePageTable() = 0;\n\n    /**\n     * \\brief Allocates new backing storage with constraints\n     *\n     * \\param [in] mode Allocation mode flags to control behaviour.\n     *    When relocating the resource to a preferred memory type,\n     *    \\c NoFallback should be set, when defragmenting device\n     *    memory then \\c NoAllocation should also be set.\n     * \\returns \\c true in the first field if the operation is\n     *    considered successful, i.e. if an new backing allocation\n     *    was successfully created or is unnecessary. The second\n     *    field will contain the new allocation itself.\n     */\n    virtual Rc<DxvkResourceAllocation> relocateStorage(\n            DxvkAllocationModes         mode) = 0;\n\n    /**\n     * \\brief Sets debug name for the backing resource\n     *\n     * The caller \\e must ensure that the backing resource\n     * is not being swapped out at the same time. This may\n     * also be ignored for certain types of resources for\n     * performance reasons, and has no effect if the device\n     * does not have debug layers enabled.\n     * \\param [in] name New debug name\n     */\n    virtual void setDebugName(const char* name) = 0;\n\n    /**\n     * \\brief Retrieves debug name\n     *\n     * May return an empty string if debug support is disabled.\n     * \\returns The resource debug name\n     */\n    virtual const char* getDebugName() const = 0;\n\n  protected:\n\n    DxvkMemoryAllocator*  m_allocator = nullptr;\n\n    /**\n     * \\brief Updates residency status\n     *\n     * Must be called whenever the backing storage\n     * for a residency-tracked resource changes.\n     * \\param [in] residency New residency status\n     */\n    void updateResidencyStatus(DxvkResourceResidency residency) {\n      m_residency.store(residency, std::memory_order_release);\n    }\n\n  private:\n\n    std::atomic<uint64_t> m_useCount = { 0u };\n    uint64_t              m_trackId = { 0u };\n    uint64_t              m_cookie = { 0u };\n\n    std::atomic<DxvkResourceResidency> m_residency = { DxvkResourceResidency::Resident };\n\n    bool                  m_hasGfxStores = false;\n\n    void makeResourceResident();\n\n    static constexpr uint64_t getIncrement(DxvkAccess access) {\n      return uint64_t(1u) << (uint32_t(access) * 20u);\n    }\n\n    static std::atomic<uint64_t> s_cookie;\n\n  };\n\n\n  /**\n   * \\brief Typed tracking reference for resources\n   *\n   * Does not provide any access information.\n   */\n  class DxvkResourceRef : public DxvkTrackingRef {\n    constexpr static uintptr_t AccessMask = 0x3u;\n\n    static_assert(alignof(DxvkPagedResource) > AccessMask);\n  public:\n\n    template<typename T>\n    explicit DxvkResourceRef(Rc<T>&& object, DxvkAccess access)\n    : m_ptr(reinterpret_cast<uintptr_t>(static_cast<DxvkPagedResource*>(object.ptr())) | uintptr_t(access)) {\n      object.unsafeExtract()->convertRef(DxvkAccess::None, access);\n    }\n\n    explicit DxvkResourceRef(DxvkPagedResource* object, DxvkAccess access)\n    : m_ptr(reinterpret_cast<uintptr_t>(object) | uintptr_t(access)) {\n      object->acquire(access);\n    }\n\n    ~DxvkResourceRef();\n\n  private:\n\n    uintptr_t m_ptr = 0u;\n\n  };\n\n\n  /**\n   * \\brief Key for sparse buffer binding entry\n   *\n   * Provides a strong ordering by resource, resource offset,\n   * and finally, size. The ordering can be used to easily\n   * merge adjacent ranges.\n   */\n  struct DxvkSparseBufferBindKey {\n    VkBuffer                    buffer;\n    VkDeviceSize                offset;\n    VkDeviceSize                size;\n\n    bool operator < (const DxvkSparseBufferBindKey& other) const {\n      if (buffer < other.buffer) return true;\n      if (buffer > other.buffer) return false;\n\n      if (offset < other.offset) return true;\n      if (offset > other.offset) return false;\n\n      return size < other.size;\n    }\n  };\n\n\n  /**\n   * \\brief Key for sparse image binding entry\n   *\n   * Provides a strong ordering by resource, subresource,\n   * offset (z -> y -> x), and finally, extent (d -> h -> w).\n   * The ordering can be used to easily merge adjacent regions.\n   */\n  struct DxvkSparseImageBindKey {\n    VkImage                     image;\n    VkImageSubresource          subresource;\n    VkOffset3D                  offset;\n    VkExtent3D                  extent;\n\n    bool operator < (const DxvkSparseImageBindKey& other) const {\n      if (image < other.image) return true;\n      if (image > other.image) return false;\n\n      uint64_t aSubresource = this->encodeSubresource();\n      uint64_t bSubresource = other.encodeSubresource();\n\n      if (aSubresource < bSubresource) return true;\n      if (aSubresource > bSubresource) return false;\n\n      uint64_t aOffset = this->encodeOffset();\n      uint64_t bOffset = other.encodeOffset();\n\n      if (aOffset < bOffset) return true;\n      if (aOffset > bOffset) return false;\n\n      uint64_t aExtent = this->encodeExtent();\n      uint64_t bExtent = other.encodeExtent();\n\n      return aExtent < bExtent;\n    }\n\n    uint64_t encodeSubresource() const {\n      return uint64_t(subresource.aspectMask) << 48\n           | uint64_t(subresource.arrayLayer) << 24\n           | uint64_t(subresource.mipLevel);\n    }\n\n    uint64_t encodeOffset() const {\n      return uint64_t(offset.z) << 48\n           | uint64_t(offset.y) << 24\n           | uint64_t(offset.x);\n    }\n\n    uint64_t encodeExtent() const {\n      return uint64_t(extent.depth) << 48\n           | uint64_t(extent.height) << 24\n           | uint64_t(extent.width);\n    }\n  };\n\n\n  /**\n   * \\brief Key for sparse opaque image binding entry\n   *\n   * Provides a strong ordering by resource, resource offset,\n   * and finally, size. The ordering can be used to easily\n   * merge adjacent ranges.\n   */\n  struct DxvkSparseImageOpaqueBindKey {\n    VkImage                     image;\n    VkDeviceSize                offset;\n    VkDeviceSize                size;\n    VkSparseMemoryBindFlags     flags;\n\n    bool operator < (const DxvkSparseImageOpaqueBindKey& other) const {\n      if (image < other.image) return true;\n      if (image > other.image) return false;\n\n      if (offset < other.offset) return true;\n      if (offset > other.offset) return false;\n\n      return size < other.size;\n    }\n  };\n\n\n  /**\n   * \\brief Arrays required for buffer binds\n   */\n  struct DxvkSparseBufferBindArrays {\n    std::vector<VkSparseMemoryBind> binds;\n    std::vector<VkSparseBufferMemoryBindInfo> infos;\n  };\n\n\n  /**\n   * \\brief Arrays required for image binds\n   */\n  struct DxvkSparseImageBindArrays {\n    std::vector<VkSparseImageMemoryBind> binds;\n    std::vector<VkSparseImageMemoryBindInfo> infos;\n  };\n\n\n  /**\n   * \\brief Arrays required for opaque image binds\n   */\n  struct DxvkSparseImageOpaqueBindArrays {\n    std::vector<VkSparseMemoryBind> binds;\n    std::vector<VkSparseImageOpaqueMemoryBindInfo> infos;\n  };\n\n\n  /**\n   * \\brief Sparse bind submission\n   *\n   * Stores information for a single sparse binding operation,\n   * and supports submitting that operation to a device queue.\n   *\n   * All methods to add bindings assume that the binding range is\n   * either identical to an existing range, in which case the old\n   * binding will be overwritten, or otherwise, that the range is\n   * disjoint from all existing ranges. Overlapping ranges are not\n   * supported. This condition is trivial to maintain when binding\n   * only one sparse page at a time.\n   */\n  class DxvkSparseBindSubmission {\n\n  public:\n\n    DxvkSparseBindSubmission();\n\n    ~DxvkSparseBindSubmission();\n\n    /**\n     * \\brief Waits for a semaphore\n     *\n     * \\param [in] semaphore Semaphore to wait for\n     * \\param [in] value Semaphore value to wait on\n     */\n    void waitSemaphore(\n            VkSemaphore             semaphore,\n            uint64_t                value);\n\n    /**\n     * \\brief Signals a semaphore\n     *\n     * \\param [in] semaphore Semaphore to signal\n     * \\param [in] value Calue to signal semaphore to\n     */\n    void signalSemaphore(\n            VkSemaphore             semaphore,\n            uint64_t                value);\n\n    /**\n     * \\brief Adds a buffer memory bind\n     *\n     * \\param [in] key Buffer range key\n     * \\param [in] memory Page handle\n     */\n    void bindBufferMemory(\n      const DxvkSparseBufferBindKey& key,\n      const DxvkResourceMemoryInfo&  memory);\n\n    /**\n     * \\brief Adds an image memory bind\n     *\n     * \\param [in] key Image region key\n     * \\param [in] memory Page handle\n     */\n    void bindImageMemory(\n      const DxvkSparseImageBindKey& key,\n      const DxvkResourceMemoryInfo& memory);\n\n    /**\n     * \\brief Adds an opaque image memory bind\n     *\n     * \\param [in] key Opaque region key\n     * \\param [in] memory Page handle\n     */\n    void bindImageOpaqueMemory(\n      const DxvkSparseImageOpaqueBindKey& key,\n      const DxvkResourceMemoryInfo& memory);\n\n    /**\n     * \\brief Submits sparse binding operation\n     *\n     * Generates structures required for the sparse bind, resolving\n     * any conflicts in the process and merging ranges where possible.\n     * Note that this operation is slow. Resets object after the call.\n     * \\param [in] device DXVK device\n     * \\param [in] queue Queue to perform the operation on\n     * \\returns Return value of the sparse bind operation\n     */\n    VkResult submit(\n            DxvkDevice*             device,\n            VkQueue                 queue);\n\n    /**\n     * \\brief Resets object\n     *\n     * Clears all internal structures.\n     */\n    void reset();\n\n  private:\n\n    std::vector<uint64_t>     m_waitSemaphoreValues;\n    std::vector<VkSemaphore>  m_waitSemaphores;\n    std::vector<uint64_t>     m_signalSemaphoreValues;\n    std::vector<VkSemaphore>  m_signalSemaphores;\n\n    std::map<DxvkSparseBufferBindKey,      DxvkResourceMemoryInfo> m_bufferBinds;\n    std::map<DxvkSparseImageBindKey,       DxvkResourceMemoryInfo> m_imageBinds;\n    std::map<DxvkSparseImageOpaqueBindKey, DxvkResourceMemoryInfo> m_imageOpaqueBinds;\n\n    static bool tryMergeMemoryBind(\n            VkSparseMemoryBind&               oldBind,\n      const VkSparseMemoryBind&               newBind);\n\n    static bool tryMergeImageBind(\n            std::pair<DxvkSparseImageBindKey, DxvkResourceMemoryInfo>& oldBind,\n      const std::pair<DxvkSparseImageBindKey, DxvkResourceMemoryInfo>& newBind);\n\n    void processBufferBinds(\n            DxvkSparseBufferBindArrays&       buffer);\n\n    void processImageBinds(\n            DxvkSparseImageBindArrays&        image);\n\n    void processOpaqueBinds(\n            DxvkSparseImageOpaqueBindArrays&  opaque);\n\n    template<typename HandleType, typename BindType, typename InfoType>\n    void populateOutputArrays(\n            std::vector<BindType>&            binds,\n            std::vector<InfoType>&            infos,\n      const std::vector<std::pair<HandleType, BindType>>& input);\n\n    void logSparseBindingInfo(\n            LogLevel                          level,\n      const VkBindSparseInfo*                 info);\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_staging.cpp",
    "content": "#include \"dxvk_device.h\"\n#include \"dxvk_staging.h\"\n\nnamespace dxvk {\n  \n  DxvkStagingBuffer::DxvkStagingBuffer(\n    const Rc<DxvkDevice>&     device,\n          VkDeviceSize        size)\n  : m_device(device), m_offset(0), m_size(size) {\n\n  }\n\n\n  DxvkStagingBuffer::~DxvkStagingBuffer() {\n\n  }\n\n\n  DxvkBufferSlice DxvkStagingBuffer::alloc(VkDeviceSize size) {\n    DxvkBufferCreateInfo info;\n    info.size   = size;\n    info.usage  = VK_BUFFER_USAGE_TRANSFER_SRC_BIT\n                | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT\n                | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n    info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT\n                | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n    info.access = VK_ACCESS_TRANSFER_READ_BIT\n                | VK_ACCESS_SHADER_READ_BIT;\n    info.debugName = \"Staging buffer\";\n\n    VkDeviceSize alignedSize = dxvk::align(size, 256u);\n    m_allocationCounter += alignedSize;\n\n    if (2 * alignedSize > m_size) {\n      return DxvkBufferSlice(m_device->createBuffer(info,\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT));\n    }\n\n    if (m_offset + alignedSize > m_size || m_buffer == nullptr) {\n      info.size = m_size;\n\n      // Free resources first if possible, in some rare\n      // situations this may help avoid a memory allocation.\n      m_buffer = nullptr;\n      m_buffer = m_device->createBuffer(info,\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n      m_offset = 0;\n    }\n\n    DxvkBufferSlice slice(m_buffer, m_offset, size);\n    m_offset += alignedSize;\n    return slice;\n  }\n\n\n  void DxvkStagingBuffer::reset() {\n    m_buffer = nullptr;\n    m_offset = 0;\n\n    m_allocationCounterValueOnReset = m_allocationCounter;\n  }\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_staging.h",
    "content": "#pragma once\n\n#include <queue>\n\n#include \"dxvk_buffer.h\"\n#include \"dxvk_device.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Staging buffer statistics\n   *\n   * Can optionally be used to throttle resource\n   * uploads through the staging buffer.\n   */\n  struct DxvkStagingBufferStats {\n    /// Total amount allocated since the buffer was created\n    VkDeviceSize allocatedTotal = 0u;\n    /// Amount allocated since the last time the buffer was reset\n    VkDeviceSize allocatedSinceLastReset = 0u;\n  };\n\n\n  /**\n   * \\brief Staging buffer\n   *\n   * Provides a simple linear staging buffer\n   * allocator for data uploads.\n   */\n  class DxvkStagingBuffer {\n\n  public:\n\n    /**\n     * \\brief Creates staging buffer\n     *\n     * \\param [in] device DXVK device\n     * \\param [in] size Buffer size\n     */\n    DxvkStagingBuffer(\n      const Rc<DxvkDevice>&     device,\n            VkDeviceSize        size);\n\n    /**\n     * \\brief Frees staging buffer\n     */\n    ~DxvkStagingBuffer();\n\n    /**\n     * \\brief Allocates staging buffer memory\n     *\n     * Tries to suballocate from existing buffer,\n     * or creates a new buffer if necessary.\n     * \\param [in] size Number of bytes to allocate\n     * \\returns Allocated slice\n     */\n    DxvkBufferSlice alloc(VkDeviceSize size);\n\n    /**\n     * \\brief Resets staging buffer and allocator\n     */\n    void reset();\n\n    /**\n     * \\brief Retrieves allocation statistics\n     * \\returns Current allocation statistics\n     */\n    DxvkStagingBufferStats getStatistics() const {\n      DxvkStagingBufferStats result = { };\n      result.allocatedTotal = m_allocationCounter;\n      result.allocatedSinceLastReset = m_allocationCounter - m_allocationCounterValueOnReset;\n      return result;\n    }\n\n  private:\n\n    Rc<DxvkDevice>  m_device = nullptr;\n    Rc<DxvkBuffer>  m_buffer = nullptr;\n    VkDeviceSize    m_offset = 0u;\n    VkDeviceSize    m_size = 0u;\n\n    VkDeviceSize    m_allocationCounter = 0u;\n    VkDeviceSize    m_allocationCounterValueOnReset = 0u;\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_stats.cpp",
    "content": "#include \"dxvk_stats.h\"\n\nnamespace dxvk {\n  \n  DxvkStatCounters::DxvkStatCounters() {\n    this->reset();\n  }\n  \n  \n  DxvkStatCounters::~DxvkStatCounters() {\n    \n  }\n  \n  \n  DxvkStatCounters DxvkStatCounters::diff(const DxvkStatCounters& other) const {\n    DxvkStatCounters result;\n    for (size_t i = 0; i < m_counters.size(); i++)\n      result.m_counters[i] = m_counters[i] - other.m_counters[i];\n    return result;\n  }\n  \n  \n  void DxvkStatCounters::merge(const DxvkStatCounters& other) {\n    for (size_t i = 0; i < m_counters.size(); i++)\n      m_counters[i] += other.m_counters[i];\n  }\n  \n  \n  void DxvkStatCounters::reset() {\n    for (size_t i = 0; i < m_counters.size(); i++)\n      m_counters[i] = 0;\n  }\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_stats.h",
    "content": "#pragma once\n\n#include \"dxvk_include.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Named stat counters\n   * \n   * Enumerates available stat counters. Used\n   * thogether with \\ref DxvkStatCounters.\n   */\n  enum class DxvkStatCounter : uint32_t {\n    CmdDrawCalls,             ///< Number of draw calls\n    CmdDrawsMerged,           ///< Number of unique draws, minus draw calls\n    CmdDispatchCalls,         ///< Number of compute calls\n    CmdRenderPassCount,       ///< Number of render passes\n    CmdBarrierCount,          ///< Number of pipeline barriers\n    PipeCountGraphics,        ///< Number of graphics pipelines\n    PipeCountLibrary,         ///< Number of graphics shader libraries\n    PipeCountCompute,         ///< Number of compute pipelines\n    PipeTasksDone,            ///< Boolean indicating compiler activity\n    PipeTasksTotal,           ///< Boolean indicating compiler activity\n    QueueSubmitCount,         ///< Number of command buffer submissions\n    QueuePresentCount,        ///< Number of present calls / frames\n    GpuSyncCount,             ///< Number of GPU synchronizations\n    GpuSyncTicks,             ///< Time spent waiting for GPU\n    GpuIdleTicks,             ///< GPU idle time in microseconds\n    CsSyncCount,              ///< CS thread synchronizations\n    CsSyncTicks,              ///< Time spent waiting on CS\n    CsIdleTicks,              ///< CS thread idle time in microseconds\n    CsChunkCount,             ///< Submitted CS chunks\n    DescriptorPoolCount,      ///< Descriptor pool count\n    DescriptorSetCount,       ///< Descriptor sets allocated\n    DescriptorHeapCount,      ///< Number of descriptor heaps created\n    DescriptorHeapSize,       ///< Amount of descriptor memory allocated\n    DescriptorHeapUsed,       ///< Amount of descriptor memory used\n    DescriptorCopyBusyTicks,  ///< Descriptor copy busy time in microseconds\n\n    NumCounters               ///< Number of counters available\n  };\n  \n  \n  /**\n   * \\brief Stat counters\n   * \n   * Collects various statistics that may be\n   * useful to identify performance bottlenecks.\n   */\n  class DxvkStatCounters {\n    \n  public:\n    \n    DxvkStatCounters();\n    ~DxvkStatCounters();\n    \n    /**\n     * \\brief Retrieves a counter value\n     * \n     * \\param [in] ctr The counter\n     * \\returns Counter value\n     */\n    uint64_t getCtr(DxvkStatCounter ctr) const {\n      return m_counters[uint32_t(ctr)];\n    }\n    \n    /**\n     * \\brief Sets a counter value\n     * \n     * \\param [in] ctr The counter\n     * \\param [in] val Counter value\n     */\n    void setCtr(DxvkStatCounter ctr, uint64_t val) {\n      m_counters[uint32_t(ctr)] = val;\n    }\n    \n    /**\n     * \\brief Increments a counter value\n     * \n     * \\param [in] ctr Counter to increment\n     * \\param [in] val Number to add to counter value\n     */\n    void addCtr(DxvkStatCounter ctr, uint64_t val) {\n      m_counters[uint32_t(ctr)] += val;\n    }\n    \n    /**\n     * \\brief Resets a counter\n     * \\param [in] ctr The counter\n     */\n    void clrCtr(DxvkStatCounter ctr) {\n      m_counters[uint32_t(ctr)] = 0;\n    }\n    \n    /**\n     * \\brief Computes difference\n     * \n     * Computes difference between counter values.\n     * \\param [in] other Counters to subtract\n     * \\returns Difference between counter sets\n     */\n    DxvkStatCounters diff(const DxvkStatCounters& other) const;\n    \n    /**\n     * \\brief Merges counters\n     * \n     * Adds counter values from another set\n     * of counters to this set of counters.\n     * \\param [in] other Counters to add\n     */\n    void merge(const DxvkStatCounters& other);\n    \n    /**\n     * \\brief Resets counters\n     * \n     * Sets all counters to zero.\n     */\n    void reset();\n    \n  private:\n    \n    std::array<uint64_t, uint32_t(DxvkStatCounter::NumCounters)> m_counters;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_swapchain_blitter.cpp",
    "content": "#include \"dxvk_swapchain_blitter.h\"\n\n#include <dxvk_cursor_frag.h>\n#include <dxvk_cursor_vert.h>\n#include <dxvk_present_frag.h>\n#include <dxvk_present_frag_blit.h>\n#include <dxvk_present_frag_ms.h>\n#include <dxvk_present_frag_ms_blit.h>\n#include <dxvk_present_vert.h>\n\nnamespace dxvk {\n  \n  DxvkSwapchainBlitter::DxvkSwapchainBlitter(\n    const Rc<DxvkDevice>& device,\n    const Rc<hud::Hud>&   hud)\n  : m_device(device), m_hud(hud),\n    m_blitLayout(createBlitPipelineLayout()),\n    m_cursorLayout(createCursorPipelineLayout()) {\n    this->createSampler();\n  }\n\n\n  DxvkSwapchainBlitter::~DxvkSwapchainBlitter() {\n    auto vk = m_device->vkd();\n\n    for (const auto& p : m_pipelines)\n      vk->vkDestroyPipeline(vk->device(), p.second, nullptr);\n\n    for (const auto& p : m_cursorPipelines)\n      vk->vkDestroyPipeline(vk->device(), p.second, nullptr);\n  }\n\n\n  void DxvkSwapchainBlitter::present(\n    const Rc<DxvkCommandList>&ctx,\n    const Rc<DxvkImageView>&  dstView,\n          VkRect2D            dstRect,\n    const Rc<DxvkImageView>&  srcView,\n          VkRect2D            srcRect) {\n    std::unique_lock lock(m_mutex);\n\n    // Update HUD, if we have one\n    if (m_hud)\n      m_hud->update();\n\n    // Fix up default present areas if necessary\n    if (!dstRect.extent.width || !dstRect.extent.height) {\n      dstRect.offset = { 0, 0 };\n      dstRect.extent = {\n        dstView->image()->info().extent.width,\n        dstView->image()->info().extent.height };\n    }\n\n    if (!srcRect.extent.width || !srcRect.extent.height) {\n      srcRect.offset = { 0, 0 };\n      srcRect.extent = {\n        srcView->image()->info().extent.width,\n        srcView->image()->info().extent.height };\n    }\n\n    if (m_gammaBuffer)\n      uploadGammaImage(ctx);\n\n    if (m_cursorBuffer)\n      uploadCursorImage(ctx);\n\n    // If we can't do proper blending, render the HUD into a separate image\n    bool composite = needsComposition(dstView);\n\n    if (m_hud && composite)\n      renderHudImage(ctx, dstView->mipLevelExtent(0));\n    else\n      destroyHudImage();\n\n    VkImageLayout renderLayout = dstView->getLayout();\n\n    VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n    barrier.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;\n    barrier.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;\n    barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    barrier.newLayout = renderLayout;\n    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.image = dstView->image()->handle();\n    barrier.subresourceRange = dstView->imageSubresources();\n\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.imageMemoryBarrierCount = 1;\n    depInfo.pImageMemoryBarriers = &barrier;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n\n    VkExtent3D dstExtent = dstView->mipLevelExtent(0u);\n\n    VkRenderingAttachmentInfo attachmentInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n    attachmentInfo.imageView = dstView->handle();\n    attachmentInfo.imageLayout = renderLayout;\n    attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\n    if (srcRect.extent != dstRect.extent)\n      attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;\n\n    VkRenderingInfo renderInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n    renderInfo.renderArea.offset = { 0u, 0u };\n    renderInfo.renderArea.extent = { dstExtent.width, dstExtent.height };\n    renderInfo.layerCount = 1u;\n    renderInfo.colorAttachmentCount = 1;\n    renderInfo.pColorAttachments = &attachmentInfo;\n\n    ctx->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderInfo);\n\n    performDraw(ctx, dstView, dstRect,\n      srcView, srcRect, composite);\n\n    if (!composite) {\n      if (m_hud)\n        m_hud->render(ctx, dstView);\n\n      if (m_cursorView)\n        renderCursor(ctx, dstView);\n    }\n\n    ctx->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n\n    barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n    barrier.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;\n    barrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;\n    barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT;\n    barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;\n    barrier.oldLayout = renderLayout;\n    barrier.newLayout = dstView->image()->info().layout;\n    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.image = dstView->image()->handle();\n    barrier.subresourceRange = dstView->imageSubresources();\n\n    depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.imageMemoryBarrierCount = 1;\n    depInfo.pImageMemoryBarriers = &barrier;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n  }\n\n\n  void DxvkSwapchainBlitter::setGammaRamp(\n          uint32_t            cpCount,\n    const DxvkGammaCp*        cpData) {\n    std::unique_lock lock(m_mutex);\n\n    if (cpCount) {\n      // Create temporary upload buffer for the curve\n      DxvkBufferCreateInfo bufferInfo = { };\n      bufferInfo.size = cpCount * sizeof(*cpData);\n      bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n      bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;\n      bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT;\n\n      m_gammaBuffer = m_device->createBuffer(bufferInfo,\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n      m_gammaCpCount = cpCount;\n\n      std::memcpy(m_gammaBuffer->mapPtr(0), cpData, cpCount * sizeof(*cpData));\n    } else {\n      // Destroy gamma image altogether\n      m_gammaBuffer = nullptr;\n      m_gammaImage = nullptr;\n      m_gammaView = nullptr;\n      m_gammaCpCount = 0;\n    }\n  }\n\n\n  void DxvkSwapchainBlitter::setCursorTexture(\n          VkExtent2D          extent,\n          VkFormat            format,\n    const void*               data) {\n    std::unique_lock lock(m_mutex);\n\n    if (extent.width && extent.height && format && data) {\n      auto formatInfo = lookupFormatInfo(format);\n\n      DxvkBufferCreateInfo bufferInfo = { };\n      bufferInfo.size = extent.width * extent.height * formatInfo->elementSize;\n      bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n      bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;\n      bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT;\n\n      m_cursorBuffer = m_device->createBuffer(bufferInfo,\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n\n      std::memcpy(m_cursorBuffer->mapPtr(0), data, bufferInfo.size);\n\n      DxvkImageCreateInfo imageInfo = { };\n      imageInfo.type = VK_IMAGE_TYPE_2D;\n      imageInfo.format = format;\n      imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;\n      imageInfo.extent = { extent.width, extent.height, 1u };\n      imageInfo.numLayers = 1u;\n      imageInfo.mipLevels = 1u;\n      imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT\n                      | VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n                      | VK_IMAGE_USAGE_SAMPLED_BIT;\n      imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                       | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n      imageInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT\n                       | VK_ACCESS_TRANSFER_READ_BIT\n                       | VK_ACCESS_SHADER_READ_BIT;\n      imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n      imageInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n      imageInfo.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n      imageInfo.debugName = \"Swapchain cursor\";\n\n      m_cursorImage = m_device->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      DxvkImageViewKey viewInfo = { };\n      viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n      viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n      viewInfo.format = format;\n      viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;\n      viewInfo.mipIndex = 0u;\n      viewInfo.mipCount = 1u;\n      viewInfo.layerIndex = 0u;\n      viewInfo.layerCount = 1u;\n\n      m_cursorView = m_cursorImage->createView(viewInfo);\n    } else {\n      // Destroy cursor image\n      m_cursorBuffer = nullptr;\n      m_cursorImage = nullptr;\n      m_cursorView = nullptr;\n    }\n  }\n\n\n  void DxvkSwapchainBlitter::setCursorPos(\n          VkRect2D            rect) {\n    std::unique_lock lock(m_mutex);\n    m_cursorRect = rect;\n  }\n\n\n  void DxvkSwapchainBlitter::performDraw(\n    const Rc<DxvkCommandList>&ctx,\n    const Rc<DxvkImageView>&  dstView,\n          VkRect2D            dstRect,\n    const Rc<DxvkImageView>&  srcView,\n          VkRect2D            srcRect,\n          VkBool32            composite) {\n    VkColorSpaceKHR dstColorSpace = dstView->image()->info().colorSpace;\n    VkColorSpaceKHR srcColorSpace = srcView->image()->info().colorSpace;\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) {\n      ctx->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xdcc0f0, \"Swapchain blit\"));\n    }\n\n    VkExtent3D dstExtent = dstView->mipLevelExtent(0);\n\n    VkOffset2D coordA = dstRect.offset;\n    VkOffset2D coordB = {\n      coordA.x + int32_t(dstRect.extent.width),\n      coordA.y + int32_t(dstRect.extent.height)\n    };\n\n    coordA.x = std::max(coordA.x, 0);\n    coordA.y = std::max(coordA.y, 0);\n    coordB.x = std::min(coordB.x, int32_t(dstExtent.width));\n    coordB.y = std::min(coordB.y, int32_t(dstExtent.height));\n\n    if (coordA.x >= coordB.x || coordA.y >= coordB.y)\n      return;\n\n    VkViewport viewport = { };\n    viewport.x = float(dstRect.offset.x);\n    viewport.y = float(dstRect.offset.y);\n    viewport.width = float(dstRect.extent.width);\n    viewport.height = float(dstRect.extent.height);\n    viewport.minDepth = 0.0f;\n    viewport.maxDepth = 0.0f;\n\n    ctx->cmdSetViewport(1, &viewport);\n\n    VkRect2D scissor = { };\n    scissor.offset = coordA;\n    scissor.extent.width = uint32_t(coordB.x - coordA.x);\n    scissor.extent.height = uint32_t(coordB.y - coordA.y);\n\n    ctx->cmdSetScissor(1, &scissor);\n\n    DxvkSwapchainPipelineKey key;\n    key.srcSpace = srcColorSpace;\n    key.srcSamples = srcView->image()->info().sampleCount;\n    key.srcIsSrgb = srcView->formatInfo()->flags.test(DxvkFormatFlag::ColorSpaceSrgb);\n    key.dstSpace = dstColorSpace;\n    key.dstFormat = dstView->info().format;\n    key.needsGamma = m_gammaView != nullptr;\n    key.needsBlit = dstRect.extent != srcRect.extent;\n    key.compositeHud = composite && m_hudSrv;\n    key.compositeCursor = composite && m_cursorView;\n\n    VkPipeline pipeline = getBlitPipeline(key);\n\n    // Set up resource bindings\n    std::array<DxvkDescriptorWrite, 4> descriptors = { };\n\n    auto& imageDescriptor = descriptors[0u];\n    imageDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n    imageDescriptor.descriptor = srcView->getDescriptor();\n\n    auto& gammaDescriptor = descriptors[1u];\n    gammaDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\n    if (m_gammaView)\n      gammaDescriptor.descriptor = m_gammaView->getDescriptor();\n\n    auto& hudDescriptor = descriptors[2u];\n    hudDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\n    if (m_hudSrv)\n      hudDescriptor.descriptor = m_hudSrv->getDescriptor();\n\n    auto& cursorDescriptor = descriptors[3u];\n    cursorDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\n    uint32_t cursorSampler = m_samplerCursorNearest->getDescriptor().samplerIndex;\n\n    if (m_cursorView) {\n      VkExtent3D extent = m_cursorImage->info().extent;\n\n      if (m_cursorRect.extent.width != extent.width\n       || m_cursorRect.extent.height != extent.height)\n        cursorSampler = m_samplerCursorLinear->getDescriptor().samplerIndex;\n\n      cursorDescriptor.descriptor = m_cursorView->getDescriptor();\n    }\n\n    PushConstants args = { };\n    args.srcOffset = srcRect.offset;\n    args.srcExtent = srcRect.extent;\n    args.dstOffset = dstRect.offset;\n    args.cursorOffset = m_cursorRect.offset;\n    args.cursorExtent = m_cursorRect.extent;\n    args.samplerGamma = m_samplerGamma->getDescriptor().samplerIndex;\n    args.samplerCursor = cursorSampler;\n\n    ctx->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);\n\n    ctx->bindResources(DxvkCmdBuffer::ExecBuffer,\n      m_blitLayout, descriptors.size(), descriptors.data(),\n      sizeof(args), &args);\n\n    ctx->cmdDraw(3, 1, 0, 0);\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      ctx->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n\n    // Make sure to keep used resources alive\n    ctx->track(srcView->image(), DxvkAccess::Read);\n    ctx->track(dstView->image(), DxvkAccess::Write);\n\n    if (m_gammaImage)\n      ctx->track(m_gammaImage, DxvkAccess::Read);\n\n    if (m_hudImage)\n      ctx->track(m_hudImage, DxvkAccess::Read);\n\n    if (m_cursorImage)\n      ctx->track(m_cursorImage, DxvkAccess::Read);\n\n    ctx->track(m_samplerGamma);\n    ctx->track(m_samplerCursorLinear);\n    ctx->track(m_samplerCursorNearest);\n  }\n\n\n  void DxvkSwapchainBlitter::renderHudImage(\n    const Rc<DxvkCommandList>&        ctx,\n          VkExtent3D                  extent) {\n    if (m_hud->empty())\n      return;\n\n    if (!m_hudImage || m_hudImage->info().extent != extent)\n      createHudImage(extent);\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) {\n      ctx->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xdcc0f0, \"HUD render\"));\n    }\n\n    // Reset image\n    VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n    barrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;\n    barrier.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;\n    barrier.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;\n    barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    barrier.newLayout = m_hudRtv->getLayout();\n    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.image = m_hudImage->handle();\n    barrier.subresourceRange = m_hudRtv->imageSubresources();\n\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.imageMemoryBarrierCount = 1;\n    depInfo.pImageMemoryBarriers = &barrier;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n\n    // Render actual HUD image\n    VkRenderingAttachmentInfo attachmentInfo = { VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO };\n    attachmentInfo.imageView = m_hudRtv->handle();\n    attachmentInfo.imageLayout = m_hudRtv->getLayout();\n    attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;\n    attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\n    VkRenderingInfo renderInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO };\n    renderInfo.renderArea.offset = { 0u, 0u };\n    renderInfo.renderArea.extent = { extent.width, extent.height };\n    renderInfo.layerCount = 1u;\n    renderInfo.colorAttachmentCount = 1;\n    renderInfo.pColorAttachments = &attachmentInfo;\n\n    ctx->cmdBeginRendering(DxvkCmdBuffer::ExecBuffer, &renderInfo);\n\n    m_hud->render(ctx, m_hudRtv);\n\n    ctx->cmdEndRendering(DxvkCmdBuffer::ExecBuffer);\n\n    // Make image shader-readable\n    barrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;\n    barrier.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;\n    barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;\n    barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;\n    barrier.oldLayout = m_hudRtv->getLayout();\n    barrier.newLayout = m_hudImage->info().layout;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n\n    m_hudImage->trackLayout(m_hudRtv->imageSubresources(), m_hudImage->info().layout);\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      ctx->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n\n    ctx->track(m_hudImage, DxvkAccess::Write);\n  }\n\n\n  void DxvkSwapchainBlitter::createHudImage(\n          VkExtent3D                  extent) {\n    DxvkImageCreateInfo imageInfo = { };\n    imageInfo.type          = VK_IMAGE_TYPE_2D;\n    imageInfo.format        = VK_FORMAT_R8G8B8A8_SRGB;\n    imageInfo.sampleCount   = VK_SAMPLE_COUNT_1_BIT;\n    imageInfo.extent        = extent;\n    imageInfo.mipLevels     = 1;\n    imageInfo.numLayers     = 1;\n    imageInfo.usage         = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n                            | VK_IMAGE_USAGE_SAMPLED_BIT;\n    imageInfo.stages        = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT\n                            | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n    imageInfo.access        = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT\n                            | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT\n                            | VK_ACCESS_SHADER_READ_BIT;\n    imageInfo.tiling        = VK_IMAGE_TILING_OPTIMAL;\n    imageInfo.layout        = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n    imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    imageInfo.colorSpace    = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n    imageInfo.debugName     = \"HUD composition\";\n\n    m_hudImage = m_device->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n    DxvkImageViewKey viewInfo = { };\n    viewInfo.viewType       = VK_IMAGE_VIEW_TYPE_2D;\n    viewInfo.format         = imageInfo.format;\n    viewInfo.aspects        = VK_IMAGE_ASPECT_COLOR_BIT;\n    viewInfo.mipIndex       = 0u;\n    viewInfo.mipCount       = 1u;\n    viewInfo.layerIndex     = 0u;\n    viewInfo.layerCount     = 1u;\n\n    viewInfo.usage          = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n    m_hudRtv = m_hudImage->createView(viewInfo);\n\n    viewInfo.usage          = VK_IMAGE_USAGE_SAMPLED_BIT;\n    m_hudSrv = m_hudImage->createView(viewInfo);\n  }\n\n\n  void DxvkSwapchainBlitter::destroyHudImage() {\n    m_hudImage = nullptr;\n    m_hudRtv = nullptr;\n    m_hudSrv = nullptr;\n  }\n\n\n  void DxvkSwapchainBlitter::renderCursor(\n    const Rc<DxvkCommandList>&        ctx,\n    const Rc<DxvkImageView>&          dstView) {\n    if (!m_cursorRect.extent.width || !m_cursorRect.extent.height)\n      return;\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) {\n      ctx->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xdcc0f0, \"Software cursor\"));\n    }\n\n    VkExtent3D dstExtent = dstView->mipLevelExtent(0u);\n\n    VkViewport viewport = { };\n    viewport.x = 0.0f;\n    viewport.y = 0.0f;\n    viewport.width = float(dstExtent.width);\n    viewport.height = float(dstExtent.height);\n    viewport.minDepth = 0.0f;\n    viewport.maxDepth = 0.0f;\n\n    ctx->cmdSetViewport(1, &viewport);\n\n    VkRect2D scissor = { };\n    scissor.extent.width = dstExtent.width;\n    scissor.extent.height = dstExtent.height;\n\n    ctx->cmdSetScissor(1, &scissor);\n\n    DxvkCursorPipelineKey key = { };\n    key.dstFormat = dstView->info().format;\n    key.dstSpace = dstView->image()->info().colorSpace;\n\n    VkPipeline pipeline = getCursorPipeline(key);\n\n    VkExtent3D cursorExtent = m_cursorImage->info().extent;\n\n    DxvkDescriptorWrite imageDescriptor = { };\n    imageDescriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n    imageDescriptor.descriptor = m_cursorView->getDescriptor();\n\n    CursorPushConstants args = { };\n    args.dstExtent = { dstExtent.width, dstExtent.height };\n    args.cursorOffset = m_cursorRect.offset;\n    args.cursorExtent = m_cursorRect.extent;\n    args.sampler = m_samplerCursorNearest->getDescriptor().samplerIndex;\n\n    if (m_cursorRect.extent.width != cursorExtent.width\n     || m_cursorRect.extent.height != cursorExtent.height)\n      args.sampler = m_samplerCursorLinear->getDescriptor().samplerIndex;\n\n    ctx->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);\n\n    ctx->bindResources(DxvkCmdBuffer::ExecBuffer,\n      m_cursorLayout, 1u, &imageDescriptor, sizeof(args), &args);\n\n    ctx->cmdDraw(4, 1, 0, 0);\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      ctx->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n\n    ctx->track(m_cursorImage, DxvkAccess::Write);\n  }\n\n\n  void DxvkSwapchainBlitter::uploadGammaImage(\n    const Rc<DxvkCommandList>&        ctx) {\n    if (!m_gammaImage || m_gammaImage->info().extent.width != m_gammaCpCount) {\n      DxvkImageCreateInfo imageInfo = { };\n      imageInfo.type = VK_IMAGE_TYPE_1D;\n      imageInfo.format = VK_FORMAT_R16G16B16A16_UNORM;\n      imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;\n      imageInfo.extent = { m_gammaCpCount, 1u, 1u };\n      imageInfo.numLayers = 1u;\n      imageInfo.mipLevels = 1u;\n      imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;\n      imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n      imageInfo.access = VK_ACCESS_2_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;\n      imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n      imageInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n      imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n      imageInfo.debugName = \"Swapchain gamma ramp\";\n\n      m_gammaImage = m_device->createImage(imageInfo,\n        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n      DxvkImageViewKey viewInfo = { };\n      viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;\n      viewInfo.format = imageInfo.format;\n      viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n      viewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;\n      viewInfo.mipIndex = 0u;\n      viewInfo.mipCount = 1u;\n      viewInfo.layerIndex = 0u;\n      viewInfo.layerCount = 1u;\n\n      m_gammaView = m_gammaImage->createView(viewInfo);\n    }\n\n    uploadTexture(ctx, m_gammaImage, m_gammaBuffer);\n    m_gammaBuffer = nullptr;\n  }\n\n\n  void DxvkSwapchainBlitter::uploadCursorImage(\n    const Rc<DxvkCommandList>&        ctx) {\n    uploadTexture(ctx, m_cursorImage, m_cursorBuffer);\n    m_cursorBuffer = nullptr;\n  }\n\n\n  void DxvkSwapchainBlitter::uploadTexture(\n    const Rc<DxvkCommandList>&        ctx,\n    const Rc<DxvkImage>&              image,\n    const Rc<DxvkBuffer>&             buffer) {\n    VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n    barrier.srcStageMask = image->info().stages;\n    barrier.srcAccessMask = image->info().access;\n    barrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n    barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    barrier.newLayout = image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    barrier.image = image->handle();\n    barrier.subresourceRange = image->getAvailableSubresources();\n\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.imageMemoryBarrierCount = 1;\n    depInfo.pImageMemoryBarriers = &barrier;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n\n    DxvkResourceBufferInfo bufferSlice = buffer->getSliceInfo();\n\n    VkBufferImageCopy2 copyRegion = { VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 };\n    copyRegion.bufferOffset = bufferSlice.offset;\n    copyRegion.imageExtent = image->info().extent;\n    copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    copyRegion.imageSubresource.layerCount = 1u;\n\n    VkCopyBufferToImageInfo2 copy = { VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 };\n    copy.srcBuffer = bufferSlice.buffer;\n    copy.dstImage = image->handle();\n    copy.dstImageLayout = image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n    copy.regionCount = 1;\n    copy.pRegions = &copyRegion;\n\n    ctx->cmdCopyBufferToImage(DxvkCmdBuffer::ExecBuffer, &copy);\n\n    barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n    barrier.dstStageMask = image->info().stages;\n    barrier.dstAccessMask = image->info().access;\n    barrier.oldLayout = barrier.newLayout;\n    barrier.newLayout = image->info().layout;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);\n\n    image->trackLayout(image->getAvailableSubresources(), image->info().layout);\n\n    ctx->track(buffer, DxvkAccess::Read);\n    ctx->track(image, DxvkAccess::Write);\n  }\n\n\n  void DxvkSwapchainBlitter::createSampler() {\n    DxvkSamplerKey samplerInfo = { };\n    samplerInfo.setFilter(VK_FILTER_LINEAR, VK_FILTER_LINEAR,\n      VK_SAMPLER_MIPMAP_MODE_NEAREST);\n    samplerInfo.setAddressModes(\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);\n    samplerInfo.setUsePixelCoordinates(false);\n \n    m_samplerGamma = m_device->createSampler(samplerInfo);\n\n    samplerInfo.setAddressModes(\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);\n\n    m_samplerCursorLinear = m_device->createSampler(samplerInfo);\n\n    samplerInfo.setFilter(VK_FILTER_NEAREST, VK_FILTER_NEAREST,\n      VK_SAMPLER_MIPMAP_MODE_NEAREST);\n\n    m_samplerCursorNearest = m_device->createSampler(samplerInfo);\n  }\n\n\n  const DxvkPipelineLayout* DxvkSwapchainBlitter::createBlitPipelineLayout() {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 4> bindings = {{\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n    }};\n\n    return m_device->createBuiltInPipelineLayout(DxvkPipelineLayoutFlag::UsesSamplerHeap,\n      VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(PushConstants), bindings.size(), bindings.data());\n  }\n\n\n  const DxvkPipelineLayout* DxvkSwapchainBlitter::createCursorPipelineLayout() {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 1> bindings = {{\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n    }};\n\n    return m_device->createBuiltInPipelineLayout(DxvkPipelineLayoutFlag::UsesSamplerHeap,\n      VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,\n      sizeof(CursorPushConstants), bindings.size(), bindings.data());\n  }\n\n\n  VkPipeline DxvkSwapchainBlitter::createBlitPipeline(\n    const DxvkSwapchainPipelineKey&   key) {\n    auto vk = m_device->vkd();\n\n    static const std::array<VkSpecializationMapEntry, 8> specMap = {{\n      { 0, offsetof(SpecConstants, sampleCount),    sizeof(VkSampleCountFlagBits) },\n      { 1, offsetof(SpecConstants, gammaBound),     sizeof(VkBool32) },\n      { 2, offsetof(SpecConstants, srcSpace),       sizeof(VkColorSpaceKHR) },\n      { 3, offsetof(SpecConstants, srcIsSrgb),      sizeof(VkBool32) },\n      { 4, offsetof(SpecConstants, dstSpace),       sizeof(VkColorSpaceKHR) },\n      { 5, offsetof(SpecConstants, dstIsSrgb),      sizeof(VkBool32) },\n      { 6, offsetof(SpecConstants, compositeHud),   sizeof(VkBool32) },\n      { 7, offsetof(SpecConstants, compositeCursor),sizeof(VkBool32) },\n    }};\n\n    SpecConstants specConstants = { };\n    specConstants.sampleCount = key.srcSamples;\n    specConstants.gammaBound = key.needsGamma && key.srcSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n    specConstants.srcSpace = key.srcSpace;\n    specConstants.srcIsSrgb = key.srcIsSrgb;\n    specConstants.dstSpace = key.dstSpace;\n    specConstants.dstIsSrgb = lookupFormatInfo(key.dstFormat)->flags.test(DxvkFormatFlag::ColorSpaceSrgb);\n    specConstants.compositeCursor = key.compositeCursor;\n    specConstants.compositeHud = key.compositeHud;\n\n    // Avoid redundant color space conversions if color spaces\n    // and images properties match and we don't do a resolve\n    if (key.srcSpace == key.dstSpace && key.srcSamples == VK_SAMPLE_COUNT_1_BIT\n     && !key.compositeCursor && !key.compositeHud) {\n      specConstants.srcSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;\n      specConstants.dstSpace = VK_COLOR_SPACE_PASS_THROUGH_EXT;\n    }\n\n    VkSpecializationInfo specInfo = { };\n    specInfo.mapEntryCount = specMap.size();\n    specInfo.pMapEntries = specMap.data();\n    specInfo.dataSize = sizeof(specConstants);\n    specInfo.pData = &specConstants;\n\n    util::DxvkBuiltInGraphicsState state = { };\n    state.vs = util::DxvkBuiltInShaderStage(dxvk_present_vert, nullptr);\n\n    if (key.srcSamples == VK_SAMPLE_COUNT_1_BIT) {\n      state.fs = key.needsBlit\n        ? util::DxvkBuiltInShaderStage(dxvk_present_frag_blit, &specInfo)\n        : util::DxvkBuiltInShaderStage(dxvk_present_frag, &specInfo);\n    } else {\n      state.fs = key.needsBlit\n        ? util::DxvkBuiltInShaderStage(dxvk_present_frag_ms_blit, &specInfo)\n        : util::DxvkBuiltInShaderStage(dxvk_present_frag_ms, &specInfo);\n    }\n\n    state.colorFormat = key.dstFormat;\n    return m_device->createBuiltInGraphicsPipeline(m_blitLayout, state);\n  }\n\n\n  VkPipeline DxvkSwapchainBlitter::getBlitPipeline(\n    const DxvkSwapchainPipelineKey&   key) {\n    auto entry = m_pipelines.find(key);\n\n    if (entry != m_pipelines.end())\n      return entry->second;\n\n    VkPipeline pipeline = createBlitPipeline(key);\n    m_pipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n\n  VkPipeline DxvkSwapchainBlitter::createCursorPipeline(\n    const DxvkCursorPipelineKey&      key) {\n    auto vk = m_device->vkd();\n\n    static const std::array<VkSpecializationMapEntry, 2> specMap = {{\n      { 0, offsetof(CursorSpecConstants, dstSpace),  sizeof(VkColorSpaceKHR) },\n      { 1, offsetof(CursorSpecConstants, dstIsSrgb), sizeof(VkBool32) },\n    }};\n\n    CursorSpecConstants specConstants = { };\n    specConstants.dstSpace = key.dstSpace;\n    specConstants.dstIsSrgb = lookupFormatInfo(key.dstFormat)->flags.test(DxvkFormatFlag::ColorSpaceSrgb);\n\n    VkSpecializationInfo specInfo = { };\n    specInfo.mapEntryCount = specMap.size();\n    specInfo.pMapEntries = specMap.data();\n    specInfo.dataSize = sizeof(specConstants);\n    specInfo.pData = &specConstants;\n\n    VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };\n    iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;\n\n    VkPipelineColorBlendAttachmentState cbAttachment = { };\n    cbAttachment.blendEnable = VK_TRUE;\n    cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;\n    cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n    cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;\n    cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;\n    cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n    cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;\n    cbAttachment.colorWriteMask =\n      VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |\n      VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n\n    util::DxvkBuiltInGraphicsState state = { };\n    state.vs = util::DxvkBuiltInShaderStage(dxvk_cursor_vert, nullptr);\n    state.fs = util::DxvkBuiltInShaderStage(dxvk_cursor_frag, &specInfo);\n    state.colorFormat = key.dstFormat;\n    state.iaState = &iaState;\n    state.cbAttachment = &cbAttachment;\n\n    return m_device->createBuiltInGraphicsPipeline(m_cursorLayout, state);\n  }\n\n\n  VkPipeline DxvkSwapchainBlitter::getCursorPipeline(\n    const DxvkCursorPipelineKey&      key) {\n    auto entry = m_cursorPipelines.find(key);\n\n    if (entry != m_cursorPipelines.end())\n      return entry->second;\n\n    VkPipeline pipeline = createCursorPipeline(key);\n    m_cursorPipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n\n  bool DxvkSwapchainBlitter::needsComposition(\n    const Rc<DxvkImageView>&          dstView) {\n    VkColorSpaceKHR colorSpace = dstView->image()->info().colorSpace;\n\n    switch (colorSpace) {\n      case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:\n        return !dstView->formatInfo()->flags.test(DxvkFormatFlag::ColorSpaceSrgb);\n\n      case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:\n        return false;\n\n      default:\n        return true;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_swapchain_blitter.h",
    "content": "#pragma once\n\n#include <functional>\n#include <thread>\n#include <unordered_map>\n\n#include \"./hud/dxvk_hud.h\"\n\n#include \"../util/thread.h\"\n\n#include \"../dxvk/dxvk_device.h\"\n#include \"../dxvk/dxvk_context.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Gamma control point\n   */\n  struct DxvkGammaCp {\n    uint16_t r, g, b, a;\n  };\n\n\n  /**\n   * \\brief Swap chain blitter pipeline key\n   *\n   * Used to look up specific pipelines.\n   */\n  struct DxvkSwapchainPipelineKey {\n    /// Input color space. If this does not match the output color\n    /// space, the input will be converted to match the output.\n    VkColorSpaceKHR srcSpace = VK_COLOR_SPACE_MAX_ENUM_KHR;\n    /// Source image sample count. Used to determine the shader to\n    /// use, and passed to it via a spec constant.\n    VkSampleCountFlagBits srcSamples = VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;\n    /// Whether the source image uses an sRGB format. Relevant for\n    /// automatic color space conversion.\n    VkBool32 srcIsSrgb = VK_FALSE;\n    /// Output color space.\n    VkColorSpaceKHR dstSpace = VK_COLOR_SPACE_MAX_ENUM_KHR;\n    /// Output image format. Used as pipeline state, but also to\n    /// determine the sRGB-ness of the format.\n    VkFormat dstFormat = VK_FORMAT_UNDEFINED;\n    /// Bit indicating whether the input and output dimensions match.\n    VkBool32 needsBlit = VK_FALSE;\n    /// Bit indicating whether a gamma curve is to be applied.\n    VkBool32 needsGamma = VK_FALSE;\n    /// Bit indicating whether the HUD needs to be composited\n    VkBool32 compositeHud = VK_FALSE;\n    /// Bit indicating whether the software cursor needs to be composited\n    VkBool32 compositeCursor = VK_FALSE;\n\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(uint32_t(srcSpace));\n      hash.add(uint32_t(srcSamples));\n      hash.add(uint32_t(srcIsSrgb));\n      hash.add(uint32_t(dstSpace));\n      hash.add(uint32_t(dstFormat));\n      hash.add(uint32_t(needsBlit));\n      hash.add(uint32_t(needsGamma));\n      hash.add(uint32_t(compositeHud));\n      hash.add(uint32_t(compositeCursor));\n      return hash;\n    }\n\n    bool eq(const DxvkSwapchainPipelineKey& other) const {\n      return srcSpace == other.srcSpace\n          && srcSamples == other.srcSamples\n          && srcIsSrgb == other.srcIsSrgb\n          && dstSpace == other.dstSpace\n          && dstFormat == other.dstFormat\n          && needsBlit == other.needsBlit\n          && needsGamma == other.needsGamma\n          && compositeHud == other.compositeHud\n          && compositeCursor == other.compositeCursor;\n    }\n  };\n\n\n  /**\n   * \\brief Swap chain cursor pipeline key\n   */\n  struct DxvkCursorPipelineKey {\n    /// Output color space.\n    VkColorSpaceKHR dstSpace = VK_COLOR_SPACE_MAX_ENUM_KHR;\n    /// Output image format. Used as pipeline state, but also to\n    /// determine the sRGB-ness of the format.\n    VkFormat dstFormat = VK_FORMAT_UNDEFINED;\n\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(uint32_t(dstSpace));\n      hash.add(uint32_t(dstFormat));\n      return hash;\n    }\n\n    bool eq(const DxvkCursorPipelineKey& other) const {\n      return dstSpace == other.dstSpace\n          && dstFormat == other.dstFormat;\n    }\n  };\n\n\n  /**\n   * \\brief Swap chain blitter\n   *\n   * Provides common rendering code for blitting\n   * rendered images to a swap chain image.\n   */\n  class DxvkSwapchainBlitter : public RcObject {\n    \n  public:\n\n    DxvkSwapchainBlitter(\n      const Rc<DxvkDevice>& device,\n      const Rc<hud::Hud>&   hud);\n    ~DxvkSwapchainBlitter();\n\n    /**\n     * \\brief Begins recording presentation commands\n     *\n     * Sets up the swap chain image and all internal resources, and\n     * blits the source image onto the swap chain appropriately.\n     * The swap chain image will remain bound for rendering.\n     * \\param [in] ctx Context objects\n     * \\param [in] dstView Swap chain image view\n     * \\param [in] dstRect Destination rectangle\n     * \\param [in] srcView Image to present\n     * \\param [in] srcColorSpace Image color space\n     * \\param [in] srcRect Source rectangle to present\n     */\n    void present(\n      const Rc<DxvkCommandList>&ctx,\n      const Rc<DxvkImageView>&  dstView,\n            VkRect2D            dstRect,\n      const Rc<DxvkImageView>&  srcView,\n            VkRect2D            srcRect);\n\n    /**\n     * \\brief Sets gamma ramp\n     *\n     * If the number of control points is non-zero, this\n     * will create a texture containing a gamma ramp that\n     * will be used for presentation.\n     * \\param [in] cpCount Number of control points\n     * \\param [in] cpData Control point data\n     */\n    void setGammaRamp(\n            uint32_t            cpCount,\n      const DxvkGammaCp*        cpData);\n\n    /**\n     * \\brief Sets software cursor texture\n     *\n     * The cursor image is assumed to be in sRGB color space.\n     * \\param [in] extent Texture size, in pixels\n     * \\param [in] format Texture format\n     * \\param [in] data Texture data. Assumed to be\n     *    tightly packed according to the format.\n     */\n    void setCursorTexture(\n            VkExtent2D          extent,\n            VkFormat            format,\n      const void*               data);\n\n    /**\n     * \\brief Sets cursor position\n     *\n     * If the size does not match the texture size, the\n     * cursor will be rendered with a linear filter.\n     * \\param [in] rect Cursor rectangle, in pixels\n     */\n    void setCursorPos(\n            VkRect2D            rect);\n\n  private:\n\n    struct SpecConstants {\n      VkSampleCountFlagBits sampleCount;\n      VkBool32 gammaBound;\n      VkColorSpaceKHR srcSpace;\n      VkBool32 srcIsSrgb;\n      VkColorSpaceKHR dstSpace;\n      VkBool32 dstIsSrgb;\n      VkBool32 compositeHud;\n      VkBool32 compositeCursor;\n    };\n\n    struct PushConstants {\n      VkOffset2D srcOffset;\n      VkExtent2D srcExtent;\n      VkOffset2D dstOffset;\n      VkOffset2D cursorOffset;\n      VkExtent2D cursorExtent;\n      uint32_t   samplerGamma;\n      uint32_t   samplerCursor;\n    };\n\n    struct CursorSpecConstants {\n      VkColorSpaceKHR dstSpace;\n      VkBool32 dstIsSrgb;\n    };\n\n    struct CursorPushConstants {\n      VkExtent2D dstExtent;\n      VkOffset2D cursorOffset;\n      VkExtent2D cursorExtent;\n      uint32_t   sampler;\n    };\n\n    Rc<DxvkDevice>      m_device;\n    Rc<hud::Hud>        m_hud;\n\n    dxvk::mutex         m_mutex;\n    Rc<DxvkBuffer>      m_gammaBuffer;\n    Rc<DxvkImage>       m_gammaImage;\n    Rc<DxvkImageView>   m_gammaView;\n    uint32_t            m_gammaCpCount = 0;\n\n    Rc<DxvkBuffer>      m_cursorBuffer;\n    Rc<DxvkImage>       m_cursorImage;\n    Rc<DxvkImageView>   m_cursorView;\n    VkRect2D            m_cursorRect = { };\n\n    Rc<DxvkSampler>     m_samplerGamma;\n    Rc<DxvkSampler>     m_samplerCursorLinear;\n    Rc<DxvkSampler>     m_samplerCursorNearest;\n\n    Rc<DxvkImage>       m_hudImage;\n    Rc<DxvkImageView>   m_hudRtv;\n    Rc<DxvkImageView>   m_hudSrv;\n\n    const DxvkPipelineLayout* m_blitLayout = nullptr;\n    const DxvkPipelineLayout* m_cursorLayout = nullptr;\n\n    std::unordered_map<DxvkSwapchainPipelineKey,\n      VkPipeline, DxvkHash, DxvkEq> m_pipelines;\n\n    std::unordered_map<DxvkCursorPipelineKey,\n      VkPipeline, DxvkHash, DxvkEq> m_cursorPipelines;\n\n    void performDraw(\n      const Rc<DxvkCommandList>&        ctx,\n      const Rc<DxvkImageView>&          dstView,\n            VkRect2D                    dstRect,\n      const Rc<DxvkImageView>&          srcView,\n            VkRect2D                    srcRect,\n            VkBool32                    composite);\n\n    void renderHudImage(\n      const Rc<DxvkCommandList>&        ctx,\n            VkExtent3D                  extent);\n\n    void createHudImage(\n            VkExtent3D                  extent);\n\n    void destroyHudImage();\n\n    void renderCursor(\n      const Rc<DxvkCommandList>&        ctx,\n      const Rc<DxvkImageView>&          dstView);\n\n    void uploadGammaImage(\n      const Rc<DxvkCommandList>&        ctx);\n\n    void uploadCursorImage(\n      const Rc<DxvkCommandList>&        ctx);\n\n    void uploadTexture(\n      const Rc<DxvkCommandList>&        ctx,\n      const Rc<DxvkImage>&              image,\n      const Rc<DxvkBuffer>&             buffer);\n\n    void createSampler();\n\n    const DxvkPipelineLayout* createBlitPipelineLayout();\n\n    const DxvkPipelineLayout* createCursorPipelineLayout();\n\n    VkPipeline createBlitPipeline(\n      const DxvkSwapchainPipelineKey&   key);\n\n    VkPipeline getBlitPipeline(\n      const DxvkSwapchainPipelineKey&   key);\n\n    VkPipeline createCursorPipeline(\n      const DxvkCursorPipelineKey&      key);\n\n    VkPipeline getCursorPipeline(\n      const DxvkCursorPipelineKey&      key);\n\n    static bool needsComposition(\n      const Rc<DxvkImageView>&          dstView);\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_unbound.cpp",
    "content": "#include \"dxvk_device.h\"\n\nnamespace dxvk {\n  \n  DxvkUnboundResources::DxvkUnboundResources(DxvkDevice* dev)\n  : m_device(dev) {\n\n  }\n  \n  \n  DxvkUnboundResources::~DxvkUnboundResources() {\n    \n  }\n\n\n  DxvkResourceBufferInfo DxvkUnboundResources::bufferInfo() {\n    if (unlikely(!m_bufferCreated.load(std::memory_order_acquire))) {\n      std::lock_guard lock(m_mutex);\n\n      if (!m_bufferCreated.load(std::memory_order_acquire)) {\n        m_buffer = createBuffer();\n        m_bufferCreated.store(true, std::memory_order_release);\n      }\n    }\n\n    return m_buffer->getSliceInfo();;\n  }\n\n\n  DxvkSamplerDescriptor DxvkUnboundResources::samplerInfo() {\n    if (unlikely(!m_samplerCreated.load(std::memory_order_acquire))) {\n      std::lock_guard lock(m_mutex);\n\n      if (!m_samplerCreated.load(std::memory_order_acquire)) {\n        m_sampler = createSampler();\n        m_samplerCreated.store(true, std::memory_order_release);\n      }\n    }\n\n    return m_sampler->getDescriptor();\n  }\n\n\n  Rc<DxvkSampler> DxvkUnboundResources::createSampler() {\n    DxvkSamplerKey info;\n    info.setFilter(VK_FILTER_LINEAR, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_LINEAR);\n    info.setLodRange(-256.0f, 256.0f, 0.0f);\n    info.setAddressModes(\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);\n    info.setReduction(VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE);\n\n    return m_device->createSampler(info);\n  }\n  \n  \n  Rc<DxvkBuffer> DxvkUnboundResources::createBuffer() {\n    DxvkBufferCreateInfo info;\n    info.size       = MaxUniformBufferSize;\n    info.usage      = VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                    | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT\n                    | VK_BUFFER_USAGE_INDEX_BUFFER_BIT\n                    | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT\n                    | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                    | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT\n                    | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT\n                    | VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;\n    info.stages     = VK_PIPELINE_STAGE_TRANSFER_BIT\n                    | m_device->getShaderPipelineStages();\n    info.access     = VK_ACCESS_UNIFORM_READ_BIT\n                    | VK_ACCESS_SHADER_READ_BIT\n                    | VK_ACCESS_SHADER_WRITE_BIT;\n    info.debugName  = \"Null buffer\";\n    \n    Rc<DxvkBuffer> buffer = m_device->createBuffer(info,\n      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n      VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |\n      VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n    std::memset(buffer->mapPtr(0), 0, info.size);\n    return buffer;\n  }\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_unbound.h",
    "content": "#pragma once\n\n#include <mutex>\n\n#include \"dxvk_buffer.h\"\n#include \"dxvk_image.h\"\n#include \"dxvk_sampler.h\"\n\nnamespace dxvk {\n\n  class DxvkContext;\n  \n  /**\n   * \\brief Unbound resources\n   * \n   * Creates dummy resources that will be used\n   * for descriptor sets when the client API did\n   * not bind a compatible resource to a slot.\n   */\n  class DxvkUnboundResources {\n    \n  public:\n    \n    DxvkUnboundResources(DxvkDevice* dev);\n    ~DxvkUnboundResources();\n    \n    /**\n     * \\brief Dummy buffer handle\n     * \n     * Returns a handle to a buffer filled with zeroes.\n     * Use for unbound transform feedback buffers only.\n     * \\returns Dummy buffer handle\n     */\n    DxvkResourceBufferInfo bufferInfo();\n\n    /**\n     * \\brief Dummy sampler object\n     * \n     * Points to a sampler which was created with\n     * reasonable default values. Client APIs may\n     * still require different behaviour.\n     * \\returns Dummy sampler\n     */\n    DxvkSamplerDescriptor samplerInfo();\n\n  private:\n    \n    DxvkDevice*             m_device;\n\n    std::atomic<bool>       m_bufferCreated = { false };\n    std::atomic<bool>       m_samplerCreated = { false };\n\n    dxvk::mutex             m_mutex;\n    Rc<DxvkSampler>         m_sampler;\n    Rc<DxvkBuffer>          m_buffer;\n    \n    Rc<DxvkSampler> createSampler();\n    \n    Rc<DxvkBuffer> createBuffer();\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/dxvk_util.cpp",
    "content": "#include <cstring>\n\n#include \"dxvk_format.h\"\n#include \"dxvk_util.h\"\n\nnamespace dxvk::util {\n  \n  uint32_t computeMipLevelCount(VkExtent3D imageSize) {\n    uint32_t maxDim = std::max(imageSize.width, imageSize.height);\n             maxDim = std::max(imageSize.depth, maxDim);\n    uint32_t mipCnt = 0;\n    \n    while (maxDim > 0) {\n      mipCnt += 1;\n      maxDim /= 2;\n    }\n    \n    return mipCnt;\n  }\n  \n  \n  void packImageData(\n          void*             dstBytes,\n    const void*             srcBytes,\n          VkExtent3D        blockCount,\n          VkDeviceSize      blockSize,\n          VkDeviceSize      pitchPerRow,\n          VkDeviceSize      pitchPerLayer) {\n    auto dstData = reinterpret_cast<      char*>(dstBytes);\n    auto srcData = reinterpret_cast<const char*>(srcBytes);\n    \n    const VkDeviceSize bytesPerRow   = blockCount.width  * blockSize;\n    const VkDeviceSize bytesPerLayer = blockCount.height * bytesPerRow;\n    const VkDeviceSize bytesTotal    = blockCount.depth  * bytesPerLayer;\n    \n    const bool directCopy = ((bytesPerRow   == pitchPerRow  ) || (blockCount.height == 1))\n                         && ((bytesPerLayer == pitchPerLayer) || (blockCount.depth  == 1));\n    \n    if (directCopy) {\n      std::memcpy(dstData, srcData, bytesTotal);\n    } else {\n      for (uint32_t i = 0; i < blockCount.depth; i++) {\n        for (uint32_t j = 0; j < blockCount.height; j++) {\n          std::memcpy(\n            dstData + j * bytesPerRow,\n            srcData + j * pitchPerRow,\n            bytesPerRow);\n        }\n        \n        srcData += pitchPerLayer;\n        dstData += bytesPerLayer;\n      }\n    }\n  }\n  \n  \n  void packImageData(\n          void*             dstBytes,\n    const void*             srcBytes,\n          VkDeviceSize      srcRowPitch,\n          VkDeviceSize      srcSlicePitch,\n          VkDeviceSize      dstRowPitchIn,\n          VkDeviceSize      dstSlicePitchIn,\n          VkImageType       imageType,\n          VkExtent3D        imageExtent,\n          uint32_t          imageLayers,\n    const DxvkFormatInfo*   formatInfo,\n          VkImageAspectFlags aspectMask) {\n    auto dstData = reinterpret_cast<      char*>(dstBytes);\n    auto srcData = reinterpret_cast<const char*>(srcBytes);\n\n    for (uint32_t k = 0; k < imageLayers; k++) {\n      for (auto aspects = aspectMask; aspects; ) {\n        auto aspect = vk::getNextAspect(aspects);\n        auto extent = imageExtent;\n        auto elementSize = formatInfo->elementSize;\n\n        if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n          auto plane = &formatInfo->planes[vk::getPlaneIndex(aspect)];\n          extent.width  /= plane->blockSize.width;\n          extent.height /= plane->blockSize.height;\n          elementSize = plane->elementSize;\n        }\n\n        auto blockCount = computeBlockCount(extent, formatInfo->blockSize);\n\n        VkDeviceSize bytesPerRow   = blockCount.width  * elementSize;\n        VkDeviceSize bytesPerSlice = blockCount.height * bytesPerRow;\n        VkDeviceSize bytesTotal    = blockCount.depth  * bytesPerSlice;\n\n        VkDeviceSize dstRowPitch   = dstRowPitchIn   ? dstRowPitchIn   : bytesPerRow;\n        VkDeviceSize dstSlicePitch = dstSlicePitchIn ? dstSlicePitchIn : bytesPerSlice;\n\n        const bool directCopy = ((bytesPerRow   == srcRowPitch   && bytesPerRow   == dstRowPitch  ) || (blockCount.height == 1))\n                             && ((bytesPerSlice == srcSlicePitch && bytesPerSlice == dstSlicePitch) || (blockCount.depth  == 1));\n\n        if (directCopy) {\n          std::memcpy(dstData, srcData, bytesTotal);\n\n          switch (imageType) {\n            case VK_IMAGE_TYPE_1D:\n              srcData += srcRowPitch;\n              dstData += dstRowPitch;\n              break;\n            case VK_IMAGE_TYPE_2D:\n              srcData += blockCount.height * srcRowPitch;\n              dstData += blockCount.height * dstRowPitch;\n              break;\n            case VK_IMAGE_TYPE_3D:\n              srcData += blockCount.depth * srcSlicePitch;\n              dstData += blockCount.depth * dstSlicePitch;\n              break;\n            default: ;\n          }\n        } else {\n          for (uint32_t i = 0; i < blockCount.depth; i++) {\n            for (uint32_t j = 0; j < blockCount.height; j++) {\n              std::memcpy(\n                dstData + j * dstRowPitch,\n                srcData + j * srcRowPitch,\n                bytesPerRow);\n            }\n\n            switch (imageType) {\n              case VK_IMAGE_TYPE_1D:\n                srcData += srcRowPitch;\n                dstData += dstRowPitch;\n                break;\n              case VK_IMAGE_TYPE_2D:\n                srcData += blockCount.height * srcRowPitch;\n                dstData += blockCount.height * dstRowPitch;\n                break;\n              case VK_IMAGE_TYPE_3D:\n                srcData += srcSlicePitch;\n                dstData += dstSlicePitch;\n                break;\n              default: ;\n            }\n          }\n        }\n      }\n    }\n  }\n\n\n  VkDeviceSize computeImageDataSize(VkFormat format, VkExtent3D extent) {\n    const DxvkFormatInfo* formatInfo = lookupFormatInfo(format);\n    return computeImageDataSize(format, extent, formatInfo->aspectMask);\n  }\n\n\n  VkDeviceSize computeImageDataSize(VkFormat format, VkExtent3D extent, VkImageAspectFlags aspects) {\n    const DxvkFormatInfo* formatInfo = lookupFormatInfo(format);\n\n    VkDeviceSize size = 0;\n\n    while (aspects) {\n      auto aspect = vk::getNextAspect(aspects);\n      auto elementSize = formatInfo->elementSize;\n      auto planeExtent = extent;\n\n      if (formatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {\n        auto plane = &formatInfo->planes[vk::getPlaneIndex(aspect)];\n        planeExtent.width  /= plane->blockSize.width;\n        planeExtent.height /= plane->blockSize.height;\n        elementSize = plane->elementSize;\n      }\n\n      size += elementSize * flattenImageExtent(computeBlockCount(planeExtent, formatInfo->blockSize));\n    }\n\n    return size;\n  }\n\n\n  static VkColorComponentFlags remapComponentFlag(\n          VkColorComponentFlags       mask,\n          VkComponentSwizzle          swizzle,\n          VkColorComponentFlagBits    identity) {\n    VkColorComponentFlags bit;\n\n    switch (swizzle) {\n      case VK_COMPONENT_SWIZZLE_IDENTITY: bit = identity;                 break;\n      case VK_COMPONENT_SWIZZLE_R:        bit = VK_COLOR_COMPONENT_R_BIT; break;\n      case VK_COMPONENT_SWIZZLE_G:        bit = VK_COLOR_COMPONENT_G_BIT; break;\n      case VK_COMPONENT_SWIZZLE_B:        bit = VK_COLOR_COMPONENT_B_BIT; break;\n      case VK_COMPONENT_SWIZZLE_A:        bit = VK_COLOR_COMPONENT_A_BIT; break;\n      default:                            bit = 0; /* SWIZZLE_ZERO, SWIZZLE_ONE */\n    }\n\n    return (mask & bit) ? identity : 0;\n  }\n\n\n  VkColorComponentFlags remapComponentMask(\n          VkColorComponentFlags       mask,\n          VkComponentMapping          mapping) {\n    VkColorComponentFlags result = 0;\n    result |= remapComponentFlag(mask, mapping.r, VK_COLOR_COMPONENT_R_BIT);\n    result |= remapComponentFlag(mask, mapping.g, VK_COLOR_COMPONENT_G_BIT);\n    result |= remapComponentFlag(mask, mapping.b, VK_COLOR_COMPONENT_B_BIT);\n    result |= remapComponentFlag(mask, mapping.a, VK_COLOR_COMPONENT_A_BIT);\n    return result;\n  }\n\n\n  static VkComponentSwizzle findComponentSwizzle(\n          VkComponentSwizzle          swizzle,\n          VkComponentSwizzle          identity,\n          VkComponentMapping          mapping) {\n    if (identity == VK_COMPONENT_SWIZZLE_IDENTITY)\n      return VK_COMPONENT_SWIZZLE_IDENTITY;\n    \n    if (mapping.r == swizzle)\n      return VK_COMPONENT_SWIZZLE_R;\n    if (mapping.g == swizzle)\n      return VK_COMPONENT_SWIZZLE_G;\n    if (mapping.b == swizzle)\n      return VK_COMPONENT_SWIZZLE_B;\n    if (mapping.a == swizzle)\n      return VK_COMPONENT_SWIZZLE_A;\n    \n    return VK_COMPONENT_SWIZZLE_ZERO;\n  }\n\n\n  VkComponentMapping invertComponentMapping(VkComponentMapping mapping) {\n    VkComponentMapping result;\n    result.r = findComponentSwizzle(VK_COMPONENT_SWIZZLE_R, mapping.r, mapping);\n    result.g = findComponentSwizzle(VK_COMPONENT_SWIZZLE_G, mapping.g, mapping);\n    result.b = findComponentSwizzle(VK_COMPONENT_SWIZZLE_B, mapping.b, mapping);\n    result.a = findComponentSwizzle(VK_COMPONENT_SWIZZLE_A, mapping.a, mapping);\n    return result;\n  }\n\n\n  static VkComponentMapping normalizeComponentMapping(\n          VkComponentMapping          mapping) {\n    mapping.r = mapping.r == VK_COMPONENT_SWIZZLE_IDENTITY ? VK_COMPONENT_SWIZZLE_R : mapping.r;\n    mapping.g = mapping.g == VK_COMPONENT_SWIZZLE_IDENTITY ? VK_COMPONENT_SWIZZLE_G : mapping.g;\n    mapping.b = mapping.b == VK_COMPONENT_SWIZZLE_IDENTITY ? VK_COMPONENT_SWIZZLE_B : mapping.b;\n    mapping.a = mapping.a == VK_COMPONENT_SWIZZLE_IDENTITY ? VK_COMPONENT_SWIZZLE_A : mapping.a;\n    return mapping;\n  }\n\n\n  static VkComponentSwizzle resolveComponentSwizzle(\n          VkComponentSwizzle          swizzle,\n          VkComponentMapping          dstMapping,\n          VkComponentMapping          srcMapping) {\n    VkComponentSwizzle dstSwizzle = VK_COMPONENT_SWIZZLE_IDENTITY;\n    if (dstMapping.r == swizzle) dstSwizzle = VK_COMPONENT_SWIZZLE_R;\n    if (dstMapping.g == swizzle) dstSwizzle = VK_COMPONENT_SWIZZLE_G;\n    if (dstMapping.b == swizzle) dstSwizzle = VK_COMPONENT_SWIZZLE_B;\n    if (dstMapping.a == swizzle) dstSwizzle = VK_COMPONENT_SWIZZLE_A;\n    \n    switch (dstSwizzle) {\n      case VK_COMPONENT_SWIZZLE_R: return srcMapping.r;\n      case VK_COMPONENT_SWIZZLE_G: return srcMapping.g;\n      case VK_COMPONENT_SWIZZLE_B: return srcMapping.b;\n      case VK_COMPONENT_SWIZZLE_A: return srcMapping.a;\n      default: return VK_COMPONENT_SWIZZLE_IDENTITY;\n    }\n  }\n\n\n  VkComponentMapping resolveSrcComponentMapping(\n          VkComponentMapping          dstMapping,\n          VkComponentMapping          srcMapping) {\n    dstMapping = normalizeComponentMapping(dstMapping);\n\n    VkComponentMapping result;\n    result.r = resolveComponentSwizzle(VK_COMPONENT_SWIZZLE_R, dstMapping, srcMapping);\n    result.g = resolveComponentSwizzle(VK_COMPONENT_SWIZZLE_G, dstMapping, srcMapping);\n    result.b = resolveComponentSwizzle(VK_COMPONENT_SWIZZLE_B, dstMapping, srcMapping);\n    result.a = resolveComponentSwizzle(VK_COMPONENT_SWIZZLE_A, dstMapping, srcMapping);\n    return result;\n  }\n\n\n  VkBlendFactor remapAlphaToColorBlendFactor(VkBlendFactor factor) {\n    switch (factor) {\n      // Make sure we use the red component from the\n      // fragment shader since alpha may be undefined\n      case VK_BLEND_FACTOR_SRC_ALPHA:\n        return VK_BLEND_FACTOR_SRC_COLOR;\n\n      case VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA:\n        return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;\n\n      case VK_BLEND_FACTOR_SRC1_ALPHA:\n        return VK_BLEND_FACTOR_SRC1_COLOR;\n\n      case VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA:\n        return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;\n\n      // This is defined to always be 1 for alpha\n      case VK_BLEND_FACTOR_SRC_ALPHA_SATURATE:\n        return VK_BLEND_FACTOR_ONE;\n\n      // Make sure we use the red component from the\n      // attachment since there is no alpha component\n      case VK_BLEND_FACTOR_DST_ALPHA:\n        return VK_BLEND_FACTOR_DST_COLOR;\n\n      case VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA:\n        return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;\n\n      // For blend constants we actually need to do the\n      // opposite and make sure we always use alpha\n      case VK_BLEND_FACTOR_CONSTANT_COLOR:\n        return VK_BLEND_FACTOR_CONSTANT_ALPHA;\n\n      case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR:\n        return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;\n\n      default:\n        return factor;\n    }\n  }\n\n\n  bool isIdentityMapping(\n          VkComponentMapping          mapping) {\n    return (mapping.r == VK_COMPONENT_SWIZZLE_R || mapping.r == VK_COMPONENT_SWIZZLE_IDENTITY)\n        && (mapping.g == VK_COMPONENT_SWIZZLE_G || mapping.g == VK_COMPONENT_SWIZZLE_IDENTITY)\n        && (mapping.b == VK_COMPONENT_SWIZZLE_B || mapping.b == VK_COMPONENT_SWIZZLE_IDENTITY)\n        && (mapping.a == VK_COMPONENT_SWIZZLE_A || mapping.a == VK_COMPONENT_SWIZZLE_IDENTITY);\n  }\n\n\n  uint32_t getComponentIndex(\n          VkComponentSwizzle          component,\n          uint32_t                    identity) {\n    switch (component) {\n      case VK_COMPONENT_SWIZZLE_R: return 0;\n      case VK_COMPONENT_SWIZZLE_G: return 1;\n      case VK_COMPONENT_SWIZZLE_B: return 2;\n      case VK_COMPONENT_SWIZZLE_A: return 3;\n      default: return identity; /* identity, zero, one */\n    }\n  }\n\n\n  VkClearColorValue swizzleClearColor(\n          VkClearColorValue           color,\n          VkComponentMapping          mapping) {\n    VkClearColorValue result;\n    auto swizzles = &mapping.r;\n\n    for (uint32_t i = 0; i < 4; i++) {\n      uint32_t index = getComponentIndex(swizzles[i], i);\n      result.uint32[i] = color.uint32[index];\n    }\n\n    return result;\n  }\n\n\n  bool isBlendConstantBlendFactor(VkBlendFactor factor) {\n    return factor == VK_BLEND_FACTOR_CONSTANT_COLOR\n        || factor == VK_BLEND_FACTOR_CONSTANT_ALPHA\n        || factor == VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR\n        || factor == VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;\n  }\n\n\n  bool isDualSourceBlendFactor(VkBlendFactor factor) {\n    return factor == VK_BLEND_FACTOR_SRC1_COLOR\n        || factor == VK_BLEND_FACTOR_SRC1_ALPHA\n        || factor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR\n        || factor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;\n  }\n\n\n  VkSampleLocationsInfoEXT setupSampleLocations(\n          VkSampleCountFlagBits       sampleCount,\n          VkBool32                    center) {\n    static const std::array<VkSampleLocationEXT, 16u> s_centerLocations = {{\n      { 0.5f, 0.5f }, { 0.5f, 0.5f }, { 0.5f, 0.5f }, { 0.5f, 0.5f },\n      { 0.5f, 0.5f }, { 0.5f, 0.5f }, { 0.5f, 0.5f }, { 0.5f, 0.5f },\n      { 0.5f, 0.5f }, { 0.5f, 0.5f }, { 0.5f, 0.5f }, { 0.5f, 0.5f },\n      { 0.5f, 0.5f }, { 0.5f, 0.5f }, { 0.5f, 0.5f }, { 0.5f, 0.5f },\n    }};\n\n    static const std::array<VkSampleLocationEXT, 32u> s_defaultLocations = {{\n      /* Invalid resource */\n      { 0.5f, 0.5f },\n      /* 1 samples */\n      { 0.5f, 0.5f },\n      /* 2 samples */\n      { 0.75f, 0.75f },\n      { 0.25f, 0.25f },\n      /* 4 samples */\n      { 0.375f, 0.125f },\n      { 0.875f, 0.375f },\n      { 0.125f, 0.625f },\n      { 0.625f, 0.875f },\n      /* 8 samples */\n      { 0.5625f, 0.3125f },\n      { 0.4375f, 0.6875f },\n      { 0.8125f, 0.5625f },\n      { 0.3125f, 0.1875f },\n      { 0.1875f, 0.8125f },\n      { 0.0625f, 0.4375f },\n      { 0.6875f, 0.9375f },\n      { 0.9375f, 0.0625f },\n      /* 16 samples */\n      { 0.5625f, 0.5625f },\n      { 0.4375f, 0.3125f },\n      { 0.3125f, 0.6250f },\n      { 0.7500f, 0.4375f },\n      { 0.1875f, 0.3750f },\n      { 0.6250f, 0.8125f },\n      { 0.8125f, 0.6875f },\n      { 0.6875f, 0.1875f },\n      { 0.3750f, 0.8750f },\n      { 0.5000f, 0.0625f },\n      { 0.2500f, 0.1250f },\n      { 0.1250f, 0.7500f },\n      { 0.0000f, 0.5000f },\n      { 0.9375f, 0.2500f },\n      { 0.8750f, 0.9375f },\n      { 0.0625f, 0.0000f },\n    }};\n\n    uint32_t count = uint32_t(sampleCount);\n\n    VkSampleLocationsInfoEXT locations = { VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT };\n    locations.sampleLocationsPerPixel = sampleCount;\n    locations.sampleLocationGridSize = { 1u, 1u };\n    locations.sampleLocationsCount = count;\n    locations.pSampleLocations = center\n      ? &s_centerLocations[0u]\n      : &s_defaultLocations[count];\n    return locations;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/dxvk_util.h",
    "content": "#pragma once\n\n#include \"dxvk_include.h\"\n\nnamespace dxvk::util {\n\n  /**\n   * \\brief Debug utils label type\n   */\n  enum class DxvkDebugLabelType : uint32_t {\n    External,               ///< App-provided scope\n    InternalRenderPass,     ///< Internal render pass markers\n    InternalBarrierControl, ///< Barrier control markers\n  };\n\n  /**\n   * \\brief Debug label wrapper\n   *\n   * Wrapper around a Vulkan debug label that\n   * persistently stores the string in question.\n   */\n  class DxvkDebugLabel {\n\n  public:\n\n    DxvkDebugLabel(const VkDebugUtilsLabelEXT& label, DxvkDebugLabelType type)\n    : m_text(label.pLabelName ? label.pLabelName : \"\"), m_type(type) {\n      for (uint32_t i = 0; i < m_color.size(); i++)\n        m_color[i] = label.color[i];\n    }\n\n    DxvkDebugLabelType type() const {\n      return m_type;\n    }\n\n    VkDebugUtilsLabelEXT get() const {\n      VkDebugUtilsLabelEXT label = { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT };\n      label.pLabelName = m_text.c_str();\n      for (uint32_t i = 0; i < m_color.size(); i++)\n        label.color[i] = m_color[i];\n      return label;\n    }\n\n  private:\n\n    std::string           m_text;\n    std::array<float, 4>  m_color = { };\n    DxvkDebugLabelType    m_type;\n\n  };\n\n\n  /**\n   * \\brief Built-in shader stage\n   *\n   * Stores pointer to shader code and code size.\n   */\n  struct DxvkBuiltInShaderStage {\n    DxvkBuiltInShaderStage() = default;\n\n    template<size_t N>\n    DxvkBuiltInShaderStage(const uint32_t (&dwords)[N], const VkSpecializationInfo* s)\n    : size(N * sizeof(uint32_t)), code(&dwords[0]), spec(s) { }\n\n    size_t                      size = 0u;\n    const uint32_t*             code = nullptr;\n    const VkSpecializationInfo* spec = nullptr;\n  };\n\n\n  /**\n   * \\brief Built-in graphics pipeline state\n   *\n   * For any state not explicitly specified, sane\n   * defaults will be chosen as necessary.\n   */\n  struct DxvkBuiltInGraphicsState {\n    /** Vertex shader. Must be defined. */\n    DxvkBuiltInShaderStage vs;\n    /** Geometry shader. */\n    DxvkBuiltInShaderStage gs;\n    /** Fragment shader. */\n    DxvkBuiltInShaderStage fs;\n    /** Color attachment format. */\n    VkFormat colorFormat = VK_FORMAT_UNDEFINED;\n    /** Depth-stencil attachment format */\n    VkFormat depthFormat = VK_FORMAT_UNDEFINED;\n    /** Sample count */\n    VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;\n    /** Vertex input state. If null, a default state containing\n     *  no vertex attributes or bindings will be used. */\n    const VkPipelineVertexInputStateCreateInfo* viState = nullptr;\n    /** Primitive topology state. If null, this will use a\n     *  triangle list without primitive restart. */\n    const VkPipelineInputAssemblyStateCreateInfo* iaState = nullptr;\n    /** Rasterization state. If null, this will use a default\n     *  state without any back-face culling. */\n    const VkPipelineRasterizationStateCreateInfo* rsState = nullptr;\n    /** Depth-stencil state. If null, no depth or stencil tests\n     *  will be performed by the pipeline. */\n    const VkPipelineDepthStencilStateCreateInfo* dsState = nullptr;\n    /** Blend state for the color attachment. If null, blending\n     *  will be disabled and all color components are written. */\n    const VkPipelineColorBlendAttachmentState* cbAttachment = nullptr;\n    /** Additional dynamic states. These will be added to the\n     *  default set of viewport and scissor states. */\n    uint32_t dynamicStateCount = 0u;\n    const VkDynamicState* dynamicStates = nullptr;\n  };\n\n\n  /**\n   * \\brief Gets pipeline stage flags for shader stages\n   * \n   * \\param [in] shaderStages Shader stage flags\n   * \\returns Corresponding pipeline stage flags\n   */\n  inline VkPipelineStageFlags pipelineStages(\n          VkShaderStageFlags shaderStages) {\n    return (shaderStages & VK_SHADER_STAGE_ALL_GRAPHICS) << 3\n         | (shaderStages & VK_SHADER_STAGE_COMPUTE_BIT) << 6;\n  }\n  \n  /**\n   * \\brief Gets shader stage flags included in pipeline stages\n   *\n   * \\param [in] pipelineStages Pipeline stage flags\n   * \\returns Corresponding shader stage flags, if any\n   */\n  inline VkShaderStageFlags shaderStages(\n          VkPipelineStageFlags pipelineStages) {\n    return ((pipelineStages >> 3) & VK_SHADER_STAGE_ALL_GRAPHICS)\n         | ((pipelineStages >> 6) & VK_SHADER_STAGE_COMPUTE_BIT);\n  }\n\n  /**\n   * \\brief Computes number of mip levels for an image\n   * \n   * \\param [in] imageSize Size of the image\n   * \\returns Number of mipmap layers\n   */\n  uint32_t computeMipLevelCount(VkExtent3D imageSize);\n  \n  /**\n   * \\brief Writes tightly packed image data to a buffer\n   * \n   * \\param [in] dstBytes Destination buffer pointer\n   * \\param [in] srcBytes Pointer to source data\n   * \\param [in] blockCount Number of blocks to copy\n   * \\param [in] blockSize Number of bytes per block\n   * \\param [in] pitchPerRow Number of bytes between rows\n   * \\param [in] pitchPerLayer Number of bytes between layers\n   */\n  void packImageData(\n          void*             dstBytes,\n    const void*             srcBytes,\n          VkExtent3D        blockCount,\n          VkDeviceSize      blockSize,\n          VkDeviceSize      pitchPerRow,\n          VkDeviceSize      pitchPerLayer);\n  \n  /**\n   * \\brief Repacks image data to a buffer\n   * \n   * Note that passing destination pitches of 0 means that the data is\n   * tightly packed, while a source pitch of 0 will not show this behaviour\n   * in order to match client API behaviour for initialization.\n   * \\param [in] dstBytes Destination buffer pointer\n   * \\param [in] srcBytes Pointer to source data\n   * \\param [in] srcRowPitch Number of bytes between rows to read\n   * \\param [in] srcSlicePitch Number of bytes between layers to read\n   * \\param [in] dstRowPitch Number of bytes between rows to write\n   * \\param [in] dstSlicePitch Number of bytes between layers to write\n   * \\param [in] imageType Image type (2D, 3D etc)\n   * \\param [in] imageExtent Image extent, in pixels\n   * \\param [in] imageLayers Image layer count\n   * \\param [in] formatInfo Image format info\n   * \\param [in] aspectMask Image aspects to pack\n   */\n  void packImageData(\n          void*             dstBytes,\n    const void*             srcBytes,\n          VkDeviceSize      srcRowPitch,\n          VkDeviceSize      srcSlicePitch,\n          VkDeviceSize      dstRowPitchIn,\n          VkDeviceSize      dstSlicePitchIn,\n          VkImageType       imageType,\n          VkExtent3D        imageExtent,\n          uint32_t          imageLayers,\n    const DxvkFormatInfo*   formatInfo,\n          VkImageAspectFlags aspectMask);\n  \n  /**\n   * \\brief Computes minimum extent\n   * \n   * \\param [in] a First value\n   * \\param [in] b Second value\n   * \\returns Component-wise \\c min\n   */\n  inline VkExtent3D minExtent3D(VkExtent3D a, VkExtent3D b) {\n    return VkExtent3D {\n      std::min(a.width,  b.width),\n      std::min(a.height, b.height),\n      std::min(a.depth,  b.depth) };\n  }\n  \n  /**\n   * \\brief Checks whether an offset is block-aligned\n   * \n   * An offset is considered block-aligned if it is\n   * a multiple of the block size. Only non-negative\n   * offset values are valid.\n   * \\param [in] offset The offset to check\n   * \\param [in] blockSize Block size\n   * \\returns \\c true if \\c offset is aligned\n   */\n  inline bool isBlockAligned(VkOffset3D offset, VkExtent3D blockSize) {\n    return (offset.x % blockSize.width  == 0)\n        && (offset.y % blockSize.height == 0)\n        && (offset.z % blockSize.depth  == 0);\n  }\n  \n  /**\n   * \\brief Checks whether an offset and extent are block-aligned\n   * \n   * A block-aligned extent can be used for image copy\n   * operations that involve block-compressed images.\n   * \\param [in] offset The base offset\n   * \\param [in] extent The extent to check\n   * \\param [in] blockSize Compressed block size\n   * \\param [in] imageSize Image size\n   * \\returns \\c true if all components of \\c extent\n   *          are aligned or touch the image border.\n   */\n  inline bool isBlockAligned(VkOffset3D offset, VkExtent3D extent, VkExtent3D blockSize, VkExtent3D imageSize) {\n    return ((extent.width  % blockSize.width  == 0) || (uint32_t(offset.x + extent.width)  == imageSize.width))\n        && ((extent.height % blockSize.height == 0) || (uint32_t(offset.y + extent.height) == imageSize.height))\n        && ((extent.depth  % blockSize.depth  == 0) || (uint32_t(offset.z + extent.depth)  == imageSize.depth))\n        && isBlockAligned(offset, blockSize);\n  }\n  \n  /**\n   * \\brief Computes mip level extent\n   *\n   * \\param [in] size Base mip level extent\n   * \\param [in] level mip level to compute\n   * \\returns Extent of the given mip level\n   */\n  inline VkExtent3D computeMipLevelExtent(VkExtent3D size, uint32_t level) {\n    size.width  = std::max(1u, size.width  >> level);\n    size.height = std::max(1u, size.height >> level);\n    size.depth  = std::max(1u, size.depth  >> level);\n    return size;\n  }\n\n  /**\n   * \\brief Computes offset in a given mip level\n   *\n   * \\param [in] size Base mip level offset\n   * \\param [in] level mip level to compute\n   * \\returns Offset on the given mip level\n   */\n  inline VkOffset3D computeMipLevelOffset(VkOffset3D offset, uint32_t level) {\n    offset.x  = offset.x >> level;\n    offset.y  = offset.y >> level;\n    offset.z  = offset.z >> level;\n    return offset;\n  }\n\n  /**\n   * \\brief Computes mip level extent\n   *\n   * This function variant takes into account planar formats.\n   * \\param [in] size Base mip level extent\n   * \\param [in] level Mip level to compute\n   * \\param [in] format Image format\n   * \\param [in] aspect Image aspect to consider\n   * \\returns Extent of the given mip level\n   */\n  inline VkExtent3D computeMipLevelExtent(VkExtent3D size, uint32_t level, VkFormat format, VkImageAspectFlags aspect) {\n    if (unlikely(!(aspect & (VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)))) {\n      auto plane = &lookupFormatInfo(format)->planes[vk::getPlaneIndex(aspect)];\n      size.width  /= plane->blockSize.width;\n      size.height /= plane->blockSize.height;\n    }\n\n    size.width  = std::max(1u, size.width  >> level);\n    size.height = std::max(1u, size.height >> level);\n    size.depth  = std::max(1u, size.depth  >> level);\n    return size;\n  }\n  \n  /**\n   * \\brief Computes block offset for compressed images\n   * \n   * Convenience function to compute the block position\n   * within a compressed image based on the block size.\n   * \\param [in] offset The offset\n   * \\param [in] blockSize Size of a pixel block\n   * \\returns The block offset\n   */\n  inline VkOffset3D computeBlockOffset(VkOffset3D offset, VkExtent3D blockSize) {\n    return VkOffset3D {\n      offset.x / int32_t(blockSize.width),\n      offset.y / int32_t(blockSize.height),\n      offset.z / int32_t(blockSize.depth) };\n  }\n  \n  /**\n   * \\brief Computes block count for compressed images\n   * \n   * Convenience function to compute the size, in\n   * blocks, of compressed images subresources.\n   * \\param [in] extent The image size\n   * \\param [in] blockSize Size of a pixel block\n   * \\returns Number of blocks in the image\n   */\n  inline VkExtent3D computeBlockCount(VkExtent3D extent, VkExtent3D blockSize) {\n    return VkExtent3D {\n      (extent.width  + blockSize.width  - 1) / blockSize.width,\n      (extent.height + blockSize.height - 1) / blockSize.height,\n      (extent.depth  + blockSize.depth  - 1) / blockSize.depth };\n  }\n  \n  /**\n   * \\brief Computes block count for compressed images\n   * \n   * Given an aligned offset, this computes \n   * Convenience function to compute the size, in\n   * blocks, of compressed images subresources.\n   * \\param [in] extent The image size\n   * \\param [in] blockSize Size of a pixel block\n   * \\returns Number of blocks in the image\n   */\n  inline VkExtent3D computeMaxBlockCount(VkOffset3D offset, VkExtent3D extent, VkExtent3D blockSize) {\n    return VkExtent3D {\n      (extent.width  + blockSize.width  - offset.x - 1) / blockSize.width,\n      (extent.height + blockSize.height - offset.y - 1) / blockSize.height,\n      (extent.depth  + blockSize.depth  - offset.z - 1) / blockSize.depth };\n  }\n  \n  /**\n   * \\brief Snaps block-aligned image extent to image edges\n   * \n   * Fixes up an image extent that is aligned to a compressed\n   * block so that it no longer exceeds the given image size.\n   * \\param [in] offset Aligned pixel offset\n   * \\param [in] extent Extent to clamp\n   * \\param [in] imageExtent Image size\n   * \\returns Number of blocks in the image\n   */\n  inline VkExtent3D snapExtent3D(VkOffset3D offset, VkExtent3D extent, VkExtent3D imageExtent) {\n    return VkExtent3D {\n      std::min(extent.width,  imageExtent.width  - uint32_t(offset.x)),\n      std::min(extent.height, imageExtent.height - uint32_t(offset.y)),\n      std::min(extent.depth,  imageExtent.depth  - uint32_t(offset.z)) };\n  }\n  \n  /**\n   * \\brief Computes block extent for compressed images\n   * \n   * \\param [in] blockCount The number of blocks\n   * \\param [in] blockSize Size of a pixel block\n   * \\returns Extent of the given blocks\n   */\n  inline VkExtent3D computeBlockExtent(VkExtent3D blockCount, VkExtent3D blockSize) {\n    return VkExtent3D {\n      blockCount.width  * blockSize.width,\n      blockCount.height * blockSize.height,\n      blockCount.depth  * blockSize.depth };\n  }\n  \n  /**\n   * \\brief Computes number of pixels or blocks of an image\n   * \n   * Basically returns the product of width, height and depth.\n   * \\param [in] extent Image extent, in pixels or blocks\n   * \\returns Flattened number of pixels or blocks\n   */\n  inline uint32_t flattenImageExtent(VkExtent3D extent) {\n    return extent.width * extent.height * extent.depth;\n  }\n\n  /**\n   * \\brief Checks whether the depth aspect is read-only in a layout\n   * \n   * \\param [in] layout Image layout. Must be a valid depth-stencil attachment laoyut.\n   * \\returns \\c true if the depth aspect for images in this layout is read-only.\n   */\n  inline bool isDepthReadOnlyLayout(VkImageLayout layout) {\n    return layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL\n        || layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL;\n  }\n  \n  /**\n   * \\brief Computes image data size, in bytes\n   * \n   * Convenience method that can be used to compute the number\n   * of bytes required to store image data in a given format.\n   * \\param [in] format The image format\n   * \\param [in] extent Image size, in pixels\n   * \\returns Data size, in bytes\n   */\n  VkDeviceSize computeImageDataSize(VkFormat format, VkExtent3D extent);\n\n  /**\n   * \\brief Computes image data size, in bytes\n   *\n   * Convenience method that can be used to compute the number\n   * of bytes required to store image data in a given format\n   * for the given aspects.\n   * \\param [in] format The image format\n   * \\param [in] extent Image size, in pixels\n   * \\param [in] aspects Aspect mask\n   * \\returns Data size, in bytes\n   */\n  VkDeviceSize computeImageDataSize(VkFormat format, VkExtent3D extent, VkImageAspectFlags aspects);\n\n  /**\n   * \\brief Applies a component mapping to a component mask\n   * \n   * For each component, the component specified in the mapping\n   * is used to look up the flag of the original component mask.\n   * If the component mapping is zero or one, the corresponding\n   * mask bit will be set to zero.\n   * \\param [in] mask The original component mask\n   * \\param [in] mapping Component mapping to apply\n   * \\returns Remapped component mask\n   */\n  VkColorComponentFlags remapComponentMask(\n          VkColorComponentFlags       mask,\n          VkComponentMapping          mapping);\n  \n  /**\n   * \\brief Inverts a component mapping\n   *\n   * Transforms a component mapping so that components can\n   * be mapped back to their original location. Requires\n   * that each component is used only once.\n   * \n   * For example. when given a mapping of (0,0,0,R),\n   * this function will return the mapping (A,0,0,0).\n   * \\returns Inverted component mapping\n   */\n  VkComponentMapping invertComponentMapping(\n          VkComponentMapping          mapping);\n\n  /**\n   * \\brief Resolves source component mapping\n   *\n   * Returns the source component mapping after rearranging\n   * the destination mapping to be the identity mapping.\n   * \\param [in] dstMapping Destination mapping\n   * \\param [in] srcMapping Source mapping\n   * \\returns Adjusted src component mapping\n   */\n  VkComponentMapping resolveSrcComponentMapping(\n          VkComponentMapping          dstMapping,\n          VkComponentMapping          srcMapping);\n\n  /**\n   * \\brief Remaps alpha blend factor to a color one\n   *\n   * Needed when rendering to alpha-only render targets\n   * which we only support through single-channel formats.\n   * \\param [in] factor Alpha blend factor\n   * \\returns Corresponding color blend factor\n   */\n  VkBlendFactor remapAlphaToColorBlendFactor(VkBlendFactor factor);\n\n  bool isIdentityMapping(\n          VkComponentMapping          mapping);\n\n  /**\n   * \\brief Computes component index for a component swizzle\n   * \n   * \\param [in] component The component swizzle\n   * \\param [in] identity Value for SWIZZLE_IDENTITY\n   * \\returns Component index\n   */\n  uint32_t getComponentIndex(\n          VkComponentSwizzle          component,\n          uint32_t                    identity);\n  \n  VkClearColorValue swizzleClearColor(\n          VkClearColorValue           color,\n          VkComponentMapping          mapping);\n  \n  bool isBlendConstantBlendFactor(\n          VkBlendFactor               factor);\n  \n  bool isDualSourceBlendFactor(\n          VkBlendFactor               factor);\n\n  /**\n   * \\brief Determines sample positions\n   *\n   * \\param [in] sampleCount Sample count\n   * \\param [in] center Whether to center samples or use default locations\n   * \\returns Sample location structure\n   */\n  VkSampleLocationsInfoEXT setupSampleLocations(\n          VkSampleCountFlagBits       sampleCount,\n          VkBool32                    center);\n\n  /**\n   * \\brief Computes unsigned normalized value\n   *\n   * Doesn't necessarily do perfect rounding.\n   * \\param [in] float Float value\n   * \\param [in] bits Bit count\n   * \\returns Normalized value\n   */\n  inline uint32_t computeUnorm(float f, uint32_t bits) {\n    f = std::max(f, 0.0f);\n    f = std::min(f, 1.0f);\n    return uint32_t((f * float((1u << bits) - 1u)) + 0.5f);\n  }\n\n  /**\n   * \\brief Computes signed normalized value\n   *\n   * Doesn't necessarily do perfect rounding.\n   * \\param [in] float Float value\n   * \\param [in] bits Bit count\n   * \\returns Normalized value\n   */\n  inline uint32_t computeSnorm(float f, uint32_t bits) {\n    f = std::max(f, -1.0f);\n    f = std::min(f,  1.0f);\n    return int32_t((f * float((1u << (bits - 1u)) - 1u)) + (f < 0.0f ? -0.5f : 0.5f));\n  }\n\n  /**\n   * \\brief Computes clear payload for compressed image blocks\n   *\n   * Ignores whether the format is sRGB or not. If this is relevant,\n   * the input color must be converted into the correct space first.\n   * Does not support BC6H or BC7 formats.\n   * \\param [in] format Image format\n   * \\param [in] color Clear color, as floats\n   * \\returns Block data as unsigned integers\n   */\n  inline VkClearColorValue encodeClearBlockValue(\n          VkFormat                    format,\n    const VkClearColorValue&          color) {\n    VkClearColorValue result = { };\n\n    switch (format) {\n      case VK_FORMAT_BC1_RGB_SRGB_BLOCK:\n      case VK_FORMAT_BC1_RGB_UNORM_BLOCK:\n      case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:\n      case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: {\n        // Encode clear color as color0, table will be all zeroes\n        result.uint32[0] = (computeUnorm(color.float32[2], 5) <<  0)\n                         | (computeUnorm(color.float32[1], 6) <<  5)\n                         | (computeUnorm(color.float32[0], 5) << 11);\n      } return result;\n\n      case VK_FORMAT_BC2_SRGB_BLOCK:\n      case VK_FORMAT_BC2_UNORM_BLOCK: {\n        // Alpha is encoded in the first four bytes as four-bit\n        // values. The color portion is identical to BC1.\n        uint32_t alpha = 0x11111111u * computeUnorm(color.float32[3], 4);\n        result.uint32[0] = alpha;\n        result.uint32[1] = alpha;\n        result.uint32[2] = (computeUnorm(color.float32[2], 5) <<  0)\n                         | (computeUnorm(color.float32[1], 6) <<  5)\n                         | (computeUnorm(color.float32[0], 5) << 11);\n      } return result;\n\n      case VK_FORMAT_BC3_UNORM_BLOCK:\n      case VK_FORMAT_BC3_SRGB_BLOCK: {\n        // Encode alpha as alpha0, color portion identical to BC1\n        result.uint32[0] = computeUnorm(color.float32[3], 8);\n        result.uint32[2] = (computeUnorm(color.float32[2], 5) <<  0)\n                         | (computeUnorm(color.float32[1], 6) <<  5)\n                         | (computeUnorm(color.float32[0], 5) << 11);\n      } return result;\n\n      case VK_FORMAT_BC4_SNORM_BLOCK: {\n        result.uint32[0] = computeSnorm(color.float32[0], 8);\n      } return result;\n\n      case VK_FORMAT_BC4_UNORM_BLOCK: {\n        result.uint32[0] = computeUnorm(color.float32[0], 8);\n      } return result;\n\n      case VK_FORMAT_BC5_SNORM_BLOCK: {\n        result.uint32[0] = computeSnorm(color.float32[0], 8);\n        result.uint32[2] = computeSnorm(color.float32[1], 8);\n      } return result;\n\n      case VK_FORMAT_BC5_UNORM_BLOCK: {\n        result.uint32[0] = computeUnorm(color.float32[0], 8);\n        result.uint32[2] = computeUnorm(color.float32[1], 8);\n      } return result;\n\n      default:\n        return color;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/hud/dxvk_hud.cpp",
    "content": "#include <algorithm>\n#include <cstring>\n\n#include \"dxvk_hud.h\"\n\nnamespace dxvk::hud {\n  \n  Hud::Hud(\n    const Rc<DxvkDevice>& device)\n  : m_device        (device),\n    m_renderer      (device),\n    m_hudItems      (device) {\n    // Retrieve and sanitize options\n    m_options.scale = std::clamp(m_hudItems.getOption<float>(\"scale\", 1.0f), 0.25f, 4.0f);\n    m_options.opacity = std::clamp(m_hudItems.getOption<float>(\"opacity\", 1.0f), 0.1f, 1.0f);\n\n    addItem<HudVersionItem>(\"version\", -1);\n    addItem<HudDeviceInfoItem>(\"devinfo\", -1, m_device);\n    addItem<HudFpsItem>(\"fps\", -1);\n    addItem<HudFrameTimeItem>(\"frametimes\", -1, device, &m_renderer);\n    addItem<HudSubmissionStatsItem>(\"submissions\", -1, device);\n    addItem<HudDrawCallStatsItem>(\"drawcalls\", -1, device);\n    addItem<HudPipelineStatsItem>(\"pipelines\", -1, device);\n    addItem<HudDescriptorStatsItem>(\"descriptors\", -1, device);\n    addItem<HudMemoryStatsItem>(\"memory\", -1, device);\n    addItem<HudMemoryDetailsItem>(\"allocations\", -1, device, &m_renderer);\n    addItem<HudCsThreadItem>(\"cs\", -1, device);\n    addItem<HudGpuLoadItem>(\"gpuload\", -1, device);\n    addItem<HudCompilerActivityItem>(\"compiler\", -1, device);\n  }\n\n\n  Hud::~Hud() {\n    \n  }\n\n\n  void Hud::update() {\n    m_hudItems.update();\n  }\n\n\n  void Hud::render(\n    const Rc<DxvkCommandList>&ctx,\n    const Rc<DxvkImageView>&  dstView) {\n    if (empty())\n      return;\n\n    auto key = m_renderer.getPipelineKey(dstView);\n\n    m_renderer.beginFrame(ctx, dstView, m_options);\n    m_hudItems.render(ctx, key, m_options, m_renderer);\n    m_renderer.flushDraws(ctx, dstView, m_options);\n    m_renderer.endFrame(ctx);\n  }\n\n\n  Rc<Hud> Hud::createHud(const Rc<DxvkDevice>& device) {\n    return new Hud(device);\n  }\n  \n}\n"
  },
  {
    "path": "src/dxvk/hud/dxvk_hud.h",
    "content": "#pragma once\n\n#include \"../dxvk_device.h\"\n\n#include \"dxvk_hud_item.h\"\n#include \"dxvk_hud_renderer.h\"\n\nnamespace dxvk::hud {\n\n  /**\n   * \\brief DXVK HUD\n   * \n   * Can be used by the presentation backend to\n   * display performance and driver information.\n   */\n  class Hud : public RcObject {\n    \n  public:\n    \n    Hud(const Rc<DxvkDevice>& device);\n    \n    ~Hud();\n\n    /**\n     * \\brief Update HUD\n     * \n     * Updates the data to display.\n     * Should be called once per frame.\n     */\n    void update();\n\n    /**\n     * \\brief Render HUD\n     * \n     * Renders the HUD to the given context.\n     * \\param [in] ctx Context objects for rendering\n     * \\param [in] dstView Swap chain image view\n     */\n    void render(\n      const Rc<DxvkCommandList>&ctx,\n      const Rc<DxvkImageView>&  dstView);\n\n    /**\n     * \\brief Checks whether the HUD is empty\n     * \\returns \\c true if the HUD is empty\n     */\n    bool empty() const {\n      return m_hudItems.empty();\n    }\n\n    /**\n     * \\brief Adds a HUD item if enabled\n     *\n     * \\tparam T The HUD item type\n     * \\param [in] name HUD item name\n     * \\param [in] args Constructor arguments\n     */\n    template<typename T, typename... Args>\n    Rc<T> addItem(const char* name, int32_t at, Args... args) {\n      return m_hudItems.add<T>(name, at, std::forward<Args>(args)...);\n    }\n    \n    /**\n     * \\brief Creates the HUD\n     * \n     * Creates and initializes the HUD if the\n     * \\c DXVK_HUD environment variable is set.\n     * \\param [in] device The DXVK device\n     * \\returns HUD object, if it was created.\n     */\n    static Rc<Hud> createHud(\n      const Rc<DxvkDevice>& device);\n    \n  private:\n    \n    Rc<DxvkDevice>        m_device;\n    \n    HudRenderer           m_renderer;\n    HudItemSet            m_hudItems;\n\n    HudOptions            m_options;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/hud/dxvk_hud_font.cpp",
    "content": "#include \"dxvk_hud_font.h\"\n\nnamespace dxvk::hud {\n  \n  // Data structures and character map generated with:\n  // https://evanw.github.io/font-texture-generator/\n  // \n  // Font texture and metadata generated from:\n  //  'Source Code Pro' by Adobe Systems Inc.\n  //  SIL Open Font License Version 1.1\n  // \n  // See: https://github.com/adobe-fonts/source-code-pro\n  const HudGlyph g_hudFontGlyphs[] = {\n    {' ', 488, 144, 12, 12, 6, 6},\n    {'!', 309, 44, 19, 34, 0, 27},\n    {'\"', 294, 144, 26, 24, 3, 28},\n    {'#', 27, 111, 27, 33, 4, 27},\n    {'$', 262, 0, 27, 39, 4, 30},\n    {'%', 450, 44, 30, 33, 5, 27},\n    {'&', 0, 44, 30, 34, 5, 27},\n    {'\\'', 320, 144, 18, 24, -1, 28},\n    {'(', 41, 0, 22, 41, 0, 29},\n    {')', 63, 0, 21, 41, 2, 29},\n    {'*', 221, 144, 27, 27, 4, 24},\n    {'+', 194, 144, 27, 27, 4, 24},\n    {',', 248, 144, 20, 26, 0, 13},\n    {'-', 461, 144, 27, 15, 4, 18},\n    {'.', 365, 144, 20, 20, 4, 13},\n    {'/', 111, 0, 27, 40, 4, 29},\n    {'0', 88, 78, 28, 33, 4, 27},\n    {'1', 244, 111, 27, 32, 3, 26},\n    {'2', 396, 78, 28, 33, 4, 27},\n    {'3', 424, 78, 28, 33, 5, 27},\n    {'4', 187, 111, 29, 32, 5, 26},\n    {'5', 284, 78, 28, 33, 4, 26},\n    {'6', 116, 78, 28, 33, 4, 27},\n    {'7', 216, 111, 28, 32, 4, 26},\n    {'8', 172, 78, 28, 33, 4, 27},\n    {'9', 368, 78, 28, 33, 4, 27},\n    {':', 320, 111, 20, 30, 0, 23},\n    {';', 342, 0, 20, 36, 0, 23},\n    {'<', 271, 111, 25, 31, 2, 26},\n    {'=', 338, 144, 27, 23, 4, 22},\n    {'>', 296, 111, 24, 31, 3, 26},\n    {'?', 284, 44, 25, 34, 3, 28},\n    {'@', 289, 0, 29, 38, 5, 27},\n    {'A', 328, 44, 31, 33, 6, 27},\n    {'B', 144, 78, 28, 33, 4, 27},\n    {'C', 59, 44, 29, 34, 4, 27},\n    {'D', 200, 78, 28, 33, 4, 27},\n    {'E', 479, 78, 27, 33, 3, 27},\n    {'F', 161, 111, 26, 33, 3, 27},\n    {'G', 174, 44, 28, 34, 5, 27},\n    {'H', 54, 111, 27, 33, 4, 27},\n    {'I', 108, 111, 27, 33, 4, 27},\n    {'J', 452, 78, 27, 33, 4, 27},\n    {'K', 59, 78, 29, 33, 4, 27},\n    {'L', 135, 111, 26, 33, 3, 27},\n    {'M', 228, 78, 28, 33, 4, 27},\n    {'N', 0, 111, 27, 33, 4, 27},\n    {'O', 117, 44, 29, 34, 5, 27},\n    {'P', 312, 78, 28, 33, 4, 27},\n    {'Q', 232, 0, 30, 39, 5, 27},\n    {'R', 30, 78, 29, 33, 4, 27},\n    {'S', 202, 44, 28, 34, 4, 27},\n    {'T', 480, 44, 30, 33, 5, 27},\n    {'U', 81, 111, 27, 33, 4, 27},\n    {'V', 0, 78, 30, 33, 5, 27},\n    {'W', 359, 44, 31, 33, 6, 27},\n    {'X', 420, 44, 30, 33, 5, 27},\n    {'Y', 390, 44, 30, 33, 5, 27},\n    {'Z', 340, 78, 28, 33, 4, 27},\n    {'[', 210, 0, 22, 40, 0, 29},\n    {'\\\\', 84, 0, 27, 40, 4, 29},\n    {']', 188, 0, 22, 40, 3, 29},\n    {'^', 268, 144, 26, 25, 3, 27},\n    {'_', 433, 144, 28, 16, 4, 4},\n    {'`', 385, 144, 20, 20, 2, 32},\n    {'a', 396, 111, 28, 29, 4, 22},\n    {'b', 391, 0, 28, 35, 4, 28},\n    {'c', 452, 111, 27, 29, 4, 22},\n    {'d', 475, 0, 27, 35, 4, 28},\n    {'e', 368, 111, 28, 29, 4, 22},\n    {'f', 447, 0, 28, 35, 3, 29},\n    {'g', 362, 0, 29, 35, 4, 22},\n    {'h', 230, 44, 27, 34, 4, 28},\n    {'i', 318, 0, 24, 36, 3, 30},\n    {'j', 16, 0, 25, 43, 5, 30},\n    {'k', 30, 44, 29, 34, 4, 28},\n    {'l', 419, 0, 28, 35, 4, 28},\n    {'m', 58, 144, 29, 28, 5, 22},\n    {'n', 114, 144, 27, 28, 4, 22},\n    {'o', 424, 111, 28, 29, 4, 22},\n    {'p', 146, 44, 28, 34, 4, 22},\n    {'q', 257, 44, 27, 34, 4, 22},\n    {'r', 168, 144, 26, 28, 2, 22},\n    {'s', 340, 111, 28, 29, 4, 22},\n    {'t', 256, 78, 28, 33, 4, 27},\n    {'u', 87, 144, 27, 28, 4, 22},\n    {'v', 29, 144, 29, 28, 5, 22},\n    {'w', 479, 111, 31, 28, 6, 22},\n    {'x', 0, 144, 29, 28, 5, 22},\n    {'y', 88, 44, 29, 34, 5, 22},\n    {'z', 141, 144, 27, 28, 4, 22},\n    {'{', 138, 0, 25, 40, 3, 29},\n    {'|', 0, 0, 16, 44, -2, 30},\n    {'}', 163, 0, 25, 40, 3, 29},\n    {'~', 405, 144, 28, 18, 4, 20},\n  };\n  \n  const uint8_t g_hudFontImage[] =\n  {\n      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e\n      ,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x0e,0x07,0x06,0x06,0x06,0x06,0x06,0x04,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06\n      ,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19\n      ,0x21,0x28,0x2a,0x25,0x1d,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x25,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a\n      ,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0a,0x01\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x28,0x21,0x14\n      ,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x15\n      ,0x1d,0x25,0x2a,0x28,0x21,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1e\n      ,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x30,0x37,0x40,0x42,0x3c,0x34,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x40,0x42,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31\n      ,0x3c,0x42,0x40,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x04,0x16,0x27,0x36,0x40,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x31,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42\n      ,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43\n      ,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1e,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02\n      ,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x40,0x36,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x40,0x37,0x30,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x34,0x3c,0x42,0x40,0x37,0x30,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x42,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21\n      ,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c,0x37,0x37,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x17,0x25,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x38,0x34\n      ,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b\n      ,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x46,0x4e,0x57,0x5a,0x51,0x4b,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x57,0x5a,0x51,0x40,0x2f,0x1e,0x0d\n      ,0x01,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x51,0x5a,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x57,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x46,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36\n      ,0x21,0x0a,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00\n      ,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x40,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x57,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x3c,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46\n      ,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x41,0x4b,0x51,0x5a,0x57,0x4e,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x5a,0x51,0x46,0x38,0x27,0x16,0x04\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x41,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51,0x4f,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x38,0x46\n      ,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x51,0x5c,0x65,0x6b,0x71,0x67,0x61,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27\n      ,0x38,0x49,0x5a,0x6a,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x66,0x71,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x71,0x61,0x4c,0x36,0x21,0x0a\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5c\n      ,0x66,0x71,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73\n      ,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x52,0x61,0x67,0x71,0x73,0x73,0x73,0x71,0x67\n      ,0x61,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x51\n      ,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x6b,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x57,0x61,0x67,0x71,0x6b,0x65,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x62\n      ,0x71,0x73,0x73,0x71,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x71,0x67,0x67,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73\n      ,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x4c,0x5a,0x65,0x6b,0x73,0x73,0x73,0x73,0x73,0x6b,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34\n      ,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x71,0x7b,0x7f,0x84,0x7f,0x72,0x62,0x51,0x3c,0x25,0x0e\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x6a,0x7b,0x84,0x72,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x0a,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x84,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x8a,0x8c,0x8c,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8a,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x71,0x7b,0x84,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x84,0x7f,0x72,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49\n      ,0x5a,0x66,0x72,0x7f,0x84,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8a,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x66,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8a,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2b,0x17,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x74,0x7f,0x84,0x7f,0x7b,0x6a,0x5a,0x46,0x30,0x19\n      ,0x04,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x62,0x72,0x84,0x8c,0x8c,0x84,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x74,0x7f,0x84,0x8c,0x8c,0x8c,0x84,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x06\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x51,0x61,0x6b,0x7b,0x7f,0x8a,0x8c,0x8c,0x8c,0x8a,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f\n      ,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f\n      ,0x8e,0x97,0x99,0x93,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x8c,0x94,0x84,0x72,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x7b,0x8c,0x97,0x8c,0x7b,0x6a,0x57,0x41,0x2f,0x1e,0x0a\n      ,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0x9f,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x98,0xa1\n      ,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x8e,0x98,0x9d,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98,0x9d,0x99,0x93,0x84,0x72,0x61,0x4b\n      ,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98,0x98,0x98,0x9b,0xa2,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa2,0x9b,0x98,0x98,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x84,0x93,0x99,0xa2,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x2b,0x43,0x5b,0x73,0x8c,0xa1,0x97,0x7f,0x67,0x4f\n      ,0x37,0x22,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x84,0x8e,0x97,0x9b,0x9e,0x9e,0x9b,0x97,0x8e,0x84,0x72,0x61,0x4c,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74\n      ,0x89,0x93,0x99,0x97,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x94,0xa2,0xa2,0x98,0x8c,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a,0x7b,0x8a,0x93,0x99,0x9f,0x9e,0x9f,0x99,0x98,0x98\n      ,0x98,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x15,0x12,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4\n      ,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x4c,0x61,0x72,0x7f,0x8c,0x97,0x9d,0xa4,0xa4,0xa4,0x9d,0x97,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x03,0x0a,0x11,0x12,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xad,0xb0,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a,0x7b,0x8c,0x9c,0x9c,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84\n      ,0x94,0xa2,0x9c,0x8a,0x74,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x98,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa2,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa1,0x9d,0x8e,0x84,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b\n      ,0x7f,0x7f,0x84,0x8e,0x9c,0xa2,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x7f,0x8e,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8e\n      ,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x98,0xa4,0xa9,0xa4,0xa4,0xa4,0xa9,0xa8,0x9c,0x8c,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31\n      ,0x3c,0x48,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x50,0x41,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x41,0x57,0x6a,0x7b,0x8c,0x98,0x9b,0x93,0x8c,0x8c,0x8c,0x8c,0x94,0x9f,0x93,0x7f,0x6b,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa6,0xb0,0xab,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa4,0xb5,0xb8,0xac,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b\n      ,0x8c,0x9c,0xa5,0x9d,0x93,0x8c,0x93,0x9d,0xa9,0xb0,0xa9,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2d,0x2a,0x2a,0x28,0x21,0x16,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x98,0x9d,0xae,0xb7,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x17,0x2b,0x41,0x57,0x6b,0x7f,0x92,0x9d,0xaa,0xa6,0xa4,0x9d,0x99,0xa2,0x99,0x84,0x71\n      ,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x16,0x21,0x28,0x2a,0x2a,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xb5,0xb5,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x3c,0x51,0x66,0x7b,0x8c,0x9c,0x9d,0x8c,0x7b,0x6a,0x5a,0x46,0x30\n      ,0x19,0x04,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x72,0x84,0x94,0xa1,0x94,0x84,0x72,0x61,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0xa3,0x97,0x7f,0x6b,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x67,0x7f,0x93,0xa2,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xa8,0x98,0x7f,0x71,0x67,0x67,0x67,0x61,0x51,0x3c\n      ,0x25,0x0e,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x71,0x7b,0x8e,0xa4,0x98,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x67,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9c,0xac,0xab,0x9c,0x8e,0x8c,0x8e,0x98,0xa4,0xab,0x9c,0x8c,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x46,0x51,0x5c,0x65,0x73,0x8c,0xa4,0x98,0x7f,0x6a,0x61,0x57,0x4c,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x74,0x8a,0x9c,0x9c,0x8c,0x7f,0x74,0x73,0x73,0x74,0x84,0x94,0x9b\n      ,0x8c,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x09,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xac,0xba,0xac,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xc5,0xc9,0xb3,0x9d,0x8a,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x98,0xaa,0x9d,0x8c,0x7f,0x76,0x7f,0x8c,0x9c,0xab,0x9d,0x8e,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x46,0x43,0x43,0x43,0x40,0x36\n      ,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7f,0x8e,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x34,0x4b,0x61,0x74,0x8a,0x9d\n      ,0xae,0xa4,0x94,0x8c,0x8a,0x84,0x8c,0x8c,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x36,0x40,0x43,0x43,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x2a,0x42,0x5a,0x71,0x84,0x94,0xa2,0xa2,0x94,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x5c,0x71\n      ,0x84,0x98,0xa2,0x93,0x7f,0x6b,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x72,0x84,0x98,0xa2,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x73,0x8a,0x9d,0x9d,0x8a,0x74,0x61,0x4b,0x34\n      ,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x99,0xa2,0x8c,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98\n      ,0xaa,0x98,0x7f,0x67,0x53,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x51,0x5e,0x73,0x8c,0xa4,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x5b,0x73\n      ,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa8,0xae,0x9c,0x8c,0x7b,0x73,0x7b,0x84,0x94\n      ,0xa8,0xac,0x98,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x0d,0x21,0x36,0x49,0x5a,0x66,0x71,0x7b,0x7f,0x8e,0xa4,0x99,0x84,0x7f,0x74,0x6b,0x61,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x03,0x16,0x2c,0x43,0x5a,0x71,0x84,0x94,0x9c\n      ,0x8c,0x7b,0x6b,0x61,0x5b,0x5b,0x62,0x72,0x84,0x98,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x21,0x36,0x4e,0x65,0x7b,0x8c,0x9c,0xa4,0x9c,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97\n      ,0xac,0xbe,0xc5,0xb0,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xae,0x98,0x7f,0x6c,0x64,0x6b,0x7b,0x8e,0xa4,0x99,0x84,0x76,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xa4,0x8c,0x73,0x5c,0x5a,0x5b,0x5b,0x5b,0x57,0x4c,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c\n      ,0x1e,0x2f,0x3c,0x42,0x43,0x4f,0x67,0x7f,0x93,0xa8,0xa8,0x94,0x84,0x74,0x73,0x71,0x73,0x73,0x71,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x41,0x4c,0x57,0x5b,0x5b,0x5b,0x5a,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37\n      ,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x3e,0x51,0x62,0x72,0x84,0x8c,0x8c,0x84,0x72,0x62,0x51,0x3c,0x25,0x0e\n      ,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x7b,0x8e,0xa1,0x98,0x84,0x72,0x61,0x4c,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x52,0x66,0x7b,0x8c,0x9d,0x9d,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40\n      ,0x57,0x6b,0x7f,0x97,0xa5,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa2,0x99,0x84,0x71,0x5a,0x43,0x2c,0x16,0x03,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x3a,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a\n      ,0x9d,0xb0,0xa3,0x8e,0x7b,0x6a,0x5e,0x66,0x74,0x8a,0x9d,0xb0,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x41,0x57,0x6a,0x7b,0x84,0x8e,0x97,0x9d,0xae,0xa4,0x99,0x93,0x8a,0x7f,0x72,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00\n      ,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8c,0x9f,0x93,0x7f,0x6b,0x5a,0x4c,0x43,0x44,0x52,0x66,0x7b,0x8e,0x9b,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x39,0x46,0x5a,0x6a,0x7b,0x8a,0x8c,0x8a,0x7b,0x6a,0x5a,0x46,0x30,0x19\n      ,0x04,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9c,0xac,0xae,0xa3,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x51,0x5c,0x73,0x8c,0xa4,0xa2,0x8c,0x73,0x5e,0x5a,0x51,0x40,0x2b,0x15,0x02\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x66,0x71,0x73,0x73,0x73,0x6b,0x61,0x57,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x74,0x64,0x61,0x60,0x61,0x60,0x5b,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x57,0x61,0x6b,0x73,0x73,0x73,0x71\n      ,0x6a,0x7f,0x93,0xa8,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x55,0x62\n      ,0x71,0x73,0x73,0x71,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x73,0x8a,0x9c,0xa1,0x8e,0x7b,0x66,0x52,0x40,0x2b,0x17,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x31,0x46,0x5a,0x6b,0x7f,0x93,0xa2,0x93,0x7f,0x6b,0x57,0x41\n      ,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa2,0x99,0x84,0x71,0x5a,0x43,0x2c,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xa5,0x93,0x7f\n      ,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x21,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f\n      ,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x1e,0x1e,0x1c,0x15,0x0a,0x01\n      ,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x99,0x84,0x71,0x5c,0x4b,0x57,0x6b,0x7f,0x97,0xad,0xa8,0x93,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x74,0x8a,0x98,0xa3,0xa7,0xa4,0xa4,0xa4,0xa4,0xa5,0x9d\n      ,0x93,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0x9c,0x8a,0x74,0x61,0x4c,0x42,0x46,0x4e,0x51,0x5f,0x73,0x8c,0x9e,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x51,0x5b\n      ,0x6a,0x73,0x73,0x73,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x7b,0x8c,0x97,0x97,0x8e,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x56,0x61,0x74,0x8c,0xa4\n      ,0xa4,0x8c,0x73,0x5b,0x45,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x76,0x7b,0x84,0x8c,0x8c,0x8a,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x71,0x73,0x73,0x73,0x7f,0x98,0xb0,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x35,0x20,0x0a,0x00,0x00,0x00,0x00,0x00,0x10,0x25\n      ,0x38,0x49,0x5a,0x6a,0x74,0x7f,0x8a,0x8c,0x8c,0x84,0x7b,0x77,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x04,0x19\n      ,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0xa3,0x98,0x84,0x71,0x5c,0x46,0x31,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38\n      ,0x4c,0x61,0x74,0x8a,0x9d,0x9d,0x8a,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x99,0xa2,0x8c,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06\n      ,0x1b,0x31,0x46,0x5c,0x73,0x8a,0x9d,0x9d,0x8a,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x08,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x12\n      ,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x06,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xa8,0x93,0x7f,0x67,0x51,0x3f,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84\n      ,0x94,0xa8,0xa4,0x98,0x8e,0x8c,0x8c,0x8e,0x98,0xa3,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0x97,0x7f,0x6b,0x57,0x4b,0x57,0x5c,0x65,0x67,0x71,0x76,0x8c,0x9e,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0e\n      ,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x7f,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a\n      ,0x9d,0xac,0x99,0x84,0x72,0x68,0x71,0x7f,0x93,0xa6,0x9d,0x8a,0x73,0x5b,0x43,0x2b,0x1b,0x0c,0x01,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x84,0x8e,0x98,0xa2,0xa4,0x9d,0x93,0x8a,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00\n      ,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x8c,0x8c,0x8c,0x8e,0x9d,0xb3,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a,0x7b,0x8a,0x93,0x9d,0xa4,0xa2,0x98,0x8e,0x84,0x8e,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x98,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x26,0x10,0x01,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x41,0x57,0x6b,0x7f,0x97,0xa5,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x67,0x7f,0x93,0xa2,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0xa3,0x97,0x7f,0x6b,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x21,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x21,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x74,0x61,0x4b,0x38,0x46,0x5c,0x73,0x8c,0xa4,0xb1,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xae,0x99,0x84,0x7b,0x73,0x73,0x7b,0x84,0x8e,0x8c,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8c,0x9e,0x8e,0x7b,0x65,0x54,0x5c,0x6a,0x73,0x7b,0x7f,0x84,0x8c,0x94\n      ,0xa0,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x65,0x67,0x67,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0a\n      ,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa4,0xa4,0x94,0x84,0x7f,0x84,0x8e,0x9d,0xa4,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xae,0x9d,0x99,0x9e,0xa2,0xa4,0xa4,0xab,0xa8,0x9c\n      ,0x8c,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa0,0xa4,0xa4,0xa4,0xae,0xbe\n      ,0xb5,0xa8,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x8c,0x9c,0xa8,0xab,0xa4,0xa4,0xa2,0x9e,0x99,0x9d,0xae,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8c,0xa2\n      ,0x9d,0x8a,0x73,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa4,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa2,0x9d,0x8a,0x73,0x5c\n      ,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x98,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x3a,0x4f,0x67,0x7f,0x93\n      ,0xa6,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x3a,0x36,0x30,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x31,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x6d,0x62,0x5f,0x66,0x71,0x7b,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0x9e,0x8c,0x73\n      ,0x5e,0x62,0x71,0x7b,0x8a,0x8e,0x97,0x98,0x9e,0xa5,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x57\n      ,0x61,0x67,0x67,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x72,0x84,0x99,0xa6,0x9f,0x98,0x98,0x99,0x9e,0x9d,0x93,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xb5,0xa4,0x98,0x8e,0x8c,0x8c,0x8e,0x9c,0xad,0xac,0x98,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a\n      ,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x9d,0xad,0xbb,0xae,0x9d,0x98,0x98,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x98,0xac,0xad,0x9c,0x8e,0x8c,0x8c,0x8e,0x98,0xa4,0xb7,0xb0,0x98,0x7f,0x67,0x4f,0x37\n      ,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa9,0xb0,0xad,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06\n      ,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8c,0xa4,0xa2,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x00,0x00,0x00,0x00,0x00,0x02,0x15\n      ,0x2c,0x43,0x5a,0x71,0x84,0x98,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa2,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x10,0x25,0x38,0x46,0x4e,0x4f,0x53,0x66,0x7b,0x8e,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x57,0x4f,0x4e,0x46,0x38,0x27,0x14,0x02,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d\n      ,0xb1,0xa4,0x8c,0x73,0x5b,0x43,0x31,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xaf,0x9d,0x8c,0x7b,0x73,0x6b,0x65,0x61,0x65,0x65,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00\n      ,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xa2,0x8c,0x73,0x61,0x71,0x84,0x8e,0x9a,0x98,0x93,0x8c,0x8e,0x9d,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xa4,0xa6,0xad,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x74,0x7f,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x72,0x84,0x99,0x94,0x8c,0x8e,0x97,0x93,0x8c,0x8a,0x7f,0x72,0x62,0x51,0x40,0x2b,0x17,0x06,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x84,0x7b,0x73,0x73,0x7b,0x8c,0x9d,0xb0,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x8c,0x9d,0xb2,0xa4,0x8e,0x7f,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa3,0xb0,0x9d,0x8c,0x7b,0x73,0x73\n      ,0x7b,0x84,0x99,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8e\n      ,0x9c,0xae,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xa8,0x98,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xa4,0x8c,0x74,0x61\n      ,0x4b,0x34,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0xa3,0x97,0x7f,0x6b,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x67,0x7f,0x93,0xa2,0x93,0x7f,0x6b,0x57\n      ,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x72,0x84,0x98,0xa5,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0x99,0x84,0x74,0x6b\n      ,0x67,0x65,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x31,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x94,0xa6,0xab,0x9c,0x8e,0x8a,0x7f,0x7b,0x71,0x66\n      ,0x5c,0x51,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa2,0x8c,0x73,0x66,0x7b,0x8e,0x9f,0x94,0x84,0x7f,0x74,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x94,0xa4,0xb4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8a,0x93,0x97,0x8e,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0x9f,0x8c,0x76,0x7b,0x7f,0x7f,0x74,0x73,0x6b\n      ,0x61,0x53,0x46,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x74,0x66,0x5c,0x5c,0x6b,0x7f,0x98,0xb0,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x6c,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x11,0x28,0x40,0x57\n      ,0x6b,0x7f,0x97,0xad,0xa8,0x93,0x7f,0x6b,0x5c,0x5c,0x69,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x05,0x1c\n      ,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x7b,0x8e,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11\n      ,0x28,0x40,0x57,0x6b,0x7f,0x98,0xa6,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x73,0x8a,0x9d,0x9d,0x8a,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30\n      ,0x46,0x5c,0x71,0x84,0x99,0xa2,0x8c,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x84,0x94,0x9f,0x98,0x8a,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12\n      ,0x2a,0x42,0x5a,0x71,0x84,0x93,0x9b,0x94,0x8a,0x7f,0x7f,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c,0x46,0x33,0x43,0x5b,0x73,0x8c,0xa4,0xb4,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x74\n      ,0x89,0x94,0xa3,0xa8,0xa4,0x9d,0x97,0x8e,0x84,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0x99,0x84,0x71,0x67,0x7f,0x97,0x9d,0x8a,0x74,0x67,0x69,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x11\n      ,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x74,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xa8,0xad,0xa3,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a\n      ,0x9d,0xa4,0x8c,0x74,0x6c,0x6d,0x6d,0x69,0x67,0x67,0x67,0x61,0x5a,0x51,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x48,0x4f,0x67,0x7f,0x93,0xa8,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x52,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x4f,0x4f,0x4f,0x4e,0x46,0x38\n      ,0x25,0x10,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xb1,0xa4,0x8c,0x74,0x61,0x4c,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5e,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa6,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa2,0x93,0x7f,0x67,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa1,0x98,0x84,0x71,0x5a,0x43,0x2c,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x99,0x9b,0x8e,0x84,0x7b,0x6a,0x57,0x41,0x2b,0x15,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x72,0x7f,0x8c,0x9c,0x9d,0x98,0x97,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7b,0x65,0x4e,0x39,0x46,0x5c,0x73,0x8c,0xa4,0xb0,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12\n      ,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x41,0x57,0x6a,0x74,0x84,0x8e,0x98,0xa3,0xaa,0xaa,0xa3,0x98,0x8e,0x84,0x72,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0x99,0x84,0x71,0x71,0x84,0x99,0x98,0x7f,0x6f,0x5f,0x6b,0x7f,0x98\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xbc,0xc3,0xb3,0x9d,0x8a,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xa7,0x94,0x84,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x74,0x71,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4b,0x61,0x74,0x8c,0xa4\n      ,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x3a,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x46,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4\n      ,0x8c,0x76,0x64,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8c,0xa2,0x99,0x84\n      ,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6b,0x7f,0x97,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x99,0xa2,0x99,0x84\n      ,0x7f,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x72,0x7f,0x84,0x99,0xa4,0x9d,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xad,0x97,0x7f,0x67,0x51,0x3f,0x4e,0x65,0x7b\n      ,0x8e,0xa4,0xb0,0x98,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x0d,0x21,0x36,0x49,0x57,0x62,0x71,0x7b,0x84,0x8e,0x97,0x9d,0xa8,0xaa,0xa3,0x94,0x84,0x72,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa2,0x8c,0x73\n      ,0x6b,0x7f,0x98,0x9d,0x8c,0x7b,0x73,0x7b,0x8c,0x9d,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e\n      ,0xa3,0xb5,0xbc,0xb9,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x94,0xa4,0xa4,0x99,0x98,0x98,0x98,0x98,0x98,0x98,0x93,0x8c,0x84,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x44,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x15,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01\n      ,0x0a,0x15,0x1c,0x21,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37\n      ,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2d,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa6,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x03,0x16,0x2c,0x43,0x5a,0x71,0x84,0x99,0xa2,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x9d,0x9d,0x8a,0x73,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06\n      ,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x84,0x8e,0x97,0x98,0x93,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x93,0x99,0x9a,0x8e,0x8a,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e\n      ,0xa4,0xb0,0x99,0x84,0x71,0x5c,0x49,0x57,0x6b,0x7f,0x97,0xad,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x07,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x49,0x57,0x5b,0x5d,0x66,0x71,0x7b,0x7f,0x8a,0x93,0x9d,0xab,0xa4,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00\n      ,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa0,0x8c,0x73,0x67,0x7f,0x93,0xa2,0x9c,0x8e,0x8c,0x8e,0x96,0x99,0x9f,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x94,0xa2,0xa4,0xae,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x11,0x28,0x3d,0x51,0x62,0x72,0x84,0x99,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa6,0xa6,0xa2,0x98,0x8c,0x7b,0x65,0x4e,0x37,0x21,0x0a\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4b,0x61,0x74,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2d,0x2a\n      ,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x08,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x4f\n      ,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x15,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xa6,0x93,0x7f,0x67\n      ,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa5,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa5,0x97,0x7f,0x6b,0x57,0x40,0x28\n      ,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x71,0x7b,0x84,0x99,0xa2,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0x9d,0x8c,0x7b,0x73\n      ,0x6b,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xb0,0xa3,0x8e,0x7b,0x68,0x5e,0x66,0x74,0x8a,0x9d,0xad,0x9d,0x8a,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6a,0x71,0x62,0x5b,0x5d,0x65,0x6b,0x74,0x7f,0x8e,0xa4\n      ,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0x9e,0x8c,0x74,0x66,0x72,0x84,0x94,0xa1,0x9e,0x9b,0x93,0x84,0x84,0x95,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x72,0x84,0x8c,0x8e,0x9d,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a,0x7b,0x8c,0x98,0x8e,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x93\n      ,0x9d,0xab,0xac,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x47,0x52,0x67,0x7f,0x93,0xa8,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x46,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b\n      ,0x73,0x8a,0x9d,0xb1,0xa4,0x8c,0x74,0x61,0x4c,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12\n      ,0x2a,0x42,0x5a,0x71,0x84,0x99,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x9d,0x9d,0x8a,0x73,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2c,0x43,0x5a\n      ,0x71,0x84,0x99,0xa2,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x51,0x5c,0x69,0x7f,0x93,0xa5,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12\n      ,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x6b,0x5c,0x57,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa4,0xac,0x98,0x84,0x7b,0x73,0x7b,0x84,0x94,0xa8,0xa8,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x52,0x66,0x7b\n      ,0x84,0x74,0x71,0x66,0x5c,0x5e,0x64,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8c,0x9f,0x93,0x7f,0x67,0x62,0x72,0x84,0x8c,0x8c,0x8a,0x7f,0x72,0x7b,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x71,0x73,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8c\n      ,0x9b,0x93,0x7f,0x73,0x73,0x73,0x73,0x73,0x74,0x7f,0x8c,0x9d,0xac,0x9a,0x88,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x74,0x62,0x5b,0x62,0x72,0x84,0x99,0xb0,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xb1,0xa4,0x8c,0x74,0x62,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xb0,0xa8,0x93,0x7f,0x6b,0x5c,0x5c,0x6b,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa6,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2c,0x43,0x5b,0x73,0x8c,0xa2,0xa2,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6b,0x7f,0x97,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8c,0xa2,0x99,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x3d,0x4e,0x65,0x7b,0x8e,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x40,0x38,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x72,0x84,0x98,0xab,0xa4,0x98,0x8e,0x8c,0x8e,0x98,0xa4,0xab,0x9c,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0b,0x00\n      ,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x72,0x84,0x91,0x8c,0x84,0x7b,0x73,0x73,0x73,0x7b,0x8e,0xa4,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x98,0x99,0x84,0x71,0x5d,0x62,0x71,0x73,0x73,0x73,0x6b,0x61,0x65\n      ,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x37,0x45,0x53,0x5f,0x71,0x84,0x99,0x9d,0x8a,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x98,0xa2,0x8c,0x74,0x63,0x5e,0x5b,0x5b,0x5e,0x64,0x71,0x84,0x99,0xa9,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x84,0x74,0x73,0x74,0x84,0x94,0xa4,0xb0\n      ,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xb0,0xa8,0x94,0x84,0x74,0x73,0x73,0x73,0x6a,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x93,0xa8,0xb1,0x9d,0x8c,0x7b,0x73,0x73,0x7b,0x8c,0x9d,0xb3,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa2\n      ,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8c,0xa4,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa3\n      ,0x98,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa2,0x93,0x7f,0x67,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x21,0x37,0x4f,0x67,0x7f,0x97\n      ,0xa8,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x22,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x98,0xa3,0xa8,0xa4,0xa4,0xa4,0xab,0xa8\n      ,0x9c,0x8c,0x7b,0x6a,0x57,0x41,0x2b,0x19,0x06,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa3,0xa2,0x98,0x8e,0x8c,0x8c,0x8c,0x8e,0x9c,0xab,0xa3,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0x9e,0x8e\n      ,0x7b,0x66,0x56,0x5a,0x5b,0x5b,0x5b,0x57,0x4d,0x4e,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x57,0x61\n      ,0x6b,0x7b,0x8e,0xa0,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xa4,0x8e,0x7b,0x73,0x6b,0x67,0x67,0x6b,0x73,0x7b,0x8e,0xa3,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xad,0x9f,0x94,0x8c,0x8c,0x8c,0x94,0xa4,0xb1,0xa4,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x93,0xa4,0xb2,0xa4,0x94,0x8c,0x8c,0x8c,0x89,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xad,0xad,0x9c,0x8e,0x8c,0x8c,0x8e,0x9b,0xa3,0xae,0xb0,0x98,0x7f,0x67,0x4f,0x37\n      ,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06\n      ,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x99,0xa4,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x29,0x40,0x57,0x6b,0x7f,0x93,0xa2,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x07,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x08,0x02,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a\n      ,0x6a,0x7b,0x84,0x8e,0x98,0xa4,0xb5,0xb5,0xa4,0x94,0x8a,0x7b,0x6a,0x5a,0x4b,0x41,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x8c,0x98,0xa2,0xa5,0xa4,0xa4,0xa4,0xa4,0xa4,0xa5,0x9d,0x93,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00\n      ,0x00,0x06,0x1b,0x31,0x46,0x5c,0x73,0x8a,0x9c,0x98,0x84,0x72,0x62,0x57,0x4d,0x49,0x4a,0x4e,0x57,0x5b,0x57,0x49,0x3b,0x2d,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x74,0x7f,0x8c,0x9c,0x9c,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xa5,0x9c,0x8e,0x8a,0x7f,0x7f,0x7f,0x7f,0x8a,0x8e,0x9c,0xa1,0x94,0x84,0x71,0x5c,0x46,0x30,0x19,0x04\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa9,0x9c,0x8e,0x94,0x9f,0xa4,0xa4,0xa8,0xab,0xa3,0x94,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x98,0xa8,0xae,0xa8,0xa4,0xa4,0xa2,0x93\n      ,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x9c,0xac,0xab,0xa4,0xa4,0xa2\n      ,0x9b,0x93,0x8e,0x9d,0xab,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x06,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa5,0x97,0x7f,0x6b,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x21,0x36,0x49,0x5c,0x73,0x8a,0x9d,0xa2,0x8c,0x74,0x61,0x4b\n      ,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa2,0x93,0x7f,0x67,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa3,0x98,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x15,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x15,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x15,0x12,0x11,0x0a,0x02,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x49,0x5a,0x66,0x71,0x7b,0x84,0x98,0xad,0xb0,0x99,0x84,0x74,0x6a,0x5b,0x54,0x5a,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x6b,0x7b,0x84,0x8c,0x93,0x98,0x9d,0xae,0xa4,0x99,0x93,0x8a\n      ,0x7f,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x7b,0x8e,0x9f,0x94,0x84,0x74,0x6b,0x61,0x5b,0x5c,0x65,0x6b,0x73,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8a,0x93,0x9c,0x9c,0x8c,0x7b,0x6a,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8a,0x93,0x9d,0x9f,0x9d,0x98,0x98,0x98,0x98,0x9b,0x9b\n      ,0x97,0x8e,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x8e,0x7b,0x84,0x93,0x99,0xa2,0xa2,0x98,0x8e,0x84,0x72,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x52\n      ,0x66,0x7b,0x8a,0x93,0x99,0xa2,0xa4,0xa2,0x99,0x95,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36\n      ,0x49,0x5a,0x6a,0x7b,0x8c,0x97,0x9d,0xa4,0x9d,0x97,0x8c,0x7f,0x7f,0x95,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00\n      ,0x04,0x10,0x19,0x1e,0x1e,0x1b,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x9d,0x9d,0x8a,0x74,0x62,0x51,0x3c,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x41\n      ,0x57,0x6a,0x7b,0x8e,0xa3,0x98,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8c,0xa2,0x99,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6b,0x7f\n      ,0x97,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a\n      ,0x2d,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2d,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2d,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x46,0x51,0x5c,0x68,0x7b,0x8e,0xa3,0xaf,0x9d,0x8c,0x7b,0x71,0x67,0x67,0x71,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4c,0x5a,0x66\n      ,0x71,0x74,0x7f,0x7f,0x8e,0xa4,0x99,0x84,0x7f,0x74,0x6b,0x61,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x5c,0x71,0x84,0x94,0x9f,0x94,0x8a,0x7f,0x74,0x73,0x73,0x7b,0x7f,0x88,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xad,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0x9c,0x93,0x8a,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x74\n      ,0x7f,0x8a,0x8e,0x97,0x98,0x98,0x98,0x93,0x8c,0x8a,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x71,0x72,0x7f,0x84,0x8c,0x8c,0x84,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6a,0x74,0x7f,0x84,0x8c,0x8c,0x8c,0x84,0x7f,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x6a,0x7b,0x7f,0x8a,0x8c,0x8a,0x7f,0x7b,0x6d,0x7b,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x36,0x30,0x2d,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6b,0x7f,0x93,0xa2,0x94,0x84,0x71,0x5c,0x49,0x38,0x27,0x16\n      ,0x04,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x3c,0x51,0x62,0x74,0x8a,0x9c,0xa1,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2c,0x43,0x5a,0x71,0x84,0x99,0xa2,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00\n      ,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x9d,0x9d,0x8a,0x73,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x43,0x43,0x40,0x36,0x27\n      ,0x14,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x48,0x5b,0x73,0x8c,0xa4,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x46,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5b,0x46,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x3c,0x48,0x5c,0x71,0x84,0x94,0xa4,0xab,0x9c,0x8e,0x84,0x7f,0x7f,0x82,0x73,0x5b,0x43,0x2c,0x15\n      ,0x02,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x38,0x46,0x51,0x5a,0x61,0x67,0x73,0x8c,0xa4,0x98,0x7f,0x6a,0x61,0x57,0x4c,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x3c,0x51,0x62,0x72,0x84,0x8e,0x9b,0x9c,0x93,0x8c,0x8c,0x8c,0x8e,0x97,0x96\n      ,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x8a,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02\n      ,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x57,0x61,0x6b,0x73,0x7b,0x7f,0x7f,0x7f,0x7f,0x7f,0x74,0x73,0x6b,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x61,0x61,0x67,0x71,0x73,0x73,0x71,0x66,0x5c\n      ,0x51,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x49,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x71,0x67,0x65,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73\n      ,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x5a,0x65,0x6b,0x73,0x73,0x73,0x6b,0x65,0x5e,0x65,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4e,0x46,0x42,0x47,0x5c,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x4c,0x61,0x74\n      ,0x8a,0x9c,0xa1,0x8e,0x7b,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5c,0x71,0x84,0x94,0xa2,0x94,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f\n      ,0x93,0xa5,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa5,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98\n      ,0xaa,0x98,0x7f,0x69,0x5c,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5c,0x66,0x74,0x8c,0xa4,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5e,0x73\n      ,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x27,0x3c,0x51,0x62,0x72,0x84,0x94,0xa3,0xa8\n      ,0xa3,0x99,0x98,0x98,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x01,0x0a,0x17,0x25,0x31,0x3c,0x43,0x4c,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x50,0x41,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x71\n      ,0x7b,0x8a,0x93,0x98,0x98,0x9d,0x98,0x97,0x8e,0x8a,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x7b\n      ,0x74,0x6b,0x61,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x36,0x41,0x4c,0x57,0x5c,0x65,0x67,0x67,0x67,0x67,0x67,0x61,0x5b,0x57,0x4e,0x46,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f\n      ,0x4f,0x4b,0x4b,0x51,0x5a,0x5b,0x5b,0x5a,0x51,0x46,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x36,0x41,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51,0x4e,0x46,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x57,0x4e,0x49,0x4e,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25\n      ,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x65,0x65,0x5c,0x5a,0x5a,0x66,0x7b,0x8e,0xa4,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06\n      ,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6a,0x7b,0x8e,0xa1,0x9c,0x8c,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x0a,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x8e,0xa1,0x9c,0x8a,0x74,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x9d,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x99,0xa2,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa6,0x99,0x84,0x7b,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x7b,0x84,0x94,0xa5,0x97,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x05\n      ,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x76,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x08,0x1b,0x2f,0x40,0x51,0x62,0x72,0x84,0x8e,0x98,0xa2,0xa4,0xa5,0xa1,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x25,0x2e,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x22,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x51,0x5c,0x6a,0x74,0x7f,0x7f,0x84,0x8c,0x84,0x7f,0x7b,0x73,0x6b,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e\n      ,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x5a,0x65,0x61,0x57,0x4c,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x36,0x40,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4b,0x43,0x40,0x37,0x30,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x34,0x34,0x3c,0x42,0x43,0x43,0x42,0x3c,0x31,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c\n      ,0x36,0x30,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x30,0x37,0x40,0x43,0x43,0x43,0x40\n      ,0x37,0x33,0x36,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x7b,0x73,0x71,0x71,0x74,0x84\n      ,0x98,0xad,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5c,0x71,0x84,0x94,0xa2,0x9c,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x7b,0x8c,0x9c,0x9c,0x8c,0x7b,0x6a,0x57,0x41,0x2f,0x1b\n      ,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6b,0x7f,0x97,0xa1,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0x9f,0x98,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x98,0x9f,0x98,0x8e,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8e,0x98,0x9f,0x9c,0x8c,0x7b,0x65,0x4e\n      ,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x94,0xa6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa6,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x51,0x62,0x71,0x7b,0x84,0x8c,0x8c,0x90,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x13,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x95,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x49,0x57,0x61,0x67,0x67,0x71,0x73,0x71,0x67,0x65,0x5c,0x57,0x4c,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46\n      ,0x4e,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x46,0x4e,0x4b,0x41,0x36,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x16,0x21,0x28,0x30,0x36,0x37,0x37,0x37,0x37,0x37,0x34,0x2c\n      ,0x28,0x21,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1c,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02\n      ,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x04,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x28,0x21,0x1b,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x15,0x2c\n      ,0x43,0x5a,0x71,0x84,0x8e,0x8c,0x84,0x84,0x8c,0x94,0xa4,0xab,0x9c,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x3c,0x51,0x62,0x72,0x84,0x94,0x98,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f\n      ,0x8c,0x98,0x8c,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x37,0x4e,0x65,0x7b,0x8a,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8a,0x8c\n      ,0x8a,0x7b,0x66,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x84,0x8e,0x97,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f\n      ,0x95,0x98,0x98,0x97,0x8e,0x8a,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98\n      ,0x98,0x98,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x5c,0x66,0x71,0x73,0x74,0x7f,0x74,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12\n      ,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x27,0x36,0x41,0x4b,0x4f,0x51,0x5a,0x5b,0x5a,0x51,0x4e,0x46,0x40,0x36,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x30,0x36,0x34,0x2b,0x21,0x16,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0a\n      ,0x11,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x06,0x06,0x06,0x05,0x06,0x0e,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x0e,0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x11,0x0a,0x05,0x06,0x06,0x06,0x06,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa0,0xa2,0x99,0x99,0xa2,0xa6,0xa7,0x9c,0x8c,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x72,0x84,0x84,0x7b,0x6a,0x5a,0x46,0x30\n      ,0x19,0x04,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x6b,0x7b,0x88,0x7b,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05\n      ,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x66,0x71,0x7b,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42\n      ,0x2a,0x12,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x7b,0x73,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00\n      ,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x46,0x51,0x5a,0x5b,0x61,0x67,0x61,0x5b,0x5a,0x51,0x40,0x2b,0x15\n      ,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x14,0x21,0x2b,0x34,0x37,0x3c,0x42,0x43,0x42,0x3c,0x36,0x30\n      ,0x28,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1c,0x15,0x0b,0x03,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x97,0x9d,0xa4,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f\n      ,0x40,0x51,0x62,0x71,0x71,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4c,0x5a,0x6a,0x73,0x6a,0x5a,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49\n      ,0x57,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x57,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x46,0x51\n      ,0x5c,0x65,0x67,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x65,0x5c,0x57,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x67\n      ,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x31,0x3c,0x42\n      ,0x43,0x4b,0x4f,0x4b,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b\n      ,0x15,0x1c,0x1e,0x25,0x2a,0x2a,0x2a,0x25,0x1e,0x19,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x5a,0x5a,0x51,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x38,0x49,0x57,0x5b,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x36,0x40,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x40,0x36,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x3c,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x40,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x1b,0x25,0x2a,0x2c,0x34,0x37,0x34,0x2c,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x07,0x0e,0x12,0x12,0x12,0x0e,0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x71\n      ,0x67,0x61,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x42,0x42,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x17,0x27,0x36,0x40,0x43,0x40,0x36,0x27,0x16,0x05,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x26,0x30,0x36,0x37,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x36,0x30,0x28,0x21,0x14,0x05\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x15,0x1c,0x1e,0x1c,0x15,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x19,0x10\n      ,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x0a,0x1e\n      ,0x2f,0x3c,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x25,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21\n      ,0x28,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12\n      ,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e\n      ,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e\n      ,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b\n      ,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x0e,0x06,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e\n      ,0x06,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x04,0x06,0x06,0x06,0x06,0x05,0x06,0x0e,0x12,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x0e,0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0a,0x11,0x12,0x12,0x12,0x12,0x0e,0x06,0x06,0x06,0x06,0x06,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x0a,0x11,0x12,0x11,0x0a,0x06\n      ,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12\n      ,0x12,0x11,0x0a,0x02,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x06,0x06,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a\n      ,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x02,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x02,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x21,0x28,0x2a,0x2a\n      ,0x2a,0x2a,0x28,0x21,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a\n      ,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x16,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x06,0x10,0x19,0x1e,0x21,0x28,0x2a,0x28,0x21,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00\n      ,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x0c,0x1b,0x25,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x04,0x06,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x30,0x37,0x40,0x43,0x43,0x43,0x40,0x37,0x30,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x40,0x37,0x30\n      ,0x26,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x10,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1e,0x2b,0x34,0x3c,0x42\n      ,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x34,0x31,0x3c,0x42,0x43,0x43,0x43,0x40,0x37,0x30,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x04,0x10,0x1e,0x2b,0x34,0x38,0x40,0x43,0x43,0x43,0x43,0x40,0x37,0x30,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x21,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c,0x36,0x30,0x26,0x1b,0x0d,0x02,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x36,0x40,0x43,0x43,0x43,0x42,0x3c,0x33,0x36,0x37,0x37,0x37,0x36,0x30\n      ,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x26,0x30,0x36,0x38,0x40,0x43,0x40,0x38,0x36,0x30,0x26,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x0a,0x1e,0x2f,0x3c,0x42\n      ,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x16,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43\n      ,0x43,0x43,0x40,0x36,0x27,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x30,0x36,0x37,0x37,0x36,0x30,0x26,0x1b,0x10,0x04,0x04,0x10,0x19,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x38,0x27\n      ,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x3c\n      ,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2f,0x1e,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x05,0x16,0x25,0x31,0x40,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4b,0x46,0x51,0x5a,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x38,0x27,0x16,0x05\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x40,0x4b,0x4f,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x36,0x41,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51\n      ,0x4e,0x46,0x3c,0x2f,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x41,0x4c,0x57,0x5b,0x5b\n      ,0x5b,0x5a,0x51,0x49,0x4e,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x3c,0x46,0x4e,0x4f,0x57,0x5b,0x57,0x4f,0x4e,0x46,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b\n      ,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b\n      ,0x57,0x49,0x36,0x21,0x0a,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x38,0x25,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00\n      ,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x46,0x4e,0x4f,0x4f,0x4e,0x46,0x3c,0x31,0x25,0x16,0x16,0x25\n      ,0x30,0x36,0x36,0x30,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a\n      ,0x65,0x6b,0x73,0x73,0x73,0x6b,0x65,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x6b,0x65,0x5c,0x51,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x71,0x62,0x51,0x3c,0x27,0x34,0x4b,0x61,0x71,0x73,0x73,0x73\n      ,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x52,0x61,0x67,0x71,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x61,0x5c,0x66,0x71\n      ,0x73,0x73,0x73,0x6b,0x65,0x5a,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x52,0x61,0x67,0x6b,0x73,0x73,0x73,0x73,0x6b,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40\n      ,0x4c,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x71,0x67,0x65,0x5c,0x51,0x41,0x36,0x27,0x16,0x04,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x04,0x16,0x27,0x38,0x49,0x57,0x61,0x6b,0x73,0x73,0x73,0x71,0x66,0x5e,0x65,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x46,0x51,0x5c,0x65,0x67,0x6b,0x73,0x6b,0x67,0x65,0x5c,0x51,0x46,0x38,0x27,0x14,0x02,0x00,0x00,0x00\n      ,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x5a,0x46,0x31,0x2b,0x41,0x57,0x6a,0x73\n      ,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x41,0x36,0x4c,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x51,0x5c,0x65\n      ,0x67,0x67,0x65,0x5c,0x51,0x46,0x38,0x25,0x27,0x38,0x46,0x4e,0x4e,0x46,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00\n      ,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x7f,0x8a,0x8c,0x8a,0x7f,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x5a,0x66,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8a,0x7f,0x7b,0x71,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x82,0x8c,0x8c,0x8c,0x84,0x71\n      ,0x5c,0x46,0x30,0x3c,0x51,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x66,0x72,0x7f,0x84,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x06,0x1e,0x36\n      ,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x72,0x71,0x7b,0x84,0x8c,0x8c,0x8a,0x7f,0x7b,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x5a,0x66,0x72,0x7f,0x7f,0x8a,0x8c,0x8c,0x8a,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1b\n      ,0x06,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x61,0x6b,0x74,0x7f,0x84,0x8c,0x8c,0x8c,0x84,0x7f,0x7b,0x71,0x62,0x57,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x74,0x7f,0x8a,0x8c,0x8c,0x84,0x7b,0x71,0x7b,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x66,0x71,0x7b,0x7f,0x7f,0x88,0x7f,0x7f,0x7b\n      ,0x71,0x66,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x6b,0x57,0x40\n      ,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x87,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x82,0x8c,0x8c,0x8c\n      ,0x8a,0x7b,0x66,0x51,0x3c,0x34,0x4b,0x61,0x74,0x89,0x8c,0x8c,0x8c,0x87,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x89,0x8c,0x8c,0x8c,0x89,0x74,0x61,0x4c,0x41,0x57,0x6b,0x7f,0x8c,0x8c,0x8c,0x8a,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00\n      ,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x71,0x7b,0x7f,0x7f,0x7b,0x71,0x66,0x5a,0x46,0x31,0x38,0x49,0x5a,0x65,0x65,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x97,0x9c,0x9e,0x9c,0x97,0x8c,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa1\n      ,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x05,0x06,0x06,0x06,0x06,0x06,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x6a,0x7b,0x84,0x8e,0x97,0x9d,0xa4,0xa4,0x9d,0x97,0x8e,0x84,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x0e\n      ,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa1,0xa1,0x8e,0x7b,0x65,0x4e,0x37,0x43,0x5a,0x71,0x84,0x99,0xa2,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x84,0x93,0x99,0xa2,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x6a,0x5a,0x49\n      ,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x92,0x7f,0x84,0x8e,0x98,0xa2,0xa4,0x9d,0x97,0x8c,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x6a,0x7b,0x84,0x93,0x98,0x9d,0xa4\n      ,0xa4,0x9d,0x97,0x8e,0x84,0x72,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x72,0x7f,0x8a,0x93,0x99,0xa2,0xa4,0xa2,0x99,0x97,0x8e,0x84,0x74,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa1\n      ,0x8c,0x73,0x5b,0x43,0x2a,0x15,0x12,0x12,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a,0x7b,0x8a,0x93,0x9d,0xa4,0xa2,0x98,0x8e,0x84,0x7f,0x95,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x04,0x19,0x30,0x46,0x5a\n      ,0x6a,0x7b,0x84,0x8e,0x97,0x98,0x9a,0x98,0x97,0x8e,0x84,0x7b,0x6a,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71\n      ,0x84,0x99,0xa4,0xa4,0xa4,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x1e,0x21,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x9f,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0x9f,0xa4,0x98,0x84,0x71,0x5a,0x43,0x3c,0x51,0x67,0x7f,0x93,0xa2,0xa1,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x98,0xa2,0xa2,0x93,0x7f,0x6b,0x57,0x4c,0x61,0x74,0x8a,0x9d\n      ,0xa4,0x9b,0x8a,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x72,0x84,0x8e,0x97,0x97,0x8e,0x84,0x7b,0x66,0x52,0x40,0x49,0x5a,0x6a,0x7b,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4\n      ,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xa2,0x94,0x8c,0x94,0xa2,0x9c,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x18,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x8c,0x98,0xa3,0xaa,0xa6,0xa4,0xa4,0xa4,0xa7,0xa3,0x98\n      ,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x9d,0xaa,0x97,0x7f,0x6b,0x57,0x41,0x4b,0x61,0x74,0x8c,0xa2,0xa4,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x98,0xa4,0xa9\n      ,0xa4,0xa4,0xa4,0xa9,0xa8,0x9c,0x8c,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xab,0x9d,0x8e,0x94,0x9e,0xa2,0xa4,0xa4,0xab,0xac,0x9c,0x8c,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36\n      ,0x49,0x5a,0x6a,0x7b,0x8c,0x98,0xa4,0xab,0xa6,0xa4,0xa4,0xa4,0xa7,0xa3,0x94,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x71,0x84,0x93,0x9d,0xa7,0xa6,0xa4,0xa4,0xa4,0xa6,0xa9,0xa3,0x94,0x8a,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2d,0x2a,0x2a,0x2a,0x28,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x8c,0x9c,0xa8,0xab,0xa4,0xa4,0xa2,0x9e,0x94,0x8e,0x9d,0xab,0x98,0x7f,0x67\n      ,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x98,0xa1,0xa3,0xa4,0xa4,0xa6,0xaa,0xa3,0x98,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8c,0xa2,0xb0,0xb0,0xb1,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x37,0x37,0x37,0x3a,0x4f,0x67,0x7f\n      ,0x98,0xaa,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x71,0x84,0x98,0xac,0xa2,0x8c,0x74,0x61,0x4c,0x46,0x5c,0x71,0x84,0x99,0xac,0x9d,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x8e,0xa3\n      ,0xaf,0x9d,0x8a,0x74,0x61,0x57,0x6b,0x7f,0x93,0xa8,0xa4,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x94,0x9a,0x8e,0x8e,0x9a,0x97,0x84,0x72,0x61,0x4e,0x5a,0x6a,0x7b,0x8c,0x8c,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xae,0xb8,0xb8,0xae,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa5,0x98,0x84,0x76,0x84,0x98,0xa3,0x8e,0x7b,0x65\n      ,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2b,0x2b,0x34,0x37,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x9c\n      ,0xaa,0xa8,0x9d,0x93,0x8c,0x8c,0x8e,0x98,0xa0,0x94,0x84,0x72,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6b,0x7f,0x93,0xa6,0x9d,0x8a,0x74,0x61,0x4b,0x51,0x67,0x7f,0x93,0xa6,0x9d,0x8a,0x73,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x0a\n      ,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9c,0xac,0xab,0x9c,0x8e,0x8c,0x8e,0x98,0xa4,0xab,0x9c,0x8c,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xad,0x9f,0x98,0x8e,0x8c,0x8c,0x8e,0x9c,0xad,0xac,0x98,0x84,0x71,0x5a,0x43\n      ,0x2c,0x15,0x02,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x9c,0xaa,0xa8,0x9d,0x93,0x8c,0x8c,0x8e,0x98,0x9f,0x8e,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0xa3,0xaf,0xa4,0x94,0x8c,0x8c,0x8c,0x93,0x99\n      ,0xa3,0x98,0x84,0x72,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x44,0x42,0x43,0x43,0x43,0x40,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x98,0xac,0xad,0x9c,0x8e\n      ,0x8c,0x8c,0x8e,0x98,0x9f,0xae,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9c,0x98,0x8e,0x8c,0x8c,0x93,0x9d,0xac,0xa8,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xae,0xa2\n      ,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa3,0x9d,0x98,0x9d,0xa8,0x97,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xa4,0x8c\n      ,0x74,0x61,0x4f,0x4f,0x4f,0x4f,0x4e,0x51,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x8e,0xa3,0xa8,0x93,0x7f,0x6b,0x57,0x4e,0x65,0x7b,0x8e,0xa3,0xa8,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x16,0x03,0x00\n      ,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x71,0x84,0x94,0xa8,0xa8,0x93,0x7f,0x6b,0x61,0x74,0x8a,0x9d,0xaa,0x98,0x84,0x72,0x61,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0x9b,0x8c,0x7b,0x7b,0x8c,0x9b,0x93,0x7f,0x67,0x56,0x66,0x7b\n      ,0x8c,0x9c,0x98,0x88,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8e,0x9d,0xb3,0xb3,0x9d,0x8e,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98\n      ,0xa4,0x8e,0x7b,0x6b,0x7b,0x8e,0xa3,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x09,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x32,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00\n      ,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9c,0xab,0xa4,0x94,0x8a,0x7f,0x74,0x73,0x7b,0x84,0x8e,0x84,0x72,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x0b,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9d,0xa6,0x93,0x7f,0x67,0x51,0x5a,0x71,0x84,0x99,0xa9,0x97,0x7f\n      ,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa8,0xae,0x9c,0x8c,0x7b,0x73,0x7b,0x84,0x94,0xa8,0xac,0x98,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x84,0x7b,0x73\n      ,0x73,0x7b,0x8c,0x9d,0xb0,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9c,0xad,0xa8,0x94,0x8a,0x7f,0x74,0x73,0x7b,0x84,0x90,0x84,0x71,0x5c,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84\n      ,0x98,0xad,0xa8,0x94,0x84,0x74,0x73,0x74,0x7f,0x84,0x8e,0x8c,0x7b,0x66,0x52,0x40,0x2b,0x15,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x51,0x5a,0x5b,0x5b,0x5b,0x57,0x4c,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x0a,0x21\n      ,0x37,0x4e,0x65,0x7b,0x8e,0xa3,0xb0,0x9d,0x8c,0x7b,0x73,0x73,0x7b,0x84,0x99,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x7b,0x8c,0x84,0x7b,0x73,0x74,0x7f,0x8e,0xa3,0xb0,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xa3,0x8e,0x7f,0x8e,0xa4,0x99,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0xa8,0x93,0x7f,0x67,0x61,0x67,0x67,0x67,0x65,0x5f,0x71,0x84,0x99,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x07,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x71,0x84,0x98,0xaa,0x9d,0x8a,0x73,0x5c,0x57,0x6b,0x7f,0x97,0xaa\n      ,0x9d,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x74,0x8a,0x9d,0xad,0x9d,0x8a,0x74,0x69,0x7f,0x93,0xa8,0xa3,0x8e,0x7b,0x66,0x52,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0x98,0x7f\n      ,0x6b,0x6b,0x7f,0x98,0x98,0x7f,0x67,0x62,0x72,0x84,0x98,0x9c,0x8c,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00\n      ,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x68,0x7f,0x93,0xa2,0x93,0x7f,0x67,0x4f,0x37,0x21,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x40,0x51,0x61,0x67,0x67,0x67\n      ,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa8,0xad,0x98,0x84,0x74,0x6b,0x61,0x5c,0x66,0x71,0x7b,0x72,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6b,0x7f,0x97,0xa9,0x99\n      ,0x84,0x71,0x5a,0x61,0x74,0x8c,0xa2,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xb0,0xa3,0x8e,0x7b,0x6a,0x5e,0x66,0x74,0x8a,0x9d,0xb0,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x74,0x66,0x5c,0x5c,0x6b,0x7f,0x98,0xb0,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa8,0xae,0x9c,0x8a,0x76,0x6b,0x61,0x5c,0x66,0x72,0x7f,0x72,0x62,0x51,0x3c,0x27,0x14\n      ,0x02,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xb0,0x9d,0x8a,0x74,0x62,0x5b,0x61,0x67,0x71,0x7b,0x7b,0x6a,0x5a,0x46,0x31,0x1e,0x0a,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x63,0x67,0x71,0x73,0x73,0x73,0x6b,0x61\n      ,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xad,0xa8,0x93,0x7f,0x6b,0x5c,0x5c,0x69,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x72,0x66,0x5c,0x61,0x71,0x84,0x99\n      ,0xae,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xa2,0x8c,0x79,0x8c,0xa2,0xa2,0x8c,0x74,0x61\n      ,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x71,0x7f,0x7f,0x7f,0x7b,0x66,0x73,0x8c,0xa2,0xab,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x8e\n      ,0xa3,0xa4,0x8e,0x7b,0x66,0x61,0x74,0x8a,0x9d,0xa6,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x41,0x57,0x6b,0x7f,0x93,0xa8,0xa8,0x93,0x7f,0x73,0x84,0x99,0xa9,0x98,0x84,0x71,0x5c,0x46,0x31,0x1e,0x0a,0x00,0x00,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0x98,0x7f,0x67,0x67,0x7f,0x98,0x99,0x84,0x71,0x72,0x84,0x94,0x94,0x8a,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x5b\n      ,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8e,0x7b,0x72,0x84,0x99,0xa2,0x8c,0x74,0x61,0x4b,0x39,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4\n      ,0x8c,0x73,0x5b,0x47,0x51,0x62,0x72,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xb0,0xa3,0x8e,0x7b,0x66,0x57,0x4c,0x46,0x51,0x5c,0x65,0x61,0x51,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00\n      ,0x00,0x0b,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa3,0xa2,0x8c,0x74,0x61,0x67,0x7f,0x93,0xa6,0x99,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x99,0x84,0x71,0x5c,0x4b,0x57,0x6b,0x7f,0x97,0xad,0xa8,0x93,0x7f\n      ,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x48,0x4f,0x67,0x7f,0x93,0xa8,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8a,0x9d,0xb0,0xa3,0x8e,0x7b,0x6a,0x58,0x4c\n      ,0x46,0x52,0x61,0x67,0x61,0x51,0x41,0x34,0x25,0x16,0x04,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x99,0x84,0x72,0x62,0x5a,0x54,0x53,0x5c,0x65,0x65,0x5a,0x49,0x38,0x25,0x10,0x01,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4\n      ,0x8c,0x74,0x74,0x7f,0x84,0x8c,0x8c,0x8a,0x7f,0x72,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xb1,0xa4,0x8c,0x74,0x61,0x4c,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x04,0x16,0x27,0x38\n      ,0x49,0x5a,0x65,0x61,0x52,0x4b,0x5c,0x71,0x84,0x99,0xa9,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e\n      ,0xa4,0x99,0x84,0x76,0x84,0x99,0xa6,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x73,0x8c,0x98,0x95,0x7f,0x67,0x73,0x8c,0xa4,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00\n      ,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x71,0x84,0x99,0xa9,0x98,0x84,0x71,0x6b,0x7f,0x93,0xa6,0x9d,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9c,0xab,0x9d,0x8a,0x7c,0x8e,0xa3,0xa3,0x8e\n      ,0x7b,0x66,0x51,0x3c,0x26,0x10,0x01,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0x98,0x7f,0x67,0x67,0x7f,0x98,0x98,0x7f,0x73,0x84,0x94,0x94,0x84,0x74,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43\n      ,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa5,0x97,0x7f,0x84,0x94,0xa0,0x94,0x84,0x71,0x5a,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x51,0x62,0x72,0x84,0x93,0x98,0x95,0x8b,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x99,0x84,0x71,0x5c,0x46,0x36,0x31,0x3c,0x46,0x4e,0x4b\n      ,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x98,0xa5,0x93,0x7f,0x67,0x6b,0x7f,0x98,0xa6,0x93,0x7f,0x67,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xad,0x97,0x7f,0x67\n      ,0x51,0x3f,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4b,0x61,0x74,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61\n      ,0x74,0x8c,0xa4,0xb0,0x99,0x84,0x71,0x5c,0x4b,0x4e,0x4f,0x4f,0x53,0x55,0x53,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xb0,0xa4,0x94,0x84,0x74,0x71,0x66,0x5c,0x53,0x53,0x4e,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x7c,0x8a,0x93,0x99,0xa2,0xa4,0x9d,0x93,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67\n      ,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x4e,0x4b,0x4b,0x5a,0x6a,0x7b,0x8e,0xa3,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xa7,0x97,0x7f,0x6d,0x7f,0x97,0xa9,0x99,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xae,0x98,0x7f,0x6c,0x7b,0x8e,0xa4,0x99,0x84,0x71,0x73,0x8c\n      ,0xa4,0xa4,0x8c,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x67,0x7f,0x93,0xa8,0xa2,0x8c,0x74,0x73,0x8a,0x9d,0xa6,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6a\n      ,0x7b,0x8e,0xa3,0xa8,0x94,0x8e,0x9c,0xa5,0x94,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0x99,0x84,0x72,0x72,0x84,0x99,0x93,0x7f,0x73,0x8a,0x94,0x84,0x72,0x62,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00\n      ,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa2,0x9d,0x8e,0x94,0xa0,0x94,0x84,0x72,0x62,0x61\n      ,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c,0x62,0x72,0x84,0x94,0xa2,0x9c,0x8c,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xa8,0x93\n      ,0x7f,0x67,0x51,0x3c,0x26,0x1b,0x26,0x30,0x36,0x34,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x8e,0xa4,0x99,0x84,0x71,0x73,0x8a,0x9d,0x9d,0x8a,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x07,0x1e\n      ,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7b,0x65,0x4e,0x39,0x46,0x5c,0x73,0x8c,0xa4,0xb1,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x46,0x5c,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xb0,0x98,0x7f,0x67,0x54,0x5a,0x65,0x67,0x67,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xac,0xb3,0xa4,0x94,0x8c,0x84,0x7b,0x71,0x67\n      ,0x61,0x57,0x49,0x3c,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8e,0x9b,0xa0,0xa4,0xa4,0xae,0xb1,0xa4,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b\n      ,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x3c,0x49,0x5a,0x6a,0x7b,0x8c,0x9c,0xa2,0x94,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa6,0x98\n      ,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8a,0x9d,0xa4,0x8e,0x7b,0x6b,0x7b,0x8e,0xa4,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xaa,0x98\n      ,0x7f,0x6d,0x7f,0x97,0xad,0xa2,0x8c,0x73,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x9d,0xa6,0x93,0x7f,0x7b,0x8e,0xa4,0x9d,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5c,0x71,0x84,0x98,0xab,0xa8,0xa4,0xab,0x9d,0x8a,0x74,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9a,0x94,0x84,0x84,0x94,0x9a,0x8a,0x74,0x6a,0x7b,0x84,0x72\n      ,0x62,0x51,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x31,0x44,0x5a,0x71,0x84\n      ,0x99,0xaa,0xa4,0xa1,0x94,0x84,0x72,0x62,0x5d,0x71,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x64,0x72,0x84,0x94,0xa4,0xa3,0x8e,0x7b,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00\n      ,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1d,0x0a,0x10,0x19,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x73,0x8a,0x9d,0xa2,0x8c,0x74,0x7b,0x8e,0xa3,0x97,0x7f,0x6b,0x57\n      ,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c,0x46,0x33,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43\n      ,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x58,0x6a,0x7b,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b\n      ,0x8c,0x9c,0xac,0xb2,0xa8,0xa2,0x98,0x8e,0x84,0x7f,0x74,0x6a,0x5c,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb5,0xa5,0x9b,0x93,0x8c,0x8c,0x8e,0x9c,0xae,0xb3,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a\n      ,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x1b,0x31,0x46,0x5a,0x6a,0x7b,0x8c,0x9c,0xa2,0x94,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00\n      ,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa4,0xa2,0x8c,0x73,0x62,0x73,0x8c,0xa2,0xa8,0x93,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x6e,0x7f,0x98,0xad,0xa4,0x8e,0x7b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6b,0x7f,0x93,0xa6,0x9d,0x8c,0x84,0x98,0xa5,0x93,0x7f\n      ,0x6b,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x3c,0x51,0x66,0x7b,0x8e,0xa4,0xb9,0xb9,0xa8,0x93,0x7f,0x6b,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x97\n      ,0x98,0x98,0x97,0x8c,0x7b,0x6a,0x5d,0x6a,0x71,0x64,0x5b,0x51,0x4b,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x72,0x84,0x99,0xb0,0xb0,0x99,0x84,0x72,0x62,0x55,0x65,0x7b,0x8e,0x98,0x97,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x71,0x84,0x94,0xa4,0xa4,0x94,0x84,0x71\n      ,0x5c,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x02,0x00,0x04,0x06,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93\n      ,0xa2,0x93,0x7f,0x7f,0x97,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x31,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x47,0x52,0x67,0x7f,0x97,0xad,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x5b,0x73,0x8a,0x97,0x98,0x98,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37\n      ,0x1e,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x7b,0x8c,0x98,0xa3,0xad,0xb0,0xad,0xa3,0x99,0x93,0x8a,0x7b,0x71,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xae,0x9c,0x8c,0x7f,0x74,0x73,0x7b,0x8e,0xa3,0xb4,0xa4\n      ,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xb1,0xa4,0x8c,0x74,0x61,0x4c,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8c,0x9c,0xa2,0x94,0x84\n      ,0x72,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa6,0x99,0x84,0x71,0x60,0x71,0x84,0x99,0xaa,0x98,0x7f\n      ,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x73,0x8a,0x9b,0x99,0x9d,0x97,0x7f,0x73,0x8c,0xa4,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x4c,0x61\n      ,0x74,0x8a,0x9d,0xaa,0x9d,0x99,0xa4,0x9d,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x4c,0x61,0x74,0x8c,0xa4,0xbc,0xbc,0xa4,0x8c,0x76,0x65,0x51,0x3c,0x26,0x12,0x01,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x84,0x8c,0x8c,0x84,0x7b,0x6a,0x5e,0x66,0x71,0x73,0x73,0x71,0x67,0x61,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x72,0x84,0x94,0xa4,0xae,0xaf,0x9d,0x8c,0x7b,0x6a,0x5c,0x6b,0x7f,0x97,0xa8,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4\n      ,0x8c,0x74,0x7b,0x8e,0xa3,0xa4,0x94,0x84,0x72,0x62,0x51,0x3c,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x02,0x02,0x0a,0x11,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9d,0x99,0x84,0x8c,0x9d,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x31,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x62,0x5b,0x62,0x72,0x84,0x99,0xb0,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x5b,0x73,0x8c\n      ,0xa4,0xaa,0xaa,0xae,0xad,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x84,0x8e,0x97,0x9d,0xa8,0xb0,0xb0,0xa8,0x9c,0x8e,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4\n      ,0x8e,0x7b,0x6b,0x61,0x5e,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xb0,0xa8,0x93,0x7f,0x6b,0x5c,0x5c,0x6b,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x12\n      ,0x2a,0x42,0x5a,0x71,0x84,0x98,0xa8,0x98,0x84,0x72,0x62,0x51,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa0,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x99,0xaa\n      ,0x98,0x7f,0x6a,0x67,0x6a,0x7f,0x98,0xab,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xa8,0x98,0x7f,0x74,0x8c,0x98,0x84,0x8e,0x98,0x7f,0x74,0x8c,0xa4,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6b,0x7f,0x93,0xa8,0xb2,0xb0,0xab,0x97,0x7f,0x6b,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6b,0x7f,0x93,0xa8,0xb0,0xb2,0xa8,0x94,0x84,0x71\n      ,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x66,0x71,0x73,0x73,0x71,0x66,0x5e,0x6a,0x7b,0x84,0x8c,0x8c,0x84,0x7f,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x94,0xa4,0xa4,0x99,0x9d,0xa9,0x9c,0x8a,0x76,0x6a,0x73,0x8a,0x9d,0xa6,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x7c,0x8c,0x9c,0xa9,0x99,0x84,0x72,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1d,0x0a,0x14,0x21,0x28,0x2a\n      ,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6b,0x7f,0x93,0xa1,0x99,0x9d,0xa5,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c\n      ,0x46,0x33,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7f,0x74,0x73,0x74,0x84,0x94,0xa4,0xb0,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x5b,0x73,0x8c,0x98,0x98,0x99,0xa4,0xae,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x66,0x71,0x7b,0x7f,0x8a,0x93,0x9d,0xa8,0xb3,0xae,0xa1,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c,0x4c,0x51,0x67,0x7f,0x98,0xb0,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x93,0xa8,0xb1,0x9d,0x8c,0x7b,0x73,0x73,0x7b,0x8c,0x9d,0xb3,0xb0,0x98,0x7f,0x67\n      ,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x9f,0xa1,0x8e,0x7b,0x66,0x52,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa2,0xae,0x99,0x84,0x7f,0x7f,0x7f,0x84,0x99,0xb0,0xa4,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0x98,0x7f,0x7f,0x93,0x93,0x7f,0x8c,0x9b,0x8a,0x79,0x8c\n      ,0xa4,0x98,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9d,0xb3,0xb8,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x26,0x3c,0x51,0x66\n      ,0x7b,0x8c,0x9d,0xa4,0x99,0x9d,0xab,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x52,0x5f,0x6a,0x7b,0x7b,0x6a,0x6a,0x7b,0x8c,0x97,0x98,0x97,0x96,0x93,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa3,0xa7,0x94,0x84,0x8c,0x9c,0xa5,0x94,0x8a,0x7b,0x7b,0x8e\n      ,0xa4,0x9d,0x8a,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8e,0x9c,0xad,0xb0,0x98,0x7f,0x6b,0x5a,0x47,0x34,0x23,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x93\n      ,0x7f,0x67,0x51,0x3c,0x26,0x1b,0x27,0x36,0x40,0x42,0x3c,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x4c,0x61,0x74,0x8c,0xa2,0xae,0xb0,0xa2,0x8c,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x1e\n      ,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7b,0x65,0x4e,0x39,0x46,0x5c,0x73,0x8c,0xa4,0xb1,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xae,0x9d,0x93,0x8c,0x8c,0x8c,0x94,0xa4,0xb1,0xa4,0x93,0x7f,0x6b,0x57,0x40\n      ,0x28,0x11,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xb0,0x98,0x7f,0x67,0x5b,0x71,0x7f,0x7f,0x7f,0x84,0x99,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3f,0x4a,0x51,0x5c,0x65,0x6b,0x74,0x7f,0x8a,0x94,0xa4\n      ,0xb5,0xad,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xad,0xad,0x9c,0x8e\n      ,0x8c,0x8c,0x8e,0x9c,0xab,0xba,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x89,0x73,0x5c,0x46,0x31,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f\n      ,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa8,0xb6,0xa4,0x99,0x98,0x98,0x98,0x99,0xa4,0xb8,0xad,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8c,0xa4,0x98\n      ,0x7f,0x7f,0x98,0x8c,0x79,0x8c,0x9d,0x8c,0x79,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6b,0x7f,0x98,0xb0,0xb0,0x99,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5c,0x71,0x84,0x98,0xa8,0x98,0x84,0x8c,0x9d,0xaa,0x98,0x84,0x71,0x5c,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x27,0x38,0x49,0x5a,0x6a,0x7b,0x8c,0x8c,0x7b,0x74,0x8a,0x9b,0x94,0x84,0x7f\n      ,0x8c,0x9b,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xab,0xa2\n      ,0x8c,0x74,0x7b,0x8e,0xa3,0xa7,0x9c,0x8c,0x84,0x98,0xa5,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb5,0xa8,0xa4,0xa5,0xa4,0xab,0x9d,0x8c,0x7b,0x66,0x52,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xb0,0x99,0x84,0x71,0x5c,0x46,0x36,0x31,0x3c,0x49,0x57,0x5a,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1e,0x2c,0x43,0x5a,0x71,0x84,0x98,0xad,0xad,0x98,0x84,0x71,0x5a,0x43,0x2c\n      ,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xad,0x97,0x7f,0x67,0x51,0x3f,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xae,0x9d,0x99,0x9f,0xa4\n      ,0xa4,0xa8,0xab,0xa3,0x94,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x99,0x84,0x71,0x5d,0x61,0x67,0x67,0x6a,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51\n      ,0x5a,0x5a,0x53,0x50,0x57,0x61,0x6b,0x74,0x84,0x94,0xa4,0xb3,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x02,0x15\n      ,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x9c,0xac,0xab,0xa4,0xa4,0xa2,0x9b,0x98,0x9d,0xae,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x12,0x01,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1b,0x2f,0x41,0x55,0x63,0x6c,0x6d,0x6c,0x63,0x55,0x41,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xae,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xae,0xb2,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00\n      ,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x84,0x98,0x8c,0x77,0x84,0x98,0x8e,0x7c,0x8c,0xa0,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x38,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x51\n      ,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x52,0x66,0x7b,0x8e,0xa3,0xa3,0x8e,0x7b,0x7f,0x93,0xa8,0xa3,0x8e,0x7b,0x6a,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x49,0x5a,0x6a,0x7b\n      ,0x8c,0x98,0x8c,0x7b,0x7f,0x93,0x9c,0x8a,0x74,0x6c,0x7f,0x98,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x99,0x84,0x71,0x71,0x84,0x94,0xa4,0xab,0x9d,0x99,0xa4,0x9d,0x8a,0x74,0x61,0x4c,0x3a,0x27,0x14,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xc2,0xb7,0xa4,0x94,0x8e,0x9c,0xa8,0x98,0x84,0x72,0x62\n      ,0x51,0x3c,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa2,0xb3,0xa3,0x8e,0x7b,0x66,0x57,0x4c,0x46,0x51,0x5c,0x6a,0x71,0x62,0x51,0x3c,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x3e,0x51,0x66\n      ,0x7b,0x8e,0xa4,0xa4,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x99,0x84,0x71,0x5c,0x4a,0x57,0x6b,0x7f,0x97,0xad,0xa8,0x93,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x84,0x93,0x99,0xa2,0xa2,0x98,0x8e,0x84,0x72,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xb0,0xa3,0x8e,0x7b,0x66,0x55,0x53,0x52,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37\n      ,0x1e,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x62,0x71,0x71,0x66,0x5c,0x51,0x51,0x57,0x62,0x72,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0\n      ,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x8c,0x97,0x9d,0xa4,0x9d,0x97,0x8c,0x7f,0x8e,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x51,0x61,0x6b,0x73,0x73,0x71,0x62,0x51\n      ,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x71,0x7b,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8c,0xa2,0xac,0x9c,0x8e,0x8c,0x8c,0x8c,0x8c,0x8c,0x8e,0x9c,0xae,0xa4\n      ,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x07,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0x98,0x7f,0x8c,0x9a,0x8a,0x73,0x7f,0x98,0x97,0x7f,0x8c,0x9e,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x4c,0x61,0x72,0x84,0x98,0xa9,0x99,0x84,0x71,0x74,0x8a,0x9d,0xab,0x9c,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0d,0x00,0x00,0x00\n      ,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5c,0x6a,0x7b,0x8c,0x98,0x8c,0x7b,0x6e,0x7f,0x98,0x98,0x7f,0x6b,0x67,0x7f,0x98,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xab,0x9d,0x8a,0x73,0x64,0x72,0x84,0x94,0xa4,0xb0,0xb0,0xae,0x98,0x7f,0x6f,0x61,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xbd\n      ,0xac,0x98,0x84,0x7b,0x8c,0x9d,0xa4,0x94,0x84,0x71,0x5c,0x49,0x36,0x21,0x0d,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x98,0xac,0xad,0x98,0x84,0x74,0x6b,0x61,0x5c,0x66,0x71,0x7b,0x84,0x71,0x5c,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4e,0x57,0x6b,0x7f,0x93,0xa6,0x9d,0x8a,0x73,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xb0,0xa3,0x8e,0x7b,0x6a,0x5e,0x66,0x74,0x8a,0x9d,0xaf,0xa2,0x8c,0x74\n      ,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x77,0x7f,0x84,0x8c,0x8c,0x84,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa8,0xad,0x98,0x84,0x72,0x67,0x61\n      ,0x5c,0x6b,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x62,0x72,0x84,0x84,0x7b,0x71,0x67,0x61,0x5b,0x62,0x72,0x84,0x99,0xae,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4\n      ,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x6a,0x7b,0x7f,0x8a,0x8c,0x8a,0x7f,0x7b,0x76,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x0a,0x21\n      ,0x36,0x4c,0x61,0x72,0x7f,0x8a,0x8c,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x8e,0x97,0x8e,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa4,0xa4,0x8e\n      ,0x7b,0x73,0x73,0x73,0x73,0x73,0x7b,0x8e,0xa4,0xad,0x97,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0x9d,0x8e,0x94,0x98,0x7f,0x6e,0x7f,0x97,0x9d,0x8e,0x94,0xa0,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6b,0x7f,0x93,0xa4,0xa4,0x93,0x7f,0x67,0x6b,0x7f,0x93,0xa8,0xa8\n      ,0x93,0x7f,0x6b,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x71,0x7b,0x8c,0x9a,0x93,0x7f,0x6b,0x67,0x7f,0x98,0x98,0x7f,0x67,0x67,0x7f,0x98,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xa4,0x8e,0x7b,0x71,0x69,0x76,0x84,0x99,0xb0,0xbc,0xb2,0x9d,0x8c,0x7f,0x74,0x6a,0x57,0x40,0x28,0x11,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xae,0x9c,0x8c,0x7b,0x6e,0x7f,0x93,0xa7,0xa3,0x8e,0x7b,0x6a,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8c,0x9d,0xb0,0xa4,0x94,0x8a,0x7f,0x74,0x73,0x7b,0x84,0x8e,0x8e\n      ,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x61,0x67,0x65,0x67,0x74,0x8a,0x9d,0xa6,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa4,0xac,0x9c,0x8c\n      ,0x7b,0x73,0x7b,0x84,0x94,0xa8,0xa8,0x94,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x67,0x71,0x73,0x73,0x71,0x66,0x5c,0x51,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c\n      ,0x61,0x74,0x8a,0x9c,0xac,0xa4,0x94,0x84,0x7f,0x74,0x73,0x7b,0x8c,0x9d,0xab,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x94,0x98,0x8e,0x84,0x7f,0x74,0x73,0x74,0x84,0x94,0xa4,0xac,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x5a,0x65,0x6b,0x73,0x73,0x73,0x6b,0x6a,0x7f,0x93,0xa8,0xb0,0x98,0x7f,0x67\n      ,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x92,0x9d,0xa2,0x94,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa1,0xaa,0xa3,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00\n      ,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xab,0xa2,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5e,0x73,0x8c,0xa2,0xae,0x99,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x98,0xaa,0xa4,0xa5,0x97,0x7f,0x6c,0x7b,0x8e,0xa3,0xa4,0xa8\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9d\n      ,0xaa,0x98,0x84,0x72,0x61,0x61,0x74,0x8a,0x9d,0xad,0x9d,0x8a,0x74,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x8e,0x9a,0x94,0x84,0x72,0x61,0x67,0x7f,0x97,0x99,0x84,0x71,0x67,0x7f,0x98,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa3,0xab,0x9c,0x8e,0x84,0x7f,0x84,0x8e,0x9d,0xaa,0xa4,0xa8\n      ,0xaa,0x9d,0x93,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7b,0x6a,0x62,0x74,0x8a,0x9c,0xa9,0x9c,0x8a,0x74,0x62,0x51,0x3c,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6b,0x7f,0x93,0xa3\n      ,0xad,0xa8,0x9d,0x93,0x8c,0x8c,0x8e,0x98,0xa3,0x9c,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x7f,0x7b,0x7f,0x84,0x94,0xa6,0x9d,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a\n      ,0x21,0x36,0x4c,0x61,0x72,0x84,0x98,0xac,0xad,0x9c,0x8e,0x8c,0x8e,0x98,0xa4,0xab,0x9c,0x8a,0x74,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c,0x5a,0x5b,0x5b,0x5a,0x51,0x46,0x3c,0x2f,0x1e,0x0d,0x01\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6a,0x7b,0x8e,0x9d,0xab,0xa4,0x99,0x93,0x8c,0x8c,0x8e,0x9c,0xad,0xac,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa1,0xaa,0xa3,0x99,0x93,0x8c,0x8c,0x8c,0x94,0xa4\n      ,0xb1,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x4e,0x57,0x5b,0x5b\n      ,0x5b,0x58,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xb1,0xb5,0xa3,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xbf,0xb0\n      ,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xac,0x99,0x84,0x71,0x5a,0x45,0x43,0x45,0x5a,0x71,0x84,0x99,0xae,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xb6,0xa4,0x8e,0x7b,0x66,0x73,0x8c,0xa4,0xb6,0xb2,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1b,0x2f,0x41,0x57,0x6b,0x7f,0x93,0xa8,0xa3,0x8e,0x7b,0x66,0x52,0x57,0x6b,0x7f,0x93,0xa8,0xa8,0x94,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x7b,0x8c,0x96,0x84,0x72,0x62,0x55,0x65,0x7b,0x8e,0x9e,0x8e,0x7b,0x74\n      ,0x84,0x99,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x94,0xa3\n      ,0xa8,0xa3,0x99,0x98,0x99,0xa1,0xa1,0x98,0x8e,0x93,0x9d,0xa6,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c,0x57,0x6a,0x7b,0x8e,0xa3,0xa7,0x94,0x84,0x71,0x5c,0x49,0x36,0x21,0x0b,0x00,0x00,0x00\n      ,0x00,0x00,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x8e,0x9c,0xa8,0xac,0xa8,0xa4,0xa4,0xa4,0xa7,0xa3,0x98,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x07,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x8e,0x97,0x99,0xa4,0xa4,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x16,0x03\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x98,0xa4,0xa9,0xa4,0xa4,0xa4,0xa9,0xa8,0x9c,0x8c,0x7b,0x6a,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x46\n      ,0x43,0x43,0x42,0x3c,0x31,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5c,0x71,0x7f,0x8c,0x9c,0xa8,0xaa,0xa8,0xa4,0xa4,0xa4,0xa8,0xa3,0x98,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8a\n      ,0x93,0x9d,0xa8,0xaa,0xa8,0xa4,0xa4,0xa4,0xa6,0xa7,0x9d,0x93,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x05,0x16,0x25,0x30,0x37,0x40,0x43,0x43,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xb8,0xbd,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00\n      ,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xb7,0xac,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa1,0xa4,0x97,0x7f,0x67,0x51,0x3c,0x2d,0x3c,0x51,0x67,0x7f,0x97,0xa4,0xa1,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa2,0xa4,0xa1,0x8c,0x73,0x61,0x73,0x8a,0x9d,0xa4,0xa4,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x74,0x8a,0x9b,0xa4,0x98,0x84,0x71,0x5c,0x46,0x4c,0x61,0x74,0x8a,0x9d,0xa4,0x9f,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x0a,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x88,0x7b\n      ,0x66,0x52,0x48,0x5c,0x71,0x84,0x94,0x9a,0x8e,0x8c,0x94,0x97,0x8a,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x72,0x84,0x8e,0x97,0x9d,0xa4,0xa4,0x9d,0x97,0x8e,0x84,0x7b,0x7f,0x8a,0x93,0x95,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xad,0xa4,0x8c,0x73,0x5b,0x4b,0x5c,0x71,0x84,0x94,0xa7,0xa2\n      ,0x8e,0x7b,0x6a,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x51,0x62,0x71,0x7b,0x8a,0x93,0x99,0xa2,0xa4,0xa4,0x9d,0x97,0x8e,0x84,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x97,0xa4,0xa7,0xa4\n      ,0x9d,0x93,0x84,0x72,0x61,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6a,0x7b,0x84,0x93,0x99,0xa2,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0d,0x00,0x00,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x98,0xad,0xa4,0x8c,0x73,0x5b,0x43,0x2d,0x2a,0x2a,0x25,0x1b,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x3c,0x51,0x61,0x6b,0x7b,0x8a,0x93,0x99,0xa2,0xa4,0xa4,0x9d,0x97,0x8e,0x84,0x7b,0x6a,0x5a,0x46,0x30\n      ,0x19,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x74,0x7f,0x8a,0x93,0x99,0xa2,0xa4,0xa4,0xa2,0x99,0x93,0x8a,0x7f,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xad,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xad,0xad\n      ,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x21,0x28,0x2a,0x2a,0x37,0x4f,0x67,0x7f,0x98,0xad,0xad,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x98,0xa8,0xac,0x9c,0x8a,0x74\n      ,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x98,0xa2,0x9c,0x8c,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8a,0x7b,0x65,0x4e,0x36,0x24,0x36,0x4e,0x65,0x7b,0x8a,0x8c\n      ,0x8c,0x8c,0x82,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x89,0x8c,0x8c,0x8c,0x89,0x73,0x5e,0x6b,0x7f,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x82,0x8a,0x8c,0x8c,0x8a,0x7b,0x66,0x51,0x3c,0x41,0x57,0x6b,0x7f,0x8c,0x8c,0x8c,0x8c,0x82,0x71,0x5a,0x42,0x2a,0x12,0x00\n      ,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x6a,0x73,0x6a,0x5a,0x46,0x3c,0x51,0x62,0x72,0x84,0x8e,0x97,0x97,0x8e,0x84,0x7b,0x6a,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8a,0x7f,0x7b,0x71,0x66,0x6b,0x74,0x7f,0x84,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98\n      ,0x8c,0x73,0x5b,0x45,0x51,0x62,0x74,0x8a,0x97,0x98,0x95,0x8a,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x5c,0x6a,0x74,0x7f,0x84,0x8c,0x8c,0x8c,0x8a,0x7f,0x7b,0x71,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00\n      ,0x12,0x2a,0x42,0x5a,0x71,0x82,0x92,0x98,0x97,0x8e,0x8a,0x7f,0x72,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x49,0x5a,0x66,0x72,0x7f,0x84,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38\n      ,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x15,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4c,0x5a,0x6a,0x74,0x7f,0x84,0x8c,0x8c\n      ,0x8c,0x8a,0x7f,0x7b,0x71,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x57,0x61,0x6b,0x74,0x7f,0x84,0x8c,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6b,0x61,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98\n      ,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x95,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x00,0x0e,0x25\n      ,0x3c,0x51,0x66,0x7b,0x8a,0x93,0x97,0x8c,0x7b,0x6a,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x84,0x8c,0x8a,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a\n      ,0x5a,0x46,0x30,0x1f,0x30,0x46,0x5a,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x5a,0x46,0x31,0x36,0x4c,0x61,0x71,0x73\n      ,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x57,0x5b,0x57,0x49,0x38,0x2f,0x40,0x51,0x62,0x71,0x7b,0x7f,0x7f,0x7b,0x71,0x66,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34\n      ,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x6b,0x65,0x5c,0x51,0x57,0x61,0x67,0x71,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00\n      ,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x41,0x57,0x6a,0x7b,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x49,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x73,0x6b,0x65,0x5c\n      ,0x51,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x74,0x7f,0x7f,0x7f,0x7b,0x73,0x6b,0x61,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x46,0x52,0x61,0x67,0x71\n      ,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c\n      ,0x1e,0x2b,0x38,0x49,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x73,0x6b,0x65,0x5c,0x51,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x04,0x16,0x27,0x36,0x41,0x4c,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x4c,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00\n      ,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x7b,0x65\n      ,0x4e,0x36,0x1e,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a,0x74,0x7f,0x7f,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x66,0x71,0x73,0x73,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x02\n      ,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x38,0x25,0x16,0x25,0x38,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x51,0x5a,0x5b,0x5b,0x5b\n      ,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b\n      ,0x5b,0x57,0x49,0x38,0x25,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x40,0x43,0x40,0x36,0x27,0x1e,0x2f,0x40,0x51,0x5c,0x65,0x67,0x67,0x65,0x5c,0x51,0x46,0x38,0x27,0x14,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x3c,0x41\n      ,0x4b,0x51,0x5a,0x57,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x36,0x49,0x5a,0x65,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x27,0x36,0x41\n      ,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x61,0x67,0x67,0x67,0x65,0x5c,0x57,0x4c,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x05,0x16,0x25,0x31,0x40,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x17,0x27,0x36,0x41,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x36,0x41,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51\n      ,0x4b,0x41,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19\n      ,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x57,0x61,0x67,0x67,0x65,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x46,0x51,0x5a,0x5b,0x5b\n      ,0x57,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x16,0x08,0x16,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43\n      ,0x43,0x43,0x43,0x40,0x36,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x16,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x28,0x21,0x14,0x0d,0x1e,0x2f,0x3c,0x46,0x4e,0x4f,0x4f\n      ,0x4e,0x46,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x30,0x37\n      ,0x40,0x43,0x43,0x43,0x43,0x40,0x37,0x30,0x26,0x2b,0x34,0x3c,0x42,0x40,0x36,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x27,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x14,0x21,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x37,0x30,0x26,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x41,0x4b,0x4f,0x4f,0x4f,0x4e,0x46,0x40,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1e,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f\n      ,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x37,0x30,0x26,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x16,0x21\n      ,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x36,0x41,0x4b,0x4f,0x4f,0x4e,0x46,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x05,0x16,0x25,0x31,0x3c,0x42,0x43,0x43,0x40,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b\n      ,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x11\n      ,0x0a,0x02,0x01,0x0c,0x1b,0x26,0x30,0x36,0x37,0x37,0x36,0x30,0x26,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x15,0x1d,0x25,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x16,0x25,0x30,0x36,0x37,0x37\n      ,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x34,0x37,0x37,0x37,0x36\n      ,0x30,0x28,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16\n      ,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x06,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x25,0x30,0x36,0x37,0x37,0x37\n      ,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x34,0x37,0x37,0x36,0x30,0x25\n      ,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x25,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12\n      ,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x02,0x06,0x0e,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e\n      ,0x1e,0x1c,0x15,0x0a,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x02,0x0b,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x19,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12\n      ,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e\n      ,0x1e,0x1c,0x15,0x0a,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x02,0x0b,0x15,0x1c,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12\n      ,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x12,0x0e,0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12\n      ,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11\n      ,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15\n      ,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x16,0x21,0x28\n      ,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0b,0x16,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a\n      ,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x1c,0x15,0x0b,0x02,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a\n      ,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x0c,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43\n      ,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x36,0x40,0x43,0x43,0x43,0x43,0x40,0x37,0x30,0x25,0x17,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x21,0x2b,0x36,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x16,0x0a\n      ,0x01,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x42,0x3c\n      ,0x34,0x2b,0x1e,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x36,0x30,0x26,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42\n      ,0x3c,0x2f,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27\n      ,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0d,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x21,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x43\n      ,0x40,0x38,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43\n      ,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x1b,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00\n      ,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x38,0x49,0x57,0x5b,0x5b,0x5b,0x5b\n      ,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x41,0x4c,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x38,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x36,0x41,0x4c,0x57,0x5b\n      ,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f\n      ,0x3c,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x40,0x31,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4e,0x46,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x02\n      ,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x38,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51\n      ,0x4b,0x41,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x41,0x4b,0x51,0x5a\n      ,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2f,0x3c,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e\n      ,0x2b,0x36,0x41,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x4f,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00\n      ,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x41,0x2b,0x25,0x3c,0x51,0x62,0x71\n      ,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73\n      ,0x6a,0x57,0x40,0x46,0x5a,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x57,0x61,0x6b,0x73,0x73,0x73,0x73,0x6b,0x65,0x5a,0x4c,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x01,0x0d,0x1e,0x2f,0x40,0x4c,0x57,0x61,0x6b,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x4c,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x49,0x38,0x27,0x16,0x04,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x52,0x46,0x38,0x25,0x10,0x01,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x67,0x65,0x5c,0x51,0x46\n      ,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x46,0x5a,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73\n      ,0x71,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73\n      ,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x4c,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00\n      ,0x00,0x05,0x16,0x27,0x38,0x49,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x36,0x41,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x49,0x38,0x27,0x16,0x04\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x4c,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x73,0x6b,0x67,0x61,0x57,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73\n      ,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x87,0x8c,0x8c,0x8c\n      ,0x89,0x74,0x61,0x4b,0x34,0x2a,0x42,0x5a,0x71,0x84,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00\n      ,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x47,0x52,0x66,0x7b,0x8a,0x8c,0x8c,0x8c,0x87,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x6a,0x74,0x7f,0x8a,0x8c,0x8c,0x8a,0x7f,0x7b,0x6b,0x61,0x51,0x40\n      ,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x61,0x6b,0x74,0x7f,0x8a,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6b,0x61,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8c,0x84,0x7f,0x72,0x66,0x5a,0x46,0x31,0x1e,0x0a,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c\n      ,0x8c,0x8c,0x8c,0x8c,0x84,0x7f,0x7b,0x71,0x66,0x5a,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x51,0x4e,0x65,0x7b,0x8a,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x02\n      ,0x0a,0x11,0x1a,0x30,0x46,0x5c,0x73,0x89,0x8c,0x8c,0x84,0x71,0x5a,0x42,0x2a,0x15,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06\n      ,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6b,0x61,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x6a,0x74,0x7f,0x84,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x57,0x62,0x71,0x7b,0x7f,0x8a,0x8c,0x8c\n      ,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x61,0x6b,0x74,0x7f,0x84,0x8c,0x8c,0x8c,0x8a,0x7f,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x89,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xa2,0xa2,0x93,0x7f,0x67,0x4f,0x38,0x30,0x46,0x5c,0x73,0x8c,0xa0,0xa4,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa2,0x99,0x93,0x8a,0x7b\n      ,0x6a,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa1,0x8c,0x73,0x5b,0x51,0x62,0x72,0x84,0x98,0xa4,0x9b,0x8c,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x8a,0x93\n      ,0x9d,0xa4,0xa4,0x9d,0x97,0x8c,0x7f,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x51,0x62,0x72,0x7f,0x8a,0x93,0x9d,0xa4,0xa4,0xa2,0x99,0x93,0x8a,0x7f,0x72,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x72,0x84,0x8e,0x97,0x9d,0xa4,0xa4,0xa2,0x99,0x93,0x84,0x7b,0x66,0x52,0x40,0x2b,0x15,0x02,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xa4,0xa4,0xa2,0x99,0x97,0x8e,0x84,0x7b,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0x99,0x84,0x71,0x5a,0x51,0x67,0x7f,0x97,0xa4,0xa4,0xa1,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x36,0x4e,0x65,0x7b,0x8e,0xa1,0xa0,0x8c,0x73,0x5b,0x43,0x2d,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4\n      ,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa2,0x99,0x93,0x8a,0x7f,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4\n      ,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x8a,0x93,0x99,0xa2,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x04,0x19,0x30\n      ,0x46,0x5a,0x6a,0x74,0x84,0x8e,0x97,0x9d,0xa4,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x72,0x7f,0x8a,0x93,0x99,0xa2,0xa4,0xa4,0x9d,0x98,0x93,0x8a,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06\n      ,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f\n      ,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x98,0x7f,0x6b,0x57,0x40,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0xa8,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb7,0xae\n      ,0x9d,0x98,0x98,0x9d,0xa4,0xa4,0xa6,0xa7,0x9c,0x8c,0x7b,0x6a,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x5c,0x71,0x84,0x94,0xa4,0xa4,0x93,0x7f,0x6b,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00\n      ,0x03,0x16,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x9c,0xa5,0xa3,0x99,0x98,0x9d,0xa5,0x9d,0x93,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x4c,0x61,0x72,0x84,0x93,0x9d,0xa5,0xa4,0xa4,0xa4,0xa4,0xa4,0xa5,0x9c,0x8f,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xae,0x9d,0x98,0x98,0x98,0x9d,0xa4,0xa9,0xa8,0x9c,0x8c,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x94,0xa3,0xa2,0x99,0x98,0x98,0x98,0x9d,0xa2\n      ,0x97,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb8,0xb3,0xa8,0xa4,0xa4,0xa4,0xa6,0xaa,0xa3,0x98,0x8c,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb7,0xa2,0x8c\n      ,0x73,0x5c,0x5a,0x71,0x84,0x99,0xb0,0xb9,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x4f,0x67,0x7f,0x97,0xad,0xa4,0x8c,0x73,0x5b,0x46,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43\n      ,0x5b,0x73,0x8c,0xa4,0xb3,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb3,0xa4,0x99,0x98,0x99,0xa2,0xa4,0xa6,0xa7,0x9d,0x93,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00\n      ,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa0,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa8,0xb3,0xb9,0xae,0x9c,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x9c,0xa5,0xa2,0x99,0x98,0x99,0xa2,0xa5,0x9c,0x8c,0x7b,0x6a,0x57\n      ,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8a,0x94,0xa1,0xa3,0xa2,0x99,0x9d,0xa4,0xa6,0xa7,0x9c,0x8c,0x7b,0x66,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x93,0x9d,0xa5,0xa4,0x9d,0x98,0x9d,0xa4\n      ,0xa9,0xa8,0x9c,0x8c,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xae,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb3,0xa8\n      ,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xad,0x9d,0x8a,0x73,0x5b,0x43,0x3c,0x51,0x67,0x7f,0x97,0xad,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8e,0x7f,0x7f,0x8a,0x8c,0x8c,0x94,0xa4,0xab,0x9c,0x8a,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5e,0x6a,0x7b,0x8e,0xa3,0xa7,0x94,0x84,0x72\n      ,0x61,0x4c,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8a,0x9c,0xa9,0x9c,0x8e,0x84,0x7f,0x8a,0x94,0xa4,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x03,0x16,0x2b,0x41,0x57,0x6b,0x7f,0x93,0xa4,0xaa,0x9c,0x8e\n      ,0x8c,0x8c,0x8c,0x8e,0x98,0x94,0x84,0x72,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7f,0x7f,0x7f,0x8a,0x8e,0x9c,0xad,0xac,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b\n      ,0x8e,0xa3,0xa4,0x94,0x84,0x7f,0x7f,0x7f,0x8c,0x9d,0xa4,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb7,0xa8,0x94,0x8c,0x8c,0x8c,0x93,0x9d,0xab,0xac,0x9c,0x8c,0x7b,0x66,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xae,0xb0,0xa4,0x8e,0x7b,0x65,0x5c,0x73,0x8c,0xa2,0xaf,0xb5,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40\n      ,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x84,0x7f,0x84,0x8c,0x8c,0x94,0xa4\n      ,0xaf,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x94,0xa8,0xb5,0xa3,0x8e,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xa9,0x9c,0x8e\n      ,0x84,0x7f,0x84,0x8e,0x9c,0xa9,0x9c,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa1,0x9c,0x8e,0x8c,0x84,0x8a,0x8c,0x94,0xa4,0xab,0x98,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57\n      ,0x6a,0x7b,0x8e,0x9f,0x98,0x8e,0x8a,0x7f,0x8a,0x8e,0x9c,0xad,0xac,0x98,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8e,0x9d,0xb1,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00\n      ,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8a,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xb0,0xa4,0x8c,0x74,0x61,0x4b,0x42,0x5a,0x71,0x84,0x99\n      ,0xac,0x9d,0x8a,0x73,0x5b,0x43,0x2c,0x15,0x02,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x6b,0x73,0x73,0x74,0x84,0x94,0xa8,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c\n      ,0x73,0x64,0x74,0x8a,0x9c,0xa9,0x9c,0x8a,0x74,0x62,0x51,0x40,0x2b,0x17,0x05,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa5,0x9c,0x8c,0x7b,0x71,0x6b,0x74,0x84,0x98,0xa8,0x98,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x0a,0x21\n      ,0x36,0x4c,0x61,0x74,0x8a,0x9d,0xaa,0x9c,0x8c,0x7b,0x73,0x73,0x73,0x7b,0x84,0x84,0x72,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x67,0x6b,0x73,0x7b,0x8c,0x9d,0xb0,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xa9,0x99,0x84,0x72,0x67,0x67,0x6c,0x7f,0x93,0xa6,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x76,0x73,0x74,0x7f,0x8c,0x9c,0xad,0xac,0x98\n      ,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x99,0x9d,0x97,0x7f,0x67,0x65,0x7b,0x8e,0x9b,0x99,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x7f,0x98,0xb0,0xa4\n      ,0x8c,0x76,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98\n      ,0xb0,0xa4,0x8c,0x74,0x68,0x71,0x73,0x74,0x84,0x94,0xa8,0xad,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x74,0x7c,0x8e,0xa4,0xa8,0x94,0x84,0x71,0x5c,0x49,0x36,0x21,0x0a,0x00,0x00,0x07\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xa3,0x8e,0x7b,0x71,0x68,0x71,0x7b,0x8c,0x9d,0xa6,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x93,0x8c,0x7b,0x73,0x71,0x73,0x74,0x84,0x98,0xac,0xa3,0x8e,0x7b,0x65,0x4e,0x36\n      ,0x1e,0x06,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x5c,0x71,0x84,0x8e,0x84,0x7b,0x73,0x6f,0x73,0x7b,0x8c,0x9d,0xb0,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x7f,0x98\n      ,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x93,0xa8\n      ,0xa8,0x93,0x7f,0x67,0x4f,0x46,0x5c,0x73,0x8c,0xa2,0xab,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5d,0x5b,0x5b,0x62,0x74,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00\n      ,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x72,0x84,0x94,0xa6,0x9d,0x8c,0x7b,0x6a,0x57,0x41,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xa4,0x8e,0x7b,0x6a,0x5c,0x57,0x66,0x7b,0x8e,0xa3,0xa3,0x8e,0x7b\n      ,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa6,0x9d,0x8c,0x7b,0x6a,0x5c,0x5b,0x5c,0x66,0x71,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x57,0x5c,0x6b\n      ,0x7f,0x98,0xae,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x53,0x4f,0x61,0x74,0x8c,0xa4,0xa0,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4\n      ,0x8c,0x73,0x5e,0x61,0x6b,0x7b,0x8c,0x9d,0xb0,0xa3,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x07,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0x99,0x84,0x8e,0x98,0x84,0x71,0x6b,0x7f,0x97,0x8e,0x84,0x99,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43\n      ,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8e,0x9d,0xb3,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02\n      ,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c,0x5a,0x5b,0x62,0x74,0x8a,0x9d,0xad,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5c,0x64,0x74,0x8a,0x9c,0xa9,0x9c,0x8a\n      ,0x74,0x62,0x51,0x3c,0x27,0x14,0x02,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xaa,0x99,0x84,0x71,0x5d,0x54,0x5c,0x6b,0x7f,0x93,0xa6,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x62,0x72,0x7f,0x7b,0x6a,0x5c,0x5a,0x5b\n      ,0x66,0x7b,0x8e,0xa4,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x3c,0x51,0x62,0x72,0x7b,0x71,0x66,0x5c,0x59,0x5c,0x6b,0x7f,0x98,0xb0,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57\n      ,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa2,0xae,0x98,0x7f,0x6b,0x57,0x4e,0x65,0x7b,0x8e,0xa4,0xa4,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x46,0x46,0x5b,0x73,0x8c,0xa4,0xb0\n      ,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x74,0x7f,0x93,0xa4,0xa4,0x93,0x7f,0x6b,0x5a,0x49,0x36,0x21,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0xa2,0x8c,0x73\n      ,0x5e,0x5a,0x5a,0x5d,0x71,0x84,0x99,0xa9,0x97,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8a,0x9d,0xaa,0x97,0x7f,0x6b,0x5b,0x57,0x5b,0x5a,0x57,0x5a,0x5a,0x51,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x4f,0x5a,0x6b,0x7f,0x98,0xaa,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xa8,0x98,0x7f,0x6b,0x5b,0x51,0x61,0x74,0x8c,0xa4,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x4d,0x5a,0x6b,0x7f,0x93,0xa8,0xad,0x97,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0x9d,0x8a,0x84,0x98,0x8c,0x74,0x73,0x8a,0x98,0x8a,0x7f,0x98,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xae,0xbe,0xb5,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x67,0x67,0x67\n      ,0x65,0x5c,0x57,0x4d,0x46,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x46,0x44,0x56,0x6b,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x42,0x43,0x43\n      ,0x44,0x4b,0x5c,0x71,0x84,0x94,0xa7,0xa3,0x8e,0x7b,0x6a,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xaa,0x98,0x7f,0x67,0x51,0x40,0x4c,0x61,0x74,0x8c,0xa4,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x0a,0x1e\n      ,0x2f,0x40,0x51,0x61,0x67,0x65,0x5a,0x49,0x42,0x48,0x5c,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x61,0x65,0x5c,0x51,0x4a,0x4c,0x53,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e\n      ,0x06,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x4a\n      ,0x3a,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x99,0xac,0x9d,0x8a,0x73,0x5b,0x51,0x67,0x7f,0x97,0xaa,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4\n      ,0x8c,0x73,0x5b,0x4e,0x51,0x61,0x74,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x7c,0x8c,0x9d,0xac,0x99,0x84,0x72,0x61,0x4c,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xa9,0x99,0x84,0x71,0x66,0x71,0x71,0x66,0x68,0x7f,0x98,0xaa,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa4,0xa4,0x8e,0x7b,0x68,0x67,0x6b,0x73,0x71,0x67,0x65,0x5c,0x51,0x40,0x2f,0x1e\n      ,0x0c,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x67,0x67,0x6b,0x7b,0x8c,0x9d,0xa4,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa3,0x9d,0x8c,0x7b,0x71,0x67,0x6b,0x7f,0x93\n      ,0xa1,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x44,0x4c,0x61,0x74,0x8c,0xa4,0xb0,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x7f,0x97\n      ,0x93,0x7f,0x74,0x8c,0x98,0x7f,0x84,0x99,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x9d,0xae,0xbd,0xae,0x9d,0x98,0x98,0x98,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e\n      ,0x65,0x7b,0x8e,0xa4,0xa4,0x8c,0x7c,0x7f,0x7f,0x7f,0x7b,0x73,0x6b,0x61,0x51,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x46,0x47,0x5c,0x71,0x84,0x99,0xae,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2b,0x33,0x46,0x5a,0x6a,0x7b,0x8e,0xa3,0xa7,0x94,0x84,0x71,0x5c,0x49,0x36,0x21,0x0d,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xaa,0x98,0x7f,0x67,0x52,0x44,0x49,0x5c,0x73,0x8c,0xa4,0xad,0x97,0x7f\n      ,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x4b,0x4f,0x4e,0x46,0x38,0x33,0x46,0x5c,0x73,0x8c,0xa4,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x4b,0x51,0x57,0x5b,0x5b,0x61,0x67,0x72\n      ,0x84,0x99,0xa9,0x9c,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c\n      ,0x76,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa8,0xa4,0x8c,0x74,0x61,0x5a,0x71,0x84,0x99,0xaa,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5f,0x65,0x67,0x71,0x7f,0x93,0xa8,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8e,0x84,0x98,0xac,0xb0,0x98,0x7f,0x68,0x55,0x41,0x2b\n      ,0x17,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x6e,0x7b,0x84,0x84,0x7b,0x6e,0x7f,0x98,0xab,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xa4,0x8c,0x73,0x72,0x7f\n      ,0x7f,0x8a,0x84,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7f,0x7f,0x7f,0x7f,0x8c,0x9a,0x99,0x93,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71\n      ,0x84,0x94,0xa2,0x9c,0x8e,0x84,0x7f,0x7b,0x8c,0x9b,0x94,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x43,0x5b,0x73,0x8c,0xa4,0xb4,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x7c,0x8e,0x98,0x7f,0x7f,0x93,0x93,0x7f,0x8c,0xa2,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x8e,0xa4,0xb6,0xa4,0x8e,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e\n      ,0x36,0x1e,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xa8,0x94,0x8e,0x97,0x98,0x97,0x8e,0x8a,0x7f,0x72,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5c,0x68,0x7b,0x8e\n      ,0xa3,0xae,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x19,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x9c,0xa9,0x9c,0x8a,0x74,0x62,0x51,0x3c,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xaa,0x99,0x84,0x72\n      ,0x62,0x5b,0x61,0x6b,0x7b,0x8e,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x34,0x37,0x36,0x30,0x2e,0x40,0x52,0x66,0x7b,0x8e,0xa4,0xa3,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d\n      ,0x1e,0x2d,0x40,0x57,0x6a,0x73,0x73,0x74,0x7f,0x84,0x94,0xa1,0x9c,0x8c,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00\n      ,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa4,0xa8,0x93,0x7f,0x67,0x5c,0x73,0x8c,0xa2,0xa8\n      ,0x93,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x76,0x73,0x7b,0x7f,0x84,0x8e,0x9d,0xaf,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb9,0xae,0x9d\n      ,0x99,0xa4,0xb8,0xb0,0x99,0x84,0x71,0x5c,0x46,0x31,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x73,0x8a,0x98,0x98,0x8c,0x7b,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x7c,0x84,0x93,0x98,0x9d,0x99,0x97,0x8e,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xae,0x9d,0x98,0x98,0x98,0x98,0x9d,0x99,0x84,0x7f,0x72,0x63,0x56,0x44,0x31,0x1e,0x0a\n      ,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x72,0x84,0x94,0xa5,0xa3,0x99,0x93,0x8e,0x9b,0x94,0x84,0x72,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x43,0x5b,0x73,0x8c,0xa4,0xb6\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x79,0x8c,0x98,0x84,0x7f,0x97,0x8c,0x79,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x73,0x8c,0xa4,0xb6,0xa4\n      ,0x8c,0x73,0x67,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xa9,0xa8,0xa0,0x9e,0xa2,0xa4,0xa5,0xa4,0x9d,0x93,0x84,0x72,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98\n      ,0xb0,0xa4,0x8c,0x76,0x73,0x73,0x73,0x7b,0x84,0x98,0xac,0xa4,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x98,0xaa,0xa3,0x8e,0x7b,0x6a,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x07\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xa4,0x94,0x84,0x74,0x73,0x74,0x7f,0x8c,0x9c,0xae,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1f,0x27,0x38,0x4c,0x61,0x72,0x84,0x98,0xa8,0x98,0x84,0x71,0x5c,0x46,0x30\n      ,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x93,0x99,0xa1,0x94,0x8a,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98\n      ,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb5,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8a\n      ,0x9d,0xab,0x98,0x7f,0x6b,0x65,0x7b,0x8e,0xa4,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb7,0xa8,0x94,0x8c,0x8c,0x8e,0x97,0x99,0xa3,0xa9,0xa3,0x94,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00\n      ,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xbc,0xbe,0xb3,0xb0,0xb0,0xb0,0xb4,0xa3,0x8e,0x7b,0x66,0x52,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x7b,0x8e,0xa4,0xa9,0x97,0x7f,0x7f,0x98,0xb0,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8e,0x97,0x98,0x98,0x98,0x9d,0xa5,0xa3,0x94,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb5,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4\n      ,0x9c,0x8e,0x84,0x7b,0x71,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x69,0x76,0x8c,0xa1,0xa4,0xa6,0xa6,0xa4,0xa3,0x8e,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4\n      ,0x8c,0x73,0x5b,0x43,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x77,0x84,0x98,0x94,0x8e,0x96,0x84,0x77,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1b,0x2f\n      ,0x40,0x4b,0x4f,0x4f,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8a,0x94,0x93,0x8c,0x8c,0x8c,0x8c,0x93,0x9d,0xaa,0xa4,0x94,0x84,0x71,0x5a,0x43,0x2c,0x15\n      ,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8e,0x98,0xa4,0xa8,0x98,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6b,0x7f,0x93,0xa4,0xa4,0x94,0x84,0x71,0x5c,0x49\n      ,0x36,0x21,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xaa,0xa4,0x94,0x8c,0x8c,0x8c,0x93,0x96,0x99,0xa4,0xae,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x09,0x16,0x27,0x38,0x49,0x5a,0x6b\n      ,0x7f,0x93,0xa4,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa6,0xac,0xa4,0x8c,0x79,0x6f,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04\n      ,0x06,0x06,0x04,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb5,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xaa,0x9d,0x8a,0x73,0x67,0x7f,0x97,0xa9,0x99,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xbb,0xb5,0xa8,0xa4,0xa4,0xa4,0xad,0xb0,0xac,0x9c,0x8e,0x84\n      ,0x72,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xbc,0xcb,0xbe,0xad,0x9d,0x99,0xa4,0xab,0x98,0x84,0x72,0x61,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x74\n      ,0x8a,0x9c,0x9d,0x8e,0x7b,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb1,0xa1,0x98,0x8e,0x84,0x7f,0x7f,0x8a,0x94,0xa7,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x94,0x9f,0x98,0x8e,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x84,0x94,0x94,0x8c,0x93,0x99,0xa3,0xa9,0x9c,0x8e,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x7f,0x93,0xa4,0xa2,0x93,0x7f,0x73,0x8c,0xa4,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x74,0x84,0x7f,0x74,0x73,0x73,0x74,0x7f\n      ,0x8c,0x9d,0xaf,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb5,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0xa5,0xa2,0x94,0x8a,0x7b,0x66,0x52,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x52,0x66\n      ,0x7b,0x8c,0x9d,0xaa,0x98,0x84,0x72,0x62,0x51,0x3c,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x98,0xa2,0xa0,0xa0,0x9e,0x9b,0x93,0x84,0x84,0x99,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x16,0x27,0x38,0x49,0x5a,0x6a,0x7b,0x8c,0x9d,0xa4,0x94,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x97,0x98,0x98,0x9d,0xa4,0x94,0x8a,0x7f,0x72,0x62,0x51,0x40,0x2b,0x16\n      ,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x19,0x11,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b\n      ,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa4,0xa4,0x8c,0x74,0x71,0x84,0x99,0xa9,0x97,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb9,0xae\n      ,0x9d,0x98,0x98,0x99,0xa4,0xb4,0xa4,0x8e,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xbc,0xbe,0xad,0x9c,0x8c,0x84,0x94,0xa8,0xa4,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x6e,0x7b,0x8a,0x8a,0x7f,0x71,0x7f,0x98,0xab,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x84,0x7b,0x71,0x67,0x6b,0x76,0x8a,0x9d,0xaa,0x97,0x7f,0x6b,0x57\n      ,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x76,0x73,0x73,0x73,0x74,0x84,0x94,0xa2,0xa3,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x98,0x94,0x84,0x76,0x7f,0x84,0x8e,0x98,0xa2\n      ,0xa3,0x94,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x46,0x5c,0x73,0x8c,0xa4,0xb4,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x74\n      ,0x8c,0xa2,0xa2,0x8c,0x74,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38\n      ,0x49,0x57,0x62,0x71,0x6b,0x61,0x5b,0x5b,0x61,0x6c,0x7f,0x97,0xad,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb5,0xa4,0x99,0x98,0x98,0x98,0x98,0x93,0x8c,0x84,0x74,0x6a,0x5a,0x46,0x31,0x1e,0x0a,0x00,0x00\n      ,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x98,0xaa,0x9d,0x8c,0x7b,0x66,0x52,0x40,0x2f,0x1c,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x84,0x8c,0x8e,0x93,0x8c,0x8a,0x7f,0x76,0x84,0x99,0xaa,0x98,0x7f\n      ,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x27,0x38,0x49,0x5a,0x6a,0x7b,0x8c,0x9c,0xa2,0x94,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2c,0x40,0x57,0x6a,0x7b,0x7f,0x7f,0x7f,0x8a\n      ,0x93,0x9c,0x9d,0x93,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x30,0x36,0x36,0x30,0x25,0x21,0x38,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c\n      ,0x76,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xa6,0x93,0x7f,0x73,0x8c,0xa2,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x07,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8e,0x7f,0x7f,0x84,0x94,0xa8,0xa8,0x93,0x7f,0x6b,0x57,0x44,0x31,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xbb,0xb3,0x9d,0x8c,0x7b,0x74,0x8a,0x9d,0xad,0x9d,0x8a,0x74,0x61\n      ,0x4c,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x6b,0x6a,0x73,0x73,0x6b,0x69,0x7f,0x98,0xaa,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xa4,0x8c,0x74,0x66,0x5c\n      ,0x51,0x58,0x6b,0x7f,0x98,0xab,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x62,0x72,0x84,0x99,0xac,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a\n      ,0x9c,0x9d,0x8a,0x74,0x65,0x67,0x71,0x7b,0x84,0x94,0xa7,0xa3,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x07,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x44,0x4e,0x65,0x7b,0x8e,0xa4,0xb0,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8c,0x73,0x71,0x84,0x99,0x99,0x84,0x71,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x02,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x1e,0x1e,0x1e,0x19,0x10\n      ,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x46,0x4e,0x55,0x5a,0x57,0x4c,0x43,0x43,0x50,0x65,0x7b,0x8e,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x84,0x7f,0x7f,0x7f,0x7f,0x7f,0x74\n      ,0x71,0x62,0x57,0x49,0x38,0x25,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6b,0x7f,0x93,0xa4,0xa4,0x93,0x7f,0x6b,0x5a,0x46,0x33,0x2b,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x66,0x71,0x73,0x7b\n      ,0x7f,0x74,0x73,0x6b,0x73,0x8c,0xa2,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x49,0x5a,0x6a,0x7b,0x8c,0x9c,0xa5,0x94,0x84,0x72,0x62,0x51,0x40,0x31,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27\n      ,0x36,0x40,0x43,0x4b,0x5a,0x65,0x67,0x67,0x6b,0x74,0x7f,0x8e,0xa4,0xa4,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x4e,0x4e,0x46,0x38,0x2e,0x40,0x57,0x6b,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00\n      ,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x22,0x0f,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xaa,0x98,0x7f,0x7b,0x8e,0xa4,0x9d,0x8a\n      ,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x67,0x74,0x8a,0x9d,0xad,0x9d,0x8a,0x74,0x61,0x4c,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb7,0xa8,0x93\n      ,0x7f,0x6b,0x6b,0x7f,0x93,0xa8,0xa8,0x93,0x7f,0x6b,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa6,0x9d,0x8a,0x73,0x5f,0x5b,0x5b,0x5d,0x71,0x84,0x99,0xa9,0x97,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x06,0x1e,0x36\n      ,0x4e,0x65,0x7b,0x8e,0xa4,0xa4,0x8c,0x74,0x61,0x4d,0x3e,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x47,0x48,0x55,0x68,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa6,0x98,0x7f,0x6b,0x57,0x51,0x5c,0x66,0x74,0x8a,0x9d,0xaa,0x97,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x4d,0x5a,0x6b,0x7f,0x97,0xad,0xad\n      ,0x97,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x8e,0x7b,0x6c,0x7f,0x92,0x95,0x7f,0x67,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4\n      ,0x8c,0x73,0x5b,0x43,0x35,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x65,0x67,0x61,0x52,0x46,0x43,0x43,0x50,0x65,0x7b,0x8e,0xa4,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98\n      ,0xb0,0xa4,0x8c,0x74,0x67,0x67,0x67,0x67,0x67,0x61,0x5a,0x51,0x41,0x36,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x9d,0xaa,0x98,0x84,0x72,0x61,0x4d,0x44,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00\n      ,0x00,0x08,0x1a,0x2b,0x3c,0x4c,0x55,0x5a,0x5c,0x65,0x67,0x61,0x5c,0x66,0x7b,0x8e,0xa4,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x5c,0x6a,0x7b,0x8c,0x9c,0xa2,0x98,0x8a,0x74,0x62,0x51,0x47,0x43,0x42,0x3c,0x2f\n      ,0x1e,0x0a,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x49,0x57,0x5b,0x5a,0x52,0x4e,0x4f,0x4f,0x57,0x62,0x73,0x8a,0x9d,0xad,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x65,0x65,0x5a,0x4c,0x41,0x43,0x5b,0x73,0x8a,0x9d\n      ,0xb1,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x49,0x49,0x49,0x49,0x49,0x49,0x47,0x43,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67\n      ,0x7f,0x93,0xa6,0x9d,0x8a,0x7f,0x97,0xa7,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5e,0x6b,0x7f,0x93,0xa8,0xa8,0x93,0x7f,0x6b,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00\n      ,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x74,0x61,0x61,0x74,0x8a,0x9d,0xad,0x9d,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0d,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa2,0xa4,0x8e,0x7b,0x66,0x52,0x4d,0x61,0x74,0x8c,0xa2,0xa4,0x8e,0x7b\n      ,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xa6,0x93,0x7f,0x67,0x53,0x47,0x52,0x67,0x7f,0x98,0xab,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x51,0x5a,0x61\n      ,0x71,0x84,0x99,0xae,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x52,0x45,0x4a,0x57,0x6b,0x7f,0x98,0xaa,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4\n      ,0x8c,0x73,0x5e,0x61,0x6b,0x7b,0x8c,0x9d,0xaf,0xa3,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x07,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa8,0x97,0x7f,0x69,0x72,0x7f,0x7f,0x7b,0x66,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xb4,0xa4,0x8c,0x73,0x5c,0x4e,0x4b,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x52,0x66,0x7b,0x7f,0x72,0x66,0x5c,0x5b,0x5b,0x61,0x6c,0x7f,0x97,0xad,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e\n      ,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x4f,0x4f,0x4f,0x4f,0x4b,0x43,0x3c,0x2f,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x98,0xaa,0x9d,0x8c,0x7b,0x66,0x5c,0x5b,0x5b,0x5b,0x5b\n      ,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x65,0x65,0x5c,0x56,0x55,0x54,0x5d,0x71,0x84,0x98,0xaa,0x9d,0x8a,0x73,0x5b,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x62,0x71,0x7b,0x8c,0x9c,0xa2,0x94\n      ,0x84,0x7b,0x6a,0x5d,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6a,0x73,0x71,0x62,0x57,0x4f,0x4f,0x51,0x5d,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a\n      ,0x7b,0x7b,0x6b,0x61,0x57,0x51,0x61,0x74,0x8c,0xa4,0xb1,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa2,0xa4,0x8e,0x84,0x99,0xa4,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x61,0x74,0x8a,0x9d,0xad,0x9d,0x8c\n      ,0x7b,0x66,0x51,0x3c,0x26,0x10,0x01,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x57,0x6b,0x7f,0x93,0xa8,0xa8,0x93,0x7f,0x6b,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x98,0xa8,0x98,0x84\n      ,0x72,0x66,0x61,0x6c,0x7f,0x93,0xa6,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xa9,0x99,0x84,0x72,0x66,0x5e,0x66,0x72,0x84,0x99,0xa9,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x67,0x67,0x71,0x74,0x7f,0x8e,0xa3,0xae,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xa9,0x99,0x84,0x72,0x62,0x5b,0x5b,0x61,0x71,0x84,0x99,0xaa,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x76,0x73,0x74,0x7f,0x8c,0x9c,0xad,0xa8,0x94,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x61,0x67,0x67,0x65,0x5f,0x73,0x8c,0xa4,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8e,0x7b,0x6b,0x65,0x61,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x72,0x84,0x91,0x84,0x7b,0x73,0x73,0x73,0x74,0x7f\n      ,0x8c,0x9d,0xad,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x37,0x37,0x37,0x34,0x2c,0x25,0x1b,0x0d,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6b,0x7f,0x93,0xa4\n      ,0xae,0x98,0x7f,0x74,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x7b,0x71,0x67,0x67,0x67,0x71,0x7b,0x8e,0xa3,0xa8,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x02,0x15,0x2b\n      ,0x40,0x51,0x62,0x72,0x84,0x8e,0x9c,0xa9,0x99,0x84,0x77,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x02,0x15,0x2b,0x40,0x52,0x66,0x7b,0x8a,0x84,0x74,0x6b,0x67,0x67,0x67,0x71,0x7b,0x8e,0xa3,0xb0,0x9d,0x8a,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x7b,0x8c,0x8c,0x7f,0x74,0x6b,0x67,0x71,0x7f,0x93,0xa8,0xad,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71\n      ,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x99,0xa9,0x9d,0x99,0xa4,0xa2,0x8c,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4\n      ,0x8c,0x73,0x5b,0x57,0x6b,0x7f,0x93,0xa8,0xac,0x98,0x84,0x71,0x5c,0x46,0x31,0x1e,0x0a,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x4d,0x61,0x74,0x8a,0x9d,0xad,0x9d,0x8a,0x74,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00\n      ,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0xa3,0xa4,0x94,0x84,0x7b,0x74,0x7f,0x8c,0x9d,0xa4,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8c,0x9d,0xa4,0x94,0x84,0x7b,0x73,0x7b,0x84,0x94,0xa4,0xa3,0x8e,0x7b,0x65,0x4e\n      ,0x37,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7f,0x7f,0x7f,0x84,0x8c,0x93,0x9d,0xab,0xa4,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa3,0xa4,0x94,0x84,0x74,0x73,0x73,0x74,0x7f,0x8e\n      ,0xa3,0xa8,0x93,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb7,0xa8,0x94,0x8c,0x8c,0x8c,0x93,0x9d,0xab,0xac,0x9c,0x8a,0x74,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67\n      ,0x51,0x4f,0x4f,0x4e,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa8,0xae,0x9c,0x8c,0x7f,0x7b,0x74,0x7f,0x7f,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x11,0x28,0x40,0x57,0x6b\n      ,0x7f,0x93,0xa0,0x98,0x8e,0x8c,0x8c,0x8c,0x8c,0x93,0x9d,0xaa,0xa4,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x1e,0x1e,0x1c,0x15,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9d,0xb3,0xb3,0x9d,0x8e,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x8e,0x84,0x7f,0x7f,0x7f,0x84,0x8e,0x9c,0xa9,0x9c,0x8a,0x74,0x61\n      ,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x94,0xa3,0xae,0xb3,0x9d,0x8e,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x98,0x94,0x8a,0x7f,0x7f,0x7f,0x7f,0x84\n      ,0x8e,0x9c,0xae,0xa8,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9c,0x9d,0x93,0x8a,0x7f,0x7f,0x84,0x8e,0x9d,0xb0,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94\n      ,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa8,0xb1,0xb0,0xae,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x4d,0x61,0x74,0x8a,0x9d,0xb0,0xa3,0x8e,0x7b,0x66,0x52,0x40,0x2b,0x15,0x02,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x47,0x57,0x6b,0x7f,0x93,0xa8,0xa8,0x94\n      ,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x71,0x84,0x94,0xa4,0xa4,0x98,0x8e,0x8c,0x93,0x9d,0xa5,0x98,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6b,0x7f,0x93,0xa3,0xa2,0x98,0x8e\n      ,0x8c,0x8e,0x98,0xa4,0xa4,0x94,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xae,0x9d,0x98,0x98,0x98,0x99,0xa2,0xa6,0xaa,0xa3,0x94,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84\n      ,0x97,0xa4,0xa4,0x94,0x8c,0x8c,0x8c,0x8c,0x93,0x9d,0xa5,0x9c,0x8a,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb8,0xb3,0xa8,0xa4,0xa4,0xa4,0xa6,0xaa,0xa3,0x98,0x8c,0x7b,0x6a,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x3a,0x37,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xad,0xad,0x9d,0x97,0x8e,0x8c,0x93,0x95,0x7f,0x6b,0x57\n      ,0x40,0x28,0x11,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x8e,0x98,0xa1,0xa3,0xa4,0xa4,0xa4,0xa4,0xa6,0xa6,0x9d,0x93,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x06,0x05\n      ,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xac,0xb8,0xb8,0xae,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xa3,0x99,0x98\n      ,0x98,0x98,0x99,0xa3,0xa8,0x9c,0x8c,0x7b,0x6a,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa4,0xb3,0xb9,0xb8,0xae,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x90,0x9f,0xa5,0x9d,0x98,0x98,0x98,0x98,0x99,0xa3,0xab,0xaa,0x9c,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9c,0xaa,0xa8,0x9d,0x98,0x98,0x99,0xa3,0xab,0xa4,0x97,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00\n      ,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb3,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa1,0xa4,0xa4,0xa2,0x93,0x7f,0x67\n      ,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa1,0x8c,0x73,0x5b,0x47,0x57,0x6b,0x7f,0x93,0xa2,0xa2,0x98,0x84,0x72,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa1,0x8c\n      ,0x73,0x5b,0x43,0x4c,0x61,0x74,0x8a,0x9d,0xa4,0x9f,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x72,0x84,0x93,0x9d,0xa5,0xa4,0xa4,0xa2,0xa2,0x94,0x8a,0x7b,0x66,0x52,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x10\n      ,0x25,0x38,0x4c,0x61,0x72,0x84,0x8e,0x98,0xa1,0xa3,0xa4,0xa3,0xa3,0x9d,0x93,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa2,0x99,0x97,0x8e,0x84,0x72,0x62,0x51,0x40,0x2b,0x15\n      ,0x02,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x84,0x93,0x9d,0xa0,0xa2,0xa4,0xa4,0xa2,0xa0,0x9d,0x93,0x8a,0x7b,0x6a,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xa4,0xa4,0xa2,0x99,0x97,0x8e,0x84,0x7b,0x6a\n      ,0x5a,0x49,0x36,0x21,0x0d,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x9c\n      ,0xa4,0xaa,0xa8,0xa4,0xa2,0xa0,0x9b,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x71,0x7b,0x84,0x8e,0x97,0x9d,0xa4,0xa4,0xa2,0x99,0x93,0x8a,0x7f,0x72,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98\n      ,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06\n      ,0x1e,0x36,0x4e,0x65,0x7b,0x84,0x93,0x9d,0xa4,0xa6,0xaa,0xaa,0xa6,0xa2,0x98,0x8c,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x8e,0x98,0xa2,0xa4,0xa8,0xaa,0xaa,0xa9,0xa4,0x9d,0x93,0x8a,0x7b,0x6a,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x7b,0x8c,0x98,0xa2,0xa6,0xaa,0xaa,0xa9,0xa4,0x9d,0x93\n      ,0x84,0x7b,0x66,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43\n      ,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8c,0x89,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x4c,0x61,0x72,0x84,0x8c,0x8c,0x8c,0x8a,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00\n      ,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x41,0x57,0x6b,0x7f,0x8c,0x8c,0x8c,0x8c,0x82,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x72,0x7f,0x8a,0x93,0x98,0x98,0x93,0x8c,0x84,0x74,0x6a,0x5a,0x46\n      ,0x31,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x51,0x62,0x71,0x7b,0x84,0x8e,0x97,0x98,0x97,0x8e,0x8a,0x7f,0x72,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x84,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x66,0x72,0x7f,0x8a,0x8e,0x97,0x98,0x98,0x97,0x8e,0x8a,0x7f,0x74,0x6a,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c\n      ,0x8c,0x8c,0x8c,0x8c,0x84,0x7f,0x7b,0x71,0x66,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x8a,0x8e,0x97,0x98,0x98,0x97,0x8e,0x8c,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5c,0x66,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6b,0x61,0x51,0x40,0x2f,0x1e,0x0a,0x00\n      ,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x66,0x72,0x7f,0x8a,0x8c,0x93,0x98,0x98,0x93,0x8c,0x84,0x7b,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x62,0x71,0x7b,0x84,0x8c,0x8e,0x97,0x98,0x98,0x97,0x8e,0x8a,0x7f,0x74,0x6a,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a\n      ,0x7b,0x84,0x8c,0x93,0x98,0x98,0x97,0x8e,0x8a,0x7f,0x72,0x66,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x40,0x51,0x62,0x71,0x73,0x73\n      ,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x36,0x4c,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x51,0x61,0x6b,0x74\n      ,0x7f,0x7f,0x7f,0x7f,0x74,0x71,0x62,0x57,0x49,0x38,0x25,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x5c,0x66,0x71,0x7b,0x7f,0x7f,0x7f,0x7b,0x73,0x6b,0x61,0x51,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61\n      ,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x67,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x52,0x61,0x6b,0x73,0x7b,0x7f,0x7f,0x7f,0x7f,0x7b,0x73,0x6b,0x61,0x57,0x49,0x38,0x27,0x14,0x02,0x00,0x00\n      ,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x67,0x65,0x5c,0x51,0x46,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73\n      ,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x6a,0x73,0x7b,0x7f,0x7f,0x7f,0x7f,0x7b,0x73,0x71,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x46,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x71\n      ,0x67,0x61,0x57,0x4c,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73\n      ,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x52,0x61,0x6b,0x73,0x74,0x7f,0x7f,0x7f,0x7f,0x74,0x71,0x66,0x5a,0x49,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34\n      ,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x5c,0x66,0x71,0x73,0x7b,0x7f,0x7f,0x7f,0x7f,0x7b,0x73,0x6b,0x61,0x57,0x49,0x38,0x27,0x14,0x02\n      ,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x66,0x71,0x74,0x7f,0x7f,0x7f,0x7f,0x7b,0x73,0x6b,0x61,0x52,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71\n      ,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b\n      ,0x5b,0x57,0x49,0x36,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00\n      ,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x4c,0x57,0x61,0x67,0x67,0x67,0x67,0x61,0x5a,0x51,0x41,0x36,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x46,0x51,0x5c,0x65,0x67,0x67,0x67,0x65,0x5c,0x57,0x4c,0x40,0x2f,0x1e,0x0d\n      ,0x01,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4e,0x46,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x31,0x40,0x4c,0x57,0x5c,0x65,0x67,0x67,0x67,0x67,0x65,0x5c\n      ,0x57,0x4c,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4e,0x46,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51\n      ,0x40,0x2b,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x57,0x5c,0x65,0x67,0x67,0x67,0x67,0x65,0x5c,0x5a,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26\n      ,0x31,0x3c,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x04,0x16,0x25,0x31,0x40,0x4c,0x57,0x5b,0x61,0x67,0x67,0x67,0x67,0x61,0x5a,0x51,0x46,0x38,0x27,0x16\n      ,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x46,0x51,0x5a,0x5c,0x65,0x67,0x67,0x67,0x67\n      ,0x65,0x5c,0x57,0x4c,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x51,0x5a,0x61,0x67,0x67,0x67,0x67,0x65,0x5c,0x57,0x4c,0x40,0x31,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b\n      ,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43\n      ,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x36,0x41,0x4b,0x4f,0x4f,0x4f,0x4f,0x4b,0x43,0x3c,0x2f,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x31,0x3c,0x46,0x4e,0x4f\n      ,0x4f,0x4f,0x4e,0x46,0x40,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x36,0x30,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1e,0x2b\n      ,0x36,0x40,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x40,0x36,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x36,0x30,0x26,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x40,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x42,0x3c,0x2f\n      ,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x04,0x10,0x1e,0x2b,0x36,0x40,0x43,0x4b,0x4f\n      ,0x4f,0x4f,0x4f,0x4b,0x43,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b\n      ,0x26,0x31,0x3c,0x42,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x40,0x36,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x3c,0x43,0x4b,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x40,0x36,0x2b,0x1e,0x10,0x04,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21\n      ,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a\n      ,0x28,0x21,0x14,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x21,0x2b,0x34,0x37,0x37,0x37,0x37,0x34,0x2c,0x25,0x1b,0x0d,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x06,0x10,0x1b,0x26,0x30,0x36,0x37,0x37,0x37,0x36,0x30,0x28,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x21,0x28,0x30,0x36,0x37,0x37,0x37,0x37,0x36,0x30,0x28,0x21,0x16,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1e,0x19,0x10,0x06\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x30\n      ,0x36,0x37,0x37,0x37,0x37,0x36,0x30,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x01,0x0a,0x16,0x21,0x28,0x2c,0x34,0x37,0x37,0x37,0x37,0x34,0x2c,0x25,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b\n      ,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x1b,0x25,0x2a,0x30,0x36,0x37,0x37,0x37,0x37,0x36,0x30,0x28,0x21,0x16,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x25,0x2c,0x34,0x37,0x37,0x37,0x37,0x36,0x30,0x28\n      ,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02\n      ,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0b,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0e,0x06,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x0e,0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0a,0x11,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x12,0x0e,0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0a,0x11,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06\n      ,0x0e,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x11,0x0a,0x0e,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x01,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x01,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11\n      ,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12\n      ,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e\n      ,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x1b,0x25,0x2a,0x2a,0x2a,0x2a\n      ,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x28,0x21,0x25,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x11,0x1b,0x25\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x11,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c\n      ,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x0a,0x11,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0b,0x15,0x1c,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0b,0x15,0x1c,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x15,0x0b,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a\n      ,0x25,0x1d,0x15,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43\n      ,0x43,0x40,0x36,0x27,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x40,0x36,0x3c,0x42,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40\n      ,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x23,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x23,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00\n      ,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f\n      ,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x21,0x28,0x31,0x3c,0x42,0x43,0x43,0x43\n      ,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x21,0x2b,0x34,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x34,0x2b,0x1e,0x10,0x04,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x42,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x36,0x30\n      ,0x26,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x16,0x21,0x2b,0x34,0x38,0x40,0x43,0x43\n      ,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x40,0x37,0x30,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10\n      ,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x34,0x2b,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x12,0x12,0x12,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00\n      ,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x38,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x57,0x49,0x51,0x5a,0x5b,0x5b,0x57,0x49,0x36,0x21\n      ,0x0a,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x31,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x31,0x40,0x51,0x5a,0x5b,0x5b\n      ,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21\n      ,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40\n      ,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x05,0x16,0x25\n      ,0x30,0x36,0x38,0x40,0x46,0x51,0x5a,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1e,0x2b,0x36,0x41,0x4b,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x06,0x1b,0x2f\n      ,0x40,0x4b,0x4f,0x4b,0x40,0x31,0x25,0x17,0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x5a,0x51,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x3c,0x46\n      ,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4e,0x46,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x41,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05\n      ,0x14,0x21,0x2b,0x36,0x41,0x4b,0x4f,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x31,0x3c,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x41,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2f,0x2a,0x2a,0x2a,0x2d,0x40,0x51,0x5a\n      ,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x5a,0x46,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73\n      ,0x6a,0x57,0x61,0x71,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x3a,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73\n      ,0x73,0x71,0x61,0x4b,0x3a,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b\n      ,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x51,0x62,0x71,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b\n      ,0x34,0x1c,0x05,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x46,0x4e,0x4f,0x57,0x5c,0x66,0x71,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x17,0x25,0x31,0x40,0x4c,0x57,0x61,0x65\n      ,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x61,0x52,0x46,0x38,0x2b,0x21,0x14,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x62,0x71,0x73,0x73,0x71,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00\n      ,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x46,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x71,0x67,0x65,0x5c,0x51,0x46,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x49,0x38\n      ,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x41,0x4c,0x57,0x61,0x67,0x6b,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73\n      ,0x6b,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x46,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x71,0x67,0x61,0x57,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x71\n      ,0x62,0x51,0x41,0x43,0x43,0x43,0x42,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8a,0x7b,0x65,0x4e,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x8c,0x89,0x73,0x5b,0x67,0x7f,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73\n      ,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x5a,0x65,0x67,0x6b,0x73,0x7b,0x84,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04\n      ,0x10,0x1e,0x2b,0x38,0x46,0x52,0x61,0x6b,0x74,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x72,0x66,0x5a,0x4c,0x41,0x36,0x27,0x1b,0x0d,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x62,0x72,0x84,0x8c\n      ,0x8c,0x84,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x66,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8c,0x84,0x7f,0x7b,0x71,0x66,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x6a,0x74,0x7f\n      ,0x84,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x61,0x6b,0x74,0x7f,0x7f,0x8a,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f\n      ,0x40,0x51,0x62,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8a,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x66,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8c,0x84,0x7f,0x74,0x6a,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00\n      ,0x12,0x2a,0x43,0x5b,0x73,0x87,0x8c,0x8c,0x8c,0x84,0x71,0x5a,0x57,0x5b,0x5b,0x5b,0x5a,0x54,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0x97,0x7f,0x6b,0x57,0x4f,0x67,0x7f,0x98,0xa4,0xa1\n      ,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x28,0x40,0x57,0x6b,0x7f,0x98,0x8c,0x73,0x5c,0x67,0x7f,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x01,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67\n      ,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4\n      ,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4\n      ,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6b,0x7f,0x93,0xa2,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x7b,0x7f,0x7f,0x8a,0x8e,0x98,0xa2,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x17,0x25,0x31,0x40,0x4c,0x5a,0x66,0x72,0x7f,0x8a,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x84,0x7b,0x6b,0x61,0x57,0x49,0x3c,0x2f,0x21,0x14,0x06,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x94,0xa2,0xa2,0x98,0x8c,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x84,0x8e,0x97,0x9d,0xa4,0xa4,0xa2,0x99,0x97,0x8e,0x84,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00\n      ,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x8a,0x93,0x99,0xa2,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x74,0x7f,0x8a,0x93,0x98,0x9d,0xa4,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x6a,0x5a,0x46,0x31\n      ,0x1b,0x06,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x51,0x62,0x72,0x84,0x8e,0x97,0x9d,0xa4,0xa4,0x9d,0x97,0x8e,0x84,0x72,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x84,0x8e,0x97,0x9d,0xa4,0xa4,0xa2,0x99\n      ,0x93,0x8a,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xa4,0xa0,0x8c,0x73,0x5e,0x6a,0x73,0x73,0x73,0x71,0x61,0x67,0x7f,0x98,0xa4,0x9b,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb9,0xb3\n      ,0x9d,0x8a,0x74,0x61,0x53,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x43,0x5b,0x73,0x8a,0x9a,0x8c,0x73,0x60,0x71,0x84,0x98,0x8c,0x73,0x5b,0x43,0x2d,0x1e,0x0c,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa8,0xb3,0xbb,0xb3,0xa8,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb3,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x9d,0xae,0xb1,0xb8,0xb0,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xae,0xb6,0xac,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x97,0x98,0x9d,0xa4,0xad,0xb5,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x1e,0x2b,0x38,0x46,0x52,0x61,0x6b,0x7b,0x84,0x93,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x8c,0x7f,0x74,0x6a,0x5c,0x51\n      ,0x41,0x36,0x27,0x1b,0x0d,0x02,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa4,0xb5,0xb8,0xac,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x98,0xa3,0xa2,0x99,0x98,0x98,0x98,0x9d,0xa3,0xa1,0x96\n      ,0x88,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a,0x7b,0x8c,0x9c,0xa5,0xa2,0x99,0x98,0x99,0xa2,0xa5,0x9c,0x8c,0x7b,0x6a,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x82,0x92,0x9d,0xa2,0xa4,0xa4,0xa4\n      ,0xa4,0xa4,0xa9,0xa8,0x9c,0x8c,0x7b,0x66,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x94,0xa3,0xa8,0xa4,0xa4,0xa4,0xa4,0xa7,0xa3,0x94,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a\n      ,0x7b,0x8c,0x98,0xa3,0xa7,0xa4,0xa4,0xa4,0xa4,0xa4,0xa3,0x98,0x88,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x61,0x73,0x89,0x8c,0x8c,0x7f,0x67,0x67,0x7f,0x98,0xaa,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xbc,0xbd,0xa8,0x93,0x7f,0x67,0x56,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x5b,0x73,0x8c,0x98,0x84,0x71,0x60,0x73,0x8c,0x9a,0x8a,0x73,0x5b,0x4c,0x40\n      ,0x2f,0x1b,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x94,0xa8,0xb9,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x4c,0x61,0x72,0x84\n      ,0x98,0xa2,0x99,0x9d,0xae,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8e,0x9d,0xab,0x9c,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b\n      ,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xae,0xb8,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x31,0x40,0x4c,0x5a,0x66,0x72,0x7f,0x8c,0x98,0xa2,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43\n      ,0x5b,0x73,0x8c,0x9f,0x9d,0x93,0x8a,0x7b,0x71,0x62,0x57,0x49,0x3c,0x2f,0x21,0x14,0x06,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xc5,0xc9,0xb3,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa8\n      ,0xa8,0x94,0x84,0x7f,0x7f,0x7f,0x8a,0x8e,0x98,0x8e,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x8c,0x9c,0xa9,0x9c,0x8e,0x84,0x7f,0x84,0x8e,0x9c,0xa9,0x9c,0x8a,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x0e,0x25\n      ,0x3c,0x51,0x66,0x7b,0x8e,0x9c,0x93,0x8c,0x8c,0x8c,0x8c,0x8e,0x98,0xa4,0xab,0x98,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6b,0x7f,0x93,0xa4,0xab,0x9c,0x8e,0x8c,0x8c,0x8e,0x98,0xa4,0xa4,0x94,0x84,0x71,0x5c,0x46,0x31,0x1b\n      ,0x06,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x8c,0x9c,0xaa,0xa4,0x98,0x8e,0x8c,0x8c,0x8c,0x8e,0x97,0x8c,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xa4,0x8c,0x73,0x66,0x7b,0x8e,0xa1,0x98,0x7f,0x6b,0x6b,0x7f\n      ,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb8,0xb3,0xad,0x99,0x84,0x71,0x5c,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x73,0x8c,0x98,0x7f\n      ,0x6a,0x67,0x73,0x8c,0x98,0x7f,0x6e,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0\n      ,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x76,0x8c,0xa4,0xb6,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f\n      ,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6b,0x7f,0x93,0xa1,0x94,0x84,0x8e,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x77,0x84,0x99,0xa2,0x8e,0x7b,0x6a,0x5a,0x46\n      ,0x30,0x19,0x04,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8e,0x9d,0xb1,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x46,0x52,0x61,0x6b,0x7b,0x84,0x93,0x9d,0xa2,0x98,0x8c,0x7b\n      ,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x8e,0x9c,0xa2,0x9c,0x8e,0x84,0x74,0x6a,0x5c,0x51,0x41,0x36,0x27,0x1b,0x0c,0x01,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xac,0xbe,0xc5,0xb0,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00\n      ,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x74,0x67,0x67,0x6b,0x73,0x7b,0x84,0x84,0x71,0x5c,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x98,0xaa,0x9d,0x8c,0x7b,0x71,0x68,0x71,0x7b,0x8c,0x9d,0xa6,0x93,0x7f\n      ,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x71,0x84,0x8a,0x7f,0x74,0x73,0x73,0x73,0x7b,0x84,0x98,0xac,0xa3,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x07,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9d,0xad,0x9d,0x8c,0x7b,0x73,0x73,0x7b\n      ,0x84,0x98,0xab,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x98,0xab,0xa4,0x94,0x84,0x7b,0x73,0x73,0x73,0x7b,0x84,0x7f,0x6b,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa4,0xa4,0x8e\n      ,0x7b,0x6c,0x7f,0x97,0xae,0x9d,0x8a,0x73,0x73,0x8a,0x9d,0xaa,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xae,0x9d,0x99,0x9f,0x8e,0x7b,0x66,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12\n      ,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x8e,0x99,0x84,0x7f,0x7f,0x7f,0x8e,0x99,0x84,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5e,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x57\n      ,0x49,0x36,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b\n      ,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x05,0x17,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x9d,0x9c,0x8a,0x79,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5f\n      ,0x6b,0x7f,0x93,0xa1,0x94,0x84,0x71,0x5c,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x51,0x5c,0x66\n      ,0x72,0x7f,0x8c,0x98,0xa1,0x9d,0x93,0x84,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x71,0x7b,0x8a,0x94,0xa1,0xa1,0x94,0x8a,0x7b,0x71,0x62,0x57,0x49,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9c,0xac\n      ,0xae,0xa3,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8e,0x7b,0x73,0x71,0x67,0x64,0x66,0x71,0x71,0x62,0x51,0x3c,0x27,0x14,0x02,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa3,0xa8,0x93,0x7f\n      ,0x6b,0x61,0x5d,0x61,0x6b,0x7f,0x98,0xaa,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x62,0x71,0x73,0x6b,0x64,0x66,0x67,0x67,0x6e,0x7c,0x8e,0xa4,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67\n      ,0x7f,0x97,0xac,0xa8,0x93,0x7f,0x6b,0x5c,0x5c,0x68,0x7b,0x8e,0xa4,0xad,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x07,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa3,0xae,0x99,0x84,0x72,0x66,0x5c,0x5b,0x5c,0x66,0x71,0x71,0x61,0x4c,0x38,0x27,0x14,0x02,0x00,0x00\n      ,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8c,0xa4,0xad,0x97,0x7f,0x6e,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x73,0x8c,0xa4,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8e,0x84,0x98,0x98,0x84,0x71,0x67,0x7f,0x98,0xb0,0xa4\n      ,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x9d,0xa4,0x99,0x98,0x98,0x98,0x9d,0xa4,0x99,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x5b,0x5b,0x5b,0x67\n      ,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x46,0x5b,0x73,0x8c\n      ,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x46,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xa4,0x8c,0x73,0x5b,0x49,0x49,0x49,0x49,0x49,0x49,0x47,0x41,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x4c,0x61,0x72,0x84,0x98,0xa1,0x8e,0x7b,0x74,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e\n      ,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x44,0x51,0x66,0x7b,0x8c,0x9d,0x9c,0x8a,0x74,0x62,0x51,0x3c,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x71,0x7b,0x84,0x93,0x9d,0xa1,0x94,0x8a,0x7f,0x72,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5c,0x6a,0x74,0x84,0x8e,0x98,0xa1,0x9c,0x8e,0x84,0x74,0x6a,0x5c,0x51,0x40,0x2b,0x15,0x00\n      ,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x7b,0x8c,0x97,0x97,0x8e,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa8,0xae,0x9c,0x8e,0x8c,0x84,0x7f,0x74,0x73,0x6b,0x63,0x59,0x49,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xa4,0x8c,0x77,0x73,0x73,0x73,0x73,0x73,0x7f,0x98,0xae,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x0d,0x20,0x33,0x45,0x56,0x60,0x66,0x71,0x73,0x7b,0x7f,0x7f,0x7f,0x8a,0x94,0xa8,0xb0,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x74,0x61,0x4c,0x48,0x5c,0x73,0x8c,0xa2,0xb3,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x97,0xad,0xa8,0x93,0x7f,0x67,0x53,0x46,0x43,0x46,0x51\n      ,0x5a,0x5a,0x51,0x40,0x2b,0x17,0x05,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xab,0x98,0x7f,0x73,0x8a,0x98,0x8e,0x94,0x8e,0x7b,0x73,0x8c,0xa4,0xa4,0x8c,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8c\n      ,0x7c,0x8e,0x9e,0x8c,0x74,0x69,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x99,0xa4,0x9d,0x98,0x98,0x98,0x99,0xa4,0x9d,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb0,0x98,0x7f,0x73,0x73,0x73,0x73,0x73,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00\n      ,0x01,0x0c,0x1b,0x25,0x2a,0x2d,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2d,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x0d,0x21,0x36,0x49,0x5a,0x6b,0x7f,0x93,0xa1,0x94,0x84,0x71,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x33,0x46,0x5c,0x71,0x84,0x98,0xa1,0x8e,0x7b,0x6a,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x72,0x84,0x8e,0x98,0xa1,0x9c,0x8e,0x84,0x74,0x6b,0x61,0x52,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x49,0x57,0x62,0x71,0x7b,0x84,0x93,0x9d\n      ,0xa1,0x94,0x8a,0x7b,0x71,0x61,0x4b,0x34,0x1c,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x7f,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8a,0x98,0xa3,0xa8,0xa4,0xa2,0x99,0x93,0x8c,0x8a,0x7f,0x74\n      ,0x6a,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8e,0x9d,0xb1,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x71,0x7b,0x84,0x8c,0x8e,0x97\n      ,0x98,0x98,0x9b,0xa2,0xb1,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xb4,0xa4,0x8c,0x73,0x5b,0x43,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99\n      ,0xb0,0xa4,0x8c,0x74,0x61,0x4b,0x35,0x2a,0x31,0x3c,0x42,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xaa,0x98,0x7f,0x73,0x8c,0x97,0x7f,0x8c,0x97,0x7f,0x74,0x8c,0xa4,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8e,0x7c,0x8a,0x9c,0x93,0x7f,0x6e,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x84,0x99,0x8e,0x7f,0x7f,0x7f,0x84,0x99,0x8e,0x7f,0x7f,0x7f,0x71,0x5a\n      ,0x42,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb1,0x9d,0x8e,0x8c,0x8c,0x8c,0x8c,0x8c,0x8e,0x9d,0xb1,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x15,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x15,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x41,0x57,0x6a,0x7b,0x8c,0x9d,0x9c,0x8a\n      ,0x74,0x64,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x22,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x15,0x26,0x3c,0x51,0x66,0x7b,0x8e,0xa1,0x98,0x84,0x71,0x5c,0x49,0x36,0x21,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21\n      ,0x28,0x2a,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x94,0xa1,0x9d,0x93,0x8a,0x7b,0x71,0x62,0x57,0x4c,0x40,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x0c\n      ,0x1b,0x27,0x36,0x41,0x51,0x5c,0x66,0x72,0x7f,0x8c,0x98,0xa1,0x9c,0x8e,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x65,0x67,0x67,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x84\n      ,0x8e,0x98,0xa2,0xa4,0xa9,0xa6,0xa4,0x9d,0x93,0x8a,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb1,0xa2,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0xa2,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x10,0x26\n      ,0x3c,0x51,0x62,0x72,0x84,0x8e,0x98,0x9d,0x98,0x98,0x93,0x8c,0x8c,0x94,0xa8,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x3e,0x54,0x68,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a\n      ,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa0,0xb3,0xa4,0x8c,0x73,0x5b,0x43,0x2c,0x19,0x21,0x2b,0x30,0x30,0x2c,0x23,0x15,0x05,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x98,0xaa,0x99,0x84,0x77,0x8c,0x8e,0x7c,0x8c,0x98,0x7f,0x7f,0x93\n      ,0xa6,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xad,0x97,0x7f,0x7f,0x93,0x9c,0x8a,0x73,0x7f,0x97,0xad,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x14,0x29,0x3d,0x51,0x61,0x67,0x6e,0x7f,0x98,0x8c,0x73\n      ,0x67,0x6a,0x7f,0x98,0x8c,0x73,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb8,0xae,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xae,0xb8,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0\n      ,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f\n      ,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x02,0x14\n      ,0x27,0x3c,0x51,0x62,0x74,0x8a,0x9c,0xa1,0x8e,0x7b,0x6a,0x5e,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x71,0x84,0x98,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x27,0x14,0x02\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xa4,0x8e,0x7f,0x74,0x6a,0x5c,0x51,0x41,0x36,0x2b,0x1e\n      ,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x14,0x21,0x2f,0x3c,0x46,0x52,0x61,0x6b,0x7b,0x84,0x99,0xa9,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x65,0x67,0x67,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00\n      ,0x00,0x00,0x00,0x0e,0x22,0x36,0x49,0x5a,0x66,0x71,0x7b,0x84,0x8c,0x8e,0x97,0x99,0xa3,0xab,0xa8,0x9c,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x07,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x94,0xa3,0x9d,0x8e,0x84,0x7f,0x7f,0x74,0x76,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xb4,0xa4,0x8c,0x73,0x5b,0x43,0x42,0x5a\n      ,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x73,0x5c,0x44,0x31,0x2a,0x31,0x3c,0x42,0x43,0x40,0x36,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xa2\n      ,0x8c,0x7f,0x93,0x8c,0x79,0x8c,0x9b,0x8a,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x74,0x8c,0x9e,0x8e,0x7b,0x7b,0x8e,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0a,0x21\n      ,0x36,0x49,0x57,0x5b,0x5e,0x73,0x8a,0x9a,0x8c,0x73,0x60,0x71,0x84,0x98,0x8c,0x73,0x5e,0x5a,0x53,0x44,0x30,0x1b,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xbb,0xb5,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xae,0xb8,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb5,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x5c,0x71,0x84,0x94,0xa2,0x94,0x84,0x71,0x5f,0x5e,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e\n      ,0xa3,0x9d,0x8a,0x73,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa5\n      ,0x94,0x84,0x7b,0x6b,0x61,0x57,0x49,0x3c,0x2f,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0d,0x1b,0x26,0x31,0x40,0x4c,0x5a,0x66,0x71,0x7b,0x8c,0x9d,0xa9,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x7f\n      ,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x51,0x61,0x65,0x61,0x66,0x71,0x73,0x7b,0x7f,0x84,0x8e,0x9c,0xae,0xac,0x97,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xa4,0x8c,0x77\n      ,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa3,0xa7,0x93,0x7f,0x71,0x67,0x67,0x63,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71\n      ,0x84,0x99,0xb0,0xa4,0x8c,0x74,0x61,0x4c,0x48,0x5c,0x73,0x8c,0xa2,0xb3,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7b,0x66,0x52,0x46,0x43,0x46,0x51,0x5a,0x5b,0x57,0x49,0x38,0x25,0x10,0x00,0x00,0x00\n      ,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa4,0xa4,0x8c,0x7f,0x98,0x8c,0x79,0x8a,0x9b,0x8c,0x7f,0x98,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x07,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x71,0x84,0x98,0x98,0x84,0x77,0x8c,0xa4,0xa4\n      ,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x76,0x8c,0x98,0x84,0x76,0x73,0x76,0x8c,0x9d,0x8c,0x76,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb4,0xa4,0x94,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x8e,0x9d,0xb1,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c\n      ,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xb5,0xa8,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x7b,0x8e,0xa3,0xa4,0x8c,0x77,0x73,0x73,0x73,0x76,0x8c,0xa4,0xb0,0x98,0x7f,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x98,0xa9,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x09,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x04,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x8e,0x9c,0xa1,0x98,0x8c,0x7f,0x74,0x6a,0x5c,0x51,0x41,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2f,0x3c,0x46,0x52,0x61,0x6b,0x7b,0x84,0x8e,0x9c,0xa2,0x98,0x8c,0x7b,0x65,0x4e,0x36,0x1e,0x00\n      ,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x7b,0x8c,0x97,0x97,0x8e,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x10,0x25,0x38,0x4c,0x61,0x72,0x7b,0x71,0x66,0x61,0x61,0x65,0x67,0x71,0x7b,0x8e,0xa4,0xb0,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06\n      ,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa3,0xa8,0x93,0x7f,0x6c,0x63,0x5e,0x5b,0x5e,0x61,0x66,0x67,0x63,0x5c,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xad,0xa4,0x8c,0x74,0x62,0x5a,0x5c,0x66,0x74,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x97,0xac,0xa8,0x93,0x7f,0x6b,0x5c,0x5c,0x68,0x7b,0x8e,0xa4,0xad,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x07,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xad,0x98,0x84,0x72,0x66,0x5c,0x5b,0x5c,0x66\n      ,0x71,0x73,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8e,0x84,0x98,0x8c,0x74,0x7f,0x98,0x8e,0x84,0x99,0xa6,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98\n      ,0x7f,0x6c,0x7b,0x8e,0x9e,0x8c,0x79,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x94,0x9d,0x8e,0x8c,0x8c,0x8c,0x94,0xa2,0x94,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb0,0x99,0x84,0x74,0x73,0x73,0x73,0x73,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x51,0x3f,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00\n      ,0x00,0x00,0x06,0x0e,0x15,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x15,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x15,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9c,0xae,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x94,0xa8,0xb3,0x9d,0x8e,0x8c,0x89\n      ,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa2,0xa8,0x93,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x21,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c\n      ,0x73,0x5b,0x43,0x2a,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x71,0x7b,0x8a,0x94,0xa1,0x9d,0x93,0x8a,0x7b,0x71,0x62,0x57,0x4c,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x41,0x51,0x5c,0x66,0x72,0x7f,0x8c,0x98,0xa0\n      ,0x9d,0x93,0x84,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9c,0xac,0xad,0xa3,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6b,0x7f,0x8d,0x84,0x7b,0x73,0x71,0x67,0x67,0x6b,0x76,0x8c,0xa4,0xb0\n      ,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x98,0xaa,0x9d,0x8c,0x7f,0x74,0x6b,0x67,0x6b,0x73,0x7b,0x7f,0x72,0x61,0x4c,0x3a,0x27,0x14,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7b,0x71,0x6b\n      ,0x73,0x7b,0x84,0x94,0xa8,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x07,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9d,0xad,0x9d,0x8c,0x7b,0x73,0x73,0x7b,0x84,0x98,0xab,0xa3,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a\n      ,0x9c,0xac,0xa4,0x94,0x84,0x7b,0x73,0x73,0x73,0x7b,0x84,0x8a,0x7b,0x66,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xab,0x9d,0x99,0x98,0x84,0x71,0x7f,0x98,0x9d,0x99,0xa4,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x73,0x8a,0x9c,0x93,0x7f,0x8c,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x9e,0x9e,0xa5,0xa9,0x9f,0x9e,0x9e,0x9e,0xa5,0xab,0xa0,0x9e,0x98,0x7f,0x67,0x4f\n      ,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x5b,0x5b,0x5b,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x99,0x84,0x71,0x5a,0x42,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2d,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2d,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2d\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0xad,0xa6,0xa4,0xa4\n      ,0xa4,0xa4,0xa4,0xa8,0xb5,0xbe,0xae,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34\n      ,0x37,0x37,0x3a,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x37,0x36,0x30,0x25,0x16,0x05,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5c,0x6a,0x74,0x84,0x8e,0x9c,0xa2,0x9c,0x8e,0x84,0x74,0x6b,0x61,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x0a,0x21,0x36\n      ,0x49,0x57,0x62,0x71,0x7b,0x84,0x93,0x9d,0xa1,0x94,0x8a,0x7f,0x72,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x97,0xac,0xbe,0xc2,0xb0,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9c,0x98\n      ,0x8e,0x8c,0x84,0x7f,0x7f,0x7f,0x8a,0x94,0xa7,0xa4,0x93,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x66,0x7b,0x8c,0x9c,0xa9,0x9d,0x93,0x8a,0x7f,0x7f,0x7f,0x8a,0x8e,0x92,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x06,0x1e,0x37\n      ,0x4f,0x67,0x7f,0x93,0xa8,0xae,0x9c,0x8e,0x84,0x7f,0x8a,0x8e,0x98,0x9f,0xad,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6b,0x7f,0x93,0xa4,0xab,0x9c,0x8e,0x8c,0x8c,0x8e,0x98,0xa4,0xa4,0x94,0x84,0x71,0x5c,0x46,0x31,0x1b\n      ,0x06,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8e,0xa1,0xad,0xa4,0x98,0x8e,0x8c,0x8c,0x8c,0x8e,0x98,0x98,0x84,0x71,0x5c,0x46,0x30,0x19,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xb0,0xb1,0xad,0x98,0x7f,0x6d,0x7f,0x93,0xa6,0xb0,0xb4\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x6b,0x7f,0x93,0x9c,0x8e,0x94,0xa8,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8e,0x9d,0x9d,0x8e,0x8c\n      ,0x8c,0x8e,0x9d,0x9d,0x8e,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xb3\n      ,0xa2,0x8c,0x73,0x5b,0x45,0x51,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x46,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x46,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f\n      ,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x46,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x12,0x2a\n      ,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x9d,0xae,0xb8,0xa4,0x99,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x52,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x4f,0x4f,0x4e,0x46,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x49,0x57,0x62,0x71,0x7b,0x8a,0x93,0x9d,0xa1,0x94,0x8a,0x7f,0x72\n      ,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x74,0x84,0x8e,0x98,0xa1,0x9c,0x8e,0x84,0x74,0x6b,0x61,0x52,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xc5,0xcb,0xb3,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8e,0x98,0xa1,0xa3,0xa2,0x99,0x98,0x98,0x98,0x9d,0xa5,0xa3,0x94,0x84,0x72,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x6a,0x7b,0x8c,0x9c,0xa6,0xa6,0x9d,0x98,0x98,0x98,0x9d,0xa0,0x99,0x8a,0x73\n      ,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xac,0xab,0xa3,0x99,0x98,0x9c,0x9a,0x8e,0x8e,0x9d,0xab,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x94,0xa3,0xa8,0xa4,0xa4,0xa4,0xa4\n      ,0xa7,0xa3,0x94,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5c,0x71,0x84,0x8e,0x9c,0xa8,0xa9,0xa4,0xa4,0xa4,0xa4,0xa3,0xa3,0x9c,0x8a,0x7b,0x65,0x4e,0x36,0x1e,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67,0x7f,0x98,0xa4\n      ,0xa4,0xa4,0x97,0x7f,0x69,0x74,0x8c,0xa1,0xa4,0xa4,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x61,0x74,0x8a,0x9d,0xa4,0xa8,0xb3,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x11,0x28\n      ,0x40,0x57,0x6a,0x73,0x73,0x7f,0x98,0x93,0x7f,0x73,0x73,0x7f,0x98,0x97,0x7f,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x74,0x62,0x55,0x5d,0x71,0x84,0x99,0xae,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5e,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x57\n      ,0x49,0x36,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5e,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x46,0x43,0x43,0x43,0x43,0x43\n      ,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x8e,0xa4,0xb0,0x99,0x84,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xac\n      ,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x6a,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x67,0x67,0x67,0x65,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x01,0x0c,0x1b,0x27,0x36,0x41,0x51\n      ,0x5c,0x6a,0x74,0x7f,0x8c,0x98,0xa1,0x9d,0x92,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x94,0xa1,0xa1,0x94,0x8a,0x7b,0x71,0x62,0x57,0x4c,0x40,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa4,0xb5\n      ,0xbb,0xac,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x7b,0x84,0x8e,0x97,0x99,0xa2,0xa4,0xa4,0xa2,0x99,0x97,0x8e,0x84,0x72,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x8a,0x93\n      ,0x98,0x9d,0xa4,0xa4,0x9d,0x98,0x93,0x8a,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x97,0x9d,0xa4,0xa2,0x99,0x93,0x8a,0x7b,0x7f,0x95,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x04,0x17,0x2b,0x40\n      ,0x51,0x62,0x72,0x84,0x8e,0x97,0x9d,0xa4,0xa4,0x9d,0x97,0x8e,0x84,0x72,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x3c,0x51,0x62,0x71,0x7b,0x8a,0x93,0x99,0xa2,0xa4,0xa4,0x9d,0x97,0x8e,0x8a,0x7b,0x71,0x61,0x4b,0x34,0x1c,0x00,0x00\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8a,0x7b,0x66,0x73,0x89,0x8c,0x8c,0x8c,0x8c,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x58,0x6b,0x7f,0x97,0xad,0xbc,0xbb,0xa4\n      ,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x67,0x7f,0x98,0x8c,0x74,0x62,0x67,0x7f,0x98,0x8e,0x7b,0x65,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa8,0xa8,0x94,0x84,0x72,0x68,0x71,0x7b,0x8e,0xa3,0xae,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x76,0x8c\n      ,0xa4,0xb6,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x76,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2d,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x6a,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xae,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7f,0x84,0x99,0xb0,0xa4,0x8e,0x7f,0x7f,0x7f,0x7f,0x7b,0x6a,0x57,0x40,0x28,0x11\n      ,0x00,0x00,0x00,0x00,0x00,0x06,0x14,0x21,0x2f,0x3c,0x49,0x57,0x61,0x6b,0x7b,0x84,0x93,0x9d,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa0,0x9c,0x8e,0x84,0x74,0x6a,0x5c,0x51,0x41,0x36,0x2b,0x1e,0x10,0x04,0x00,0x00,0x00\n      ,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x94,0xa2,0xa2,0x98,0x8c,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5c,0x66,0x71,0x7b,0x7f,0x84,0x8c,0x8c,0x8c,0x8c,0x84,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00\n      ,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x6a,0x74,0x7f,0x7f,0x8a,0x8c,0x8c,0x8a,0x7f,0x7f,0x74,0x6b,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x7f,0x8a,0x8c,0x8c,0x84,0x7f,0x74,0x6d,0x7b,0x7f,0x7f,0x7f,0x7b,0x65,0x4e\n      ,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x62,0x71,0x7b,0x7f,0x8a,0x8c,0x8c,0x8a,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5c,0x6a,0x74,0x7f,0x84,0x8c,0x8c,0x8c,0x8a,0x7f\n      ,0x7b,0x73,0x6a,0x5c,0x51,0x40,0x2b,0x15,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x6a,0x5d,0x6a,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98\n      ,0x7f,0x67,0x54,0x65,0x7b,0x8e,0xa3,0xb8,0xbc,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x14,0x27,0x36,0x44,0x57,0x6b,0x7f,0x98,0x8c,0x73,0x5c,0x67,0x7f,0x98,0x8c,0x73,0x5c,0x48,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa2,0xb1,0xa4,0x94,0x84,0x7f,0x84,0x8e,0x9c,0xac,0xa4,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x06\n      ,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x94,0xa8,0xb9,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x94,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e\n      ,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x15,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x52\n      ,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x98,0x99,0xa4,0xb8,0xae,0x9d\n      ,0x98,0x98,0x98,0x97,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0d,0x1b,0x27,0x36,0x41,0x4c,0x5a,0x66,0x72,0x7f,0x8c,0x94,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x94,0x8a,0x7b,0x71,0x62,0x57,0x49\n      ,0x3c,0x2f,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x62,0x72,0x84,0x8c,0x8c,0x84,0x7b,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x46,0x51,0x5c,0x65,0x67,0x71,0x73,0x73,0x73,0x73,0x71,0x67,0x65,0x5c\n      ,0x51,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x38,0x49,0x57,0x61,0x67,0x6b,0x73,0x73,0x73,0x73,0x6b,0x67,0x61,0x57,0x4c,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x65,0x6b,0x73,0x73,0x73,0x71\n      ,0x67,0x61,0x5c,0x65,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x5c,0x65,0x6b,0x73,0x73,0x73,0x73,0x6b,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c\n      ,0x49,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x73,0x6b,0x65,0x5c,0x57,0x49,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x4d,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x50,0x5c,0x71,0x84,0x99,0xb0,0xb9,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x05,0x15,0x2a,0x43,0x5b,0x73,0x8a,0x9b,0x8c,0x73,0x60,0x71,0x84,0x98,0x8c,0x73,0x5b,0x43,0x2d,0x25,0x1b\n      ,0x0c,0x01,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x94,0xa4,0xaf,0xa4,0x99,0x98,0x99,0xa3,0xab,0xa9,0x98\n      ,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa8,0xb3,0xbb,0xb3,0xa8,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb3,0xa8,0xa4,0xa4,0xa4,0xa4\n      ,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x37,0x37,0x37,0x37\n      ,0x43,0x5b,0x73,0x8c,0xa4,0xad,0x98,0x7f,0x67,0x4f,0x39,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8c,0xa4,0xad,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x8c,0xa4,0xaa,0xaa,0xaa,0xaa,0xae,0xb0,0xb0,0xab,0xaa,0xaa,0xaa,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x14,0x21,0x2b,0x38,0x46,0x52,0x61,0x6b,0x7b,0x84,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x12,0x2a,0x43\n      ,0x5b,0x73,0x89,0x84,0x74,0x6a,0x5c,0x51,0x41,0x36,0x27,0x1b,0x0d,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x62,0x71,0x73,0x73,0x71,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x31,0x3c,0x46,0x4e\n      ,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x4e,0x46,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x41,0x4b,0x4f,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x4f,0x4b,0x41,0x36,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x05\n      ,0x16,0x27,0x38,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5a,0x51,0x4b,0x46,0x4e,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x46,0x4e,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x27,0x36,0x41,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x4e,0x46,0x40,0x36,0x27,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x40,0x39,0x40,0x43,0x43,0x43,0x43\n      ,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0x98,0x7f,0x67,0x4f,0x51,0x67,0x7f,0x93,0xa2,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x9b,0x8a,0x73,0x61\n      ,0x73,0x8c,0x9d,0x8c,0x73,0x5b,0x43,0x2a,0x13,0x06,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0x98,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x72,0x84\n      ,0x93,0x9d,0xa4,0xa9,0xaa,0xaa,0xa8,0xa2,0x94,0x8a,0x7b,0x66,0x52,0x40,0x2b,0x15,0x02,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f\n      ,0x67,0x7f,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01\n      ,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x0b,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8d,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x17,0x25,0x31,0x40,0x4c,0x5a,0x66,0x72,0x7b\n      ,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7b,0x71,0x62,0x57,0x49,0x3c,0x2f,0x21,0x14,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x5a,0x51,0x46,0x38,0x27,0x16,0x04,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x06,0x10,0x1b,0x26,0x30,0x36,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x36,0x30,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x34,0x38,0x40,0x43,0x43,0x43,0x43,0x40,0x38,0x34,0x2b,0x21\n      ,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x30,0x37,0x40,0x43,0x43,0x43,0x42,0x3c,0x34,0x30,0x36,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x26,0x30,0x37,0x40,0x43,0x43,0x43,0x43\n      ,0x40,0x37,0x30,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x14,0x21,0x2b,0x34,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x37,0x30,0x28,0x21,0x14,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x28,0x23,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x4b,0x61,0x74,0x89,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00\n      ,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x7f,0x6b,0x5e,0x73,0x89,0x8c,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x3d,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x62,0x72,0x7f,0x8a,0x8e,0x97,0x98,0x98,0x93,0x8c,0x84,0x74,0x6a,0x5a,0x46,0x31,0x1e,0x0a,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73\n      ,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x06,0x06,0x06,0x06,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f\n      ,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x04,0x10,0x1e,0x2b,0x38,0x46,0x52,0x61,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x65,0x5c,0x51,0x41,0x36,0x27,0x1b,0x0d,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x42,0x43\n      ,0x43,0x42,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x1e,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x15,0x1c,0x21\n      ,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x1c,0x15,0x0b,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x25,0x1d,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x10,0x19,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x15,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x19,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0c,0x11,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x41,0x57,0x6a,0x73,0x73,0x73,0x73\n      ,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x71,0x61,0x57,0x6a,0x73,0x73,0x71,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x3a,0x4b,0x61\n      ,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x51,0x61,0x6b,0x73,0x7b,0x7f,0x7f,0x7f,0x7f,0x74,0x71,0x62,0x57,0x49,0x38,0x25,0x10,0x01,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73\n      ,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73\n      ,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x17,0x25,0x31,0x40,0x4b,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4e,0x46,0x3c,0x2f,0x21,0x14,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x07,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x0e,0x06,0x04,0x06,0x06,0x06,0x06,0x06,0x04,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11\n      ,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b\n      ,0x5a,0x51,0x40,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5a,0x51,0x49,0x57,0x5b,0x5b,0x5a,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57\n      ,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x31,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x4c,0x57,0x5c,0x65,0x67,0x67,0x67,0x67,0x61,0x5a,0x51,0x41,0x36,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x02\n      ,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15\n      ,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x38\n      ,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f\n      ,0x4f,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x1e,0x2b,0x34,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x36,0x30,0x26,0x1b,0x0d,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x42,0x3c,0x36,0x40,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0c,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x23,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x36,0x40,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4b,0x43,0x3c\n      ,0x2f,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43\n      ,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c\n      ,0x1e,0x2b,0x34,0x37,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34\n      ,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x0a\n      ,0x15,0x1c,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x25,0x21\n      ,0x28,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x11,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x21\n      ,0x28,0x30,0x36,0x37,0x37,0x37,0x37,0x34,0x2c,0x25,0x1b,0x0d,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b\n      ,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x0e,0x0a,0x11,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x01,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0a,0x11,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11\n      ,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x01,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x06,0x0e,0x12,0x12,0x12\n      ,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x06,0x06,0x06,0x0a,0x11,0x12,0x12,0x12,0x0e,0x08,0x0e,0x12,0x12,0x12,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x11,0x0a\n      ,0x02,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x06,0x06,0x06,0x05,0x03,0x06,0x0e,0x12,0x12,0x12,0x12,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x06,0x06,0x06,0x05,0x02,0x03,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x06,0x06,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x06,0x06,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x03,0x0a,0x11,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11\n      ,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x06,0x06,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x04,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x06,0x06,0x04,0x00,0x00,0x00,0x02,0x05,0x06,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x11,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a\n      ,0x28,0x21,0x14,0x05,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x21,0x28,0x2a,0x2a,0x2a,0x25,0x1e,0x25,0x2a,0x2a,0x2a,0x28,0x21,0x16,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x01\n      ,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1c,0x18,0x1d,0x25,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x16,0x0a,0x01,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x16,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x16\n      ,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x21,0x28,0x2a,0x2a,0x2a,0x25,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00\n      ,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x01,0x0a,0x15,0x1c,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0b,0x15,0x1c,0x1e,0x1e,0x1e,0x19,0x10,0x06,0x0a,0x15,0x1c,0x1e,0x1c,0x15,0x0b,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a\n      ,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x23,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00\n      ,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x36,0x36,0x40,0x43,0x43,0x42,0x3c,0x33,0x3c,0x42,0x43,0x43,0x40\n      ,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x34,0x2e,0x34,0x3c,0x42,0x43\n      ,0x43,0x43,0x40,0x36,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x37,0x34,0x2b\n      ,0x2b,0x36,0x40,0x43,0x43,0x43,0x43,0x40,0x36,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04\n      ,0x16,0x25,0x30,0x36,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x36,0x40,0x43,0x43,0x42,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43\n      ,0x43,0x43,0x43,0x40,0x36,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43\n      ,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x25,0x30,0x36,0x37,0x37,0x36,0x30,0x26,0x1b\n      ,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x34,0x36,0x30,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x2b,0x34,0x37,0x37,0x36,0x30,0x26,0x1b,0x1e,0x2b,0x34,0x37,0x34,0x2b,0x21,0x16,0x0a\n      ,0x01,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43\n      ,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x32,0x40,0x51,0x5a,0x5b,0x5b\n      ,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4e,0x4b,0x57\n      ,0x5b,0x5b,0x5a,0x51,0x49,0x51,0x5a,0x5b,0x5b,0x57,0x4c,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x00,0x00,0x00,0x10,0x25,0x38,0x46\n      ,0x4e,0x4f,0x4f,0x4f,0x4b,0x44,0x4b,0x51,0x5a,0x5b,0x5b,0x5b,0x57,0x4c,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x06\n      ,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x41,0x4c,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x4c,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x40,0x4c,0x57,0x5b,0x5b,0x5a,0x51,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00\n      ,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x05,0x16\n      ,0x27,0x38,0x46,0x4e,0x4f,0x4f,0x4e,0x46,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x4b,0x4e,0x46,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16,0x27,0x36,0x41,0x4b,0x4f,0x4f,0x4e,0x46,0x3c\n      ,0x31,0x2f,0x40,0x4b,0x4f,0x4b,0x41,0x36,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5a,0x5b,0x5b\n      ,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73\n      ,0x73,0x71,0x62,0x51,0x40,0x4c,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x0e\n      ,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x65,0x5e,0x6a,0x73,0x73,0x71,0x66,0x5e,0x66,0x71,0x73,0x73,0x6b,0x61,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x61\n      ,0x4b,0x34,0x1c,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x61,0x57,0x61,0x67,0x71,0x73,0x73,0x73,0x6b,0x61,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73\n      ,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x61,0x51,0x57,0x61,0x6b,0x73,0x73,0x73,0x73,0x6b,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x65\n      ,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5a,0x65,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x06,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x61,0x6b,0x73,0x73,0x71\n      ,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x73,0x73,0x73,0x73,0x6a,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x61,0x71,0x73\n      ,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a\n      ,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5a,0x65,0x67,0x67,0x65,0x5c,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x61,0x65,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x16\n      ,0x27,0x38,0x49,0x57,0x61,0x67,0x67,0x65,0x5c,0x51,0x46,0x3f,0x51,0x61,0x67,0x61,0x57,0x4c,0x40,0x2f,0x1b,0x06,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11\n      ,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x87,0x8c,0x8c,0x8c,0x84,0x72,0x61,0x4c,0x57,0x6b,0x7f,0x8c,0x8c,0x8c,0x89,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x82,0x8c,0x8c,0x8c,0x89,0x73,0x5c,0x46,0x30,0x3c,0x51,0x67,0x7f,0x8c,0x8c\n      ,0x8c,0x87,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7b,0x6f,0x7b,0x8a,0x8c,0x84,0x7b,0x6d,0x7b,0x84,0x8c,0x8a,0x7f,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x89,0x73,0x5b\n      ,0x43,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x72,0x6a,0x74,0x7f,0x84,0x8c,0x8c,0x8a,0x7f,0x72,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c\n      ,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x71,0x5f,0x6a,0x74,0x7f,0x8a,0x8c,0x8c,0x8a,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x13,0x25,0x3c,0x51,0x66,0x7b,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x1d,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00\n      ,0x10,0x26,0x3c,0x51,0x62,0x72,0x7f,0x8a,0x8c,0x84,0x7b,0x6a,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x89,0x8c,0x8c,0x8a,0x7b,0x65,0x4e,0x37,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73\n      ,0x89,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x7f,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x51,0x62,0x72,0x7b,0x6a,0x5a,0x49,0x36,0x21\n      ,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x02,0x14,0x27,0x38,0x49,0x5a,0x6a,0x74,0x7f,0x7f,0x7b,0x71,0x66,0x5a,0x4e,0x5c,0x71,0x7f,0x74,0x6b,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6b,0x7f,0x93,0x9f,0xa2,0x93,0x7f,0x6b,0x5a,0x62,0x74,0x8a,0x9d,0xa1,0x94,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x93,0xa1,0xa1,0x8e\n      ,0x7b,0x65,0x4e,0x37,0x43,0x5a,0x71,0x84,0x99,0xa2,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x97,0x8a,0x7c,0x8c,0x9c,0xa2,0x97,0x84,0x7b,0x8c,0x98,0xa2,0x9d,0x92,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e\n      ,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x92,0x7f,0x7b,0x8a,0x93,0x99,0xa2,0xa4,0x9d,0x93,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a\n      ,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x8c,0x73,0x6a,0x7b,0x8a,0x93,0x9d,0xa4,0xa4,0x99,0x8a,0x73,0x5b\n      ,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x15,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2d,0x42,0x5a,0x71,0x84,0x95,0x8c,0x73,0x5b,0x43,0x2f\n      ,0x34,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x93,0x9d,0xa2,0x98,0x8c,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x67,0x7f,0x93,0xa2,0xa4,0x97,0x7f,0x6b,0x57,0x41,0x2b,0x15,0x02\n      ,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0x98,0x7f,0x67,0x67,0x7f,0x98,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xa4,0xa4,0x9b,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12\n      ,0x2a,0x43,0x5b,0x73,0x8c,0xa1,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x6a,0x7b,0x8c,0x97,0x97,0x8e,0x84,0x72,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x02,0x15,0x2b,0x40\n      ,0x51,0x62,0x72,0x84,0x8c,0x7b,0x6a,0x57,0x41,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x8a,0x93,0x97,0x8e,0x84,0x7b,0x6b,0x61,0x6a,0x7b,0x8c,0x8a,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f\n      ,0x98,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa0,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x98,0xaa,0x9d,0x8c,0x7b,0x66,0x71,0x84,0x94,0xa7,0x9d,0x8a,0x74,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00\n      ,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa2,0xab,0x97,0x7f,0x6b,0x57,0x40,0x4b,0x61,0x74,0x8c,0xa2,0xa4,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa7,0x94,0x8e,0x9a,0xa3,0xac,0xa3,0x8e,0x84,0x98,0xa3,0xae,0xb0,0x9d\n      ,0x8a,0x73,0x5c,0x46,0x30,0x19,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xaa,0x99,0x84,0x8c,0x9b,0xa0,0xa4,0xa4\n      ,0xae,0xb1,0xa4,0x93,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x98,0x98,0x98,0x9d,0xae,0xba,0xb8,0xad,0x9c,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xa4,0x8e,0x7b,0x7b\n      ,0x8c,0x9c,0xa5,0xa4,0xa4,0xa4,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2d,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43\n      ,0x45,0x5b,0x73,0x8c,0x9d,0x8c,0x73,0x5b,0x45,0x43,0x4b,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa3,0xb3,0xb8,0xac,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x99\n      ,0xad,0xad,0x9d,0x8a,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb8,0xb0,0x98,0x7f,0x67,0x67,0x7f,0x97,0xad,0xb7,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0\n      ,0x98,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x97,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8c,0x9c,0xac,0xad,0xa3,0x93,0x7f,0x67\n      ,0x51,0x3c,0x25,0x0e,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x94,0x9c,0x8a,0x74,0x62,0x51,0x40,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x02,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x9c,0xa0,0xa2,0x9e,0x98,0x8c,0x7f,0x74,0x7b,0x8c,0x9b,0x93,0x7f,0x6b,0x57,0x40\n      ,0x28,0x11,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98\n      ,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x9d,0xaa,0x98,0x84,0x71,0x74,0x8c,0xa2,0xa4,0x93\n      ,0x7f,0x6b,0x57,0x41,0x2f,0x1b,0x06,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x98,0xaa,0x9d,0x8a,0x73,0x5c,0x46,0x51,0x67,0x7f,0x93,0xa6,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb3,0xa6,0x9a,0x8e\n      ,0x8e,0x9c,0xa9,0x9d,0x96,0x8e,0x8e,0x9c,0xae,0xa4,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xae,0xa4,0x99,0x98,0x93,0x8c,0x8c,0x8e,0x9c,0xae,0xb3,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x8e,0xa4,0xb8,0xad,0x9c,0x8c,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x12\n      ,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xad,0x98,0x84,0x8c,0x9b,0x9d,0x97,0x8e,0x8c,0x8e,0x92,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x46,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02\n      ,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x57,0x5b,0x73,0x8c,0x9e,0x8c,0x73,0x5b,0x57,0x5b,0x61,0x65,0x5a,0x46,0x31,0x1b,0x06,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xbd,0xc5,0xb8,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00\n      ,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa1,0x9d,0x99,0xa1,0x93,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8a,0x9d,0xb3,0xb0,0x98,0x7f,0x67,0x65,0x7b,0x8e,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x38,0x21,0x0a,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x7b,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f\n      ,0x67,0x7f,0x97,0xac,0xbe,0xc2,0xb0,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x90,0xa0,0xa7,0x94,0x84,0x72,0x62,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0x9c,0x8e,0x8c,0x8e,0x9c,0x9c\n      ,0x93,0x8c,0x8e,0x9c,0x9c,0x8a,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f\n      ,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6b,0x7f\n      ,0x93,0xa4,0xa3,0x8e,0x7b,0x7f,0x93,0xa5,0x98,0x84,0x72,0x61,0x4c,0x36,0x21,0x0d,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0xa4,0xa4,0x8e,0x7b,0x65,0x4e,0x5a,0x71,0x84,0x99,0xa9,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x12\n      ,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb4,0xa3,0x8e,0x7b,0x7b,0x8e,0xa4,0xa3,0x8e,0x7b,0x7b,0x8e,0xa4,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67\n      ,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xae,0x9c,0x8c,0x7f,0x74,0x73,0x7b,0x8e,0xa3,0xb4,0xa4,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x67,0x6e,0x7b,0x8e,0xa4,0xb1,0x9d,0x8c\n      ,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb4,0xa4,0x99,0x9b,0x94,0x8a,0x7f,0x7b,0x73,0x7b,0x7f,0x72,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5e,0x73,0x8c,0xa4,0x98,0x7f\n      ,0x67,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x71,0x73,0x73,0x6b,0x67,0x73,0x8c,0x9e,0x8c,0x73,0x67,0x6b,0x73,0x74,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8a,0x9c,0xac,0xb0,0xb5\n      ,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0x9d,0x8c,0x84,0x99,0x99,0x84,0x71,0x5a,0x43,0x2c,0x16,0x03,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x98,0xb0,0xad,0x97,0x7f,0x67,0x5c,0x73,0x8c\n      ,0xa4,0xb4,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x0a,0x21,0x36,0x49,0x5a,0x65,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x61\n      ,0x51,0x3c,0x25,0x0e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xc5,0xcb,0xb3,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x94,0xa4,0xa4,0x94,0x84,0x72,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x06,0x1e,0x37,0x4f\n      ,0x67,0x7f,0x91,0x9b,0x8c,0x7b,0x73,0x7b,0x8a,0x93,0x9c,0xa0,0x9f,0x9c,0x8c,0x7b,0x6a,0x57,0x41,0x2b,0x16,0x03,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e\n      ,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x98,0xa8,0x9c,0x8e,0x8e,0x9d,0x9d,0x8c,0x7b,0x66,0x52,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x73,0x8a,0x9d,0xaa,0x97,0x7f,0x6b,0x57,0x61,0x74,0x8c,0xa2,0xa3,0x8e\n      ,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x99,0x84,0x71,0x73,0x8c,0xa4,0x99,0x84,0x71,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b\n      ,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8e,0x7b,0x6b,0x61,0x5e,0x71,0x84,0x99,0xb0,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f\n      ,0x4f,0x55,0x62,0x74,0x8a,0x9c,0xab,0xa4,0x93,0x7f,0x6b,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xbb,0xb4,0xa4,0x94,0x84,0x74,0x6b,0x65,0x5e,0x65,0x67,0x61,0x51,0x40,0x2b,0x15,0x02,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62\n      ,0x71,0x73,0x73,0x73,0x73,0x76,0x8c,0xa4,0x98,0x7f,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x8c,0x8a,0x7f,0x7f,0x77,0x8c,0x9e,0x8c,0x77,0x7f,0x7f,0x8a,0x8c,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x02\n      ,0x15,0x2b,0x41,0x57,0x6a,0x7b,0x8c,0x97,0x99,0xa4,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x73,0x8a,0x9d,0x97,0x7f,0x7f,0x93,0x9f,0x8c,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x0a,0x21,0x38,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xa4,0x8e,0x7b,0x65,0x5b,0x73,0x8c,0xa4,0xb0,0x99,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4e,0x54,0x55,0x55,0x55,0x55,0x55\n      ,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x51,0x45,0x33,0x1e,0x08,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa4,0xb5,0xbb,0xac,0x97,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x62,0x72,0x84,0x93,0x9d,0xa2,0x93,0x7f,0x6b\n      ,0x57,0x40,0x28,0x11,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x84,0x8d,0x7f,0x6b,0x5e,0x6a,0x74,0x7f,0x8a,0x93,0x8e,0x8a,0x7b,0x6a,0x5a,0x49,0x36,0x21,0x0b,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f\n      ,0x4f,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x9d,0xab,0xa4,0xa4,0xa5,0x93,0x7f,0x6b,0x5a,0x46,0x31,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xaa\n      ,0x9d,0x8a,0x73,0x5c,0x67,0x7f,0x93,0xa6,0x99,0x84,0x71,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e\n      ,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c,0x4c,0x51,0x67,0x7f,0x98,0xb0,0xa8,0x93,0x7f,0x67,0x4f,0x37,0x1e\n      ,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x38,0x42,0x51,0x62,0x72,0x84,0x94,0xa7,0xa4,0x94,0x84,0x72,0x61,0x4c,0x38,0x27,0x16,0x05,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb9,0xad,0x98,0x84,0x72,0x62,0x57,0x4e,0x49,0x4e,0x4f,0x4b,0x40,0x2f\n      ,0x1e,0x0a,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x8c,0x8c,0x8c,0x8c,0x8c,0x94,0xa8,0x9d,0x8e,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x82,0x92,0x98,0x98,0x93,0x8c,0x94,0xa2,0x94,0x8c,0x93,0x98,0x98\n      ,0x97,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0b,0x21,0x36,0x49,0x5a,0x6a,0x7b,0x7f,0x84,0x99,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0x9f,0x8e,0x7b,0x74,0x8c,0x9f,0x93,0x7f,0x6b,0x57,0x40,0x28\n      ,0x11,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5c,0x5b,0x73,0x8c,0xa2,0xae,0x98,0x7f,0x67,0x51,0x3c,0x25,0x0e,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa4,0xad,0x97,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00,0x00,0x0e\n      ,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x94,0xa2,0xa2,0x98,0x8c,0x7b,0x65,0x4e,0x37,0x21,0x0a,0x00,0x00,0x00,0x0a,0x1e,0x2f\n      ,0x40,0x51,0x62,0x72,0x7f,0x8c,0x9c,0x94,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x66,0x71,0x7b,0x72,0x61,0x4f,0x57,0x61,0x6b,0x74,0x7f,0x7b,0x73,0x6a,0x5a,0x49,0x38,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36\n      ,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6b,0x7f,0x98,0xb0,0xbc,0xb2,0x9d,0x8a,0x74,0x61,0x4c,0x38,0x25,0x10,0x01,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8e,0xa3,0xa4,0x8e,0x7b,0x65,0x71,0x84,0x99,0xa6,0x93,0x7f,0x67,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x73,0x8c,0xa4,0xb0\n      ,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x28,0x3c,0x51,0x62,0x72,0x84,0x94,0xa4,0xa7,0x94,0x84,0x72,0x62,0x51,0x40,0x2b,0x17,0x05,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8e,0x7b\n      ,0x66,0x52,0x41,0x37,0x33,0x36,0x37,0x34,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa0,0xa4,0xa4,0xa4,0xa4,0xa8,0xb5,0xae,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x74,0x7f,0x8a\n      ,0x93,0x9c,0xa0,0xa8,0xb1,0xa8,0xa2,0x9c,0x93,0x8a,0x7f,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x03,0x14,0x27,0x38,0x49,0x5a,0x65,0x6e,0x7f,0x98,0xa2,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x03,0x16,0x2c,0x43,0x5a,0x71,0x84,0x98,0x9d,0x8a\n      ,0x73,0x71,0x84,0x99,0x9d,0x8a,0x73,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x93,0xa8,0xa4,0x8c,0x73,0x5b,0x5a,0x71,0x84,0x99,0xaa,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x07,0x00,0x00,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8c,0xa4,0xa4\n      ,0x8e,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x62,0x72,0x84,0x8c,0x8c,0x84,0x7b,0x6a,0x5a\n      ,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x61,0x6b,0x7b,0x8c,0x84,0x72,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x51,0x5c,0x65,0x61,0x51,0x40,0x41,0x4c,0x57,0x61,0x67,0x65,0x5c,0x57,0x49,0x38,0x27,0x16,0x05\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e\n      ,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x99,0xad,0xb0,0xae,0x99,0x84,0x71,0x5c\n      ,0x49,0x38,0x25,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x71,0x84,0x99,0xa9,0x97,0x7f,0x69,0x74,0x8c,0xa2,0xa2,0x8c,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67\n      ,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x38,0x49,0x5c,0x71,0x84,0x94,0xa4,0xab,0x9c,0x8a,0x74,0x62,0x51,0x40,0x2f,0x23,0x19,0x10,0x04,0x00,0x00,0x00,0x12\n      ,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5c,0x46,0x31,0x21,0x1b,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x98,0x98,0x9d,0xae,0xa4,0x99,0x98,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12\n      ,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x61,0x6b,0x74,0x7f,0x8a,0x94,0xa8,0xba,0xb3,0x9d,0x8c,0x7f,0x74,0x6b,0x65,0x5a,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x4c,0x57,0x62,0x74,0x8a,0x9d,0x98,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00\n      ,0x0a,0x21,0x36,0x4c,0x61,0x74,0x8c,0xa1,0x97,0x7f,0x6b,0x67,0x7f,0x93,0xa0,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0xa4,0x9d,0x8a,0x73,0x5b,0x51,0x67,0x7f,0x98,0xa6,0x93,0x7f,0x67,0x4f,0x37,0x1e,0x06,0x00\n      ,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa2,0xa4,0x8c,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x0a,0x1e,0x2f\n      ,0x40,0x51,0x62,0x71,0x73,0x73,0x71,0x66,0x5a,0x49,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x4c,0x5a,0x6a,0x7b,0x72,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x04,0x16,0x25,0x31,0x3c,0x46,0x4e,0x4b,0x40,0x2f,0x2b,0x36,0x41\n      ,0x4b,0x4f,0x4e,0x46,0x40,0x36,0x27,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6b\n      ,0x7f,0x93,0xa2,0x9d,0x99,0xa4,0xa3,0x8e,0x7b,0x6a,0x5a,0x46,0x31,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x26,0x3c,0x51,0x67,0x7f,0x93,0xa6,0x99,0x84,0x71,0x7f,0x93,0xa6,0x99,0x84,0x71,0x5a,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x00,0x12\n      ,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x74,0x61,0x4b,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67\n      ,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x49,0x5a,0x6a,0x7b,0x8e,0xa3,0xab,0x9c,0x8c,0x7b,0x6a,0x57,0x42\n      ,0x38,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x05,0x06,0x06,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x8e,0xa4,0x99,0x84\n      ,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x02,0x14,0x27,0x36,0x41,0x4c,0x57,0x61,0x6f,0x7c,0x8e,0xa3,0xa4,0xa6,0x98,0x7f,0x6f,0x62,0x57,0x4e,0x46,0x38,0x27,0x14,0x02,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x61,0x6b,0x74,0x84,0x94,0xa0\n      ,0x8e,0x7b,0x66,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x93,0xa0,0x8e,0x7b,0x65,0x61,0x74,0x8c,0xa1,0x98,0x84,0x71,0x5a,0x43,0x2c,0x16,0x03,0x00,0x00,0x02,0x15,0x2c,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x6b,0x57,0x4f,0x67,0x7f\n      ,0x98,0xa4,0x8c,0x74,0x61,0x4b,0x34,0x1c,0x05,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x99,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa0,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa1,0x8c,0x73\n      ,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x5a,0x51,0x46,0x38,0x27,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x38,0x49,0x5a,0x65,0x61,0x51,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x04,0x10\n      ,0x1b,0x26,0x30,0x36,0x34,0x2b,0x1e,0x16,0x21,0x2b,0x34,0x37,0x36,0x30,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x04,0x17,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x9d,0xa3,0x8e,0x84,0x98,0xa8,0x9c,0x8c,0x7b,0x66,0x52,0x40,0x2b,0x17,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x34,0x4b,0x61,0x74,0x8c,0xa2,0xa2,0x8c,0x74,0x7f,0x98,0xa6,0x93,0x7f,0x67\n      ,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa8,0x93,0x7f,0x67\n      ,0x56,0x5c,0x6b,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x0c,0x1e,0x31,0x46,0x5a,0x6a,0x7b\n      ,0x8c,0x9c,0xa9,0x9c,0x8c,0x7b,0x6a,0x5b,0x51,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61\n      ,0x67,0x67,0x67,0x67,0x67,0x73,0x8c,0xa4,0x98,0x7f,0x6a,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x05,0x14,0x21,0x2d,0x3c,0x4c,0x61,0x74,0x8a,0x9c,0x9c,0x8e,0x94,0x9c,0x8c,0x7b,0x66,0x51,0x3e,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x05\n      ,0x1c,0x34,0x4b,0x61,0x72,0x7f,0x8a,0x94,0xa0,0x94,0x84,0x71,0x5c,0x46,0x31,0x1b,0x06,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0x9d,0x8a,0x73,0x5c,0x5a,0x71,0x84,0x99,0xa2,0x8c,0x74,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b\n      ,0x73,0x8a,0x9d,0x98,0x7f,0x67,0x4f,0x4f,0x67,0x7f,0x93,0x9f,0x8c,0x73,0x5b,0x43,0x2c,0x15,0x02,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x67,0x7f,0x98,0x9d,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x8c,0x8c,0x8c,0x8c,0x8c\n      ,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x42,0x3c,0x31,0x25,0x16,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x17,0x27,0x38,0x46,0x4e,0x4b,0x40,0x2f\n      ,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x19,0x1e,0x1c,0x15,0x0a,0x03,0x0b,0x15,0x1c,0x1e,0x1e,0x19,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x25,0x38,0x4c,0x61,0x72,0x84,0x98,0xa8,0x98,0x84,0x7b,0x8c,0x9d,0xaa,0x98,0x84,0x72,0x61,0x4c,0x38,0x25,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84\n      ,0x98,0xa5,0x93,0x7f,0x8a,0x9d,0x9d,0x8a,0x74,0x61,0x4b,0x34,0x1d,0x06,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e\n      ,0x37,0x4f,0x67,0x7f,0x97,0xad,0xb0,0x98,0x7f,0x71,0x68,0x71,0x7b,0x8c,0x9d,0xb3,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e\n      ,0x00,0x00,0x06,0x1b,0x2f,0x40,0x52,0x66,0x7b,0x8c,0x9c,0xad,0xa4,0x8e,0x7b,0x6e,0x67,0x67,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x52,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x07,0x1b,0x31,0x46,0x5a,0x6b,0x7f,0x93,0x9b,0x8c,0x7c,0x8a,0x9c,0x98,0x84,0x71\n      ,0x5c,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8f,0x9d,0xa0,0x94,0x84,0x72,0x62,0x51,0x3c,0x26,0x10,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8d,0x98,0x95,0x7f,0x6b,0x57,0x51,0x67,0x7f,0x92,0x98,0x91,0x7f,0x6b,0x57\n      ,0x40,0x28,0x11,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x8c,0x8c,0x7f,0x67,0x4f,0x4b,0x61,0x74,0x89,0x8c,0x89,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x07,0x1e,0x37,0x4f,0x67,0x7f,0x8c,0x8c,0x7f,0x6b,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x0e\n      ,0x25,0x3c,0x51,0x62,0x71,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x05,0x16,0x25,0x30,0x36,0x34,0x2b,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x31,0x46,0x5a,0x6b,0x7f,0x93,0xa4,0x9d,0x8c,0x7b,0x6e,0x7f,0x93,0xa4,0xa4,0x93,0x7f,0x6b,0x5a,0x46,0x31,0x1e,0x0c,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b,0x8e,0xa4,0x99,0x84,0x8e,0xa3,0x97,0x7f,0x6b,0x57,0x41,0x2b,0x15,0x02,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x73,0x8c,0xa4,0xb0\n      ,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x8e,0xa4,0xb2,0x9d,0x8e,0x84,0x7f,0x84,0x8e,0x9a,0xa5,0xb5,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x72,0x84,0x98,0xac,0xb8,0xa4,0x8e,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73\n      ,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x3a,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x66,0x7b\n      ,0x8c,0x98,0x8c,0x7b,0x6f,0x7b,0x8c,0x9a,0x8e,0x7b,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x74,0x8c,0x97,0x8e,0x84,0x72,0x62,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7b,0x65\n      ,0x4e,0x4b,0x61,0x72,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x71,0x73,0x73,0x71,0x61,0x4b,0x41,0x57,0x6a,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73\n      ,0x71,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x0e,0x06,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x52,0x66,0x7b,0x8c,0x9d,0xa6,0x93,0x7f,0x6b,0x62,0x72,0x84,0x98,0xaa,0x9d\n      ,0x8c,0x7b,0x66,0x52,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x31,0x46,0x5c,0x73,0x8a,0x9d,0xa4,0x99,0x9d,0xa3,0x8e,0x7b,0x65,0x4e,0x37,0x21,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67\n      ,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x73,0x8c,0xa4,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x04,0x19,0x30,0x46,0x5c,0x73,0x8a,0x9d,0xb1,0xae,0xa3,0x99,0x98,0x99,0x9a,0x8e,0x94,0xa8,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67\n      ,0x7f,0x98,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xb0,0xb0,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x94,0xa4,0xb5,0xba,0xae,0x9d,0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x12\n      ,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb6,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x21,0x1e,0x1c,0x15,0x0a,0x01,0x00\n      ,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x84,0x97,0x93,0x7f,0x6b,0x5e,0x6a,0x7b,0x8c,0x98,0x8a,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x02,0x15,0x2c,0x43,0x5a,0x71,0x84,0x7f,0x7b,0x71,0x62,0x51,0x40,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x04\n      ,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x65,0x5a,0x46,0x40,0x51,0x61,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x03,0x16,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5a,0x51,0x40,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00\n      ,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x72,0x84,0x98,0xa2\n      ,0x9d,0x8a,0x74,0x61,0x54,0x66,0x7b,0x8c,0x9d,0xa2,0x98,0x84,0x72,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x97,0xa4,0xa4,0xa4,0x9d,0x8a,0x73,0x5c,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x12\n      ,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xad,0x98,0x7f,0x67,0x73,0x8c,0xa4,0x98,0x7f,0x67,0x73,0x8c,0xa4,0xad,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x11,0x28,0x40,0x57,0x6b,0x7f,0x92,0x9d,0xa8,0xae,0xa9,0xa2,0x98,0x8c,0x7c,0x8a,0x9d,0xa4,0x98,0x7f,0x67\n      ,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x98,0xad,0xa4,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x98,0xad,0xad,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x9f,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4\n      ,0xa4,0x98,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0xa4,0xb0,0xa4,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x95,0x7f\n      ,0x67,0x4f,0x37,0x1e,0x09,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x8c,0x84,0x72,0x61,0x4f,0x5a,0x6b,0x7f,0x8d,0x84,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x62,0x71,0x6b,0x65,0x5c,0x51\n      ,0x40,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x42,0x3c,0x2f,0x27,0x36,0x40\n      ,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28\n      ,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x8a,0x8c,0x8c,0x8c,0x7f,0x6b,0x57,0x49,0x5a,0x6b,0x7f,0x8c,0x8c,0x8c,0x8a,0x82,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x21,0x37,0x4e,0x65,0x7b,0x8a,0x8c,0x8c,0x8c,0x8c,0x7f,0x6b,0x57,0x40\n      ,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x95,0x7f,0x67,0x73,0x8c,0x98,0x95,0x7f,0x67,0x73,0x8c,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x00,0x0a,0x21,0x36,0x4c,0x61,0x72,0x7f,0x8a,0x93,0x98,0x97\n      ,0x8e,0x84,0x7b,0x6f,0x7f,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x06,0x1e,0x37,0x4f,0x67,0x7f,0x95,0x98,0x98,0x8c,0x73,0x5b,0x43,0x4f,0x67,0x7f,0x95,0x98,0x98,0x95,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x89,0x8c,0x8c\n      ,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x7f,0x67,0x4f,0x37,0x1e,0x00,0x00,0x12,0x2a,0x43,0x5b,0x73,0x8c,0x98,0x98,0x98,0x8c,0x73,0x5b,0x43,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x6b,0x7b,0x72,0x62,0x51,0x41,0x4c,0x61,0x72,0x7b,0x71,0x62,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00\n      ,0x06,0x1b,0x2f,0x40,0x51,0x5a,0x57,0x4e,0x46,0x3c,0x2f,0x1e,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x36,0x30,0x25,0x1e,0x2b,0x34,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b\n      ,0x25,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x1c,0x34,0x4b,0x61,0x71,0x73,0x73,0x73,0x73,0x71,0x61,0x4c,0x3c,0x4c,0x61,0x71,0x73,0x73,0x73,0x73,0x73,0x6a,0x57,0x40,0x28,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x19,0x30,0x46,0x5a\n      ,0x6a,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4c,0x36,0x21,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x71,0x7f,0x7f,0x7f,0x7b,0x65,0x71,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e,0x00,0x00,0x00,0x03\n      ,0x16,0x2b,0x40,0x51,0x61,0x6b,0x74,0x7f,0x7f,0x7f,0x7b,0x71,0x66,0x62,0x71,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x00,0x00,0x06,0x1e,0x36,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x4e,0x65,0x7b,0x7f,0x7f,0x7f,0x7f,0x7b,0x65,0x4e,0x36,0x1e\n      ,0x00,0x00,0x11,0x28,0x40,0x57,0x6a,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x73,0x71,0x61,0x4b,0x34,0x1c,0x00,0x00,0x12,0x2a,0x42,0x5a,0x71,0x7f,0x7f,0x7f,0x7f,0x7f,0x71,0x5a,0x42,0x2a,0x12,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4c,0x5a,0x65,0x61,0x51,0x40,0x32,0x40,0x51,0x61,0x65,0x5c\n      ,0x51,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2f,0x3c,0x42,0x40,0x37,0x30,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1c,0x15\n      ,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x0e,0x06,0x02,0x0a,0x11,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x15,0x2b,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x31,0x40,0x51,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x57,0x49,0x36,0x21,0x0a,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x25,0x38,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x65,0x5a,0x61,0x67,0x67,0x67,0x65,0x5a,0x61,0x67,0x67,0x67\n      ,0x67,0x65,0x5a,0x46,0x30,0x19,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x40,0x4c,0x57,0x61,0x67,0x67,0x67,0x65,0x5c,0x51,0x51,0x5a,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x00,0x00,0x04,0x19,0x30,0x46,0x5a,0x65,0x67,0x67,0x67,0x67,0x61,0x51,0x3c,0x46,0x5a\n      ,0x65,0x67,0x67,0x67,0x67,0x65,0x5a,0x46,0x30,0x19,0x00,0x00,0x0a,0x21,0x36,0x49,0x57,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5a,0x51,0x40,0x2b,0x15,0x00,0x00,0x0e,0x25,0x3c,0x51,0x61,0x67,0x67,0x67,0x67,0x67,0x61\n      ,0x51,0x3c,0x25,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x38,0x46\n      ,0x4e,0x4b,0x40,0x2f,0x23,0x2f,0x40,0x4b,0x4e,0x46,0x3c,0x2f,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x28,0x21,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x06,0x06,0x06,0x06,0x06,0x04\n      ,0x00,0x00,0x02,0x05,0x06,0x06,0x06,0x06,0x05,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x1e,0x2f,0x3c,0x42,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x23,0x2f,0x3c,0x42,0x43,0x43\n      ,0x43,0x43,0x43,0x40,0x36,0x27,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x16,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x4e,0x46\n      ,0x4b,0x4f,0x4f,0x4f,0x4e,0x46,0x4b,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x00,0x00,0x01,0x0d,0x1e,0x2b,0x36,0x41,0x4b,0x4f,0x4f,0x4f,0x4e,0x46,0x3c,0x3c,0x42,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x00,0x10,0x25,0x38,0x46\n      ,0x4e,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x38,0x46,0x4e,0x4f,0x4f,0x4f,0x4f,0x4e,0x46,0x38,0x25,0x10,0x00,0x00,0x02,0x14,0x27,0x36,0x40,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x3c,0x2f,0x1e,0x0a,0x00,0x00,0x06\n      ,0x1b,0x2f,0x40,0x4b,0x4f,0x4f,0x4f,0x4f,0x4f,0x4b,0x40,0x2f,0x1b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x17,0x25,0x30,0x36,0x34,0x2b,0x1e,0x11,0x1e,0x2b,0x34,0x36,0x30,0x26,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0c,0x1b,0x25,0x2a,0x2a,0x2a,0x2a\n      ,0x2a,0x2a,0x25,0x1b,0x11,0x1b,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x28,0x21,0x14,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x37,0x36,0x30,0x34,0x37,0x37,0x37,0x36,0x30,0x34,0x37,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x16,0x21,0x2b,0x34,0x37,0x37,0x37,0x36,0x30,0x26,0x25,0x2a,0x2a,0x2a,0x2a,0x2a,0x25\n      ,0x1b,0x0c,0x01,0x00,0x00,0x00,0x04,0x16,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x25,0x30,0x36,0x37,0x37,0x37,0x37,0x36,0x30,0x25,0x16,0x04,0x00,0x00,0x00,0x05,0x14,0x21,0x28,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a\n      ,0x2a,0x2a,0x2a,0x25,0x1b,0x0c,0x01,0x00,0x00,0x00,0x0c,0x1e,0x2b,0x34,0x37,0x37,0x37,0x37,0x37,0x34,0x2b,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e\n      ,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1c,0x15,0x0a,0x02,0x0a,0x15,0x1c,0x1e,0x19,0x10,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x01,0x06,0x0e,0x12,0x12,0x12,0x12,0x12,0x12,0x11,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x1c,0x1e,0x1e,0x1e,0x1e,0x19,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0b,0x15,0x1c,0x1e,0x1e,0x1e\n      ,0x1e,0x19,0x10,0x0e,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x10,0x19,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x19,0x10,0x04,0x00,0x00,0x00,0x00,0x00,0x02,0x0a,0x11,0x12,0x12,0x12\n      ,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x01,0x0a,0x15,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x15,0x0a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n      ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\n  };\n  \n  const HudFont g_hudFont = { 32, 510, 172, 5, 24, 95, g_hudFontGlyphs, g_hudFontImage };\n  \n}"
  },
  {
    "path": "src/dxvk/hud/dxvk_hud_font.h",
    "content": "#pragma once\n\n#include <cstdint>\n\nnamespace dxvk::hud {\n  \n  struct HudGlyph {\n    uint32_t codePoint;\n    int32_t  x;\n    int32_t  y;\n    int32_t  w;\n    int32_t  h;\n    int32_t  originX;\n    int32_t  originY;\n  };\n\n  struct HudFont {\n    int32_t  size;\n    uint32_t width;\n    uint32_t height;\n    uint32_t falloff;\n    uint32_t advance;\n    uint32_t charCount;\n    \n    const HudGlyph* glyphs;\n    const uint8_t* texture;\n  };\n  \n  extern const HudFont g_hudFont;\n  \n}"
  },
  {
    "path": "src/dxvk/hud/dxvk_hud_item.cpp",
    "content": "#include \"dxvk_hud_item.h\"\n\n#include <hud_chunk_frag_background.h>\n#include <hud_chunk_frag_visualize.h>\n#include <hud_chunk_vert_background.h>\n#include <hud_chunk_vert_visualize.h>\n\n#include <hud_frame_time_eval.h>\n\n#include <hud_graph_frag.h>\n#include <hud_graph_vert.h>\n\n#include <iomanip>\n#include <version.h>\n\nnamespace dxvk::hud {\n\n  HudItem::~HudItem() {\n\n  }\n\n\n  void HudItem::update(dxvk::high_resolution_clock::time_point time) {\n    // Do nothing by default. Some items won't need this.\n  }\n\n\n  HudItemSet::HudItemSet(const Rc<DxvkDevice>& device) {\n    std::string configStr = env::getEnvVar(\"DXVK_HUD\");\n\n    if (configStr.empty())\n      configStr = device->config().hud;\n\n    std::string::size_type pos = 0;\n    std::string::size_type end = 0;\n    std::string::size_type mid = 0;\n    \n    while (pos < configStr.size()) {\n      end = configStr.find(',', pos);\n      mid = configStr.find('=', pos);\n      \n      if (end == std::string::npos)\n        end = configStr.size();\n      \n      if (mid != std::string::npos && mid < end) {\n        m_options.insert({\n          configStr.substr(pos,     mid - pos),\n          configStr.substr(mid + 1, end - mid - 1) });\n      } else {\n        m_enabled.insert(configStr.substr(pos, end - pos));\n      }\n\n      pos = end + 1;\n    }\n\n    if (m_enabled.find(\"full\") != m_enabled.end())\n      m_enableFull = true;\n    \n    if (m_enabled.find(\"1\") != m_enabled.end()) {\n      m_enabled.insert(\"devinfo\");\n      m_enabled.insert(\"fps\");\n    }\n  }\n\n\n  HudItemSet::~HudItemSet() {\n\n  }\n\n\n  void HudItemSet::update() {\n    auto time = dxvk::high_resolution_clock::now();\n\n    for (const auto& item : m_items)\n      item->update(time);\n  }\n\n\n  void HudItemSet::render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer) {\n    HudPos position = { 8, 8 };\n\n    for (const auto& item : m_items)\n      position = item->render(ctx, key, options, renderer, position);\n  }\n\n\n  void HudItemSet::parseOption(const std::string& str, float& value) {\n    try {\n      value = std::stof(str);\n    } catch (const std::invalid_argument&) {\n      return;\n    }\n  }\n\n\n  HudPos HudVersionItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n    renderer.drawText(16, position, 0xffffffffu, \"DXVK \" DXVK_VERSION);\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudClientApiItem::HudClientApiItem(std::string api)\n  : m_api(std::move(api)) {\n\n  }\n\n\n  HudClientApiItem::~HudClientApiItem() {\n\n  }\n\n\n  HudPos HudClientApiItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    std::lock_guard lock(m_mutex);\n\n    position.y += 16;\n    renderer.drawText(16, position, 0xffffffffu, m_api);\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudDeviceInfoItem::HudDeviceInfoItem(const Rc<DxvkDevice>& device) {\n    const auto& props = device->properties();\n\n    std::string driverInfo = props.vk12.driverInfo;\n\n    if (driverInfo.empty())\n      driverInfo = props.driverVersion.toString();\n\n    m_deviceName = props.core.properties.deviceName;\n    m_driverName = str::format(\"Driver:  \", props.vk12.driverName);\n    m_driverVer = str::format(\"Version: \", driverInfo);\n  }\n\n\n  HudDeviceInfoItem::~HudDeviceInfoItem() {\n\n  }\n\n\n  HudPos HudDeviceInfoItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n    renderer.drawText(16, position, 0xffffffffu, m_deviceName);\n    \n    position.y += 24;\n    renderer.drawText(16, position, 0xffffffffu, m_driverName);\n    \n    position.y += 20;\n    renderer.drawText(16, position, 0xffffffffu, m_driverVer);\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudFpsItem::HudFpsItem() { }\n  HudFpsItem::~HudFpsItem() { }\n\n\n  void HudFpsItem::update(dxvk::high_resolution_clock::time_point time) {\n    m_frameCount += 1;\n\n    auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);\n\n    if (elapsed.count() >= UpdateInterval) {\n      int64_t fps = (10'000'000ll * m_frameCount) / elapsed.count();\n\n      m_frameRate = str::format(fps / 10, \".\", fps % 10);\n      m_frameCount = 0;\n      m_lastUpdate = time;\n    }\n  }\n\n\n  HudPos HudFpsItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n\n    renderer.drawText(16, position, 0xff4040ffu, \"FPS:\");\n    renderer.drawText(16, { position.x + 60, position.y },\n      0xffffffffu, m_frameRate);\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudFrameTimeItem::HudFrameTimeItem(const Rc<DxvkDevice>& device, HudRenderer* renderer)\n  : m_device            (device),\n    m_gfxPipelineLayout (createPipelineLayout()) {\n    createComputePipeline(*renderer);\n  }\n\n\n  HudFrameTimeItem::~HudFrameTimeItem() {\n    auto vk = m_device->vkd();\n\n    for (const auto& p : m_gfxPipelines)\n      vk->vkDestroyPipeline(vk->device(), p.second, nullptr);\n\n    vk->vkDestroyPipeline(vk->device(), m_computePipeline, nullptr);\n  }\n\n\n  HudPos HudFrameTimeItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    if (!m_gpuBuffer)\n      createResources(ctx);\n\n    HudPos minPos = {  12, -128 };\n    HudPos maxPos = { 162, -128 };\n\n    HudPos graphPos = { 8, -120 };\n    HudPos graphSize = { NumDataPoints, 80 };\n\n    uint32_t dataPoint = m_nextDataPoint++;\n\n    processFrameTimes(ctx, key, renderer,\n      dataPoint, minPos, maxPos);\n\n    drawFrameTimeGraph(ctx, key, renderer,\n      dataPoint, graphPos, graphSize);\n\n    if (m_nextDataPoint >= NumDataPoints)\n      m_nextDataPoint = 0u;\n\n    return position;\n  }\n\n\n  void HudFrameTimeItem::processFrameTimes(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n          HudRenderer&        renderer,\n          uint32_t            dataPoint,\n          HudPos              minPos,\n          HudPos              maxPos) {\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) {\n      ctx->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::InitBuffer,\n        vk::makeLabel(0xf0c0dc, \"HUD frame time processing\"));\n    }\n\n    // Write current time stamp to the buffer\n    DxvkResourceBufferInfo slice = m_gpuBuffer->getSliceInfo();\n    std::pair<VkQueryPool, uint32_t> query = m_query->getQuery();\n\n    ctx->cmdResetQueryPool(DxvkCmdBuffer::InitBuffer,\n      query.first, query.second, 1);\n\n    ctx->cmdWriteTimestamp(DxvkCmdBuffer::InitBuffer,\n      VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,\n      query.first, query.second);\n\n    ctx->cmdCopyQueryPoolResults(DxvkCmdBuffer::InitBuffer,\n      query.first, query.second, 1, slice.buffer,\n      slice.offset + (dataPoint & 1u) * sizeof(uint64_t), sizeof(uint64_t),\n      VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);\n\n    VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n    barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;\n    barrier.dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;\n\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.memoryBarrierCount = 1u;\n    depInfo.pMemoryBarriers = &barrier;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);\n\n    // Process contents of the buffer and write out text draws\n    auto bufferLayout = computeBufferLayout();\n\n    auto drawParamBuffer = m_gpuBuffer->getSliceInfo(\n      bufferLayout.drawParamOffset, bufferLayout.drawParamSize);\n    auto drawInfoBuffer = m_gpuBuffer->getSliceInfo(\n      bufferLayout.drawInfoOffset, bufferLayout.drawInfoSize);\n\n    std::array<DxvkDescriptorWrite, 4u> descriptors = { };\n    descriptors[0u].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n    descriptors[0u].buffer = m_gpuBuffer->getSliceInfo(0, bufferLayout.timestampSize);\n\n    descriptors[1u].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n    descriptors[1u].buffer = drawParamBuffer;\n\n    descriptors[2u].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n    descriptors[2u].buffer = drawInfoBuffer;\n\n    descriptors[3u].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;\n    descriptors[3u].descriptor = m_textWrView->getDescriptor(false);\n\n    ComputePushConstants pushConstants = { };\n    pushConstants.msPerTick = m_device->properties().core.properties.limits.timestampPeriod / 1000000.0f;\n    pushConstants.dataPoint = dataPoint;\n    pushConstants.textPosMinX = minPos.x + 48;\n    pushConstants.textPosMinY = minPos.y;\n    pushConstants.textPosMaxX = maxPos.x + 48;\n    pushConstants.textPosMaxY = maxPos.y;\n\n    ctx->cmdBindPipeline(DxvkCmdBuffer::InitBuffer,\n      VK_PIPELINE_BIND_POINT_COMPUTE, m_computePipeline);\n\n    ctx->bindResources(DxvkCmdBuffer::InitBuffer,\n      m_computePipelineLayout, descriptors.size(), descriptors.data(),\n      sizeof(pushConstants), &pushConstants);\n\n    ctx->cmdDispatch(DxvkCmdBuffer::InitBuffer, 1, 1, 1);\n\n    barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;\n    barrier.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;\n    barrier.dstAccessMask = m_gpuBuffer->info().access;\n    barrier.dstStageMask = m_gpuBuffer->info().stages;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);\n\n    // Display the min/max numbers\n    renderer.drawText(12, minPos, 0xff4040ff, \"min:\");\n    renderer.drawText(12, maxPos, 0xff4040ff, \"max:\");\n\n    renderer.drawTextIndirect(ctx, key, drawParamBuffer,\n      drawInfoBuffer, m_textRdView, 2u);\n\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      ctx->cmdEndDebugUtilsLabel(DxvkCmdBuffer::InitBuffer);\n\n    // Make sure GPU resources are being kept alive as necessary\n    ctx->track(m_gpuBuffer, DxvkAccess::Write);\n    ctx->track(m_query);\n  }\n\n\n  void HudFrameTimeItem::drawFrameTimeGraph(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n          HudRenderer&        renderer,\n          uint32_t            dataPoint,\n          HudPos              graphPos,\n          HudPos              graphSize) {\n    DxvkDescriptorWrite descriptorWrite;\n    descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n    descriptorWrite.buffer = m_gpuBuffer->getSliceInfo(0u, computeBufferLayout().timestampSize);\n\n    RenderPushConstants pushConstants = { };\n    pushConstants.hud = renderer.getPushConstants();\n    pushConstants.x = graphPos.x;\n    pushConstants.y = graphPos.y;\n    pushConstants.w = graphSize.x;\n    pushConstants.h = graphSize.y;\n    pushConstants.frameIndex = dataPoint;\n\n    ctx->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, getPipeline(renderer, key));\n\n    ctx->bindResources(DxvkCmdBuffer::ExecBuffer,\n      m_gfxPipelineLayout, 1u, &descriptorWrite,\n      sizeof(pushConstants), &pushConstants);\n\n    ctx->cmdDraw(4, 1, 0, 0);\n\n    ctx->track(m_gpuBuffer, DxvkAccess::Read);\n  }\n\n\n  void HudFrameTimeItem::createResources(\n    const Rc<DxvkCommandList>&ctx) {\n    auto bufferLayout = computeBufferLayout();\n\n    DxvkBufferCreateInfo bufferInfo = { };\n    bufferInfo.size = bufferLayout.totalSize;\n    bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                     | VK_BUFFER_USAGE_TRANSFER_SRC_BIT\n                     | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT\n                     | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                     | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT\n                     | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;\n    bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                      | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT\n                      | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT\n                      | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT\n                      | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n    bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT\n                      | VK_ACCESS_TRANSFER_WRITE_BIT\n                      | VK_ACCESS_INDIRECT_COMMAND_READ_BIT\n                      | VK_ACCESS_SHADER_READ_BIT\n                      | VK_ACCESS_SHADER_WRITE_BIT;\n    bufferInfo.debugName = \"HUD frame time data\";\n\n    m_gpuBuffer = m_device->createBuffer(bufferInfo, VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);\n\n    DxvkBufferViewKey textViewInfo = { };\n    textViewInfo.format = VK_FORMAT_R8_UINT;\n    textViewInfo.offset = bufferLayout.textOffset;\n    textViewInfo.size = bufferLayout.textSize;\n    textViewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;\n    m_textWrView = m_gpuBuffer->createView(textViewInfo);\n\n    textViewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n    m_textRdView = m_gpuBuffer->createView(textViewInfo);\n\n    // Zero-init buffer so we don't display random garbage at the start\n    DxvkResourceBufferInfo bufferSlice = m_gpuBuffer->getSliceInfo();\n\n    ctx->cmdFillBuffer(DxvkCmdBuffer::InitBuffer,\n      bufferSlice.buffer, bufferSlice.offset, bufferSlice.size, 0u);\n\n    VkMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n    barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    barrier.dstAccessMask = m_gpuBuffer->info().access;\n    barrier.dstStageMask = m_gpuBuffer->info().stages;\n\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.memoryBarrierCount = 1u;\n    depInfo.pMemoryBarriers = &barrier;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);\n    ctx->track(m_gpuBuffer, DxvkAccess::Write);\n\n    m_query = m_device->createRawQuery(VK_QUERY_TYPE_TIMESTAMP);\n  }\n\n\n  void HudFrameTimeItem::createComputePipeline(\n          HudRenderer&        renderer) {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 4> bindings = {{\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,       1, VK_SHADER_STAGE_COMPUTE_BIT },\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,       1, VK_SHADER_STAGE_COMPUTE_BIT },\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,       1, VK_SHADER_STAGE_COMPUTE_BIT },\n      { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },\n    }};\n\n    m_computePipelineLayout = m_device->createBuiltInPipelineLayout(0u,\n      VK_SHADER_STAGE_COMPUTE_BIT, sizeof(ComputePushConstants),\n      bindings.size(), bindings.data());\n\n    m_computePipeline = m_device->createBuiltInComputePipeline(m_computePipelineLayout,\n      util::DxvkBuiltInShaderStage(hud_frame_time_eval, nullptr));\n  }\n\n\n  const DxvkPipelineLayout* HudFrameTimeItem::createPipelineLayout() {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 1> bindings = {{\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n    }};\n\n    return m_device->createBuiltInPipelineLayout(0u,\n      VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,\n      sizeof(RenderPushConstants), bindings.size(), bindings.data());\n  }\n\n\n  VkPipeline HudFrameTimeItem::getPipeline(\n          HudRenderer&        renderer,\n    const HudPipelineKey&     key) {\n    auto entry = m_gfxPipelines.find(key);\n\n    if (entry != m_gfxPipelines.end())\n      return entry->second;\n\n    VkPipeline pipeline = createPipeline(renderer, key);\n    m_gfxPipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n\n  VkPipeline HudFrameTimeItem::createPipeline(\n          HudRenderer&        renderer,\n    const HudPipelineKey&     key) {\n    auto vk = m_device->vkd();\n\n    HudSpecConstants specConstants = renderer.getSpecConstants(key);\n    VkSpecializationInfo specInfo = renderer.getSpecInfo(&specConstants);\n\n    VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };\n    iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;\n\n    VkPipelineColorBlendAttachmentState cbAttachment = { };\n    cbAttachment.blendEnable = VK_TRUE;\n    cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;\n    cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n    cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;\n    cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;\n    cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n    cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;\n    cbAttachment.colorWriteMask =\n      VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |\n      VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n\n    util::DxvkBuiltInGraphicsState state = { };\n    state.vs = util::DxvkBuiltInShaderStage(hud_graph_vert, nullptr);\n    state.fs = util::DxvkBuiltInShaderStage(hud_graph_frag, &specInfo);\n    state.iaState = &iaState;\n    state.colorFormat = key.format;\n    state.cbAttachment = &cbAttachment;\n\n    return m_device->createBuiltInGraphicsPipeline(m_gfxPipelineLayout, state);\n  }\n\n\n  HudFrameTimeItem::BufferLayout HudFrameTimeItem::computeBufferLayout() {\n    struct ComputeTimestampBuffer {\n      std::array<uint64_t, 2u> timestamps;\n      std::array<float, NumDataPoints> intervals;\n      float avgMs;\n      float minMs;\n      float maxMs;\n    };\n\n    BufferLayout result = { };\n    result.timestampSize = align(sizeof(ComputeTimestampBuffer), 256u);\n    result.drawInfoOffset = result.timestampSize;\n    result.drawInfoSize = align(sizeof(HudTextDrawInfo) * NumTextDraws, 256u);\n    result.drawParamOffset = result.drawInfoOffset + result.drawInfoSize;\n    result.drawParamSize = align(sizeof(VkDrawIndirectCommand) * NumTextDraws, 256u);\n    result.textOffset = result.drawParamOffset + result.drawParamSize;\n    result.textSize = 256u;\n    result.totalSize = result.textOffset + result.textSize;\n    return result;\n  }\n\n\n\n\n  HudSubmissionStatsItem::HudSubmissionStatsItem(const Rc<DxvkDevice>& device)\n  : m_device(device) {\n\n  }\n\n\n  HudSubmissionStatsItem::~HudSubmissionStatsItem() {\n\n  }\n\n\n  void HudSubmissionStatsItem::update(dxvk::high_resolution_clock::time_point time) {\n    DxvkStatCounters counters = m_device->getStatCounters();\n    \n    uint64_t currSubmitCount = counters.getCtr(DxvkStatCounter::QueueSubmitCount);\n    uint64_t currSyncCount = counters.getCtr(DxvkStatCounter::GpuSyncCount);\n    uint64_t currSyncTicks = counters.getCtr(DxvkStatCounter::GpuSyncTicks);\n\n    m_maxSubmitCount = std::max(m_maxSubmitCount, currSubmitCount - m_prevSubmitCount);\n    m_maxSyncCount = std::max(m_maxSyncCount, currSyncCount - m_prevSyncCount);\n    m_maxSyncTicks = std::max(m_maxSyncTicks, currSyncTicks - m_prevSyncTicks);\n\n    m_prevSubmitCount = currSubmitCount;\n    m_prevSyncCount = currSyncCount;\n    m_prevSyncTicks = currSyncTicks;\n\n    auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);\n\n    if (elapsed.count() >= UpdateInterval) {\n      m_submitString = str::format(m_maxSubmitCount);\n\n      uint64_t syncTicks = m_maxSyncTicks / 100;\n\n      m_syncString = m_maxSyncCount\n        ? str::format(m_maxSyncCount, \" (\", (syncTicks / 10), \".\", (syncTicks % 10), \" ms)\")\n        : str::format(m_maxSyncCount);\n\n      m_maxSubmitCount = 0;\n      m_maxSyncCount = 0;\n      m_maxSyncTicks = 0;\n\n      m_lastUpdate = time;\n    }\n  }\n\n\n  HudPos HudSubmissionStatsItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n    renderer.drawText(16, position, 0xff4080ff, \"Queue submissions:\");\n    renderer.drawText(16, { position.x + 228, position.y }, 0xffffffffu, m_submitString);\n\n    position.y += 20;\n    renderer.drawText(16, position, 0xff4080ff, \"Queue syncs:\");\n    renderer.drawText(16, { position.x + 228, position.y }, 0xffffffffu, m_syncString);\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudDrawCallStatsItem::HudDrawCallStatsItem(const Rc<DxvkDevice>& device)\n  : m_device(device) {\n\n  }\n\n\n  HudDrawCallStatsItem::~HudDrawCallStatsItem() {\n\n  }\n\n\n  void HudDrawCallStatsItem::update(dxvk::high_resolution_clock::time_point time) {\n    auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate);\n\n    DxvkStatCounters counters = m_device->getStatCounters();\n    auto diffCounters = counters.diff(m_prevCounters);\n\n    if (elapsed.count() >= UpdateInterval) {\n      m_drawCallCount   = diffCounters.getCtr(DxvkStatCounter::CmdDrawCalls);\n      m_drawCount       = diffCounters.getCtr(DxvkStatCounter::CmdDrawsMerged) + m_drawCallCount;\n      m_dispatchCount   = diffCounters.getCtr(DxvkStatCounter::CmdDispatchCalls);\n      m_renderPassCount = diffCounters.getCtr(DxvkStatCounter::CmdRenderPassCount);\n      m_barrierCount    = diffCounters.getCtr(DxvkStatCounter::CmdBarrierCount);\n\n      m_lastUpdate = time;\n    }\n\n    m_prevCounters = counters;\n  }\n\n\n  HudPos HudDrawCallStatsItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    std::string drawCount = m_drawCount > m_drawCallCount\n      ? str::format(m_drawCallCount, \" (\", m_drawCount, \")\")\n      : str::format(m_drawCallCount);\n\n    position.y += 16;\n    renderer.drawText(16, position, 0xffff8040, \"Draw calls:\");\n    renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, drawCount);\n    \n    position.y += 20;\n    renderer.drawText(16, position, 0xffff8040, \"Dispatch calls:\");\n    renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_dispatchCount));\n    \n    position.y += 20;\n    renderer.drawText(16, position, 0xffff8040, \"Render passes:\");\n    renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_renderPassCount));\n    \n    position.y += 20;\n    renderer.drawText(16, position, 0xffff8040, \"Barriers:\");\n    renderer.drawText(16, { position.x + 192, position.y }, 0xffffffffu, str::format(m_barrierCount));\n    \n    position.y += 8;\n    return position;\n  }\n\n\n  HudPipelineStatsItem::HudPipelineStatsItem(const Rc<DxvkDevice>& device)\n  : m_device(device) {\n\n  }\n\n\n  HudPipelineStatsItem::~HudPipelineStatsItem() {\n\n  }\n\n\n  void HudPipelineStatsItem::update(dxvk::high_resolution_clock::time_point time) {\n    DxvkStatCounters counters = m_device->getStatCounters();\n\n    m_graphicsPipelines = counters.getCtr(DxvkStatCounter::PipeCountGraphics);\n    m_graphicsLibraries = counters.getCtr(DxvkStatCounter::PipeCountLibrary);\n    m_computePipelines  = counters.getCtr(DxvkStatCounter::PipeCountCompute);\n  }\n\n\n  HudPos HudPipelineStatsItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n    renderer.drawText(16, position, 0xffff40ff, \"Graphics pipelines:\");\n    renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsPipelines));\n\n    if (m_graphicsLibraries) {\n      position.y += 20;\n      renderer.drawText(16, position, 0xffff40ff, \"Graphics shaders:\");\n      renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_graphicsLibraries));\n    }\n\n    position.y += 20;\n    renderer.drawText(16, position, 0xffff40ff, \"Compute shaders:\");\n    renderer.drawText(16, { position.x + 240, position.y }, 0xffffffffu, str::format(m_computePipelines));\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudDescriptorStatsItem::HudDescriptorStatsItem(const Rc<DxvkDevice>& device)\n  : m_device(device) {\n\n  }\n\n\n  HudDescriptorStatsItem::~HudDescriptorStatsItem() {\n\n  }\n\n\n  void HudDescriptorStatsItem::update(dxvk::high_resolution_clock::time_point time) {\n    uint64_t ticks = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate).count();\n\n    DxvkStatCounters counters = m_device->getStatCounters();\n\n    if (ticks >= UpdateInterval) {\n      uint64_t busyTicks = counters.getCtr(DxvkStatCounter::DescriptorCopyBusyTicks);\n\n      m_copyThreadLoad = uint32_t(double(100.0 * (busyTicks - m_copyThreadBusyTicks)) / ticks);\n      m_copyThreadBusyTicks = busyTicks;\n\n      m_descriptorSetCountDisplay = m_descriptorSetCountMax;\n      m_descriptorSetCountMax = 0u;\n\n      m_descriptorHeapUsed = m_descriptorHeapMax;\n      m_descriptorHeapMax = 0u;\n\n      m_lastUpdate = time;\n    }\n\n    auto descriptorSetCount  = counters.getCtr(DxvkStatCounter::DescriptorSetCount);\n    m_descriptorPoolCount = counters.getCtr(DxvkStatCounter::DescriptorPoolCount);\n\n    m_descriptorHeapCount = counters.getCtr(DxvkStatCounter::DescriptorHeapCount);\n    m_descriptorHeapAlloc = counters.getCtr(DxvkStatCounter::DescriptorHeapSize);\n\n    m_descriptorSetCountMax = std::max(m_descriptorSetCountMax, descriptorSetCount - m_descriptorSetCount);\n    m_descriptorSetCount = descriptorSetCount;\n\n    auto descriptorHeapUsed = counters.getCtr(DxvkStatCounter::DescriptorHeapUsed);\n    m_descriptorHeapMax = std::max(descriptorHeapUsed - m_descriptorHeapPrev, m_descriptorHeapMax);\n    m_descriptorHeapPrev = descriptorHeapUsed;\n  }\n\n\n  HudPos HudDescriptorStatsItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    if (m_descriptorPoolCount) {\n      position.y += 16;\n      renderer.drawText(16, position, 0xff8040ff, \"Descriptor pools:\");\n      renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorPoolCount));\n\n      position.y += 20;\n      renderer.drawText(16, position, 0xff8040ff, \"Descriptor sets:\");\n      renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorSetCountDisplay));\n    }\n\n    if (m_descriptorHeapAlloc) {\n      position.y += 16;\n      renderer.drawText(16, position, 0xff8040ff, \"Descriptor heaps:\");\n      renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorHeapCount, \" (\", m_descriptorHeapAlloc >> 20, \" MB)\"));\n\n      position.y += 20;\n      renderer.drawText(16, position, 0xff8040ff, \"Descriptor usage:\");\n      renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_descriptorHeapUsed >> 10, \" kB\"));\n\n      position.y += 20;\n      renderer.drawText(16, position, 0xff8040ff, \"Copy worker:\");\n      renderer.drawText(16, { position.x + 216, position.y }, 0xffffffffu, str::format(m_copyThreadLoad, \"%\"));\n    }\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudMemoryStatsItem::HudMemoryStatsItem(const Rc<DxvkDevice>& device)\n  : m_device(device), m_memory(device->adapter()->memoryProperties()) {\n\n  }\n\n\n  HudMemoryStatsItem::~HudMemoryStatsItem() {\n\n  }\n\n\n  void HudMemoryStatsItem::update(dxvk::high_resolution_clock::time_point time) {\n    for (uint32_t i = 0; i < m_memory.memoryHeapCount; i++)\n      m_heaps[i] = m_device->getMemoryStats(i);\n  }\n\n\n  HudPos HudMemoryStatsItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    for (uint32_t i = 0; i < m_memory.memoryHeapCount; i++) {\n      bool isDeviceLocal = m_memory.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;\n\n      uint64_t memUsedMib = m_heaps[i].memoryUsed >> 20;\n      uint64_t memAllocatedMib = m_heaps[i].memoryAllocated >> 20;\n      uint64_t percentage = m_heaps[i].memoryBudget\n        ? (100u * m_heaps[i].memoryAllocated) / m_heaps[i].memoryBudget\n        : 0u;\n\n      std::string label = str::format(isDeviceLocal ? \"Vidmem\" : \"Sysmem\", \" heap \", i, \": \");\n      std::string text  = str::format(std::setfill(' '), std::setw(5), memAllocatedMib, \" MB (\", percentage, \"%) \",\n        std::setw(5 + (percentage < 10 ? 1 : 0) + (percentage < 100 ? 1 : 0)), memUsedMib, \" MB used\");\n\n      position.y += 16;\n      renderer.drawText(16, position, 0xff40ffffu, label);\n      renderer.drawText(16, { position.x + 168, position.y }, 0xffffffffu, text);\n\n      position.y += 4;\n    }\n\n    position.y += 4;\n    return position;\n  }\n\n\n\n\n  HudMemoryDetailsItem::HudMemoryDetailsItem(\n    const Rc<DxvkDevice>&     device,\n          HudRenderer*        renderer)\n  : m_device          (device),\n    m_pipelineLayout  (createPipelineLayout()) {\n\n  }\n\n\n  HudMemoryDetailsItem::~HudMemoryDetailsItem() {\n    auto vk = m_device->vkd();\n\n    for (const auto& p : m_pipelines) {\n      vk->vkDestroyPipeline(vk->device(), p.second.background, nullptr);\n      vk->vkDestroyPipeline(vk->device(), p.second.visualize, nullptr);\n    }\n  }\n\n\n  void HudMemoryDetailsItem::update(dxvk::high_resolution_clock::time_point time) {\n    uint64_t ticks = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate).count();\n\n    if (ticks >= UpdateInterval) {\n      m_cacheStats = m_device->getMemoryAllocationStats(m_stats);\n      m_displayCacheStats |= m_cacheStats.requestCount != 0u;\n\n      m_lastUpdate = time;\n    }\n  }\n\n\n  HudPos HudMemoryDetailsItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    // Layout, align the entire element to the bottom right.\n    int32_t x = -564;\n    int32_t y = -20;\n\n    if (m_displayCacheStats) {\n      uint32_t hitCount = m_cacheStats.requestCount - m_cacheStats.missCount;\n      uint32_t hitRate = (100 * hitCount) / std::max(m_cacheStats.requestCount, 1u);\n\n      std::string cacheStr = str::format(\"Cache: \", m_cacheStats.size >> 10, \" kB (\", hitRate, \"% hit)\");\n      renderer.drawText(14, { x, y }, 0xffffffffu, cacheStr);\n\n      y -= 24;\n    }\n\n    for (uint32_t i = m_stats.memoryTypes.size(); i; i--) {\n      const auto& type = m_stats.memoryTypes.at(i - 1);\n\n      if (!type.chunkCount)\n        continue;\n\n      // Compute layout and gather memory stats\n      DxvkMemoryStats stats = { };\n\n      int32_t w = 0;\n      int32_t h = 0;\n\n      for (uint32_t j = 0; j < type.chunkCount; j++) {\n        const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);\n        stats.memoryAllocated += chunk.capacity;\n        stats.memoryUsed += chunk.used;\n\n        int32_t chunkWidth = (chunk.pageCount + 15u) / 16u + 2;\n\n        bool separate = j && m_stats.chunks.at(type.chunkIndex + j - 1u).mapped != chunk.mapped;\n\n        if (x + w + chunkWidth > -8 || separate) {\n          w = 0;\n          h += 34;\n        }\n\n        w += chunkWidth + 6;\n      }\n\n      if (w)\n        h += 34;\n\n      y -= h;\n\n      w = 0;\n      h = 8;\n\n      // Draw individual chunks\n      for (uint32_t j = 0; j < type.chunkCount; j++) {\n        const auto& chunk = m_stats.chunks.at(type.chunkIndex + j);\n\n        // Default VRAM, blue\n        uint32_t color = 0xff804020u;\n\n        if (type.properties.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {\n          // Cached memory, green\n          color = 0xff208020u;\n        } else if (!(type.properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {\n          if (!chunk.mapped) {\n            // Fallback allocation, grey\n            color = 0xff201c18u;\n          } else {\n            // Uncached memory, red\n            color = 0xff202080u;\n          }\n        } else if (chunk.mapped) {\n          // Host-visible VRAM, yellow\n          color = 0xff208080u;\n        }\n\n        int32_t chunkWidth = (chunk.pageCount + 15u) / 16u + 2;\n\n        bool separate = j && m_stats.chunks.at(type.chunkIndex + j - 1u).mapped != chunk.mapped;\n\n        if (x + w + chunkWidth > -8 || separate) {\n          w = 0;\n          h += 34;\n        }\n\n        drawChunk({ x + w, y + h }, { chunkWidth, 24 }, color, chunk);\n\n        w += chunkWidth + 6;\n      }\n\n      // Render descriptive text\n      std::string headline = str::format(\"Mem type \", (i - 1), \" [\", type.properties.heapIndex, \"]: \",\n        type.chunkCount, \" chunk\", type.chunkCount != 1u ? \"s\" : \"\", \" (\", (stats.memoryAllocated >> 20u), \" MB, \",\n        ((stats.memoryUsed >= (1u << 20u)) ? stats.memoryUsed >> 20 : stats.memoryUsed >> 10),\n        (stats.memoryUsed >= (1u << 20u) ? \" MB\" : \" kB\"), \" used)\");\n\n      renderer.drawText(14, { x, y }, 0xffffffffu, headline);\n\n      y -= 24;\n    }\n\n    flushDraws(ctx, key, options, renderer);\n    return position;\n  }\n\n\n  void HudMemoryDetailsItem::drawChunk(\n          HudPos            pos,\n          HudPos            size,\n          uint32_t          color,\n    const DxvkMemoryChunkStats& chunk) {\n    auto& draw = m_drawInfos.emplace_back();\n    draw.x = pos.x;\n    draw.y = pos.y;\n    draw.w = size.x;\n    draw.h = size.y;\n    draw.pageMask = chunk.pageMaskOffset;\n    draw.pageCountAndActiveBit = chunk.pageCount;\n    draw.color = color;\n\n    if (chunk.active)\n      draw.pageCountAndActiveBit |= 1u << 15;\n  }\n\n\n  void HudMemoryDetailsItem::flushDraws(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer) {\n    if (m_drawInfos.empty())\n      return;\n\n    PipelinePair pipelines = getPipeline(renderer, key);\n\n    // Update relevant buffers\n    DxvkResourceBufferInfo drawDescriptor = { };\n    DxvkResourceBufferInfo dataDescriptor = { };\n\n    updateDataBuffer(ctx, drawDescriptor, dataDescriptor);\n\n    // Bind resources\n    std::array<DxvkDescriptorWrite, 2u> descriptors = { };\n    descriptors[0u].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n    descriptors[0u].buffer = drawDescriptor;\n\n    descriptors[1u].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n    descriptors[1u].buffer = dataDescriptor;\n\n    HudPushConstants pushConstants = renderer.getPushConstants();\n\n    ctx->bindResources(DxvkCmdBuffer::ExecBuffer,\n      m_pipelineLayout, descriptors.size(), descriptors.data(),\n      sizeof(pushConstants), &pushConstants);\n\n    // Draw background first, then the actual usage info. The pipeline\n    // layout is the same for both pipelines, so don't rebind resources.\n    ctx->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.background);\n\n    ctx->cmdDraw(4, m_drawInfos.size(), 0, 0);\n\n    ctx->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.visualize);\n    ctx->cmdDraw(4, m_drawInfos.size(), 0, 0);\n\n    // Track data buffer lifetime\n    ctx->track(m_dataBuffer, DxvkAccess::Read);\n\n    m_drawInfos.clear();\n  }\n\n\n  void HudMemoryDetailsItem::updateDataBuffer(\n    const Rc<DxvkCommandList>&ctx,\n          DxvkResourceBufferInfo& drawDescriptor,\n          DxvkResourceBufferInfo& dataDescriptor) {\n    size_t drawInfoSize = m_drawInfos.size() * sizeof(DrawInfo);\n    size_t drawInfoSizeAligned = align(drawInfoSize, 256u);\n\n    size_t chunkDataSize = m_stats.pageMasks.size() * sizeof(uint32_t);\n    size_t chunkDataSizeAligned = align(chunkDataSize, 256u);\n\n    size_t bufferSize = align(drawInfoSizeAligned + chunkDataSizeAligned, 2048u);\n\n    if (!m_dataBuffer || m_dataBuffer->info().size < bufferSize) {\n      DxvkBufferCreateInfo bufferInfo;\n      bufferInfo.size = bufferSize;\n      bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n      bufferInfo.access = VK_ACCESS_SHADER_READ_BIT;\n      bufferInfo.stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n      bufferInfo.debugName = \"HUD memory data\";\n\n      m_dataBuffer = m_device->createBuffer(bufferInfo,\n        VK_MEMORY_HEAP_DEVICE_LOCAL_BIT |\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n        VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n    } else {\n      // Ensure we can update the buffer without overriding live data\n      auto allocation = m_dataBuffer->assignStorage(m_dataBuffer->allocateStorage());\n      ctx->track(std::move(allocation));\n    }\n\n    // Update draw infos and pad unused area with zeroes\n    std::memcpy(m_dataBuffer->mapPtr(0), m_drawInfos.data(), drawInfoSize);\n    std::memset(m_dataBuffer->mapPtr(drawInfoSize), 0, drawInfoSizeAligned - drawInfoSize);\n\n    // Update chunk data and pad with zeroes\n    std::memcpy(m_dataBuffer->mapPtr(drawInfoSizeAligned), m_stats.pageMasks.data(), chunkDataSize);\n    std::memset(m_dataBuffer->mapPtr(drawInfoSizeAligned + chunkDataSize), 0, chunkDataSizeAligned - chunkDataSize);\n\n    // Write back descriptors\n    drawDescriptor = m_dataBuffer->getSliceInfo(0u, drawInfoSizeAligned);\n    dataDescriptor = m_dataBuffer->getSliceInfo(drawInfoSizeAligned, chunkDataSizeAligned);\n  }\n\n\n  const DxvkPipelineLayout* HudMemoryDetailsItem::createPipelineLayout() {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 2> bindings = {{\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT   },\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT },\n    }};\n\n    return m_device->createBuiltInPipelineLayout(0u,\n      VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,\n      sizeof(HudPushConstants), bindings.size(), bindings.data());\n  }\n\n\n  HudMemoryDetailsItem::PipelinePair HudMemoryDetailsItem::createPipeline(\n          HudRenderer&        renderer,\n    const HudPipelineKey&     key) {\n    auto vk = m_device->vkd();\n\n    HudSpecConstants specConstants = renderer.getSpecConstants(key);\n    VkSpecializationInfo specInfo = renderer.getSpecInfo(&specConstants);\n\n    PipelinePair pipelines = { };\n\n    VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };\n    iaState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;\n\n    VkPipelineColorBlendAttachmentState cbAttachment = { };\n    cbAttachment.blendEnable = VK_TRUE;\n    cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;\n    cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n    cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;\n    cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;\n    cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n    cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;\n    cbAttachment.colorWriteMask =\n      VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |\n      VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n\n    util::DxvkBuiltInGraphicsState state = { };\n    state.iaState = &iaState;\n    state.colorFormat = key.format;\n    state.cbAttachment = &cbAttachment;\n\n    state.vs = util::DxvkBuiltInShaderStage(hud_chunk_vert_background, nullptr);\n    state.fs = util::DxvkBuiltInShaderStage(hud_chunk_frag_background, &specInfo);\n\n    pipelines.background = m_device->createBuiltInGraphicsPipeline(m_pipelineLayout, state);\n\n    state.vs = util::DxvkBuiltInShaderStage(hud_chunk_vert_visualize, nullptr);\n    state.fs = util::DxvkBuiltInShaderStage(hud_chunk_frag_visualize, &specInfo);\n\n    pipelines.visualize = m_device->createBuiltInGraphicsPipeline(m_pipelineLayout, state);\n    return pipelines;\n  }\n\n\n  HudMemoryDetailsItem::PipelinePair HudMemoryDetailsItem::getPipeline(\n          HudRenderer&        renderer,\n    const HudPipelineKey&     key) {\n    auto entry = m_pipelines.find(key);\n\n    if (entry != m_pipelines.end())\n      return entry->second;\n\n    PipelinePair pipeline = createPipeline(renderer, key);\n    m_pipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n\n\n\n  HudCsThreadItem::HudCsThreadItem(const Rc<DxvkDevice>& device)\n  : m_device(device) {\n\n  }\n\n\n  HudCsThreadItem::~HudCsThreadItem() {\n\n  }\n\n\n  void HudCsThreadItem::update(dxvk::high_resolution_clock::time_point time) {\n    uint64_t ticks = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate).count();\n\n    // Capture the maximum here since it's more useful to\n    // identify stutters than using any sort of average\n    DxvkStatCounters counters = m_device->getStatCounters();\n    uint64_t currCsSyncCount = counters.getCtr(DxvkStatCounter::CsSyncCount);\n    uint64_t currCsSyncTicks = counters.getCtr(DxvkStatCounter::CsSyncTicks);\n\n    m_maxCsSyncCount = std::max(m_maxCsSyncCount, currCsSyncCount - m_prevCsSyncCount);\n    m_maxCsSyncTicks = std::max(m_maxCsSyncTicks, currCsSyncTicks - m_prevCsSyncTicks);\n\n    m_prevCsSyncCount = currCsSyncCount;\n    m_prevCsSyncTicks = currCsSyncTicks;\n\n    m_updateCount++;\n\n    if (ticks >= UpdateInterval) {\n      uint64_t currCsChunks = counters.getCtr(DxvkStatCounter::CsChunkCount);\n      uint64_t diffCsChunks = (currCsChunks - m_prevCsChunks) / m_updateCount;\n      m_prevCsChunks = currCsChunks;\n\n      uint64_t syncTicks = m_maxCsSyncTicks / 100;\n\n      m_csChunkString = str::format(diffCsChunks);\n      m_csSyncString = m_maxCsSyncCount\n        ? str::format(m_maxCsSyncCount, \" (\", (syncTicks / 10), \".\", (syncTicks % 10), \" ms)\")\n        : str::format(m_maxCsSyncCount);\n\n      uint64_t currCsIdleTicks = counters.getCtr(DxvkStatCounter::CsIdleTicks);\n\n      m_diffCsIdleTicks = currCsIdleTicks - m_prevCsIdleTicks;\n      m_prevCsIdleTicks = currCsIdleTicks;\n\n      uint64_t busyTicks = ticks > m_diffCsIdleTicks\n        ? uint64_t(ticks - m_diffCsIdleTicks)\n        : uint64_t(0);\n\n      m_csLoadString = str::format((100 * busyTicks) / ticks, \"%\");\n\n      m_maxCsSyncCount = 0;\n      m_maxCsSyncTicks = 0;\n\n      m_updateCount = 0;\n      m_lastUpdate = time;\n    }\n  }\n\n\n  HudPos HudCsThreadItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n    renderer.drawText(16, position, 0xff40ff40, \"CS chunks:\");\n    renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csChunkString);\n\n    position.y += 20;\n    renderer.drawText(16, position, 0xff40ff40, \"CS syncs:\");\n    renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csSyncString);\n\n    position.y += 20;\n    renderer.drawText(16, position, 0xff40ff40, \"CS load:\");\n    renderer.drawText(16, { position.x + 132, position.y }, 0xffffffffu, m_csLoadString);\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudGpuLoadItem::HudGpuLoadItem(const Rc<DxvkDevice>& device)\n  : m_device(device) {\n\n  }\n\n\n  HudGpuLoadItem::~HudGpuLoadItem() {\n\n  }\n\n\n  void HudGpuLoadItem::update(dxvk::high_resolution_clock::time_point time) {\n    uint64_t ticks = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate).count();\n\n    if (ticks >= UpdateInterval) {\n      DxvkStatCounters counters = m_device->getStatCounters();\n      uint64_t currGpuIdleTicks = counters.getCtr(DxvkStatCounter::GpuIdleTicks);\n\n      m_diffGpuIdleTicks = currGpuIdleTicks - m_prevGpuIdleTicks;\n      m_prevGpuIdleTicks = currGpuIdleTicks;\n\n      uint64_t busyTicks = ticks > m_diffGpuIdleTicks\n        ? uint64_t(ticks - m_diffGpuIdleTicks)\n        : uint64_t(0);\n\n      m_gpuLoadString = str::format((100 * busyTicks) / ticks, \"%\");\n      m_lastUpdate = time;\n    }\n  }\n\n\n  HudPos HudGpuLoadItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    position.y += 16;\n    renderer.drawText(16, position, 0xff408040u, \"GPU:\");\n    renderer.drawText(16, { position.x + 60, position.y }, 0xffffffffu, m_gpuLoadString);\n\n    position.y += 8;\n    return position;\n  }\n\n\n  HudCompilerActivityItem::HudCompilerActivityItem(const Rc<DxvkDevice>& device)\n  : m_device(device) {\n\n  }\n\n\n  HudCompilerActivityItem::~HudCompilerActivityItem() {\n\n  }\n\n\n  void HudCompilerActivityItem::update(dxvk::high_resolution_clock::time_point time) {\n    DxvkStatCounters counters = m_device->getStatCounters();\n\n    m_tasksDone = counters.getCtr(DxvkStatCounter::PipeTasksDone);\n    m_tasksTotal = counters.getCtr(DxvkStatCounter::PipeTasksTotal);\n\n    bool doShow = m_tasksDone < m_tasksTotal;\n\n    if (!doShow)\n      m_timeDone = time;\n\n    if (!m_show) {\n      m_timeShown = time;\n      m_showPercentage = false;\n    } else {\n      auto durationShown = std::chrono::duration_cast<std::chrono::milliseconds>(time - m_timeShown);\n      auto durationWorking = std::chrono::duration_cast<std::chrono::milliseconds>(time - m_timeDone);\n\n      if (!doShow) {\n        m_offset = m_tasksTotal;\n\n        // Ensure the item stays up long enough to be legible\n        doShow = durationShown.count() <= MinShowDuration;\n      }\n\n      if (!m_showPercentage) {\n        // Don't show percentage if it's just going to be stuck at 99%\n        // because the workers are not being fed tasks fast enough\n        m_showPercentage = durationWorking.count() >= (MinShowDuration / 5)\n                        && (computePercentage() < 50);\n      }\n    }\n\n    m_show = doShow;\n  }\n\n\n  HudPos HudCompilerActivityItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    if (m_show) {\n      std::string string = \"Compiling shaders...\";\n\n      if (m_showPercentage)\n        string = str::format(string, \" (\", computePercentage(), \"%)\");\n\n      renderer.drawText(16, { position.x, -20 }, 0xffffffffu, string);\n    }\n\n    return position;\n  }\n\n\n  uint32_t HudCompilerActivityItem::computePercentage() const {\n    if (m_offset == m_tasksTotal)\n      return 100;\n\n    return (uint32_t(m_tasksDone - m_offset) * 100)\n         / (uint32_t(m_tasksTotal - m_offset));\n  }\n\n\n\n  HudLatencyItem::HudLatencyItem() {\n\n  }\n\n\n  HudLatencyItem::~HudLatencyItem() {\n\n  }\n\n\n  void HudLatencyItem::accumulateStats(const DxvkLatencyStats& stats) {\n    std::lock_guard lock(m_mutex);\n\n    if (stats.frameLatency.count()) {\n      m_accumStats.frameLatency += stats.frameLatency;\n      m_accumStats.sleepDuration += stats.sleepDuration;\n\n      m_accumFrames += 1u;\n    } else {\n      m_accumStats = { };\n      m_accumFrames = 0u;\n    }\n  }\n\n\n  void HudLatencyItem::update(dxvk::high_resolution_clock::time_point time) {\n    uint64_t ticks = std::chrono::duration_cast<std::chrono::microseconds>(time - m_lastUpdate).count();\n\n    if (ticks >= UpdateInterval) {\n      std::lock_guard lock(m_mutex);\n\n      if (m_accumFrames) {\n        uint32_t latency = (m_accumStats.frameLatency / m_accumFrames).count() / 100u;\n        uint32_t sleep = (m_accumStats.sleepDuration / m_accumFrames).count() / 100u;\n\n        m_latencyString = str::format(latency / 10, \".\", latency % 10, \" ms\");\n        m_sleepString = str::format(sleep / 10, \".\", sleep % 10, \" ms\");\n\n        m_accumStats = { };\n        m_accumFrames = 0u;\n\n        m_invalidUpdates = 0u;\n      } else {\n        m_latencyString = \"--\";\n        m_sleepString = \"--\";\n\n        if (m_invalidUpdates < MaxInvalidUpdates)\n          m_invalidUpdates += 1u;\n      }\n\n      m_lastUpdate = time;\n    }\n  }\n\n\n  HudPos HudLatencyItem::render(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const HudOptions&         options,\n          HudRenderer&        renderer,\n          HudPos              position) {\n    if (m_invalidUpdates >= MaxInvalidUpdates)\n      return position;\n\n    position.y += 16;\n\n    renderer.drawText(16, position, 0xffff60a0u, \"Latency: \");\n    renderer.drawText(16, { position.x + 108, position.y }, 0xffffffffu, m_latencyString);\n\n    position.y += 20;\n\n    renderer.drawText(16, position, 0xffff60a0u, \"Sleep: \");\n    renderer.drawText(16, { position.x + 108, position.y }, 0xffffffffu, m_sleepString);\n\n    position.y += 8;\n    return position;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/hud/dxvk_hud_item.h",
    "content": "#pragma once\n\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"../../util/util_time.h\"\n\n#include \"../dxvk_gpu_query.h\"\n\n#include \"dxvk_hud_renderer.h\"\n\nnamespace dxvk::hud {\n\n  /**\n   * \\brief HUD item\n   *\n   * A single named item in the HUD that\n   * can be enabled by the user.\n   */\n  class HudItem : public RcObject {\n\n  public:\n\n    virtual ~HudItem();\n\n    /**\n     * \\brief Updates the HUD item\n     * \\param [in] time Current time\n     */\n    virtual void update(\n            dxvk::high_resolution_clock::time_point time);\n\n    /**\n     * \\brief Renders the HUD\n     *\n     * \\param [in] ctx Raw context objects\n     * \\param [in] options HUD options\n     * \\param [in] renderer HUD renderer for text rendering\n     * \\param [in] position Base offset\n     * \\returns Base offset for next item\n     */\n    virtual HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position) = 0;\n\n  };\n\n\n  /**\n   * \\brief HUD item set\n   *\n   * Manages HUD items.\n   */\n  class HudItemSet {\n\n  public:\n\n    HudItemSet(const Rc<DxvkDevice>& device);\n\n    ~HudItemSet();\n\n    /**\n     * \\brief Updates the HUD\n     * Updates all enabled HUD items.\n     */\n    void update();\n\n    /**\n     * \\brief Renders the HUD\n     *\n     * \\param [in] renderer HUD renderer\n     * \\returns Base offset for next item\n     */\n    void render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer);\n\n    /**\n     * \\brief Checks whether the item set is empty\n     * \\returns \\c true if there are no items\n     */\n    bool empty() const {\n      return m_items.empty();\n    }\n\n    /**\n     * \\brief Creates a HUD item if enabled\n     *\n     * \\tparam T The HUD item type\n     * \\param [in] name HUD item name\n     * \\param [in] at Position at which to insert the item\n     * \\param [in] args Constructor arguments\n     */\n    template<typename T, typename... Args>\n    Rc<T> add(const char* name, int32_t at, Args... args) {\n      bool enable = m_enableFull;\n\n      if (!enable) {\n        auto entry = m_enabled.find(name);\n        enable = entry != m_enabled.end();\n      }\n\n      if (at < 0 || at > int32_t(m_items.size()))\n        at = m_items.size();\n\n      Rc<T> item;\n\n      if (enable) {\n        item = new T(std::forward<Args>(args)...);\n        m_items.insert(m_items.begin() + at, item);\n      }\n\n      return item;\n    }\n\n    template<typename T>\n    T getOption(const char *option, T fallback) {\n      auto entry = m_options.find(option);\n      if (entry == m_options.end())\n        return fallback;\n\n      T value = fallback;\n      parseOption(entry->second, value);\n      return value;\n    }\n\n  private:\n\n    bool                                          m_enableFull = false;\n    std::unordered_set<std::string>               m_enabled;\n    std::unordered_map<std::string, std::string>  m_options;\n    std::vector<Rc<HudItem>>                      m_items;\n\n    static void parseOption(const std::string& str, float& value);\n\n  };\n\n\n  /**\n   * \\brief HUD item to display DXVK version\n   */\n  class HudVersionItem : public HudItem {\n\n  public:\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  };\n\n\n  /**\n   * \\brief HUD item to display the client API\n   */\n  class HudClientApiItem : public HudItem {\n\n  public:\n\n    HudClientApiItem(std::string api);\n\n    ~HudClientApiItem();\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    sync::Spinlock  m_mutex;\n    std::string     m_api;\n\n  };\n\n\n  /**\n   * \\brief HUD item to display device info\n   */\n  class HudDeviceInfoItem : public HudItem {\n\n  public:\n\n    HudDeviceInfoItem(const Rc<DxvkDevice>& device);\n\n    ~HudDeviceInfoItem();\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    std::string m_deviceName;\n    std::string m_driverName;\n    std::string m_driverVer;\n\n  };\n\n\n  /**\n   * \\brief HUD item to display the frame rate\n   */\n  class HudFpsItem : public HudItem {\n    constexpr static int64_t UpdateInterval = 500'000;\n  public:\n\n    HudFpsItem();\n\n    ~HudFpsItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    uint32_t                                m_frameCount = 0;\n    dxvk::high_resolution_clock::time_point m_lastUpdate\n      = dxvk::high_resolution_clock::now();\n\n    std::string m_frameRate;\n\n  };\n\n\n  /**\n   * \\brief HUD item to display the frame rate\n   */\n  class HudFrameTimeItem : public HudItem {\n    constexpr static size_t NumDataPoints = 420u;\n    constexpr static size_t NumTextDraws = 2u;\n  public:\n\n    HudFrameTimeItem(\n      const Rc<DxvkDevice>&     device,\n            HudRenderer*        renderer);\n\n    ~HudFrameTimeItem();\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    struct ComputePushConstants {\n      float msPerTick;\n      uint32_t dataPoint;\n      int16_t textPosMinX;\n      int16_t textPosMinY;\n      int16_t textPosMaxX;\n      int16_t textPosMaxY;\n    };\n\n    struct RenderPushConstants {\n      HudPushConstants hud;\n      int16_t x;\n      int16_t y;\n      int16_t w;\n      int16_t h;\n      uint32_t frameIndex;\n    };\n\n    struct BufferLayout {\n      size_t timestampSize;\n      size_t drawInfoOffset;\n      size_t drawInfoSize;\n      size_t drawParamOffset;\n      size_t drawParamSize;\n      size_t textOffset;\n      size_t textSize;\n      size_t totalSize;\n    };\n\n    Rc<DxvkDevice>            m_device;\n    Rc<DxvkBuffer>            m_gpuBuffer;\n    Rc<DxvkBufferView>        m_textWrView;\n    Rc<DxvkBufferView>        m_textRdView;\n    Rc<DxvkGpuQuery>          m_query;\n\n    const DxvkPipelineLayout* m_computePipelineLayout = nullptr;\n    const DxvkPipelineLayout* m_gfxPipelineLayout     = nullptr;\n\n    VkPipeline                m_computePipeline = VK_NULL_HANDLE;\n\n    std::unordered_map<HudPipelineKey,\n      VkPipeline, DxvkHash, DxvkEq> m_gfxPipelines;\n\n    uint32_t                  m_nextDataPoint = 0u;\n\n    void processFrameTimes(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n            HudRenderer&        renderer,\n            uint32_t            dataPoint,\n            HudPos              minPos,\n            HudPos              maxPos);\n\n    void drawFrameTimeGraph(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n            HudRenderer&        renderer,\n            uint32_t            dataPoint,\n            HudPos              graphPos,\n            HudPos              graphSize);\n\n    void createResources(\n      const Rc<DxvkCommandList>&ctx);\n\n    void createComputePipeline(\n          HudRenderer&          renderer);\n\n    const DxvkPipelineLayout* createPipelineLayout();\n\n    VkPipeline getPipeline(\n            HudRenderer&        renderer,\n      const HudPipelineKey&     key);\n\n    VkPipeline createPipeline(\n            HudRenderer&        renderer,\n      const HudPipelineKey&     key);\n\n    static BufferLayout computeBufferLayout();\n\n  };\n\n\n  /**\n   * \\brief HUD item to display queue statistics\n   */\n  class HudSubmissionStatsItem : public HudItem {\n    constexpr static int64_t UpdateInterval = 500'000;\n  public:\n\n    HudSubmissionStatsItem(const Rc<DxvkDevice>& device);\n\n    ~HudSubmissionStatsItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    Rc<DxvkDevice>  m_device;\n\n    uint64_t        m_prevSubmitCount = 0;\n    uint64_t        m_prevSyncCount   = 0;\n    uint64_t        m_prevSyncTicks   = 0;\n\n    uint64_t        m_maxSubmitCount  = 0;\n    uint64_t        m_maxSyncCount    = 0;\n    uint64_t        m_maxSyncTicks    = 0;\n\n    std::string     m_submitString;\n    std::string     m_syncString;\n\n    dxvk::high_resolution_clock::time_point m_lastUpdate\n      = dxvk::high_resolution_clock::now();\n\n  };\n\n\n  /**\n   * \\brief HUD item to display draw call counts\n   */\n  class HudDrawCallStatsItem : public HudItem {\n    constexpr static int64_t UpdateInterval = 500'000;\n  public:\n\n    HudDrawCallStatsItem(const Rc<DxvkDevice>& device);\n\n    ~HudDrawCallStatsItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    Rc<DxvkDevice>    m_device;\n\n    DxvkStatCounters  m_prevCounters;\n\n    uint64_t          m_drawCallCount   = 0;\n    uint64_t          m_drawCount       = 0;\n    uint64_t          m_dispatchCount   = 0;\n    uint64_t          m_renderPassCount = 0;\n    uint64_t          m_barrierCount    = 0;\n\n    dxvk::high_resolution_clock::time_point m_lastUpdate\n      = dxvk::high_resolution_clock::now();\n\n  };\n\n\n  /**\n   * \\brief HUD item to display pipeline counts\n   */\n  class HudPipelineStatsItem : public HudItem {\n\n  public:\n\n    HudPipelineStatsItem(const Rc<DxvkDevice>& device);\n\n    ~HudPipelineStatsItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    Rc<DxvkDevice> m_device;\n\n    uint64_t m_graphicsPipelines  = 0;\n    uint64_t m_graphicsLibraries  = 0;\n    uint64_t m_computePipelines   = 0;\n\n  };\n\n\n  /**\n   * \\brief HUD item to display descriptor stats\n   */\n  class HudDescriptorStatsItem : public HudItem {\n    constexpr static int64_t UpdateInterval = 500'000;\n  public:\n\n    HudDescriptorStatsItem(const Rc<DxvkDevice>& device);\n\n    ~HudDescriptorStatsItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    Rc<DxvkDevice> m_device;\n\n    uint64_t m_descriptorPoolCount = 0;\n    uint64_t m_descriptorSetCount = 0;\n    uint64_t m_descriptorSetCountDisplay = 0;\n    uint64_t m_descriptorSetCountMax = 0;\n\n    uint64_t m_descriptorHeapCount = 0;\n    uint64_t m_descriptorHeapAlloc = 0;\n    uint64_t m_descriptorHeapUsed  = 0;\n    uint64_t m_descriptorHeapMax   = 0;\n    uint64_t m_descriptorHeapPrev  = 0;\n\n    uint64_t m_copyThreadBusyTicks = 0;\n    uint32_t m_copyThreadLoad      = 0u;\n\n    high_resolution_clock::time_point m_lastUpdate = { };\n\n  };\n\n\n  /**\n   * \\brief HUD item to display memory usage\n   */\n  class HudMemoryStatsItem : public HudItem {\n\n  public:\n\n    HudMemoryStatsItem(const Rc<DxvkDevice>& device);\n\n    ~HudMemoryStatsItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    Rc<DxvkDevice>                    m_device;\n    VkPhysicalDeviceMemoryProperties  m_memory;\n    DxvkMemoryStats                   m_heaps[VK_MAX_MEMORY_HEAPS];\n\n  };\n\n\n  /**\n   * \\brief HUD item to display detailed memory allocation info\n   */\n  class HudMemoryDetailsItem : public HudItem {\n    constexpr static int64_t UpdateInterval = 500'000;\n  public:\n\n    HudMemoryDetailsItem(\n      const Rc<DxvkDevice>&     device,\n            HudRenderer*        renderer);\n\n    ~HudMemoryDetailsItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    struct DrawInfo {\n      int16_t x;\n      int16_t y;\n      int16_t w;\n      int16_t h;\n      uint16_t pageMask;\n      uint16_t pageCountAndActiveBit;\n      uint32_t color;\n    };\n\n    struct PipelinePair {\n      VkPipeline background = VK_NULL_HANDLE;\n      VkPipeline visualize = VK_NULL_HANDLE;\n    };\n\n    Rc<DxvkDevice>                    m_device;\n    DxvkMemoryAllocationStats         m_stats;\n    DxvkSharedAllocationCacheStats    m_cacheStats;\n\n    high_resolution_clock::time_point m_lastUpdate = { };\n\n    bool                      m_displayCacheStats = false;\n\n    Rc<DxvkBuffer>            m_dataBuffer;\n    std::vector<DrawInfo>     m_drawInfos;\n\n    const DxvkPipelineLayout* m_pipelineLayout = nullptr;\n\n    std::unordered_map<HudPipelineKey,\n      PipelinePair, DxvkHash, DxvkEq> m_pipelines;\n\n    void drawChunk(\n            HudPos              pos,\n            HudPos              size,\n            uint32_t            color,\n      const DxvkMemoryChunkStats& chunk);\n\n    void flushDraws(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer);\n\n    void updateDataBuffer(\n      const Rc<DxvkCommandList>&ctx,\n            DxvkResourceBufferInfo& drawDescriptor,\n            DxvkResourceBufferInfo& dataDescriptor);\n\n    const DxvkPipelineLayout* createPipelineLayout();\n\n    PipelinePair createPipeline(\n            HudRenderer&        renderer,\n      const HudPipelineKey&     key);\n\n    PipelinePair getPipeline(\n            HudRenderer&        renderer,\n      const HudPipelineKey&     key);\n\n  };\n\n\n  /**\n   * \\brief HUD item to display CS thread statistics\n   */\n  class HudCsThreadItem : public HudItem {\n    constexpr static int64_t UpdateInterval = 500'000;\n  public:\n\n    HudCsThreadItem(const Rc<DxvkDevice>& device);\n\n    ~HudCsThreadItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    Rc<DxvkDevice> m_device;\n\n    uint64_t m_prevCsSyncCount  = 0;\n    uint64_t m_prevCsSyncTicks  = 0;\n    uint64_t m_prevCsChunks     = 0;\n    uint64_t m_prevCsIdleTicks = 0;\n\n    uint64_t m_maxCsSyncCount   = 0;\n    uint64_t m_maxCsSyncTicks   = 0;\n\n    uint64_t m_diffCsIdleTicks = 0;\n\n    uint64_t m_updateCount      = 0;\n\n    std::string m_csSyncString;\n    std::string m_csChunkString;\n    std::string m_csLoadString;\n\n    dxvk::high_resolution_clock::time_point m_lastUpdate\n      = dxvk::high_resolution_clock::now();\n\n  };\n\n\n  /**\n   * \\brief HUD item to display GPU load\n   */\n  class HudGpuLoadItem : public HudItem {\n    constexpr static int64_t UpdateInterval = 500'000;\n  public:\n\n    HudGpuLoadItem(const Rc<DxvkDevice>& device);\n\n    ~HudGpuLoadItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    Rc<DxvkDevice> m_device;\n\n    uint64_t m_prevGpuIdleTicks = 0;\n    uint64_t m_diffGpuIdleTicks = 0;\n\n    std::string m_gpuLoadString;\n\n    dxvk::high_resolution_clock::time_point m_lastUpdate\n      = dxvk::high_resolution_clock::now();\n\n  };\n\n\n  /**\n   * \\brief HUD item to display pipeline compiler activity\n   */\n  class HudCompilerActivityItem : public HudItem {\n    constexpr static int64_t MinShowDuration = 1500;\n  public:\n\n    HudCompilerActivityItem(const Rc<DxvkDevice>& device);\n\n    ~HudCompilerActivityItem();\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    Rc<DxvkDevice> m_device;\n\n    bool m_show           = false;\n    bool m_showPercentage = false;\n\n    uint64_t m_tasksDone    = 0ull;\n    uint64_t m_tasksTotal   = 0ull;\n    uint64_t m_offset       = 0ull;\n\n    dxvk::high_resolution_clock::time_point m_timeShown = dxvk::high_resolution_clock::now();\n    dxvk::high_resolution_clock::time_point m_timeDone = dxvk::high_resolution_clock::now();\n\n    uint32_t computePercentage() const;\n\n  };\n\n\n  /**\n   * \\brief Frame latency item\n   */\n  class HudLatencyItem : public HudItem {\n    constexpr static int64_t UpdateInterval = 500'000;\n\n    constexpr static uint32_t MaxInvalidUpdates = 20u;\n  public:\n\n    HudLatencyItem();\n\n    ~HudLatencyItem();\n\n    void accumulateStats(const DxvkLatencyStats& stats);\n\n    void update(dxvk::high_resolution_clock::time_point time);\n\n    HudPos render(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const HudOptions&         options,\n            HudRenderer&        renderer,\n            HudPos              position);\n\n  private:\n\n    sync::Spinlock      m_mutex;\n\n    DxvkLatencyStats    m_accumStats = { };\n    uint32_t            m_accumFrames = 0u;\n\n    uint32_t            m_invalidUpdates = MaxInvalidUpdates;\n\n    std::string         m_latencyString;\n    std::string         m_sleepString;\n\n    dxvk::high_resolution_clock::time_point m_lastUpdate\n      = dxvk::high_resolution_clock::now();\n\n  };\n\n}\n"
  },
  {
    "path": "src/dxvk/hud/dxvk_hud_renderer.cpp",
    "content": "#include \"dxvk_hud_renderer.h\"\n\n#include <hud_text_frag.h>\n#include <hud_text_vert.h>\n\nnamespace dxvk::hud {\n  \n  struct HudGlyphGpuData {\n    int16_t x;\n    int16_t y;\n    int16_t w;\n    int16_t h;\n    int16_t originX;\n    int16_t originY;\n  };\n\n\n  struct HudFontGpuData {\n    float size;\n    float advance;\n    uint32_t padding[2];\n    HudGlyphGpuData glyphs[256];\n  };\n\n\n\n  static const std::array<VkSpecializationMapEntry, 2> HudSpecConstantMap = {{\n    { 0, offsetof(HudSpecConstants, dstSpace),  sizeof(VkColorSpaceKHR) },\n    { 1, offsetof(HudSpecConstants, dstIsSrgb), sizeof(VkBool32) },\n  }};\n\n\n\n  HudRenderer::HudRenderer(const Rc<DxvkDevice>& device)\n  : m_device              (device),\n    m_textPipelineLayout  (createPipelineLayout()) {\n\n  }\n  \n  \n  HudRenderer::~HudRenderer() {\n    auto vk = m_device->vkd();\n\n    for (const auto& p : m_textPipelines)\n      vk->vkDestroyPipeline(vk->device(), p.second, nullptr);\n  }\n  \n  \n  void HudRenderer::beginFrame(\n    const Rc<DxvkCommandList>&ctx,\n    const Rc<DxvkImageView>&  dstView,\n    const HudOptions&         options) {\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture))) {\n      ctx->cmdBeginDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer,\n        vk::makeLabel(0xf0c0dc, \"HUD\"));\n    }\n\n    if (!m_fontTextureView) {\n      createFontResources();\n      uploadFontResources(ctx);\n    }\n\n    VkExtent3D extent = dstView->mipLevelExtent(0u);\n\n    m_pushConstants.surfaceSize = { extent.width, extent.height };\n    m_pushConstants.opacity = options.opacity;\n    m_pushConstants.scale = options.scale;\n    m_pushConstants.sampler = m_fontSampler->getDescriptor().samplerIndex;\n\n    VkViewport viewport = { };\n    viewport.x = 0.0f;\n    viewport.y = 0.0f;\n    viewport.width = float(extent.width);\n    viewport.height = float(extent.height);\n    viewport.minDepth = 0.0f;\n    viewport.maxDepth = 1.0f;\n\n    VkRect2D scissor = { };\n    scissor.offset = { 0u, 0u };\n    scissor.extent = { extent.width, extent.height };\n\n    ctx->cmdSetViewport(1, &viewport);\n    ctx->cmdSetScissor(1, &scissor);\n  }\n  \n  \n  void HudRenderer::endFrame(\n    const Rc<DxvkCommandList>&ctx) {\n    if (unlikely(m_device->debugFlags().test(DxvkDebugFlag::Capture)))\n      ctx->cmdEndDebugUtilsLabel(DxvkCmdBuffer::ExecBuffer);\n  }\n\n\n  void HudRenderer::drawText(\n          uint32_t            size,\n          HudPos              pos,\n          uint32_t            color,\n    const std::string&        text) {\n    if (text.empty())\n      return;\n\n    auto& draw = m_textDraws.emplace_back();\n    draw.textOffset = m_textData.size();\n    draw.textLength = text.size();\n    draw.fontSize = size;\n    draw.posX = pos.x;\n    draw.posY = pos.y;\n    draw.color = color;\n\n    m_textData.resize(draw.textOffset + draw.textLength);\n    std::memcpy(&m_textData[draw.textOffset], text.data(), draw.textLength);\n  }\n\n\n  void HudRenderer::flushDraws(\n    const Rc<DxvkCommandList>&ctx,\n    const Rc<DxvkImageView>&  dstView,\n    const HudOptions&         options) {\n    if (m_textDraws.empty())\n      return;\n\n    // Align text size so that we're guaranteed to be able to put draw\n    // parameters where we want, and can also upload data without\n    // running into perf issues due to incomplete cache lines.\n    size_t textSizeAligned = align(m_textData.size(), 256u);\n    m_textData.resize(textSizeAligned);\n\n    // We'll use indirect draws and then just use aligned subsections\n    // of the data buffer to write our draw parameters\n    size_t drawInfoSize = align(m_textDraws.size() * sizeof(HudTextDrawInfo), 256u);\n    size_t drawArgsSize = align(m_textDraws.size() * sizeof(VkDrawIndirectCommand), 256u);\n\n    // Align buffer size to something large so we don't recreate it all the time\n    size_t bufferSize = align(textSizeAligned + drawInfoSize + drawArgsSize, 2048u);\n\n    if (!m_textBuffer || m_textBuffer->info().size < bufferSize) {\n      DxvkBufferCreateInfo textBufferInfo = { };\n      textBufferInfo.size = bufferSize;\n      textBufferInfo.usage = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT\n                           | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n                           | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n      textBufferInfo.stages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT\n                            | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;\n      textBufferInfo.access = VK_ACCESS_SHADER_READ_BIT\n                            | VK_ACCESS_INDIRECT_COMMAND_READ_BIT;\n      textBufferInfo.debugName = \"HUD text buffer\";\n\n      m_textBuffer = m_device->createBuffer(textBufferInfo,\n        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n        VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n\n      DxvkBufferViewKey textViewInfo = { };\n      textViewInfo.format = VK_FORMAT_R8_UINT;\n      textViewInfo.offset = 0u;\n      textViewInfo.size = bufferSize;\n      textViewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;\n\n      m_textBufferView = m_textBuffer->createView(textViewInfo);\n    } else {\n      // Discard and invalidate buffer so we can safely update it\n      auto storage = m_textBuffer->assignStorage(Rc<DxvkResourceAllocation>(m_textBuffer->allocateStorage()));\n      ctx->track(std::move(storage));\n    }\n\n    // Upload aligned text data in such a way that we write full cache lines\n    std::memcpy(m_textBuffer->mapPtr(0), m_textData.data(), textSizeAligned);\n\n    // Upload draw parameters and pad aligned region with zeroes\n    size_t drawInfoCopySize = m_textDraws.size() * sizeof(HudTextDrawInfo);\n    std::memcpy(m_textBuffer->mapPtr(textSizeAligned), m_textDraws.data(), drawInfoCopySize);\n    std::memset(m_textBuffer->mapPtr(textSizeAligned + drawInfoCopySize), 0, drawInfoSize - drawInfoCopySize);\n\n    // Emit indirect draw parameters\n    size_t drawArgWriteSize = m_textDraws.size() * sizeof(VkDrawIndirectCommand);\n    size_t drawArgOffset = textSizeAligned + drawInfoSize;\n\n    auto drawArgs = reinterpret_cast<VkDrawIndirectCommand*>(m_textBuffer->mapPtr(drawArgOffset));\n\n    for (size_t i = 0; i < m_textDraws.size(); i++) {\n      drawArgs[i].vertexCount = 6u * m_textDraws[i].textLength;\n      drawArgs[i].instanceCount = 1u;\n      drawArgs[i].firstVertex = 0u;\n      drawArgs[i].firstInstance = 0u;\n    }\n\n    std::memset(m_textBuffer->mapPtr(drawArgOffset + drawArgWriteSize), 0, drawArgsSize - drawArgWriteSize);\n\n    // Draw the actual text\n    DxvkResourceBufferInfo textBufferInfo = m_textBuffer->getSliceInfo(textSizeAligned, drawInfoSize);\n    DxvkResourceBufferInfo drawBufferInfo = m_textBuffer->getSliceInfo(drawArgOffset, drawArgWriteSize);\n\n    drawTextIndirect(ctx, getPipelineKey(dstView),\n      drawBufferInfo, textBufferInfo,\n      m_textBufferView, m_textDraws.size());\n\n    // Ensure all used resources are kept alive\n    ctx->track(m_textBuffer, DxvkAccess::Read);\n    ctx->track(m_fontBuffer, DxvkAccess::Read);\n    ctx->track(m_fontTexture, DxvkAccess::Read);\n    ctx->track(m_fontSampler);\n\n    // Reset internal text buffers\n    m_textDraws.clear();\n    m_textData.clear();\n  }\n\n\n  void HudRenderer::drawTextIndirect(\n    const Rc<DxvkCommandList>&ctx,\n    const HudPipelineKey&     key,\n    const DxvkResourceBufferInfo& drawArgs,\n    const DxvkResourceBufferInfo& drawInfos,\n    const Rc<DxvkBufferView>& textView,\n          uint32_t            drawCount) {\n    // Bind the correct pipeline for the swap chain\n    VkPipeline pipeline = getPipeline(key);\n\n    ctx->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,\n      VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);\n\n    // Bind resources\n    std::array<DxvkDescriptorWrite, 4u> descriptors = { };\n    descriptors[0u].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n    descriptors[0u].buffer = m_fontBuffer->getSliceInfo();\n\n    descriptors[1u].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n    descriptors[1u].buffer = drawInfos;\n\n    descriptors[2u].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;\n    descriptors[2u].descriptor = textView->getDescriptor(false);\n\n    descriptors[3u].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n    descriptors[3u].descriptor = m_fontTextureView->getDescriptor();\n\n    ctx->bindResources(DxvkCmdBuffer::ExecBuffer,\n      m_textPipelineLayout, descriptors.size(), descriptors.data(),\n      sizeof(m_pushConstants), &m_pushConstants);\n\n    // Emit the actual draw call\n    ctx->cmdDrawIndirect(drawArgs.buffer, drawArgs.offset,\n      drawCount, sizeof(VkDrawIndirectCommand));\n  }\n\n\n  HudPipelineKey HudRenderer::getPipelineKey(\n    const Rc<DxvkImageView>&  dstView) const {\n    HudPipelineKey key;\n    key.format = dstView->info().format;\n    key.colorSpace = dstView->image()->info().colorSpace;\n    return key;\n  }\n\n\n  HudSpecConstants HudRenderer::getSpecConstants(\n    const HudPipelineKey&     key) const {\n    HudSpecConstants result = { };\n    result.dstSpace = key.colorSpace;\n    result.dstIsSrgb = lookupFormatInfo(key.format)->flags.test(DxvkFormatFlag::ColorSpaceSrgb);\n    return result;\n  }\n\n\n  HudPushConstants HudRenderer::getPushConstants() const {\n    return m_pushConstants;\n  }\n\n\n  VkSpecializationInfo HudRenderer::getSpecInfo(\n    const HudSpecConstants*   constants) const {\n    VkSpecializationInfo specInfo = { };\n    specInfo.mapEntryCount = HudSpecConstantMap.size();\n    specInfo.pMapEntries = HudSpecConstantMap.data();\n    specInfo.dataSize = sizeof(*constants);\n    specInfo.pData = constants;\n    return specInfo;\n  }\n\n\n  void HudRenderer::createFontResources() {\n    DxvkBufferCreateInfo fontBufferInfo;\n    fontBufferInfo.size = sizeof(HudFontGpuData);\n    fontBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT\n                         | VK_BUFFER_USAGE_TRANSFER_SRC_BIT\n                         | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;\n    fontBufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                          | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;\n    fontBufferInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT\n                          | VK_ACCESS_TRANSFER_READ_BIT\n                          | VK_ACCESS_SHADER_READ_BIT;\n    fontBufferInfo.debugName = \"HUD font metadata\";\n\n    m_fontBuffer = m_device->createBuffer(fontBufferInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n    DxvkImageCreateInfo fontTextureInfo;\n    fontTextureInfo.type = VK_IMAGE_TYPE_2D;\n    fontTextureInfo.format = VK_FORMAT_R8_UNORM;\n    fontTextureInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;\n    fontTextureInfo.extent = { g_hudFont.width, g_hudFont.height, 1u };\n    fontTextureInfo.numLayers = 1u;\n    fontTextureInfo.mipLevels = 1u;\n    fontTextureInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT\n                          | VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n                          | VK_IMAGE_USAGE_SAMPLED_BIT;\n    fontTextureInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT\n                           | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n    fontTextureInfo.access = VK_ACCESS_TRANSFER_WRITE_BIT\n                           | VK_ACCESS_TRANSFER_READ_BIT\n                           | VK_ACCESS_SHADER_READ_BIT;\n    fontTextureInfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n    fontTextureInfo.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n    fontTextureInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    fontTextureInfo.debugName = \"HUD font texture\";\n\n    m_fontTexture = m_device->createImage(fontTextureInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\n    DxvkImageViewKey fontTextureViewInfo;\n    fontTextureViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    fontTextureViewInfo.format = VK_FORMAT_R8_UNORM;\n    fontTextureViewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;\n    fontTextureViewInfo.aspects = VK_IMAGE_ASPECT_COLOR_BIT;\n    fontTextureViewInfo.mipIndex = 0u;\n    fontTextureViewInfo.mipCount = 1u;\n    fontTextureViewInfo.layerIndex = 0u;\n    fontTextureViewInfo.layerCount = 1u;\n\n    m_fontTextureView = m_fontTexture->createView(fontTextureViewInfo);\n\n    DxvkSamplerKey samplerInfo;\n    samplerInfo.setFilter(VK_FILTER_LINEAR,\n      VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST);\n    samplerInfo.setAddressModes(\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n      VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);\n    samplerInfo.setUsePixelCoordinates(true);\n\n    m_fontSampler = m_device->createSampler(samplerInfo);\n  }\n\n\n  void HudRenderer::uploadFontResources(\n    const Rc<DxvkCommandList>&ctx) {\n    size_t bufferDataSize = sizeof(HudFontGpuData);\n    size_t textureDataSize = g_hudFont.width * g_hudFont.height;\n\n    DxvkBufferCreateInfo bufferInfo;\n    bufferInfo.size = bufferDataSize + textureDataSize;\n    bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n    bufferInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    bufferInfo.access = VK_ACCESS_TRANSFER_READ_BIT;\n\n    auto uploadBuffer = m_device->createBuffer(bufferInfo,\n      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n\n    HudFontGpuData glyphData = { };\n    glyphData.size = float(g_hudFont.size);\n    glyphData.advance = float(g_hudFont.advance);\n\n    for (size_t i = 0; i < g_hudFont.charCount; i++) {\n      auto& src = g_hudFont.glyphs[i];\n      auto& dst = glyphData.glyphs[src.codePoint];\n\n      dst.x = src.x;\n      dst.y = src.y;\n      dst.w = src.w;\n      dst.h = src.h;\n      dst.originX = src.originX;\n      dst.originY = src.originY;\n    }\n\n    std::memcpy(uploadBuffer->mapPtr(0), &glyphData, bufferDataSize);\n    std::memcpy(uploadBuffer->mapPtr(bufferDataSize), g_hudFont.texture, textureDataSize);\n\n    auto uploadSlice = uploadBuffer->getSliceInfo();\n    auto fontSlice = m_fontBuffer->getSliceInfo();\n\n    VkImageMemoryBarrier2 imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n    imageBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n    imageBarrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    imageBarrier.newLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n    imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    imageBarrier.image = m_fontTexture->handle();\n    imageBarrier.subresourceRange = m_fontTexture->getAvailableSubresources();\n\n    VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.imageMemoryBarrierCount = 1u;\n    depInfo.pImageMemoryBarriers = &imageBarrier;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);\n\n    VkBufferCopy2 bufferRegion = { VK_STRUCTURE_TYPE_BUFFER_COPY_2 };\n    bufferRegion.srcOffset = uploadSlice.offset;\n    bufferRegion.dstOffset = fontSlice.offset;\n    bufferRegion.size = bufferDataSize;\n\n    VkCopyBufferInfo2 bufferCopy = { VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2 };\n    bufferCopy.srcBuffer = uploadSlice.buffer;\n    bufferCopy.dstBuffer = fontSlice.buffer;\n    bufferCopy.regionCount = 1;\n    bufferCopy.pRegions = &bufferRegion;\n\n    ctx->cmdCopyBuffer(DxvkCmdBuffer::InitBuffer, &bufferCopy);\n\n    VkBufferImageCopy2 imageRegion = { VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2 };\n    imageRegion.bufferOffset = uploadSlice.offset + bufferDataSize;\n    imageRegion.imageExtent = m_fontTexture->info().extent;\n    imageRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    imageRegion.imageSubresource.layerCount = 1u;\n\n    VkCopyBufferToImageInfo2 imageCopy = { VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2 };\n    imageCopy.srcBuffer = uploadSlice.buffer;\n    imageCopy.dstImage = m_fontTexture->handle();\n    imageCopy.dstImageLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n    imageCopy.regionCount = 1;\n    imageCopy.pRegions = &imageRegion;\n\n    ctx->cmdCopyBufferToImage(DxvkCmdBuffer::InitBuffer, &imageCopy);\n\n    VkMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };\n    memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n    memoryBarrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    memoryBarrier.dstAccessMask = m_fontBuffer->info().access;\n    memoryBarrier.dstStageMask = m_fontBuffer->info().stages;\n\n    imageBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };\n    imageBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n    imageBarrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;\n    imageBarrier.dstAccessMask = m_fontTexture->info().access;\n    imageBarrier.dstStageMask = m_fontTexture->info().stages;\n    imageBarrier.oldLayout = m_fontTexture->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n    imageBarrier.newLayout = m_fontTexture->info().layout;\n    imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    imageBarrier.image = m_fontTexture->handle();\n    imageBarrier.subresourceRange = m_fontTexture->getAvailableSubresources();\n\n    depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };\n    depInfo.memoryBarrierCount = 1u;\n    depInfo.pMemoryBarriers = &memoryBarrier;\n    depInfo.imageMemoryBarrierCount = 1u;\n    depInfo.pImageMemoryBarriers = &imageBarrier;\n\n    ctx->cmdPipelineBarrier(DxvkCmdBuffer::InitBuffer, &depInfo);\n\n    m_fontTexture->trackLayout(m_fontTexture->getAvailableSubresources(), m_fontTexture->info().layout);\n\n    ctx->track(uploadBuffer, DxvkAccess::Read);\n    ctx->track(m_fontBuffer, DxvkAccess::Write);\n    ctx->track(m_fontTexture, DxvkAccess::Write);\n  }\n\n\n  const DxvkPipelineLayout* HudRenderer::createPipelineLayout() {\n    static const std::array<DxvkDescriptorSetLayoutBinding, 4> bindings = {{\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,          1, VK_SHADER_STAGE_VERTEX_BIT   },\n      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,          1, VK_SHADER_STAGE_VERTEX_BIT   },\n      { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,    1, VK_SHADER_STAGE_VERTEX_BIT   },\n      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,           1, VK_SHADER_STAGE_FRAGMENT_BIT },\n    }};\n\n    return m_device->createBuiltInPipelineLayout(DxvkPipelineLayoutFlag::UsesSamplerHeap,\n      VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,\n      sizeof(HudPushConstants), bindings.size(), bindings.data());\n  }\n\n\n  VkPipeline HudRenderer::createPipeline(\n    const HudPipelineKey&     key) {\n    auto vk = m_device->vkd();\n\n    HudSpecConstants specConstants = getSpecConstants(key);\n    VkSpecializationInfo specInfo = getSpecInfo(&specConstants);\n\n    VkPipelineColorBlendAttachmentState cbAttachment = { };\n    cbAttachment.blendEnable = VK_TRUE;\n    cbAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;\n    cbAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n    cbAttachment.colorBlendOp = VK_BLEND_OP_ADD;\n    cbAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;\n    cbAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\n    cbAttachment.alphaBlendOp = VK_BLEND_OP_ADD;\n    cbAttachment.colorWriteMask =\n      VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |\n      VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n\n    util::DxvkBuiltInGraphicsState state;\n    state.vs = util::DxvkBuiltInShaderStage(hud_text_vert, nullptr);\n    state.fs = util::DxvkBuiltInShaderStage(hud_text_frag, &specInfo);\n    state.colorFormat = key.format;\n    state.cbAttachment = &cbAttachment;\n\n    return m_device->createBuiltInGraphicsPipeline(m_textPipelineLayout, state);\n  }\n\n\n  VkPipeline HudRenderer::getPipeline(\n    const HudPipelineKey&     key) {\n    auto entry = m_textPipelines.find(key);\n\n    if (entry != m_textPipelines.end())\n      return entry->second;\n\n    VkPipeline pipeline = createPipeline(key);\n    m_textPipelines.insert({ key, pipeline });\n    return pipeline;\n  }\n\n}\n"
  },
  {
    "path": "src/dxvk/hud/dxvk_hud_renderer.h",
    "content": "#pragma once\n\n#include \"../dxvk_device.h\"\n\n#include \"dxvk_hud_font.h\"\n\nnamespace dxvk::hud {\n\n  /**\n   * \\brief HUD options\n   */\n  struct HudOptions {\n    float scale = 1.0f;\n    float opacity = 1.0f;\n  };\n\n\n  /**\n   * \\brief HUD coordinates\n   * \n   * Coordinates relative to the top-left\n   * corner of the swap image, in pixels.\n   */\n  struct HudPos {\n    int32_t x = 0;\n    int32_t y = 0;\n  };\n\n\n  /**\n   * \\brief Draw parameters for text\n   */\n  struct HudTextDrawInfo {\n    uint32_t textOffset = 0u;\n    uint16_t textLength = 0u;\n    uint16_t fontSize = 0u;\n    int16_t  posX = 0;\n    int16_t  posY = 0;\n    uint32_t color = 0u;\n  };\n\n\n  struct HudPushConstants {\n    VkExtent2D surfaceSize;\n    float opacity;\n    float scale;\n    uint32_t sampler;\n  };\n\n\n  /**\n   * \\brief Pipeline key\n   */\n  struct HudPipelineKey {\n    VkFormat format = VK_FORMAT_UNDEFINED;\n    VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n\n    size_t hash() const {\n      DxvkHashState hash;\n      hash.add(uint32_t(format));\n      hash.add(uint32_t(colorSpace));\n      return hash;\n    }\n\n    bool eq(const HudPipelineKey& other) const {\n      return format == other.format && colorSpace == other.colorSpace;\n    }\n  };\n\n\n  /**\n   * \\brief Specialization constants\n   */\n  struct HudSpecConstants {\n    VkColorSpaceKHR dstSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n    VkBool32 dstIsSrgb = VK_FALSE;\n  };\n\n\n  /**\n   * \\brief Text renderer for the HUD\n   * \n   * Can be used by the presentation backend to\n   * display performance and driver information.\n   */\n  class HudRenderer {\n    constexpr static VkDeviceSize DataBufferSize = 16384;\n  public:\n    \n    HudRenderer(\n      const Rc<DxvkDevice>&   device);\n    \n    ~HudRenderer();\n\n    void beginFrame(\n      const Rc<DxvkCommandList>&ctx,\n      const Rc<DxvkImageView>&  dstView,\n      const HudOptions&         options);\n\n    void endFrame(\n      const Rc<DxvkCommandList>&ctx);\n\n    void drawText(\n            uint32_t            size,\n            HudPos              pos,\n            uint32_t            color,\n      const std::string&        text);\n\n    void drawTextIndirect(\n      const Rc<DxvkCommandList>&ctx,\n      const HudPipelineKey&     key,\n      const DxvkResourceBufferInfo& drawArgs,\n      const DxvkResourceBufferInfo& drawInfos,\n      const Rc<DxvkBufferView>& textView,\n            uint32_t            drawCount);\n\n    void flushDraws(\n      const Rc<DxvkCommandList>&ctx,\n      const Rc<DxvkImageView>&  dstView,\n      const HudOptions&         options);\n\n    HudPipelineKey getPipelineKey(\n      const Rc<DxvkImageView>&  dstView) const;\n\n    HudSpecConstants getSpecConstants(\n      const HudPipelineKey&     key) const;\n\n    HudPushConstants getPushConstants() const;\n\n    VkSpecializationInfo getSpecInfo(\n      const HudSpecConstants*   constants) const;\n\n  private:\n\n    Rc<DxvkDevice>          m_device;\n\n    Rc<DxvkBuffer>          m_fontBuffer;\n    Rc<DxvkImage>           m_fontTexture;\n    Rc<DxvkImageView>       m_fontTextureView;\n    Rc<DxvkSampler>         m_fontSampler;\n\n    Rc<DxvkBuffer>          m_textBuffer;\n    Rc<DxvkBufferView>      m_textBufferView;\n\n    std::vector<HudTextDrawInfo>  m_textDraws;\n    std::vector<char>             m_textData;\n\n    const DxvkPipelineLayout* m_textPipelineLayout = nullptr;\n\n    HudPushConstants        m_pushConstants = { };\n\n    std::unordered_map<HudPipelineKey,\n      VkPipeline, DxvkHash, DxvkEq> m_textPipelines;\n\n    void createFontResources();\n\n    void uploadFontResources(\n      const Rc<DxvkCommandList>&ctx);\n\n    const DxvkPipelineLayout* createPipelineLayout();\n\n    VkPipeline createPipeline(\n      const HudPipelineKey&     key);\n\n    VkPipeline getPipeline(\n      const HudPipelineKey&     key);\n\n  };\n  \n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_chunk_frag_background.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : require\n\n#include \"hud_frag_common.glsl\"\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec2 surface_size;\n  float opacity;\n  float scale;\n  uint  samplerIndex;\n};\n\nlayout(location = 0) flat in uint v_active;\n\nlayout(location = 0) out vec4 o_color;\n\nvoid main() {\n  o_color = vec4(0.0f, 0.0f, 0.0f, 0.75f);\n\n  if (v_active == 0u) {\n    uvec2 pos = uvec2(gl_FragCoord.xy);\n\n    if (((pos.x + pos.y) & 7u) < 2u)\n      o_color.xyz = vec3(0.25f);\n  }\n\n  o_color.a *= opacity;\n  o_color.rgb *= o_color.a;\n  o_color = linear_to_output(o_color);\n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_chunk_frag_visualize.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : require\n\n#include \"hud_frag_common.glsl\"\n\nlayout(location = 0) in  vec2 v_coord;\n\nlayout(location = 1, component = 0) flat in uint v_color;\nlayout(location = 1, component = 1) flat in uint v_mask_index;\nlayout(location = 1, component = 2) flat in uint v_page_count;\nlayout(location = 1, component = 3) flat in uint v_active;\n\nlayout(location = 0) out vec4 o_color;\n\nlayout(binding = 1, std430)\nreadonly buffer mask_data_t {\n  uint masks[];\n};\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec2 surface_size;\n  float opacity;\n  float scale;\n  uint  samplerIndex;\n};\n\nvoid main() {\n  float dx = dFdx(v_coord.x * float(v_page_count)) / 2.0f - 0.5f;\n\n  uvec2 pageRange = uvec2(clamp(\n    (v_coord.xx * float(v_page_count)) + vec2(-dx, dx),\n    vec2(0.0), vec2(float(v_page_count - 1u))));\n\n  uint bitsTotal = max(pageRange.y - pageRange.x, 1u);\n  uint bitsSet = 0u;\n\n  uint index = pageRange.x / 32u;\n  uint shift = pageRange.x % 32u;\n\n  if (shift + bitsTotal <= 32u) {\n    bitsSet = bitCount(bitfieldExtract(\n      masks[v_mask_index + index], int(shift), int(bitsTotal)));\n  } else {\n    bitsSet = bitCount(masks[v_mask_index + (index++)] >> shift);\n    uint bitsCounted = 32u - shift;\n\n    while (bitsCounted + 32u <= bitsTotal) {\n      bitsSet += bitCount(masks[v_mask_index + (index++)]);\n      bitsCounted += 32u;\n    }\n\n    if (bitsCounted < bitsTotal) {\n      bitsSet += bitCount(bitfieldExtract(\n        masks[v_mask_index + (index++)], 0, int(bitsTotal - bitsCounted)));\n    }\n  }\n\n  if (bitsSet == 0u)\n    discard;\n\n  vec4 color = unpackUnorm4x8(v_color);\n\n  float blendFactor = 0.5f * float(bitsSet) / max(float(bitsTotal), 1.0f);\n  o_color = vec4(mix(color.rgb, vec3(1.0f), blendFactor), color.a * opacity);\n  o_color.rgb *= o_color.a;\n  o_color = linear_to_output(o_color);\n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_chunk_vert_background.vert",
    "content": "#version 450\n\nstruct draw_info_t {\n  uint packed_xy;\n  uint packed_wh;\n  uint packed_range;\n  uint color;\n};\n\nlayout(binding = 0, std430)\nreadonly buffer draw_data_t {\n  draw_info_t draw_infos[];\n};\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec2 surface_size;\n  float opacity;\n  float scale;\n  uint  samplerIndex;\n};\n\nvec2 unpack_u16(uint v) {\n  // Inputs may be signed\n  int hi = int(v);\n  int lo = int(v << 16);\n  return vec2(float(lo >> 16), float(hi >> 16));\n}\n\nlayout(location = 0) out uint o_active;\n\nvoid main() {\n  draw_info_t draw = draw_infos[gl_InstanceIndex];\n\n  vec2 coord = vec2(\n    float(gl_VertexIndex  & 1),\n    float(gl_VertexIndex >> 1));\n\n  vec2 surface_size_f = vec2(surface_size) / scale;\n\n  vec2 pos = unpack_u16(draw.packed_xy);\n  vec2 size = unpack_u16(draw.packed_wh);\n\n  pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f)));\n\n  vec2 pixel_pos = pos + size * coord + (2.0f * coord - 1.0f);\n  vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;\n\n  o_active = bitfieldExtract(draw.packed_range, 31, 1);\n  gl_Position = vec4(scaled_pos, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_chunk_vert_visualize.vert",
    "content": "#version 450\n\nstruct draw_info_t {\n  uint packed_xy;\n  uint packed_wh;\n  uint packed_range;\n  uint color;\n};\n\nlayout(binding = 0, std430)\nreadonly buffer draw_data_t {\n  draw_info_t draw_infos[];\n};\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec2 surface_size;\n  float opacity;\n  float scale;\n  uint  samplerIndex;\n};\n\nlayout(location = 0) out vec2 o_coord;\n\nlayout(location = 1, component = 0) out uint o_color;\nlayout(location = 1, component = 1) out uint o_mask_index;\nlayout(location = 1, component = 2) out uint o_page_count;\nlayout(location = 1, component = 3) out uint o_active;\n\nvec2 unpack_u16(uint v) {\n  // Inputs may be signed\n  int hi = int(v);\n  int lo = int(v << 16);\n  return vec2(float(lo >> 16), float(hi >> 16));\n}\n\nvoid main() {\n  draw_info_t draw = draw_infos[gl_InstanceIndex];\n\n  vec2 coord = vec2(\n    float(gl_VertexIndex  & 1),\n    float(gl_VertexIndex >> 1));\n\n  o_coord = coord;\n  o_color = draw.color;\n  o_mask_index = bitfieldExtract(draw.packed_range,  0, 16);\n  o_page_count = bitfieldExtract(draw.packed_range, 16, 15);\n  o_active = bitfieldExtract(draw.packed_range, 31, 1);\n\n  vec2 surface_size_f = vec2(surface_size) / scale;\n\n  vec2 pos = unpack_u16(draw.packed_xy);\n  vec2 size = unpack_u16(draw.packed_wh);\n\n  pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f)));\n\n  vec2 pixel_pos = pos + size * coord;\n  vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;\n\n  gl_Position = vec4(scaled_pos, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_frag_common.glsl",
    "content": "#include \"../../shaders/dxvk_color_space.glsl\"\n\nlayout(constant_id = 0) const uint s_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\nlayout(constant_id = 1) const bool s_srgb = false;\n\nvec4 linear_to_output(vec4 color) {\n  switch (s_color_space) {\n    case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: {\n      if (!s_srgb)\n        color.rgb = linear_to_srgb(color.rgb);\n\n      return color;\n    }\n\n    case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:\n      color.rgb = nits_to_sc_rgb(color.rgb * SDR_NITS);\n      return color;\n\n    case VK_COLOR_SPACE_HDR10_ST2084_EXT:\n      color.rgb = rec709_to_rec2020 * color.rgb;\n      color.rgb = nits_to_pq(color.rgb * SDR_NITS);\n      return color;\n\n    default: /* pass through */\n      return color;\n  }\n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_frame_time_eval.comp",
    "content": "#version 460\n\n#extension GL_KHR_memory_scope_semantics : enable\n\n#define NUM_FRAME_TIME_STAMPS (420u)\n\n#define MAX_TEXT_LENGTH (16u)\n#define MAX_TEXT_DRAWS (2u)\n\nstruct draw_param_t {\n  uint vertex_count;\n  uint instance_count;\n  uint first_vertex;\n  uint first_instance;\n};\n\nstruct draw_info_t {\n  uint text_offset;\n  uint text_length_and_size;\n  uint packed_xy;\n  uint color;\n};\n\nlayout(binding = 0, std430)\nworkgroupcoherent buffer timestamp_buffer_t {\n  uvec2 frame_timestamps_raw[2u];\n  float frame_interval_ms[NUM_FRAME_TIME_STAMPS];\n\n  float frame_time_avg_ms;\n  float frame_time_min_ms;\n  float frame_time_max_ms;\n};\n\nlayout(binding = 1, std430)\nwriteonly buffer draw_param_buffer_t {\n  draw_param_t draw_params[];\n};\n\nlayout(binding = 2, std430)\nwriteonly buffer draw_info_buffer_t {\n  draw_info_t draw_infos[];\n};\n\nlayout(binding = 3)\nuniform writeonly uimageBuffer text_buffer;\n\nlayout(push_constant)\nuniform push_data_t {\n  float ms_per_tick;\n  uint curr_data_point;\n  uint packed_xy_for_min;\n  uint packed_xy_for_max;\n};\n\n\nlayout(local_size_x = 256) in;\n\nuint extract_digit(inout float number) {\n  float high_part = floor(number / 10.0f + 0.03125f);\n  float digit = number - 10.0f * high_part;\n  number = high_part;\n  return uint(digit);\n}\n\n// Three-way reduction: Sum, Min, Max\nshared vec3 ms_shared[NUM_FRAME_TIME_STAMPS / 2u];\n\nshared uint text_chars[MAX_TEXT_DRAWS][MAX_TEXT_LENGTH];\nshared uint text_length[MAX_TEXT_DRAWS];\n\nvoid main() {\n  uint tid = gl_LocalInvocationIndex;\n\n  if (tid == 0u) {\n    uint curr_index = curr_data_point & 1u;\n    uint prev_index = curr_index ^ 1u;\n\n    uvec2 curr_time = frame_timestamps_raw[curr_index];\n    uvec2 prev_time = frame_timestamps_raw[prev_index];\n\n    // We can't require 64-bit integer support, just do this manually\n    // and account for the possibility of timestamps wrapping around.\n    uvec2 borrow;\n\n    uvec2 time_diff;\n    time_diff.x = usubBorrow(curr_time.x, prev_time.x, borrow.x);\n    time_diff.y = usubBorrow(curr_time.y, prev_time.y + borrow.x, borrow.y);\n\n    if (borrow.y != 0u)\n      time_diff = uvec2(0u);\n\n    // Ignore first frame that has no timestamp at all\n    if ((prev_time.x | prev_time.y) == 0u)\n      time_diff = uvec2(0u);\n\n    // We will most likely lose a few bits here, but that's fine\n    float ticks = time_diff.x + (time_diff.y * pow(2.0f, 32.0f));\n    frame_interval_ms[curr_data_point] = ticks * ms_per_tick;\n  }\n\n  controlBarrier(gl_ScopeWorkgroup, gl_ScopeWorkgroup,\n    gl_StorageSemanticsBuffer, gl_SemanticsAcquireRelease);\n\n  // Perform initial reduction on frame interval data and write\n  // everything to shared memory. There are more efficient ways\n  // to do reductions, but we cannot require a lot of shader\n  // features just for the frame time HUD.\n  if (2u * tid < NUM_FRAME_TIME_STAMPS) {\n    float a = frame_interval_ms[2u * tid + 0u];\n    float b = frame_interval_ms[2u * tid + 1u];\n\n    ms_shared[tid] = vec3(a + b, min(a, b), max(a, b));\n  }\n\n  barrier();\n\n  uint input_count = NUM_FRAME_TIME_STAMPS / 2u;\n\n  while (input_count > 1u) {\n    uint output_count = (input_count + 1u) / 2u;\n\n    if (tid + output_count < input_count) {\n      vec3 a = ms_shared[tid];\n      vec3 b = ms_shared[tid + output_count];\n\n      ms_shared[tid] = vec3(a.x + b.x,\n        min(a.y, b.y), max(a.z, b.z));\n    }\n\n    barrier();\n\n    input_count = output_count;\n  }\n\n  // Write reduced stats to buffer \n  vec3 stats = ms_shared[0u];\n\n  if (tid == 0u) {\n    frame_time_avg_ms = stats.x / float(NUM_FRAME_TIME_STAMPS);\n    frame_time_min_ms = stats.y;\n    frame_time_max_ms = stats.z;\n  }\n\n  if (tid < MAX_TEXT_DRAWS) {\n    // Convert number to string with a single decimal point. This\n    // is fairly naive code, but this is not performance-critical.\n    text_chars[tid][0] = 0x73; // 's'\n    text_chars[tid][1] = 0x6d; // 'm'\n    text_chars[tid][2] = 0x20; // ' '\n\n    uint zero = 0x30; // '0'\n\n    float number = round(10.0f * (tid == 0u ? stats.y : stats.z));\n    text_chars[tid][3] = zero + extract_digit(number);\n    text_chars[tid][4] = 0x2e; // '.'\n    text_chars[tid][5] = zero + extract_digit(number);\n\n    uint len = 6u;\n\n    while (number > 0.0f && len < MAX_TEXT_LENGTH)\n      text_chars[tid][len++] = zero + extract_digit(number);\n\n    text_length[tid] = len;\n\n    // Emit draw infos for the text\n    draw_infos[tid].text_offset = MAX_TEXT_LENGTH * tid;\n    draw_infos[tid].text_length_and_size = len | (12u << 16u);\n    draw_infos[tid].packed_xy = tid == 0u\n      ? packed_xy_for_min : packed_xy_for_max;\n    draw_infos[tid].color = 0xffffffffu;\n\n    // Emit indirect draw parameters\n    draw_params[tid].vertex_count = 6u * len;\n    draw_params[tid].instance_count = 1u;\n    draw_params[tid].first_vertex = 0u;\n    draw_params[tid].first_instance = 0u;\n  }\n\n  barrier();\n\n  // Use remaining threads to write text into the acual string\n  // buffer. Characters are stored in reverse order in LDS, so\n  // fix that up.\n  uint text_id = tid / MAX_TEXT_LENGTH;\n  uint text_ch = tid % MAX_TEXT_LENGTH;\n\n  if (text_id < MAX_TEXT_DRAWS) {\n    uint len = text_length[text_id];\n\n    uint ch = 0x20; // ' '\n\n    if (text_ch < len)\n      ch = text_chars[text_id][len - text_ch - 1u];\n\n    imageStore(text_buffer, int(tid), uvec4(ch));\n  }\n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_graph_frag.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : require\n\n#include \"hud_frag_common.glsl\"\n\nlayout(location = 0) in  vec2 v_coord;\nlayout(location = 0) out vec4 o_color;\n\n#define NUM_FRAME_TIME_STAMPS (420)\n\nlayout(binding = 0, std430)\nreadonly buffer timestamp_buffer_t {\n  uvec2 frame_timestamps_raw[2u];\n  float frame_interval_ms[NUM_FRAME_TIME_STAMPS];\n\n  float frame_time_avg_ms;\n  float frame_time_min_ms;\n  float frame_time_max_ms;\n};\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec2 surface_size;\n  float opacity;\n  float scale;\n  uint  samplerIndex;\n  uint  packed_xy;\n  uint  packed_wh;\n  uint  frame_index;\n};\n\nint max_index = NUM_FRAME_TIME_STAMPS - 1;\n\nint compute_real_index(int local_index) {\n  int real_index = int(frame_index) - local_index;\n\n  if (real_index < 0)\n    real_index += NUM_FRAME_TIME_STAMPS;\n\n  return real_index;\n}\n\nfloat sample_at(float position) {\n  int local_index = int(position);\n\n  int lo_index = compute_real_index(clamp(local_index, 0, max_index));\n  int hi_index = compute_real_index(clamp(local_index + 1, 0, max_index));\n\n  float lo_value = frame_interval_ms[lo_index];\n  float hi_value = frame_interval_ms[hi_index];\n\n  return mix(lo_value, hi_value, fract(position));\n}\n\nvoid main() {\n  float x_pos = (1.0f - v_coord.x) * float(NUM_FRAME_TIME_STAMPS);\n\n  float x_delta = abs(dFdx(x_pos)) / 2.0f;\n  float y_delta = abs(dFdy(v_coord.y));\n\n  float ms_l = sample_at(x_pos - x_delta);\n  float ms_r = sample_at(x_pos + x_delta);\n\n  float ms_lo = min(ms_l, ms_r);\n  float ms_hi = max(ms_l, ms_r);\n\n  float ms_max = clamp(max(frame_time_max_ms, frame_time_avg_ms * 2.0f), 20.0f, 200.0f);\n\n  float val_lo = min(ms_lo / ms_max, 1.0f) - y_delta;\n  float val_hi = min(ms_hi / ms_max, 1.0f) - y_delta;\n\n  float diff_lo = min(v_coord.y - val_lo, 0.0f);\n  float diff_hi = min(val_hi - v_coord.y, 0.0f);\n\n  float ms_y = ms_max * v_coord.y;\n\n  vec4 line_color = vec4(0.776f, 0.812f, 0.882f, 1.0f);\n  vec4 bg_color = vec4(0.0f, 0.0f, 0.0f, 0.75f);\n\n  // Try to draw a somewhat defined line\n  float diff = (diff_lo + diff_hi) + y_delta;\n  o_color = mix(bg_color, line_color, clamp(diff / y_delta, 0.0f, 1.0f));\n\n  o_color.a *= opacity;\n  o_color.rgb *= o_color.a;\n\n  o_color = linear_to_output(o_color);\n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_graph_vert.vert",
    "content": "#version 450\n\nlayout(location = 0) out vec2 o_coord;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec2 surface_size;\n  float opacity;\n  float scale;\n  uint  samplerIndex;\n  uint  packed_xy;\n  uint  packed_wh;\n  uint  frame_index;\n};\n\nvec2 unpack_u16(uint v) {\n  // Inputs may be signed\n  int hi = int(v);\n  int lo = int(v << 16);\n  return vec2(float(lo >> 16), float(hi >> 16));\n}\n\nvoid main() {\n  vec2 coord = vec2(\n    float(gl_VertexIndex  & 1),\n    float(gl_VertexIndex >> 1));\n  o_coord = vec2(coord.x, 1.0f - coord.y);\n\n  vec2 surface_size_f = vec2(surface_size) / scale;\n\n  vec2 pos = unpack_u16(packed_xy);\n  pos = mix(pos, surface_size_f + pos, lessThan(pos, vec2(0.0f)));\n\n  vec2 size = unpack_u16(packed_wh);\n\n  vec2 pixel_pos = pos + size * coord;\n  vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;\n  gl_Position = vec4(scaled_pos, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_text_frag.frag",
    "content": "#version 460\n\n#extension GL_GOOGLE_include_directive : require\n#extension GL_EXT_nonuniform_qualifier : require\n\n#include \"hud_frag_common.glsl\"\n\nlayout(set = 0, binding = 0) uniform sampler s_samplers[];\nlayout(set = 1, binding = 3) uniform texture2D s_font;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec2 surface_size;\n  float opacity;\n  float scale;\n  uint samplerIndex;\n};\n\nlayout(location = 0) in vec2 v_texcoord;\nlayout(location = 1) in vec4 v_color;\n\nlayout(location = 0) out vec4 o_color;\n\nfloat sampleAlpha(float alpha_bias, float dist_range) {\n  float value = textureLod(sampler2D(s_font, s_samplers[samplerIndex]), v_texcoord, 0).r + alpha_bias - 0.5f;\n  float dist  = value * dot(vec2(dist_range, dist_range), 1.0f / fwidth(v_texcoord.xy));\n  return clamp(dist + 0.5f, 0.0f, 1.0f);\n}\n\nvoid main() {\n  float r_alpha_center = sampleAlpha(0.0f, 5.0f);\n  float r_alpha_shadow = sampleAlpha(0.3f, 5.0f);\n  \n  vec3 r_center = v_color.rgb;\n  vec3 r_shadow = vec3(0.0f, 0.0f, 0.0f);\n\n  o_color.rgb = mix(r_shadow, r_center, r_alpha_center);\n  o_color.a = r_alpha_shadow * v_color.a * opacity;\n  o_color.rgb *= o_color.a;\n\n  o_color = linear_to_output(o_color);\n}\n"
  },
  {
    "path": "src/dxvk/hud/shaders/hud_text_vert.vert",
    "content": "#version 460\n\nstruct font_info_t {\n  float size;\n  float advance;\n  uvec2 padding;\n};\n\nstruct glyph_info_t {\n  uint packed_xy;\n  uint packed_wh;\n  uint packed_origin;\n};\n\nstruct draw_info_t {\n  uint text_offset;\n  uint text_length_and_size;\n  uint packed_xy;\n  uint color;\n};\n\nlayout(set = 1, binding = 0, std430)\nreadonly buffer font_buffer_t {\n  font_info_t font_data;\n  glyph_info_t glyph_data[];\n};\n\nlayout(set = 1, binding = 1, std430)\nreadonly buffer draw_buffer_t {\n  draw_info_t draw_infos[];\n};\n\nlayout(set = 1, binding = 2) uniform usamplerBuffer text_buffer;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec2 surface_size;\n  float opacity;\n  float scale;\n  uint samplerIndex;\n};\n\nlayout(location = 0) out vec2 o_texcoord;\nlayout(location = 1) out vec4 o_color;\n\nconst uvec2 coord_mask = uvec2(0x2a, 0x1c);\n\nvec2 unpack_u16(uint v) {\n  // Inputs may be signed\n  int hi = int(v);\n  int lo = int(v << 16);\n  return vec2(float(lo >> 16), float(hi >> 16));\n}\n\nvoid main() {\n  draw_info_t draw_info = draw_infos[gl_DrawID];\n  o_color = unpackUnorm4x8(draw_info.color);\n\n  // Compute character index and vertex index for the current\n  // character. We'll render two triangles per character.\n  uint chr_idx = gl_VertexIndex / 6;\n  uint vtx_idx = gl_VertexIndex - 6 * chr_idx;\n\n  // Load glyph info based on vertex index\n  uint glyph_idx = texelFetch(text_buffer, int(draw_info.text_offset + chr_idx)).x;\n  glyph_info_t glyph_info = glyph_data[glyph_idx];\n\n  // Compute texture coordinate from glyph data\n  vec2 coord = vec2((coord_mask >> vtx_idx) & 0x1);\n\n  vec2 tex_xy = unpack_u16(glyph_info.packed_xy);\n  vec2 tex_wh = unpack_u16(glyph_info.packed_wh);\n\n  o_texcoord = tex_xy + coord * tex_wh;\n\n  // Compute vertex position. We can easily do this here since our\n  // font is a monospace font, otherwise we'd need to preprocess\n  // the strings to render in a compute shader.\n  uint text_size = bitfieldExtract(draw_info.text_length_and_size, 16, 16);\n  float size_factor = float(text_size) / font_data.size;\n\n  vec2 surface_size_f = vec2(surface_size) / scale;\n\n  vec2 text_pos = unpack_u16(draw_info.packed_xy);\n  text_pos = mix(text_pos, surface_size_f + text_pos, lessThan(text_pos, vec2(0.0f)));\n\n  vec2 local_pos = tex_wh * coord - unpack_u16(glyph_info.packed_origin)\n    + vec2(font_data.advance * float(chr_idx), 0.0f);\n  vec2 pixel_pos = text_pos + size_factor * local_pos;\n  vec2 scaled_pos = 2.0f * (pixel_pos / surface_size_f) - 1.0f;\n\n  gl_Position = vec4(scaled_pos, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "src/dxvk/meson.build",
    "content": "dxvk_shaders = files([\n  'shaders/dxvk_blit_frag_1d.frag',\n  'shaders/dxvk_blit_frag_2d.frag',\n  'shaders/dxvk_blit_frag_3d.frag',\n  'shaders/dxvk_blit_frag_2d_ms.frag',\n\n  'shaders/dxvk_buffer_to_image_d.frag',\n  'shaders/dxvk_buffer_to_image_ds_export.frag',\n  'shaders/dxvk_buffer_to_image_f.frag',\n  'shaders/dxvk_buffer_to_image_s_discard.frag',\n  'shaders/dxvk_buffer_to_image_u.frag',\n\n  'shaders/dxvk_clear_buffer_u.comp',\n  'shaders/dxvk_clear_buffer_f.comp',\n  'shaders/dxvk_clear_image1d_u.comp',\n  'shaders/dxvk_clear_image1d_f.comp',\n  'shaders/dxvk_clear_image1darr_u.comp',\n  'shaders/dxvk_clear_image1darr_f.comp',\n  'shaders/dxvk_clear_image2d_u.comp',\n  'shaders/dxvk_clear_image2d_f.comp',\n  'shaders/dxvk_clear_image2darr_u.comp',\n  'shaders/dxvk_clear_image2darr_f.comp',\n  'shaders/dxvk_clear_image3d_u.comp',\n  'shaders/dxvk_clear_image3d_f.comp',\n\n  'shaders/dxvk_copy_buffer_image.comp',\n  'shaders/dxvk_copy_color_1d.frag',\n  'shaders/dxvk_copy_color_2d.frag',\n  'shaders/dxvk_copy_color_ms.frag',\n  'shaders/dxvk_copy_depth_stencil_1d.frag',\n  'shaders/dxvk_copy_depth_stencil_2d.frag',\n  'shaders/dxvk_copy_depth_stencil_ms.frag',\n\n  'shaders/dxvk_cursor_vert.vert',\n  'shaders/dxvk_cursor_frag.frag',\n\n  'shaders/dxvk_dummy_frag.frag',\n\n  'shaders/dxvk_fullscreen_geom.geom',\n  'shaders/dxvk_fullscreen_vert.vert',\n  'shaders/dxvk_fullscreen_layer_vert.vert',\n\n  'shaders/dxvk_image_to_buffer_ds.comp',\n  'shaders/dxvk_image_to_buffer_f.comp',\n\n  'shaders/dxvk_mipgen.comp',\n\n  'shaders/dxvk_present_frag.frag',\n  'shaders/dxvk_present_frag_blit.frag',\n  'shaders/dxvk_present_frag_ms.frag',\n  'shaders/dxvk_present_frag_ms_blit.frag',\n  'shaders/dxvk_present_vert.vert',\n\n  'shaders/dxvk_resolve_frag_d.frag',\n  'shaders/dxvk_resolve_frag_ds.frag',\n  'shaders/dxvk_resolve_frag_f.frag',\n  'shaders/dxvk_resolve_frag_i.frag',\n  'shaders/dxvk_resolve_frag_u.frag',\n\n  'hud/shaders/hud_chunk_frag_background.frag',\n  'hud/shaders/hud_chunk_frag_visualize.frag',\n  'hud/shaders/hud_chunk_vert_background.vert',\n  'hud/shaders/hud_chunk_vert_visualize.vert',\n\n  'hud/shaders/hud_frame_time_eval.comp',\n\n  'hud/shaders/hud_graph_frag.frag',\n  'hud/shaders/hud_graph_vert.vert',\n\n  'hud/shaders/hud_text_frag.frag',\n  'hud/shaders/hud_text_vert.vert',\n])\n\ndxvk_src = [\n  'dxvk_access.cpp',\n  'dxvk_adapter.cpp',\n  'dxvk_allocator.cpp',\n  'dxvk_barrier.cpp',\n  'dxvk_buffer.cpp',\n  'dxvk_cmdlist.cpp',\n  'dxvk_compute.cpp',\n  'dxvk_constant_state.cpp',\n  'dxvk_context.cpp',\n  'dxvk_cs.cpp',\n  'dxvk_descriptor_heap.cpp',\n  'dxvk_descriptor_info.cpp',\n  'dxvk_descriptor_pool.cpp',\n  'dxvk_descriptor_worker.cpp',\n  'dxvk_device.cpp',\n  'dxvk_device_filter.cpp',\n  'dxvk_device_info.cpp',\n  'dxvk_fence.cpp',\n  'dxvk_format.cpp',\n  'dxvk_framebuffer.cpp',\n  'dxvk_gpu_event.cpp',\n  'dxvk_gpu_query.cpp',\n  'dxvk_graphics.cpp',\n  'dxvk_image.cpp',\n  'dxvk_implicit_resolve.cpp',\n  'dxvk_instance.cpp',\n  'dxvk_latency_builtin.cpp',\n  'dxvk_latency_reflex.cpp',\n  'dxvk_memory.cpp',\n  'dxvk_meta_blit.cpp',\n  'dxvk_meta_clear.cpp',\n  'dxvk_meta_copy.cpp',\n  'dxvk_meta_mipgen.cpp',\n  'dxvk_meta_resolve.cpp',\n  'dxvk_options.cpp',\n  'dxvk_pipelayout.cpp',\n  'dxvk_pipemanager.cpp',\n  'dxvk_platform_exts.cpp',\n  'dxvk_presenter.cpp',\n  'dxvk_queue.cpp',\n  'dxvk_sampler.cpp',\n  'dxvk_shader.cpp',\n  'dxvk_shader_cache.cpp',\n  'dxvk_shader_io.cpp',\n  'dxvk_shader_ir.cpp',\n  'dxvk_shader_key.cpp',\n  'dxvk_shader_spirv.cpp',\n  'dxvk_signal.cpp',\n  'dxvk_sparse.cpp',\n  'dxvk_staging.cpp',\n  'dxvk_stats.cpp',\n  'dxvk_swapchain_blitter.cpp',\n  'dxvk_unbound.cpp',\n  'dxvk_util.cpp',\n\n  'hud/dxvk_hud.cpp',\n  'hud/dxvk_hud_font.cpp',\n  'hud/dxvk_hud_item.cpp',\n  'hud/dxvk_hud_renderer.cpp',\n]\n\nif platform == 'windows'\n  dxvk_src += [\n    'dxvk_openvr.cpp',\n    'dxvk_openxr.cpp',\n  ]\nendif\n\ndxvk_extra_deps = [ dependency('threads') ]\nif platform == 'linux'\n  dxvk_extra_deps += [ cpp.find_library('dl', required: false) ]\nendif\n\ndxvk_lib = static_library('dxvk', dxvk_src, glsl_generator.process(dxvk_shaders), dxvk_version,\n  link_with           : [ util_lib, spirv_lib, wsi_lib ],\n  dependencies        : [ dxbc_spirv_dep, vkcommon_dep ] + dxvk_extra_deps,\n  include_directories : [ dxvk_include_path ],\n)\n\ndxvk_dep = declare_dependency(\n  link_with           : [ dxvk_lib ],\n  include_directories : [ dxvk_include_path ],\n)\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_blit_frag_1d.frag",
    "content": "#version 460\n\n#extension GL_EXT_nonuniform_qualifier : require\n#extension GL_EXT_scalar_block_layout : require\n\nlayout(set = 0, binding = 0) uniform sampler s_samplers[];\nlayout(set = 1, binding = 0) uniform texture1DArray s_texture;\n\nlayout(location = 0) in  vec2 i_pos;\nlayout(location = 0) out vec4 o_color;\n\nlayout(push_constant)\nuniform push_block {\n  float p_src_coord0_x, p_src_coord0_y, p_src_coord0_z;\n  float p_src_coord1_x, p_src_coord1_y, p_src_coord1_z;\n  uint p_layer_count;\n  uint p_sampler;\n};\n\nvoid main() {\n  float coord = mix(p_src_coord0_x, p_src_coord1_x, i_pos.x);\n  o_color = texture(sampler1DArray(s_texture, s_samplers[p_sampler]), vec2(coord, gl_Layer));\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_blit_frag_2d.frag",
    "content": "#version 460\n\n#extension GL_EXT_nonuniform_qualifier : require\n#extension GL_EXT_scalar_block_layout : require\n\nlayout(set = 0, binding = 0) uniform sampler s_samplers[];\nlayout(set = 1, binding = 0) uniform texture2DArray s_texture;\n\nlayout(location = 0) in  vec2 i_pos;\nlayout(location = 0) out vec4 o_color;\n\nlayout(push_constant)\nuniform push_block {\n  float p_src_coord0_x, p_src_coord0_y, p_src_coord0_z;\n  float p_src_coord1_x, p_src_coord1_y, p_src_coord1_z;\n  uint p_layer_count;\n  uint p_sampler;\n};\n\nvoid main() {\n  vec2 coord = mix(\n    vec2(p_src_coord0_x, p_src_coord0_y),\n    vec2(p_src_coord1_x, p_src_coord1_y), i_pos);\n\n  o_color = texture(sampler2DArray(s_texture, s_samplers[p_sampler]), vec3(coord, gl_Layer));\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_blit_frag_2d_ms.frag",
    "content": "#version 460\n\n#extension GL_ARB_gpu_shader_int64 : require\n#extension GL_EXT_nonuniform_qualifier : require\n#extension GL_EXT_scalar_block_layout : require\n#extension GL_EXT_samplerless_texture_functions : enable\n\nlayout(set = 0, binding = 0) uniform sampler s_samplers[];\nlayout(set = 1, binding = 0) uniform texture2DMSArray s_image_ms;\n\nlayout(location = 0) in vec2 i_pos;\nlayout(location = 0) out vec4 o_color;\n\nlayout(push_constant)\nuniform push_block {\n  float p_src_coord0_x, p_src_coord0_y, p_src_coord0_z;\n  float p_src_coord1_x, p_src_coord1_y, p_src_coord1_z;\n  uint p_layer_count;\n};\n\n#define FILTER_NEAREST  (0u)\n#define FILTER_LINEAR   (1u)\n#define RESOLVE_AVERAGE (2u)\n\nlayout(constant_id = 0) const uint c_src_samples = 1;\nlayout(constant_id = 1) const uint c_dst_samples = 1;\nlayout(constant_id = 2) const uint c_resolve_mode = FILTER_LINEAR;\n\n/* Sample grid layout for each pixel */\nconst uvec2 sample_scale[] = {\n  uvec2(2u, 1u),\n  uvec2(2u, 2u),\n  uvec2(4u, 2u),\n  uvec2(4u, 4u),\n};\n\n/* Order of samples within the grid in row-major order */\nconst uint64_t sample_maps[] = {\n  0x0000000000000001ul,\n  0x0000000000003210ul,\n  0x0000000026147035ul,\n  0xe58b602c3714d9aful,\n};\n\nvoid main() {\n  if (c_resolve_mode == RESOLVE_AVERAGE) {\n    vec2 coord = vec2(p_src_coord0_x, p_src_coord0_y) + vec2(p_src_coord1_x - p_src_coord0_x, p_src_coord1_y - p_src_coord0_y) * i_pos;\n    ivec2 i_coord = ivec2(coord);\n\n    uint sample_count = max(1u, c_src_samples / c_dst_samples);\n    o_color = vec4(0.0f);\n\n    for (uint i = 0u; i < sample_count; i++) {\n      uint sample_index = (gl_SampleID * c_src_samples) / c_dst_samples + i;\n      o_color += texelFetch(s_image_ms, ivec3(i_coord, gl_Layer), int(sample_index));\n    }\n\n    o_color /= float(sample_count);\n  } else {\n    vec2 coord = fma(interpolateAtSample(i_pos, gl_SampleID),\n      vec2(p_src_coord1_x - p_src_coord0_x - 1u, p_src_coord1_y - p_src_coord0_y - 1u),\n      vec2(p_src_coord0_x, p_src_coord0_y));\n\n    int lookup_index = max(findLSB(c_src_samples) - 1, 0);\n\n    uvec2 scale = sample_scale[lookup_index];\n    uint64_t map = sample_maps[lookup_index];\n\n    coord *= vec2(scale);\n\n    vec2 i_coord = trunc(coord);\n    vec2 f_coord = (c_resolve_mode == FILTER_NEAREST) ? vec2(0.0f) : vec2(coord - i_coord);\n\n    o_color = vec4(0.0f);\n\n    for (uint i = 0u; i < ((c_resolve_mode == FILTER_NEAREST) ? 1u : 4u); i++) {\n      uvec2 lookup_offset = uvec2(i & 1u, i >> 1u);\n\n      uvec2 sample_coord = uvec2(i_coord + lookup_offset) % scale;\n      uvec2 pixel_coord = uvec2(i_coord + lookup_offset) / scale;\n\n      uint sample_index = scale.x * sample_coord.y + sample_coord.x;\n      sample_index = uint(map >> (4u * sample_index)) & 0xfu;\n\n      vec4 color = texelFetch(s_image_ms, ivec3(pixel_coord, gl_Layer), int(sample_index));\n      vec2 factor = mix(f_coord, 1.0f - f_coord, equal(lookup_offset, 0u.xx));\n\n      o_color += color * factor.x * factor.y;\n    }\n  }\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_blit_frag_3d.frag",
    "content": "#version 460\n\n#extension GL_EXT_nonuniform_qualifier : require\n#extension GL_EXT_scalar_block_layout : require\n\nlayout(set = 0, binding = 0) uniform sampler s_samplers[];\nlayout(set = 1, binding = 0) uniform texture3D s_texture;\n\nlayout(location = 0) in  vec2 i_pos;\nlayout(location = 0) out vec4 o_color;\n\nlayout(push_constant)\nuniform push_block {\n  float p_src_coord0_x, p_src_coord0_y, p_src_coord0_z;\n  float p_src_coord1_x, p_src_coord1_y, p_src_coord1_z;\n  uint p_layer_count;\n  uint p_sampler;\n};\n\nvoid main() {\n  vec3 coord = mix(\n    vec3(p_src_coord0_x, p_src_coord0_y, p_src_coord0_z),\n    vec3(p_src_coord1_x, p_src_coord1_y, p_src_coord1_z),\n    vec3(i_pos, (float(gl_Layer) + 0.5f) / float(p_layer_count)));\n\n  o_color = texture(sampler3D(s_texture, s_samplers[p_sampler]), coord);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_buffer_to_image_d.frag",
    "content": "#version 460\n\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"dxvk_formats.glsl\"\n\nlayout(constant_id = 0) const uint src_format = VK_FORMAT_UNDEFINED;\n\nlayout(binding = 0) uniform usamplerBuffer u_data;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec3 image_offset;\n  uint buffer_offset;\n  uvec3 image_extent;\n  uint buffer_image_width;\n  uint buffer_image_height;\n  uint stencil_bit_index;\n};\n\nvoid main() {\n  uvec2 location = uvec2(gl_FragCoord.xy) - image_offset.xy;\n\n  int offset = int(buffer_offset + location.x +\n    buffer_image_width * (location.y + buffer_image_height * gl_Layer));\n\n  switch (src_format) {\n    case VK_FORMAT_D16_UNORM:\n    case VK_FORMAT_D16_UNORM_S8_UINT: {\n      uint data = texelFetch(u_data, offset).x;\n      gl_FragDepth = float(data & 0xffffu) / float(0xffffu);\n    } break;\n\n    case VK_FORMAT_D24_UNORM_S8_UINT:\n    case VK_FORMAT_X8_D24_UNORM_PACK32: {\n      uint data = texelFetch(u_data, offset).x;\n      gl_FragDepth = float(data & 0xffffffu) / float(0xffffffu);\n    } break;\n\n    case VK_FORMAT_D32_SFLOAT:\n    case VK_FORMAT_D32_SFLOAT_S8_UINT: {\n      uint data = texelFetch(u_data, offset).x;\n      gl_FragDepth = uintBitsToFloat(data);\n    } break;\n\n    default:\n      gl_FragDepth = 0.0f;\n  }\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_buffer_to_image_ds_export.frag",
    "content": "#version 460\n\n#extension GL_GOOGLE_include_directive : enable\n#extension GL_ARB_shader_stencil_export : enable\n\n#include \"dxvk_formats.glsl\"\n\nlayout(constant_id = 0) const uint src_format = VK_FORMAT_UNDEFINED;\n\nlayout(binding = 0) uniform usamplerBuffer u_data;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec3 image_offset;\n  uint buffer_offset;\n  uvec3 image_extent;\n  uint buffer_image_width;\n  uint buffer_image_height;\n  uint stencil_bit_index;\n};\n\nvoid main() {\n  uvec2 location = uvec2(gl_FragCoord.xy) - image_offset.xy;\n\n  int offset = int(buffer_offset + location.x +\n    buffer_image_width * (location.y + buffer_image_height * gl_Layer));\n\n  switch (src_format) {\n    case VK_FORMAT_D16_UNORM:\n    case VK_FORMAT_D16_UNORM_S8_UINT: {\n      uvec2 data = texelFetch(u_data, offset).xy;\n      gl_FragDepth = float(data & 0xffffu) / float(0xffffu);\n      gl_FragStencilRefARB = int(data.y & 0xffu);\n    } break;\n\n    case VK_FORMAT_D24_UNORM_S8_UINT:\n    case VK_FORMAT_X8_D24_UNORM_PACK32: {\n      uint data = texelFetch(u_data, offset).x;\n      gl_FragDepth = float(data & 0xffffffu) / float(0xffffffu);\n      gl_FragStencilRefARB = int(data >> 24);\n    } break;\n\n    case VK_FORMAT_D32_SFLOAT:\n    case VK_FORMAT_D32_SFLOAT_S8_UINT: {\n      uvec2 data = texelFetch(u_data, offset).xy;\n      gl_FragDepth = uintBitsToFloat(data.x);\n      gl_FragStencilRefARB = int(data.y & 0xffu);\n    } break;\n\n    case VK_FORMAT_S8_UINT: {\n      uint data = texelFetch(u_data, offset).x;\n      gl_FragDepth = 0.0f;\n      gl_FragStencilRefARB = int(data & 0xffu);\n    } break;\n\n    default:\n      gl_FragDepth = 0.0f;\n      gl_FragStencilRefARB = 0;\n  }\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_buffer_to_image_f.frag",
    "content": "#version 460\n\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"dxvk_formats.glsl\"\n\nlayout(binding = 0) uniform samplerBuffer u_data;\n\nlayout(location = 0) out vec4 o_color;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec3 image_offset;\n  uint buffer_offset;\n  uvec3 image_extent;\n  uint buffer_image_width;\n  uint buffer_image_height;\n  uint stencil_bit_index;\n};\n\nvoid main() {\n  uvec2 location = uvec2(gl_FragCoord.xy) - image_offset.xy;\n\n  int offset = int(buffer_offset + location.x +\n    buffer_image_width * (location.y + buffer_image_height * gl_Layer));\n\n  o_color = texelFetch(u_data, offset);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_buffer_to_image_s_discard.frag",
    "content": "#version 460\n\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"dxvk_formats.glsl\"\n\nlayout(constant_id = 0) const uint src_format = VK_FORMAT_UNDEFINED;\n\nlayout(binding = 0) uniform usamplerBuffer u_data;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec3 image_offset;\n  uint buffer_offset;\n  uvec3 image_extent;\n  uint buffer_image_width;\n  uint buffer_image_height;\n  uint stencil_bit_index;\n};\n\nvoid main() {\n  uvec2 location = uvec2(gl_FragCoord.xy) - image_offset.xy;\n\n  int offset = int(buffer_offset + location.x +\n    buffer_image_width * (location.y + buffer_image_height * gl_Layer));\n\n  uint stencil = 0u;\n\n  switch (src_format) {\n    case VK_FORMAT_D24_UNORM_S8_UINT: {\n      uint data = texelFetch(u_data, offset).x;\n      stencil = data >> 24;\n    } break;\n\n    case VK_FORMAT_D16_UNORM_S8_UINT:\n    case VK_FORMAT_D32_SFLOAT_S8_UINT: {\n      uint data = texelFetch(u_data, offset).y;\n      stencil = data & 0xffu;\n    } break;\n\n    case VK_FORMAT_S8_UINT: {\n      uint data = texelFetch(u_data, offset).x;\n      stencil = data & 0xffu;\n    } break;\n  }\n\n  if ((stencil & (1u << stencil_bit_index)) == 0u)\n    discard;\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_buffer_to_image_u.frag",
    "content": "#version 460\n\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"dxvk_formats.glsl\"\n\nlayout(binding = 0) uniform usamplerBuffer u_data;\n\nlayout(location = 0) out uvec4 o_color;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec3 image_offset;\n  uint buffer_offset;\n  uvec3 image_extent;\n  uint buffer_image_width;\n  uint buffer_image_height;\n  uint stencil_bit_index;\n};\n\nvoid main() {\n  uvec2 location = uvec2(gl_FragCoord.xy) - image_offset.xy;\n\n  int offset = int(buffer_offset + location.x +\n    buffer_image_width * (location.y + buffer_image_height * gl_Layer));\n\n  o_color = texelFetch(u_data, offset);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_buffer_f.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 128,\n  local_size_y = 1,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform imageBuffer dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  vec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  int thread_id = int(gl_GlobalInvocationID.x);\n  \n  if (thread_id < u_info.dst_extent.x) {\n    imageStore(dst,\n      u_info.dst_offset.x + thread_id,\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_buffer_u.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 128,\n  local_size_y = 1,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform uimageBuffer dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  int thread_id = int(gl_GlobalInvocationID.x);\n  \n  if (thread_id < u_info.dst_extent.x) {\n    imageStore(dst,\n      u_info.dst_offset.x + thread_id,\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image1d_f.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 64,\n  local_size_y = 1,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image1D dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  vec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (thread_id.x < u_info.dst_extent.x) {\n    imageStore(dst,\n      u_info.dst_offset.x + thread_id.x,\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image1d_u.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 64,\n  local_size_y = 1,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform uimage1D dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (thread_id.x < u_info.dst_extent.x) {\n    imageStore(dst,\n      u_info.dst_offset.x + thread_id.x,\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image1darr_f.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 64,\n  local_size_y = 1,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image1DArray dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  vec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (thread_id.x < u_info.dst_extent.x) {\n    imageStore(dst,\n      ivec2(u_info.dst_offset.x + thread_id.x, thread_id.y),\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image1darr_u.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 64,\n  local_size_y = 1,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform uimage1DArray dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (thread_id.x < u_info.dst_extent.x) {\n    imageStore(dst,\n      ivec2(u_info.dst_offset.x + thread_id.x, thread_id.y),\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image2d_f.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image2D dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  vec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (all(lessThan(thread_id.xy, u_info.dst_extent.xy))) {\n    imageStore(dst,\n      u_info.dst_offset.xy + thread_id.xy,\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image2d_u.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform uimage2D dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (all(lessThan(thread_id.xy, u_info.dst_extent.xy))) {\n    imageStore(dst,\n      u_info.dst_offset.xy + thread_id.xy,\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image2darr_f.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform image2DArray dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  vec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (all(lessThan(thread_id.xy, u_info.dst_extent.xy))) {\n    imageStore(dst,\n      ivec3(u_info.dst_offset.xy + thread_id.xy, thread_id.z),\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image2darr_u.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0)\nwriteonly uniform uimage2DArray dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (all(lessThan(thread_id.xy, u_info.dst_extent.xy))) {\n    imageStore(dst,\n      ivec3(u_info.dst_offset.xy + thread_id.xy, thread_id.z),\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image3d_f.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 4,\n  local_size_y = 4,\n  local_size_z = 4) in;\n\nlayout(binding = 0)\nwriteonly uniform image3D dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  vec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (all(lessThan(thread_id.xyz, u_info.dst_extent.xyz))) {\n    imageStore(dst,\n      u_info.dst_offset.xyz + thread_id.xyz,\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_clear_image3d_u.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 4,\n  local_size_y = 4,\n  local_size_z = 4) in;\n\nlayout(binding = 0)\nwriteonly uniform uimage3D dst;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec4 clear_value;\n  ivec4 dst_offset;\n  ivec4 dst_extent;\n} u_info;\n\nvoid main() {\n  ivec3 thread_id = ivec3(gl_GlobalInvocationID);\n  \n  if (all(lessThan(thread_id.xyz, u_info.dst_extent.xyz))) {\n    imageStore(dst,\n      u_info.dst_offset.xyz + thread_id.xyz,\n      u_info.clear_value);\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_color_space.glsl",
    "content": "#define VK_COLOR_SPACE_SRGB_NONLINEAR_KHR (0)\n#define VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT (1000104002)\n#define VK_COLOR_SPACE_HDR10_ST2084_EXT (1000104008)\n#define VK_COLOR_SPACE_PASS_THROUGH_EXT (1000104013)\n\n#define SDR_NITS (203.0f)\n\nconst mat3 rec709_to_xyz = mat3(\n   0.4123908,  0.2126390,  0.0193308,\n   0.3575843,  0.7151687,  0.1191948,\n   0.1804808,  0.0721923,  0.9505322);\n\nconst mat3 xyz_to_rec2020 = mat3(\n   1.7166512, -0.6666844,  0.0176399,\n  -0.3556708,  1.6164812, -0.0427706,\n  -0.2533663,  0.0157685,  0.9421031);\n\nmat3 rec709_to_rec2020 = xyz_to_rec2020 * rec709_to_xyz;\nmat3 rec2020_to_rec709 = inverse(rec709_to_rec2020);\n\n\n// sRGB functions\nvec3 linear_to_srgb(vec3 linear) {\n  bvec3 isLo = lessThanEqual(linear, vec3(0.0031308f));\n\n  vec3 loPart = linear * 12.92f;\n  vec3 hiPart = pow(linear, vec3(5.0f / 12.0f)) * 1.055f - 0.055f;\n  return mix(hiPart, loPart, isLo);\n}\n\nvec3 srgb_to_linear(vec3 srgb) {\n  bvec3 isLo = lessThanEqual(srgb, vec3(0.04045f));\n\n  vec3 loPart = srgb / 12.92f;\n  vec3 hiPart = pow((srgb + 0.055f) / 1.055f, vec3(12.0f / 5.0f));\n  return mix(hiPart, loPart, isLo);\n}\n\n\n// Perceptual quantizer conversion\nvec3 nits_to_pq(vec3 nits) {\n  const float c1 = 0.8359375f;\n  const float c2 = 18.8515625f;\n  const float c3 = 18.6875f;\n  const float m1 = 0.1593017578125f;\n  const float m2 = 78.84375f;\n\n  vec3 y = clamp(nits / 10000.0f, vec3(0.0f), vec3(1.0f));\n  vec3 y_m1 = pow(y, vec3(m1));\n\n  vec3 num = c1 + c2 * y_m1;\n  vec3 den = 1.0f + c3 * y_m1;\n\n  return pow(num / den, vec3(m2));\n}\n\nvec3 pq_to_nits(vec3 pq) {\n  const float c1 = 0.8359375f;\n  const float c2 = 18.8515625f;\n  const float c3 = 18.6875f;\n  const float m1 = 0.1593017578125f;\n  const float m2 = 78.84375f;\n\n  vec3 pq_m2 = pow(pq, vec3(1.0f / m2));\n\n  vec3 num = max(pq_m2 - c1, 0.0f);\n  vec3 den = c2 - c3 * pq_m2;\n\n  vec3 y = pow(num / den, vec3(1.0f / m1));\n  return 10000.0f * y;  \n}\n\n\n// scRGB conversion\nvec3 nits_to_sc_rgb(vec3 nits) {\n  return nits / 80.0f;\n}\n\nvec3 sc_rgb_to_nits(vec3 sc_rgb) {\n  return sc_rgb * 80.0f;\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_copy_buffer_image.comp",
    "content": "#version 450\n\nlayout(\n  local_size_x = 8,\n  local_size_y = 8,\n  local_size_z = 1) in;\n\nlayout(binding = 0) writeonly uniform uimageBuffer u_dst;\nlayout(binding = 1) uniform usamplerBuffer u_src;\n\nlayout(push_constant)\nuniform u_info_t {\n  uvec3 dst_offset;\n  uvec3 src_offset;\n  uvec3 extent;\n  uvec2 dst_size;\n  uvec2 src_size;\n} u_info;\n\nvoid main() {\n  if (all(lessThan(gl_GlobalInvocationID, u_info.extent))) {\n    uvec3 dst_coord = u_info.dst_offset + gl_GlobalInvocationID;\n    uvec3 src_coord = u_info.src_offset + gl_GlobalInvocationID;\n\n    uint dst_index = dst_coord.x + u_info.dst_size.x * (dst_coord.y + u_info.dst_size.y * dst_coord.z);\n    uint src_index = src_coord.x + u_info.src_size.x * (src_coord.y + u_info.src_size.y * src_coord.z);\n\n    imageStore(u_dst, int(dst_index), texelFetch(u_src, int(src_index)));\n  }\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_copy_color_1d.frag",
    "content": "#version 450\n\n#extension GL_EXT_samplerless_texture_functions : require\n\nlayout(set = 0, binding = 0)\nuniform texture1DArray s_image;\n\nlayout(location = 0) out vec4 o_color;\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  vec4 color = texelFetch(s_image,\n    ivec2(gl_FragCoord.x + u_info.offset.x, gl_Layer), 0);\n  o_color = color;\n  gl_FragDepth = color.r;\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_copy_color_2d.frag",
    "content": "#version 450\n\n#extension GL_EXT_samplerless_texture_functions : require\n\nlayout(set = 0, binding = 0)\nuniform texture2DArray s_image;\n\nlayout(location = 0) out vec4 o_color;\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  vec4 color = texelFetch(s_image,\n    ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer), 0);\n  o_color = color;\n  gl_FragDepth = color.r;\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_copy_color_ms.frag",
    "content": "#version 450\n\n#extension GL_EXT_samplerless_texture_functions : require\n\nlayout(set = 0, binding = 0)\nuniform texture2DMSArray s_image;\n\nlayout(location = 0) out vec4 o_color;\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  vec4 color = texelFetch(s_image,\n    ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer),\n    gl_SampleID);\n  o_color = color;\n  gl_FragDepth = color.r;\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_copy_depth_stencil_1d.frag",
    "content": "#version 450\n\n#extension GL_ARB_shader_stencil_export : require\n#extension GL_EXT_samplerless_texture_functions : require\n\nlayout(set = 0, binding = 0)\nuniform texture1DArray s_depth;\n\nlayout(set = 0, binding = 1)\nuniform utexture1DArray s_stencil;\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  ivec2 coord = ivec2(gl_FragCoord.x + u_info.offset.x, gl_Layer);\n  gl_FragDepth         = texelFetch(s_depth,   coord, 0).r;\n  gl_FragStencilRefARB = int(texelFetch(s_stencil, coord, 0).r);\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_copy_depth_stencil_2d.frag",
    "content": "#version 450\n\n#extension GL_ARB_shader_stencil_export : enable\n#extension GL_EXT_samplerless_texture_functions : require\n\nlayout(set = 0, binding = 0)\nuniform texture2DArray s_depth;\n\nlayout(set = 0, binding = 1)\nuniform utexture2DArray s_stencil;\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  ivec3 coord = ivec3(gl_FragCoord.xy + u_info.offset.xy, gl_Layer);\n  gl_FragDepth         = texelFetch(s_depth,   coord, 0).r;\n  gl_FragStencilRefARB = int(texelFetch(s_stencil, coord, 0).r);\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_copy_depth_stencil_ms.frag",
    "content": "#version 450\n\n#extension GL_ARB_shader_stencil_export : enable\n#extension GL_EXT_samplerless_texture_functions : require\n\nlayout(set = 0, binding = 0)\nuniform texture2DMSArray s_depth;\n\nlayout(set = 0, binding = 1)\nuniform utexture2DMSArray s_stencil;\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  ivec3 coord = ivec3(gl_FragCoord.xy + u_info.offset.xy, gl_Layer);\n  gl_FragDepth         = texelFetch(s_depth,   coord, gl_SampleID).r;\n  gl_FragStencilRefARB = int(texelFetch(s_stencil, coord, gl_SampleID).r);\n}"
  },
  {
    "path": "src/dxvk/shaders/dxvk_cursor_frag.frag",
    "content": "#version 460\n\n#extension GL_EXT_nonuniform_qualifier : require\n#extension GL_GOOGLE_include_directive : require\n\n#include \"dxvk_color_space.glsl\"\n\nlayout(constant_id = 0) const uint s_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\nlayout(constant_id = 1) const bool s_srgb = false;\n\nlayout(set = 0, binding = 0) uniform sampler s_samplers[];\nlayout(set = 1, binding = 0) uniform texture2D s_texture;\n\nlayout(location = 0) in vec2 i_texcoord;\nlayout(location = 0) out vec4 o_color;\n\nlayout(push_constant)\nuniform present_info_t {\n  ivec2 dst_extent;\n  ivec2 cursor_offset;\n  ivec2 cursor_extent;\n  uint sampler_index;\n};\n\nvec4 linear_to_output(vec4 color) {\n  switch (s_color_space) {\n    case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: {\n      if (!s_srgb)\n        color.rgb = linear_to_srgb(color.rgb);\n\n      return color;\n    }\n\n    case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:\n      color.rgb = nits_to_sc_rgb(color.rgb * SDR_NITS);\n      return color;\n\n    case VK_COLOR_SPACE_HDR10_ST2084_EXT:\n      color.rgb = rec709_to_rec2020 * color.rgb;\n      color.rgb = nits_to_pq(color.rgb * SDR_NITS);\n      return color;\n\n    default: /* pass through */\n      return color;\n  }\n}\n\n\nvoid main() {\n  o_color = linear_to_output(texture(sampler2D(s_texture, s_samplers[sampler_index]), i_texcoord));\n  o_color.rgb *= o_color.a;\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_cursor_vert.vert",
    "content": "#version 460\n\nlayout(push_constant)\nuniform present_info_t {\n  ivec2 dst_extent;\n  ivec2 cursor_offset;\n  ivec2 cursor_extent;\n  uint sampler_index;\n};\n\nlayout(location = 0) out vec2 o_texcoord;\n\nvoid main() {\n  vec2 coord = vec2(\n    float((gl_VertexIndex >> 1) & 1),\n    float(gl_VertexIndex & 1));\n\n  o_texcoord = coord;\n\n  coord *= vec2(cursor_extent) / vec2(dst_extent);\n  coord += vec2(cursor_offset) / vec2(dst_extent);\n\n  gl_Position = vec4(-1.0f + 2.0f * coord, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_dummy_frag.frag",
    "content": "#version 450\n\nvoid main() {\n\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_formats.glsl",
    "content": "#define VK_FORMAT_UNDEFINED (0)\n#define VK_FORMAT_R4G4_UNORM_PACK8 (1)\n#define VK_FORMAT_R4G4B4A4_UNORM_PACK16 (2)\n#define VK_FORMAT_B4G4R4A4_UNORM_PACK16 (3)\n#define VK_FORMAT_R5G6B5_UNORM_PACK16 (4)\n#define VK_FORMAT_B5G6R5_UNORM_PACK16 (5)\n#define VK_FORMAT_R5G5B5A1_UNORM_PACK16 (6)\n#define VK_FORMAT_B5G5R5A1_UNORM_PACK16 (7)\n#define VK_FORMAT_A1R5G5B5_UNORM_PACK16 (8)\n#define VK_FORMAT_R8_UNORM (9)\n#define VK_FORMAT_R8_SNORM (10)\n#define VK_FORMAT_R8_USCALED (11)\n#define VK_FORMAT_R8_SSCALED (12)\n#define VK_FORMAT_R8_UINT (13)\n#define VK_FORMAT_R8_SINT (14)\n#define VK_FORMAT_R8_SRGB (15)\n#define VK_FORMAT_R8G8_UNORM (16)\n#define VK_FORMAT_R8G8_SNORM (17)\n#define VK_FORMAT_R8G8_USCALED (18)\n#define VK_FORMAT_R8G8_SSCALED (19)\n#define VK_FORMAT_R8G8_UINT (20)\n#define VK_FORMAT_R8G8_SINT (21)\n#define VK_FORMAT_R8G8_SRGB (22)\n#define VK_FORMAT_R8G8B8_UNORM (23)\n#define VK_FORMAT_R8G8B8_SNORM (24)\n#define VK_FORMAT_R8G8B8_USCALED (25)\n#define VK_FORMAT_R8G8B8_SSCALED (26)\n#define VK_FORMAT_R8G8B8_UINT (27)\n#define VK_FORMAT_R8G8B8_SINT (28)\n#define VK_FORMAT_R8G8B8_SRGB (29)\n#define VK_FORMAT_B8G8R8_UNORM (30)\n#define VK_FORMAT_B8G8R8_SNORM (31)\n#define VK_FORMAT_B8G8R8_USCALED (32)\n#define VK_FORMAT_B8G8R8_SSCALED (33)\n#define VK_FORMAT_B8G8R8_UINT (34)\n#define VK_FORMAT_B8G8R8_SINT (35)\n#define VK_FORMAT_B8G8R8_SRGB (36)\n#define VK_FORMAT_R8G8B8A8_UNORM (37)\n#define VK_FORMAT_R8G8B8A8_SNORM (38)\n#define VK_FORMAT_R8G8B8A8_USCALED (39)\n#define VK_FORMAT_R8G8B8A8_SSCALED (40)\n#define VK_FORMAT_R8G8B8A8_UINT (41)\n#define VK_FORMAT_R8G8B8A8_SINT (42)\n#define VK_FORMAT_R8G8B8A8_SRGB (43)\n#define VK_FORMAT_B8G8R8A8_UNORM (44)\n#define VK_FORMAT_B8G8R8A8_SNORM (45)\n#define VK_FORMAT_B8G8R8A8_USCALED (46)\n#define VK_FORMAT_B8G8R8A8_SSCALED (47)\n#define VK_FORMAT_B8G8R8A8_UINT (48)\n#define VK_FORMAT_B8G8R8A8_SINT (49)\n#define VK_FORMAT_B8G8R8A8_SRGB (50)\n#define VK_FORMAT_A8B8G8R8_UNORM_PACK32 (51)\n#define VK_FORMAT_A8B8G8R8_SNORM_PACK32 (52)\n#define VK_FORMAT_A8B8G8R8_USCALED_PACK32 (53)\n#define VK_FORMAT_A8B8G8R8_SSCALED_PACK32 (54)\n#define VK_FORMAT_A8B8G8R8_UINT_PACK32 (55)\n#define VK_FORMAT_A8B8G8R8_SINT_PACK32 (56)\n#define VK_FORMAT_A8B8G8R8_SRGB_PACK32 (57)\n#define VK_FORMAT_A2R10G10B10_UNORM_PACK32 (58)\n#define VK_FORMAT_A2R10G10B10_SNORM_PACK32 (59)\n#define VK_FORMAT_A2R10G10B10_USCALED_PACK32 (60)\n#define VK_FORMAT_A2R10G10B10_SSCALED_PACK32 (61)\n#define VK_FORMAT_A2R10G10B10_UINT_PACK32 (62)\n#define VK_FORMAT_A2R10G10B10_SINT_PACK32 (63)\n#define VK_FORMAT_A2B10G10R10_UNORM_PACK32 (64)\n#define VK_FORMAT_A2B10G10R10_SNORM_PACK32 (65)\n#define VK_FORMAT_A2B10G10R10_USCALED_PACK32 (66)\n#define VK_FORMAT_A2B10G10R10_SSCALED_PACK32 (67)\n#define VK_FORMAT_A2B10G10R10_UINT_PACK32 (68)\n#define VK_FORMAT_A2B10G10R10_SINT_PACK32 (69)\n#define VK_FORMAT_R16_UNORM (70)\n#define VK_FORMAT_R16_SNORM (71)\n#define VK_FORMAT_R16_USCALED (72)\n#define VK_FORMAT_R16_SSCALED (73)\n#define VK_FORMAT_R16_UINT (74)\n#define VK_FORMAT_R16_SINT (75)\n#define VK_FORMAT_R16_SFLOAT (76)\n#define VK_FORMAT_R16G16_UNORM (77)\n#define VK_FORMAT_R16G16_SNORM (78)\n#define VK_FORMAT_R16G16_USCALED (79)\n#define VK_FORMAT_R16G16_SSCALED (80)\n#define VK_FORMAT_R16G16_UINT (81)\n#define VK_FORMAT_R16G16_SINT (82)\n#define VK_FORMAT_R16G16_SFLOAT (83)\n#define VK_FORMAT_R16G16B16_UNORM (84)\n#define VK_FORMAT_R16G16B16_SNORM (85)\n#define VK_FORMAT_R16G16B16_USCALED (86)\n#define VK_FORMAT_R16G16B16_SSCALED (87)\n#define VK_FORMAT_R16G16B16_UINT (88)\n#define VK_FORMAT_R16G16B16_SINT (89)\n#define VK_FORMAT_R16G16B16_SFLOAT (90)\n#define VK_FORMAT_R16G16B16A16_UNORM (91)\n#define VK_FORMAT_R16G16B16A16_SNORM (92)\n#define VK_FORMAT_R16G16B16A16_USCALED (93)\n#define VK_FORMAT_R16G16B16A16_SSCALED (94)\n#define VK_FORMAT_R16G16B16A16_UINT (95)\n#define VK_FORMAT_R16G16B16A16_SINT (96)\n#define VK_FORMAT_R16G16B16A16_SFLOAT (97)\n#define VK_FORMAT_R32_UINT (98)\n#define VK_FORMAT_R32_SINT (99)\n#define VK_FORMAT_R32_SFLOAT (100)\n#define VK_FORMAT_R32G32_UINT (101)\n#define VK_FORMAT_R32G32_SINT (102)\n#define VK_FORMAT_R32G32_SFLOAT (103)\n#define VK_FORMAT_R32G32B32_UINT (104)\n#define VK_FORMAT_R32G32B32_SINT (105)\n#define VK_FORMAT_R32G32B32_SFLOAT (106)\n#define VK_FORMAT_R32G32B32A32_UINT (107)\n#define VK_FORMAT_R32G32B32A32_SINT (108)\n#define VK_FORMAT_R32G32B32A32_SFLOAT (109)\n#define VK_FORMAT_R64_UINT (110)\n#define VK_FORMAT_R64_SINT (111)\n#define VK_FORMAT_R64_SFLOAT (112)\n#define VK_FORMAT_R64G64_UINT (113)\n#define VK_FORMAT_R64G64_SINT (114)\n#define VK_FORMAT_R64G64_SFLOAT (115)\n#define VK_FORMAT_R64G64B64_UINT (116)\n#define VK_FORMAT_R64G64B64_SINT (117)\n#define VK_FORMAT_R64G64B64_SFLOAT (118)\n#define VK_FORMAT_R64G64B64A64_UINT (119)\n#define VK_FORMAT_R64G64B64A64_SINT (120)\n#define VK_FORMAT_R64G64B64A64_SFLOAT (121)\n#define VK_FORMAT_B10G11R11_UFLOAT_PACK32 (122)\n#define VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 (123)\n#define VK_FORMAT_D16_UNORM (124)\n#define VK_FORMAT_X8_D24_UNORM_PACK32 (125)\n#define VK_FORMAT_D32_SFLOAT (126)\n#define VK_FORMAT_S8_UINT (127)\n#define VK_FORMAT_D16_UNORM_S8_UINT (128)\n#define VK_FORMAT_D24_UNORM_S8_UINT (129)\n#define VK_FORMAT_D32_SFLOAT_S8_UINT (130)\n#define VK_FORMAT_BC1_RGB_UNORM_BLOCK (131)\n#define VK_FORMAT_BC1_RGB_SRGB_BLOCK (132)\n#define VK_FORMAT_BC1_RGBA_UNORM_BLOCK (133)\n#define VK_FORMAT_BC1_RGBA_SRGB_BLOCK (134)\n#define VK_FORMAT_BC2_UNORM_BLOCK (135)\n#define VK_FORMAT_BC2_SRGB_BLOCK (136)\n#define VK_FORMAT_BC3_UNORM_BLOCK (137)\n#define VK_FORMAT_BC3_SRGB_BLOCK (138)\n#define VK_FORMAT_BC4_UNORM_BLOCK (139)\n#define VK_FORMAT_BC4_SNORM_BLOCK (140)\n#define VK_FORMAT_BC5_UNORM_BLOCK (141)\n#define VK_FORMAT_BC5_SNORM_BLOCK (142)\n#define VK_FORMAT_BC6H_UFLOAT_BLOCK (143)\n#define VK_FORMAT_BC6H_SFLOAT_BLOCK (144)\n#define VK_FORMAT_BC7_UNORM_BLOCK (145)\n#define VK_FORMAT_BC7_SRGB_BLOCK (146)\n#define VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK (147)\n#define VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK (148)\n#define VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK (149)\n#define VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK (150)\n#define VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK (151)\n#define VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK (152)\n#define VK_FORMAT_EAC_R11_UNORM_BLOCK (153)\n#define VK_FORMAT_EAC_R11_SNORM_BLOCK (154)\n#define VK_FORMAT_EAC_R11G11_UNORM_BLOCK (155)\n#define VK_FORMAT_EAC_R11G11_SNORM_BLOCK (156)\n#define VK_FORMAT_ASTC_4x4_UNORM_BLOCK (157)\n#define VK_FORMAT_ASTC_4x4_SRGB_BLOCK (158)\n#define VK_FORMAT_ASTC_5x4_UNORM_BLOCK (159)\n#define VK_FORMAT_ASTC_5x4_SRGB_BLOCK (160)\n#define VK_FORMAT_ASTC_5x5_UNORM_BLOCK (161)\n#define VK_FORMAT_ASTC_5x5_SRGB_BLOCK (162)\n#define VK_FORMAT_ASTC_6x5_UNORM_BLOCK (163)\n#define VK_FORMAT_ASTC_6x5_SRGB_BLOCK (164)\n#define VK_FORMAT_ASTC_6x6_UNORM_BLOCK (165)\n#define VK_FORMAT_ASTC_6x6_SRGB_BLOCK (166)\n#define VK_FORMAT_ASTC_8x5_UNORM_BLOCK (167)\n#define VK_FORMAT_ASTC_8x5_SRGB_BLOCK (168)\n#define VK_FORMAT_ASTC_8x6_UNORM_BLOCK (169)\n#define VK_FORMAT_ASTC_8x6_SRGB_BLOCK (170)\n#define VK_FORMAT_ASTC_8x8_UNORM_BLOCK (171)\n#define VK_FORMAT_ASTC_8x8_SRGB_BLOCK (172)\n#define VK_FORMAT_ASTC_10x5_UNORM_BLOCK (173)\n#define VK_FORMAT_ASTC_10x5_SRGB_BLOCK (174)\n#define VK_FORMAT_ASTC_10x6_UNORM_BLOCK (175)\n#define VK_FORMAT_ASTC_10x6_SRGB_BLOCK (176)\n#define VK_FORMAT_ASTC_10x8_UNORM_BLOCK (177)\n#define VK_FORMAT_ASTC_10x8_SRGB_BLOCK (178)\n#define VK_FORMAT_ASTC_10x10_UNORM_BLOCK (179)\n#define VK_FORMAT_ASTC_10x10_SRGB_BLOCK (180)\n#define VK_FORMAT_ASTC_12x10_UNORM_BLOCK (181)\n#define VK_FORMAT_ASTC_12x10_SRGB_BLOCK (182)\n#define VK_FORMAT_ASTC_12x12_UNORM_BLOCK (183)\n#define VK_FORMAT_ASTC_12x12_SRGB_BLOCK (184)\n#define VK_FORMAT_A4R4G4B4_UNORM_PACK16 (1000340000)\n#define VK_FORMAT_A4B4G4R4_UNORM_PACK16 (1000340001)\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_fullscreen_geom.geom",
    "content": "#version 450\n\nlayout(triangles) in;\nlayout(triangle_strip, max_vertices = 3) out;\n\nlayout(location = 0) in  int  i_instance[3];\nlayout(location = 1) in  vec2 i_texcoord[3];\nlayout(location = 0) out vec2 o_texcoord;\n\nvoid main() {\n  for (int i = 0; i < 3; i++) {\n    o_texcoord  = i_texcoord[i];\n    gl_Layer    = i_instance[i];\n    gl_Position = gl_in[i].gl_Position;\n    EmitVertex();\n  }\n  \n  EndPrimitive();\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_fullscreen_layer_vert.vert",
    "content": "#version 450\n\n#extension GL_ARB_shader_viewport_layer_array : enable\n\nlayout(location = 0) out vec2 o_texcoord;\n\nvoid main() {\n  vec2 coord = vec2(\n    float(gl_VertexIndex & 2),\n    float(gl_VertexIndex & 1) * 2.0f);\n\n  o_texcoord  = coord;\n  gl_Layer    = gl_InstanceIndex;\n  gl_Position = vec4(-1.0f + 2.0f * coord, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_fullscreen_vert.vert",
    "content": "#version 450\n\nlayout(location = 0) out int  o_instance;\nlayout(location = 1) out vec2 o_texcoord;\n\nvoid main() {\n  vec2 coord = vec2(\n    float(gl_VertexIndex & 2),\n    float(gl_VertexIndex & 1) * 2.0f);\n\n  o_instance  = gl_InstanceIndex;\n  o_texcoord  = coord;\n  gl_Position = vec4(-1.0f + 2.0f * coord, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_image_to_buffer_ds.comp",
    "content": "#version 460\n\nlayout(local_size_x = 16, local_size_y = 16) in;\n\n#extension GL_GOOGLE_include_directive : enable\n#extension GL_EXT_samplerless_texture_functions : enable\n\n#include \"dxvk_formats.glsl\"\n\nlayout(constant_id = 0) const uint dst_format = VK_FORMAT_UNDEFINED;\n\nlayout(binding = 0) uniform writeonly uimageBuffer u_buffer;\nlayout(binding = 1) uniform texture2DArray u_depth;\nlayout(binding = 2) uniform utexture2DArray u_stencil;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec3 image_offset;\n  uint buffer_offset;\n  uvec3 image_extent;\n  uint buffer_image_width;\n  uint buffer_image_height;\n  uint stencil_bit_index;\n};\n\nvoid main() {\n  uvec3 location = uvec3(gl_GlobalInvocationID);\n\n  if (any(greaterThanEqual(location, image_extent)))\n    return;\n\n  int offset = int(buffer_offset + location.x +\n    buffer_image_width * (location.y + buffer_image_height * location.z));\n\n  uvec4 dst_value = uvec4(0u);\n\n  float src_depth = texelFetch(u_depth, ivec3(location.xy + image_offset.xy, location.z), 0).x;\n  uint src_stencil = texelFetch(u_stencil, ivec3(location.xy + image_offset.xy, location.z), 0).x;\n\n  switch (dst_format) {\n    case VK_FORMAT_D16_UNORM:\n    case VK_FORMAT_D16_UNORM_S8_UINT:\n      dst_value.x = uint(roundEven(src_depth * float(0xffffu)));\n      dst_value.y = src_stencil;\n      break;\n\n    case VK_FORMAT_D24_UNORM_S8_UINT:\n    case VK_FORMAT_X8_D24_UNORM_PACK32:\n      dst_value.x = uint(roundEven(src_depth * float(0xffffffu)));\n      dst_value.x |= src_stencil << 24;\n      break;\n\n    case VK_FORMAT_D32_SFLOAT:\n    case VK_FORMAT_D32_SFLOAT_S8_UINT:\n      dst_value.x = floatBitsToUint(src_depth);\n      dst_value.y = src_stencil;\n      break;\n\n    case VK_FORMAT_S8_UINT:\n      dst_value.x = src_stencil;\n      break;\n  }\n\n  imageStore(u_buffer, offset, dst_value);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_image_to_buffer_f.comp",
    "content": "#version 460\n\nlayout(local_size_x = 16, local_size_y = 16) in;\n\n#extension GL_GOOGLE_include_directive : enable\n#extension GL_EXT_samplerless_texture_functions : enable\n\n#include \"dxvk_formats.glsl\"\n\nlayout(constant_id = 0) const uint dst_format = VK_FORMAT_UNDEFINED;\n\nlayout(binding = 0) uniform writeonly uimageBuffer u_buffer;\nlayout(binding = 1) uniform texture2DArray u_image;\n\nlayout(push_constant)\nuniform push_data_t {\n  uvec3 image_offset;\n  uint buffer_offset;\n  uvec3 image_extent;\n  uint buffer_image_width;\n  uint buffer_image_height;\n  uint stencil_bit_index;\n};\n\nvoid main() {\n  uvec3 location = uvec3(gl_GlobalInvocationID);\n\n  if (any(greaterThanEqual(location, image_extent)))\n    return;\n\n  int offset = int(buffer_offset + location.x +\n    buffer_image_width * (location.y + buffer_image_height * location.z));\n\n  uvec4 dst_color = uvec4(0u);\n  vec4 src_color = texelFetch(u_image, ivec3(location.xy + image_offset.xy, location.z), 0);\n\n  switch (dst_format) {\n    case VK_FORMAT_D16_UNORM:\n    case VK_FORMAT_D16_UNORM_S8_UINT:\n      dst_color.x = uint(roundEven(src_color.x * float(0xffffu)));\n      break;\n\n    case VK_FORMAT_D24_UNORM_S8_UINT:\n    case VK_FORMAT_X8_D24_UNORM_PACK32:\n      dst_color.x = uint(roundEven(src_color.x * float(0xffffffu)));\n      break;\n\n    case VK_FORMAT_D32_SFLOAT:\n    case VK_FORMAT_D32_SFLOAT_S8_UINT:\n      dst_color.x = floatBitsToUint(src_color.x);\n      break;\n  }\n\n  imageStore(u_buffer, offset, dst_color);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_mipgen.comp",
    "content": "#version 460\n\n#pragma use_vulkan_memory_model\n\n#extension GL_EXT_buffer_reference2 : enable\n#extension GL_EXT_control_flow_attributes : enable\n#extension GL_EXT_nonuniform_qualifier : enable\n#extension GL_EXT_null_initializer : enable\n#extension GL_EXT_samplerless_texture_functions : enable\n#extension GL_EXT_scalar_block_layout : enable\n#extension GL_EXT_shader_explicit_arithmetic_types : enable\n#extension GL_EXT_shader_image_load_formatted : enable\n#extension GL_EXT_subgroup_uniform_control_flow : enable\n\n#extension GL_KHR_memory_scope_semantics : enable\n#extension GL_KHR_shader_subgroup_basic : enable\n#extension GL_KHR_shader_subgroup_ballot : enable\n#extension GL_KHR_shader_subgroup_shuffle : enable\n\n#extension GL_GOOGLE_include_directive : enable\n\n#define FORMAT_MODE_NONE  0\n#define FORMAT_MODE_F16x2 1\n#define FORMAT_MODE_F16x4 2\n#define FORMAT_MODE_F32x1 3\n#define FORMAT_MODE_F32x2 4\n#define FORMAT_MODE_F32x4 5\n\n#include \"dxvk_formats.glsl\"\n\n#define USE_SUBGROUP_PATH (1)\n\n#define MAX_MIP_COUNT (6)\n\n/* Workgroup size. Can be chosen somewhat arbitrarily, and for larger\n * formats this shader may need to run with larger workgroups in order\n * to not suffer too much from the increased LDS usage. */\nlayout(local_size_x = 512) in;\n\n/* Image format */\nlayout(constant_id = 0) const uint c_format = VK_FORMAT_UNDEFINED;\n\n/* Number of DWORDs required to store a single pixel in LDS. Must match\n * the format size exactly as this will actually do packing. */\nlayout(constant_id = 1) const uint c_format_dwords = 2u;\n\n\n/* Global sampler heap */\nlayout(set = 0, binding = 0)\nuniform sampler s_samplers[];\n\n\n/* Source mip level */\nlayout(set = 1, binding = 0)\nuniform texture2DArray s_base_mip;\n\n\n/* Mip levels that will be written */\nlayout(set = 1, binding = 1)\nuniform queuefamilycoherent image2DArray s_dst_mips[MAX_MIP_COUNT + MAX_MIP_COUNT];\n\n\n/* Address of atomic counter used to synchronize workgroups.\n * Must be initialized to 0 by app code once. */\nlayout(buffer_reference, buffer_reference_align = 4, scalar)\nbuffer workgroup_ctr_t {\n  uint counters[];\n};\n\n\n/* Shader parameters */\nlayout(push_constant)\nuniform push_data_t {\n  workgroup_ctr_t ctr;\n  uint sampler_index;\n  uint mip_count;\n} push_args;\n\n\n\n/* Mip n of any given image can have a size of 2*size(n-1)+1, so we need to\n * allocate for one extra mip minus one pixel. However, we can also skip the\n * top-level mip since we will filter it directly, so it evens out. */\nconst uint LDS_SIZE = (1u << MAX_MIP_COUNT) - 1u;\n\n/* Allocate given number of dwords per pixel, shouldn't be more than a total\n * of 32k LDS per workgroup and even that is going to be stretching things a\n * little w.r.t. occupancy. */\nshared uint g_pixels[LDS_SIZE * LDS_SIZE * c_format_dwords];\n\n\n/* Pixel type. We typically use FP16 internally, but for formats with larger\n * value ranges, as well as 16-bit normalized formats, we want FP32 to be\n * able to represent everything. */\n#define pixel_t uvec4\n\n\n/* Quad of pixels */\nstruct quad_t {\n  pixel_t px00;\n  pixel_t px01;\n  pixel_t px10;\n  pixel_t px11;\n};\n\n\nuint determine_format_mode() {\n  switch (c_format) {\n    case VK_FORMAT_R8_UNORM:\n    case VK_FORMAT_R8_SNORM:\n    case VK_FORMAT_R8G8_UNORM:\n    case VK_FORMAT_R8G8_SNORM:\n    case VK_FORMAT_R16_SFLOAT:\n    case VK_FORMAT_R16G16_SFLOAT:\n      return FORMAT_MODE_F16x2;\n\n    case VK_FORMAT_R8G8B8A8_UNORM:\n    case VK_FORMAT_B8G8R8A8_UNORM:\n    case VK_FORMAT_A8B8G8R8_UNORM_PACK32:\n    case VK_FORMAT_R8G8B8A8_SNORM:\n    case VK_FORMAT_B8G8R8A8_SNORM:\n    case VK_FORMAT_A8B8G8R8_SNORM_PACK32:\n    case VK_FORMAT_A2R10G10B10_UNORM_PACK32:\n    case VK_FORMAT_A2B10G10R10_UNORM_PACK32:\n    case VK_FORMAT_A2R10G10B10_SNORM_PACK32:\n    case VK_FORMAT_A2B10G10R10_SNORM_PACK32:\n    case VK_FORMAT_B10G11R11_UFLOAT_PACK32:\n    case VK_FORMAT_R16G16B16A16_SFLOAT:\n      return FORMAT_MODE_F16x4;\n\n    case VK_FORMAT_R16_UNORM:\n    case VK_FORMAT_R16_SNORM:\n    case VK_FORMAT_R32_SFLOAT:\n      return FORMAT_MODE_F32x1;\n\n    case VK_FORMAT_R16G16_UNORM:\n    case VK_FORMAT_R16G16_SNORM:\n    case VK_FORMAT_R32G32_SFLOAT:\n      return FORMAT_MODE_F32x2;\n\n    case VK_FORMAT_R16G16B16A16_UNORM:\n    case VK_FORMAT_R16G16B16A16_SNORM:\n      return FORMAT_MODE_F32x4;\n  }\n\n  return FORMAT_MODE_NONE;\n}\n\n\npixel_t px_set_f16(f16vec4 f) {\n  pixel_t result = {};\n\n  switch (determine_format_mode()) {\n    case FORMAT_MODE_F16x2: {\n      result.x = packFloat2x16(f.xy);\n    } break;\n\n    case FORMAT_MODE_F16x4: {\n      result.x = packFloat2x16(f.xy);\n      result.y = packFloat2x16(f.zw);\n    } break;\n\n    case FORMAT_MODE_F32x1: {\n      result.x = floatBitsToUint(float(f.x));\n    } break;\n\n    case FORMAT_MODE_F32x2: {\n      result.xy = floatBitsToUint(vec2(f.xy));\n    } break;\n\n    case FORMAT_MODE_F32x4: {\n      result = floatBitsToUint(vec4(f));\n    } break;\n  }\n\n  return result;\n}\n\n\nf16vec4 px_as_f16(pixel_t px) {\n  f16vec4 result = {};\n\n  switch (determine_format_mode()) {\n    case FORMAT_MODE_F16x2: {\n      result.xy = unpackFloat2x16(px.x);\n    } break;\n\n    case FORMAT_MODE_F16x4: {\n      result.xy = unpackFloat2x16(px.x);\n      result.zw = unpackFloat2x16(px.y);\n    } break;\n\n    case FORMAT_MODE_F32x1: {\n      result.x = float16_t(uintBitsToFloat(px.x));\n    } break;\n\n    case FORMAT_MODE_F32x2: {\n      result.xy = f16vec2(uintBitsToFloat(px.xy));\n    } break;\n\n    case FORMAT_MODE_F32x4: {\n      result = f16vec4(uintBitsToFloat(px));\n    } break;\n  }\n\n  return result;\n}\n\n\npixel_t px_set_f32(vec4 f) {\n  pixel_t result = {};\n\n  switch (determine_format_mode()) {\n    case FORMAT_MODE_F16x2: {\n      result.x = packFloat2x16(f16vec2(f.xy));\n    } break;\n\n    case FORMAT_MODE_F16x4: {\n      result.x = packFloat2x16(f16vec2(f.xy));\n      result.y = packFloat2x16(f16vec2(f.zw));\n    } break;\n\n    case FORMAT_MODE_F32x1: {\n      result.x = floatBitsToUint(f.x);\n    } break;\n\n    case FORMAT_MODE_F32x2: {\n      result.xy = floatBitsToUint(f.xy);\n    } break;\n\n    case FORMAT_MODE_F32x4: {\n      result = floatBitsToUint(f);\n    } break;\n  }\n\n  return result;\n}\n\n\nvec4 px_as_f32(pixel_t px) {\n  vec4 result = {};\n\n  switch (determine_format_mode()) {\n    case FORMAT_MODE_F16x2: {\n      result.xy = vec2(unpackFloat2x16(px.x));\n    } break;\n\n    case FORMAT_MODE_F16x4: {\n      result.xy = vec2(unpackFloat2x16(px.x));\n      result.zw = vec2(unpackFloat2x16(px.y));\n    } break;\n\n    case FORMAT_MODE_F32x1: {\n      result.x = uintBitsToFloat(px.x);\n    } break;\n\n    case FORMAT_MODE_F32x2: {\n      result.xy = uintBitsToFloat(px.xy);\n    } break;\n\n    case FORMAT_MODE_F32x4: {\n      result = uintBitsToFloat(px);\n    } break;\n  }\n\n  return result;\n}\n\n\npixel_t px_fadd(pixel_t a, pixel_t b) {\n  switch (determine_format_mode()) {\n    case FORMAT_MODE_F16x2:\n    case FORMAT_MODE_F16x4:\n      return px_set_f16(px_as_f16(a) + px_as_f16(b));\n\n    case FORMAT_MODE_F32x1:\n    case FORMAT_MODE_F32x2:\n    case FORMAT_MODE_F32x4:\n      return px_set_f32(px_as_f32(a) + px_as_f32(b));\n  }\n\n  return pixel_t(0u);\n}\n\n\npixel_t px_fscale(pixel_t a, vec4 b) {\n  switch (determine_format_mode()) {\n    case FORMAT_MODE_F16x2:\n    case FORMAT_MODE_F16x4:\n      return px_set_f16(px_as_f16(a) * f16vec4(b));\n\n    case FORMAT_MODE_F32x1:\n    case FORMAT_MODE_F32x2:\n    case FORMAT_MODE_F32x4:\n      return px_set_f32(px_as_f32(a) * b);\n  }\n\n  return pixel_t(0u);\n}\n\n\npixel_t px_fmix(pixel_t a, pixel_t b, float x) {\n  switch (determine_format_mode()) {\n    case FORMAT_MODE_F16x2:\n    case FORMAT_MODE_F16x4:\n      return px_set_f16(mix(px_as_f16(a), px_as_f16(b), float16_t(x)));\n\n    case FORMAT_MODE_F32x1:\n    case FORMAT_MODE_F32x2:\n    case FORMAT_MODE_F32x4:\n      return px_set_f32(mix(px_as_f32(a), px_as_f32(b), x));\n  }\n\n  return pixel_t(0u);\n}\n\n\npixel_t px_quantize(pixel_t a, vec4 b) {\n  switch (determine_format_mode()) {\n    case FORMAT_MODE_F16x2:\n    case FORMAT_MODE_F16x4:\n      return px_set_f16(roundEven(px_as_f16(a) * f16vec4(b)) / f16vec4(b));\n\n    case FORMAT_MODE_F32x1:\n    case FORMAT_MODE_F32x2:\n    case FORMAT_MODE_F32x4:\n      return px_set_f32(roundEven(px_as_f32(a) * b) / b);\n  }\n\n  return pixel_t(0u);\n}\n\n\n/* Helper to decode unmasked 6-bit morton code into 2D coordinate.\n * Used to establish Z-order curve for the subgroup operations. */\nuvec2 decode_morton_2d(uint tid) {\n  uint coord = tid | (tid << 7u);\n  coord &= 0x1515u;\n  coord += coord & 0x0101u;\n  coord += coord & 0x0606u;\n\n  return uvec2(\n    bitfieldExtract(coord,  2, 3),\n    bitfieldExtract(coord, 10, 3));\n}\n\n\n/* Pixel block dimensions (.x), pixel count (.y) and even mip count (.z).\n * Known at compile time, and always small enough that one block fits into\n * one subgroup. */\nuvec3 determine_block_size() {\n  if (gl_SubgroupSize <  4u) return uvec3(1u,  1u, 0u * USE_SUBGROUP_PATH);\n  if (gl_SubgroupSize < 16u) return uvec3(2u,  4u, 1u * USE_SUBGROUP_PATH);\n  if (gl_SubgroupSize < 64u) return uvec3(4u, 16u, 2u * USE_SUBGROUP_PATH);\n  return uvec3(8u, 64u, 3u * USE_SUBGROUP_PATH);\n}\n\n\n/* Base coordinate that the workgroup is going to write into the shared mip.\n * Used to compute coordinates to read and write for individual threads. */\nuvec2 determine_base_coord(uint base_mip) {\n  return base_mip < MAX_MIP_COUNT ? gl_WorkGroupID.xy : uvec2(0u);\n}\n\n\n/* Computes size of a given mip level based on the top level mip. */\nuvec2 compute_mip_size(uvec2 mip0_size, uint mip) {\n  return max(mip0_size >> mip, 1u.xx);\n}\n\n\n/* Computes size of the shared mip based on the mip 0 size. The\n * dispatch size must match this. */\nuvec2 compute_shared_mip_size(uvec2 mip0_size) {\n  return compute_mip_size(mip0_size, MAX_MIP_COUNT);\n}\n\n\n/* Computes mask of odd-sized mips in each dimension.\n *\n * This is actually almost equivalent to the image size itself. Since\n * mips simply double in size, simply shifting the image size by the\n * mip count will return 1 LSB for odd mips, and 0 LSB for even.\n *\n * We also need to consider any mips as odd hat have a size of 1 in\n * any given dimension, so we actually need to scan both bit masks. */\nuint compute_odd_mip_mask_flat(uvec2 mip0_size) {\n  int msb = min(findMSB(mip0_size.x), findMSB(mip0_size.y));\n  return bitfieldInsert(~0u, mip0_size.x | mip0_size.y, 0, msb);\n}\n\n\n/* Convenience check on whether a mip is even. */\nbool is_mip_odd(uvec2 mip0_size, uint mip) {\n  return bitfieldExtract(compute_odd_mip_mask_flat(mip0_size), int(mip), 1) != 0;\n}\n\n\n/* Determines pixel coordinates that the current workgroup will read\n * from the given mip to process the *next* mip, counting from the top\n * of the mip chain. The .xy coordiates receive the top-left coordinate\n * (inclusive), and the .zw coordiates the bottom right (exclusive). */\nuvec4 compute_read_rect(uvec2 mip0_size, uint mip) {\n  if (mip > MAX_MIP_COUNT)\n    return uvec4(0u, 0u, compute_mip_size(mip0_size, mip));\n\n  uvec2 this_group = (gl_WorkGroupID.xy + 0u) << (MAX_MIP_COUNT - mip);\n  uvec2 next_group = (gl_WorkGroupID.xy + 1u) << (MAX_MIP_COUNT - mip);\n\n  /* Take into account that odd mips need to read a 3x3 region for each\n   * pixel of the next smaller mip. */\n  next_group.x += bitfieldExtract(mip0_size.x, int(mip), int(MAX_MIP_COUNT - mip));\n  next_group.y += bitfieldExtract(mip0_size.y, int(mip), int(MAX_MIP_COUNT - mip));\n\n  return uvec4(this_group, next_group);\n}\n\n\n/* Determines pixel coordinates that the current workgroup will write\n * for the given mip. This ensures that pixels are only ever written\n * once, even if read image regions may overlap in some cases. */\nuvec4 compute_write_rect(uvec2 mip0_size, uint mip) {\n  uvec2 mip_size = compute_mip_size(mip0_size, mip);\n\n  if (mip > MAX_MIP_COUNT)\n    return uvec4(0u.xx, mip_size);\n\n  uvec2 this_group = (gl_WorkGroupID.xy + 0u) << (MAX_MIP_COUNT - mip);\n  uvec2 next_group = (gl_WorkGroupID.xy + 1u) << (MAX_MIP_COUNT - mip);\n\n  /* Make sure that the last group in each dimension writes everything */\n  uvec2 group_count = compute_shared_mip_size(mip0_size);\n\n  if (gl_WorkGroupID.x + 1u == group_count.x) next_group.x = mip_size.x;\n  if (gl_WorkGroupID.y + 1u == group_count.y) next_group.y = mip_size.y;\n\n  return uvec4(this_group, next_group);\n}\n\n\n/* Helper to quantize pixel data between interpolation steps. */\npixel_t quantize_pixel(pixel_t data) {\n  switch (c_format) {\n    case VK_FORMAT_R8_UNORM:\n    case VK_FORMAT_R8G8_UNORM:\n    case VK_FORMAT_R8G8B8A8_UNORM:\n    case VK_FORMAT_B8G8R8A8_UNORM:\n    case VK_FORMAT_A8B8G8R8_UNORM_PACK32:\n      return px_quantize(data, 255.0f.xxxx);\n\n    case VK_FORMAT_R8_SNORM:\n    case VK_FORMAT_R8G8_SNORM:\n    case VK_FORMAT_R8G8B8A8_SNORM:\n    case VK_FORMAT_B8G8R8A8_SNORM:\n    case VK_FORMAT_A8B8G8R8_SNORM_PACK32:\n      return px_quantize(data, 127.0f.xxxx);\n\n    case VK_FORMAT_A2R10G10B10_UNORM_PACK32:\n    case VK_FORMAT_A2B10G10R10_UNORM_PACK32:\n      return px_quantize(data, vec4(1023.0f.xxx, 3.0f));\n\n    case VK_FORMAT_A2R10G10B10_SNORM_PACK32:\n    case VK_FORMAT_A2B10G10R10_SNORM_PACK32:\n      return px_quantize(data, vec4(511.0f.xxx, 1.0f));\n\n    /* Weird special case, ignore since rounding is painful */\n    case VK_FORMAT_B10G11R11_UFLOAT_PACK32:\n      return data;\n\n    case VK_FORMAT_R16_SFLOAT:\n    case VK_FORMAT_R16G16_SFLOAT:\n    case VK_FORMAT_R16G16B16A16_SFLOAT:\n      return data;\n\n    case VK_FORMAT_R16_UNORM:\n    case VK_FORMAT_R16G16_UNORM:\n    case VK_FORMAT_R16G16B16A16_UNORM:\n      return px_quantize(data, 65535.0f.xxxx);\n\n    case VK_FORMAT_R16_SNORM:\n    case VK_FORMAT_R16G16_SNORM:\n    case VK_FORMAT_R16G16B16A16_SNORM:\n      return px_quantize(data, 32767.0f.xxxx);\n\n    case VK_FORMAT_R32_SFLOAT:\n    case VK_FORMAT_R32G32_SFLOAT:\n      return data;\n\n    default:\n      return data;\n  }\n}\n\n\n/* Packs pixel data to compact LDS representation */\nvoid store_pixel_to_lds(uvec2 coord, pixel_t px) {\n  uint idx = LDS_SIZE * coord.y + coord.x;\n  idx *= c_format_dwords;\n\n  switch (c_format) {\n    case VK_FORMAT_R8_UNORM: {\n      float16_t data = float16_t(255.0f) * px_as_f16(px).x;\n      g_pixels[idx] = pack32(u8vec4(roundEven(data), 0u.xxx));\n    } break;\n\n    case VK_FORMAT_R8G8_UNORM: {\n      f16vec2 data = float16_t(255.0f) * px_as_f16(px).xy;\n      g_pixels[idx] = pack32(u8vec4(roundEven(data.xy), 0u.xx));\n    } break;\n\n    case VK_FORMAT_R8G8B8A8_UNORM:\n    case VK_FORMAT_B8G8R8A8_UNORM:\n    case VK_FORMAT_A8B8G8R8_UNORM_PACK32: {\n      f16vec4 data = float16_t(255.0f) * px_as_f16(px);\n      g_pixels[idx] = pack32(u8vec4(roundEven(data)));\n    } break;\n\n    case VK_FORMAT_R8_SNORM: {\n      float16_t data = float16_t(127.0f) * px_as_f16(px).x;\n      g_pixels[idx] = pack32(u8vec4(roundEven(data), 0u.xxx));\n    } break;\n\n    case VK_FORMAT_R8G8_SNORM: {\n      f16vec2 data = float16_t(127.0f) * px_as_f16(px).xy;\n      g_pixels[idx] = pack32(u8vec4(roundEven(data.xy), 0u.xx));\n    } break;\n\n    case VK_FORMAT_R8G8B8A8_SNORM:\n    case VK_FORMAT_B8G8R8A8_SNORM:\n    case VK_FORMAT_A8B8G8R8_SNORM_PACK32: {\n      f16vec4 data = float16_t(127.0f) * px_as_f16(px);\n      g_pixels[idx] = pack32(u8vec4(roundEven(data)));\n    } break;\n\n    case VK_FORMAT_A2R10G10B10_UNORM_PACK32:\n    case VK_FORMAT_A2B10G10R10_UNORM_PACK32: {\n      f16vec4 data = f16vec4(1023.0f.xxx, 3.0f) * px_as_f16(px);\n      uvec4 udata = uvec4(roundEven(data));\n\n      g_pixels[idx] = (bitfieldExtract(udata.x, 0, 10) <<  0) |\n                      (bitfieldExtract(udata.y, 0, 10) << 10) |\n                      (bitfieldExtract(udata.z, 0, 10) << 20) |\n                      (bitfieldExtract(udata.w, 0,  2) << 30);\n    } break;\n\n    case VK_FORMAT_A2R10G10B10_SNORM_PACK32:\n    case VK_FORMAT_A2B10G10R10_SNORM_PACK32: {\n      f16vec4 data = f16vec4(511.0f.xxx, 1.0f) * px_as_f16(px);\n      uvec4 udata = uvec4(ivec4(roundEven(data)));\n\n      g_pixels[idx] = (bitfieldExtract(udata.x, 0, 10) <<  0) |\n                      (bitfieldExtract(udata.y, 0, 10) << 10) |\n                      (bitfieldExtract(udata.z, 0, 10) << 20) |\n                      (bitfieldExtract(udata.w, 0,  2) << 30);\n    } break;\n\n    case VK_FORMAT_B10G11R11_UFLOAT_PACK32: {\n      g_pixels[idx] = (bitfieldExtract(px.x,  4, 11) <<  0) |\n                      (bitfieldExtract(px.x, 20, 11) << 11) |\n                      (bitfieldExtract(px.y,  5, 10) << 22);\n    } break;\n\n    case VK_FORMAT_R16_SFLOAT:\n    case VK_FORMAT_R16G16_SFLOAT: {\n      g_pixels[idx] = packFloat2x16(px_as_f16(px).xy);\n    } break;\n\n    case VK_FORMAT_R16G16B16A16_SFLOAT: {\n      g_pixels[idx + 0u] = packFloat2x16(px_as_f16(px).xy);\n      g_pixels[idx + 1u] = packFloat2x16(px_as_f16(px).zw);\n    } break;\n\n    case VK_FORMAT_R16_UNORM: {\n      g_pixels[idx] = packUnorm2x16(vec2(px_as_f32(px).x, 0.0f));\n    } break;\n\n    case VK_FORMAT_R16_SNORM: {\n      g_pixels[idx] = packSnorm2x16(vec2(px_as_f32(px).x, 0.0f));\n    } break;\n\n    case VK_FORMAT_R16G16_UNORM: {\n      g_pixels[idx] = packUnorm2x16(px_as_f32(px).xy);\n    } break;\n\n    case VK_FORMAT_R16G16_SNORM: {\n      g_pixels[idx] = packSnorm2x16(px_as_f32(px).xy);\n    } break;\n\n    case VK_FORMAT_R16G16B16A16_UNORM: {\n      g_pixels[idx + 0u] = packUnorm2x16(px_as_f32(px).xy);\n      g_pixels[idx + 1u] = packUnorm2x16(px_as_f32(px).zw);\n    } break;\n\n    case VK_FORMAT_R16G16B16A16_SNORM: {\n      g_pixels[idx + 0u] = packSnorm2x16(px_as_f32(px).xy);\n      g_pixels[idx + 1u] = packSnorm2x16(px_as_f32(px).zw);\n    } break;\n\n    case VK_FORMAT_R32_SFLOAT: {\n      g_pixels[idx] = floatBitsToUint(px_as_f32(px).x);\n    } break;\n\n    case VK_FORMAT_R32G32_SFLOAT: {\n      g_pixels[idx + 0u] = floatBitsToUint(px_as_f32(px).x);\n      g_pixels[idx + 1u] = floatBitsToUint(px_as_f32(px).y);\n    } break;\n  }\n}\n\n\n/* Unpacks pixel from LDS reresentation */\npixel_t load_pixel_from_lds(uvec2 coord, uvec2 ofs) {\n  uint idx = LDS_SIZE * coord.y + coord.x;\n  idx += LDS_SIZE * ofs.y + ofs.x;\n  idx *= c_format_dwords;\n\n  uvec2 px = {};\n  px.x = g_pixels[idx];\n\n  if (c_format_dwords >= 2u)\n    px.y = g_pixels[idx + 1u];\n\n  switch (c_format) {\n    case VK_FORMAT_R8_UNORM: {\n      float16_t data = float16_t(unpack8(px.x).x) / float16_t(255.0f);\n      return px_set_f16(f16vec4(data, 0.0f.xxx));\n    }\n\n    case VK_FORMAT_R8G8_UNORM: {\n      f16vec2 data = f16vec2(unpack8(px.x).xy) / float16_t(255.0f);\n      return px_set_f16(f16vec4(data, 0.0f.xx));\n    }\n\n    case VK_FORMAT_R8G8B8A8_UNORM:\n    case VK_FORMAT_B8G8R8A8_UNORM:\n    case VK_FORMAT_A8B8G8R8_UNORM_PACK32: {\n      f16vec4 data = f16vec4(unpack8(px.x)) / float16_t(255.0f);\n      return px_set_f16(data);\n    }\n\n    case VK_FORMAT_R8_SNORM: {\n      float16_t data = float16_t(unpack8(int(px.x)).x) / float16_t(127.0f);\n      return px_set_f16(f16vec4(data, 0.0f.xxx));\n    }\n\n    case VK_FORMAT_R8G8_SNORM: {\n      f16vec2 data = f16vec2(unpack8(int(px.x)).xy) / float16_t(127.0f);\n      return px_set_f16(f16vec4(data, 0.0f.xx));\n    }\n\n    case VK_FORMAT_R8G8B8A8_SNORM:\n    case VK_FORMAT_B8G8R8A8_SNORM:\n    case VK_FORMAT_A8B8G8R8_SNORM_PACK32: {\n      f16vec4 data = f16vec4(unpack8(int(px.x))) / float16_t(127.0f);\n      return px_set_f16(data);\n    }\n\n    case VK_FORMAT_A2R10G10B10_UNORM_PACK32:\n    case VK_FORMAT_A2B10G10R10_UNORM_PACK32: {\n      u16vec4 udata = u16vec4(\n        bitfieldExtract(px.x,  0, 10),\n        bitfieldExtract(px.x, 10, 10),\n        bitfieldExtract(px.x, 20, 10),\n        bitfieldExtract(px.x, 30,  2));\n\n      f16vec4 data = f16vec4(udata) / f16vec4(1023.0f.xxx, 3.0f);\n      return px_set_f16(data);\n    }\n\n    case VK_FORMAT_A2R10G10B10_SNORM_PACK32:\n    case VK_FORMAT_A2B10G10R10_SNORM_PACK32: {\n      i16vec4 udata = i16vec4(\n        bitfieldExtract(px.x,  0, 10),\n        bitfieldExtract(px.x, 10, 10),\n        bitfieldExtract(px.x, 20, 10),\n        bitfieldExtract(px.x, 30,  2));\n\n      f16vec4 data = f16vec4(udata) / f16vec4(511.0f.xxx, 1.0f);\n      return px_set_f16(data);\n    }\n\n    case VK_FORMAT_B10G11R11_UFLOAT_PACK32: {\n      uvec2 udata = uvec2(\n        (bitfieldExtract(px.x,  0, 11) <<  4) |\n        (bitfieldExtract(px.x, 11, 11) << 20),\n        (bitfieldExtract(px.x, 22, 10) <<  5));\n\n      return px_set_f16(f16vec4(\n        unpackFloat2x16(udata.x),\n        unpackFloat2x16(udata.y)));\n    }\n\n    case VK_FORMAT_R16_SFLOAT: {\n      return px_set_f16(f16vec4(\n        unpackFloat2x16(px.x).x, 0.0f.xxx));\n    }\n\n    case VK_FORMAT_R16G16_SFLOAT: {\n      return px_set_f16(f16vec4(\n        unpackFloat2x16(px.x), 0.0f.xx));\n    }\n\n    case VK_FORMAT_R16G16B16A16_SFLOAT: {\n      f16vec2 xy = unpackFloat2x16(px.x);\n      f16vec2 zw = unpackFloat2x16(px.y);\n\n      return px_set_f16(f16vec4(xy, zw));\n    }\n\n    case VK_FORMAT_R16_UNORM: {\n      float data = float(unpack16(px.x).x) / float(65535.0f);\n      return px_set_f32(vec4(data, 0.0f.xxx));\n    }\n\n    case VK_FORMAT_R16_SNORM: {\n      float data = float(unpack16(int(px.x))) / float(32767.0f);\n      return px_set_f32(vec4(data, 0.0f.xxx));\n    }\n\n    case VK_FORMAT_R16G16_UNORM: {\n      vec2 data = vec2(unpack16(px.x)) / float(65535.0f);\n      return px_set_f32(vec4(data, 0.0f.xx));\n    }\n\n    case VK_FORMAT_R16G16_SNORM: {\n      vec2 data = vec2(unpack16(int(px.x))) / float(32767.0f);\n      return px_set_f32(vec4(data, 0.0f.xx));\n    }\n\n    case VK_FORMAT_R16G16B16A16_UNORM: {\n      uvec2 xy = unpack16(px.x);\n      uvec2 zw = unpack16(px.y);\n\n      vec4 data = vec4(xy, zw) / float(65535.0f);\n      return px_set_f32(vec4(data));\n    }\n\n    case VK_FORMAT_R16G16B16A16_SNORM: {\n      uvec2 xy = unpack16(px.x);\n      uvec2 zw = unpack16(px.y);\n\n      vec4 data = vec4(xy, zw) / float(32767.0f);\n      return px_set_f32(vec4(data));\n    }\n\n    case VK_FORMAT_R32_SFLOAT: {\n      return px_set_f32(vec4(\n        uintBitsToFloat(px.x), 0.0f.xxx));\n    }\n\n    case VK_FORMAT_R32G32_SFLOAT: {\n      float x = uintBitsToFloat(px.x);\n      float y = uintBitsToFloat(px.y);\n\n      return px_set_f32(vec4(x, y, 0.0f.xx));\n    }\n  }\n\n  return pixel_t(0u);\n}\n\n\n/* Gigafast path to average and re-quantize four pixels.\n * Must only be used for even-sized mips. */\npixel_t interpolate_quad_center(quad_t q) {\n  pixel_t result = px_fscale(q.px00, 0.25f.xxxx);\n  result = px_fadd(result, px_fscale(q.px01, 0.25f.xxxx));\n  result = px_fadd(result, px_fscale(q.px10, 0.25f.xxxx));\n  result = px_fadd(result, px_fscale(q.px11, 0.25f.xxxx));\n  return result;\n}\n\n\n/* GLSL is such an awesome language that makes writing generic code\n * really clean and easy, and needing special Vulkan features for\n * shuffling f16 vectors that trivially map to uints is such a good\n * design decision, mmmh I love it, so gooood */\n#define shuffle_interpolate(var, cluster_size) do {                 \\\n  var = px_fscale(quantize_pixel(var), 0.25f.xxxx);                 \\\n  var = px_fadd(var, subgroupShuffleXor(var, cluster_size / 4));    \\\n  if (gl_SubgroupSize == cluster_size)                              \\\n    var = px_fadd(var, subgroupBroadcast(var, cluster_size / 2));   \\\n  else                                                              \\\n    var = px_fadd(var, subgroupShuffleXor(var, cluster_size / 2));  \\\n} while (false)\n\n/* Regular sampling path with interpolation. Use for odd-sized mips. */\npixel_t interpolate_quad_at(quad_t q, vec2 coord, uvec2 mip_size) {\n  pixel_t r0 = q.px00;\n  pixel_t r1 = q.px01;\n\n  [[flatten]]\n  if (mip_size.x > 1u) {\n    r0 = px_fmix(r0, q.px10, fract(coord.x));\n    r1 = px_fmix(r1, q.px11, fract(coord.x));\n  }\n\n  [[flatten]]\n  if (mip_size.y > 1u)\n    r0 = px_fmix(r0, r1, fract(coord.y));\n\n  return r0;\n}\n\n\n/* Load quad from shared mip from given image coordinate. */\nquad_t load_quad_from_shared_mip(uvec2 coord) {\n  uint z = gl_GlobalInvocationID.z;\n\n  quad_t result;\n  result.px00 = px_set_f32(imageLoad(s_dst_mips[MAX_MIP_COUNT - 1u], ivec3(coord, z) + ivec3(0, 0, 0)));\n  result.px10 = px_set_f32(imageLoad(s_dst_mips[MAX_MIP_COUNT - 1u], ivec3(coord, z) + ivec3(1, 0, 0)));\n  result.px01 = px_set_f32(imageLoad(s_dst_mips[MAX_MIP_COUNT - 1u], ivec3(coord, z) + ivec3(0, 1, 0)));\n  result.px11 = px_set_f32(imageLoad(s_dst_mips[MAX_MIP_COUNT - 1u], ivec3(coord, z) + ivec3(1, 1, 0)));\n  return result;\n}\n\n\n/* Store pixel into given mip level. Must not be used to write the\n * shared mip, which has its own method. */\nvoid store_pixel_to_mip(uint mip, uvec2 coord, pixel_t data) {\n  uint z = gl_GlobalInvocationID.z;\n  vec4 px = px_as_f32(data);\n\n  imageStore(s_dst_mips[mip - 1u], ivec3(coord, z), px);\n}\n\n\n/* Load quad at given coordinate from LDS */\nquad_t load_quad_from_lds(uvec2 coord) {\n  quad_t result;\n  result.px00 = load_pixel_from_lds(coord, uvec2(0u, 0u));\n  result.px10 = load_pixel_from_lds(coord, uvec2(1u, 0u));\n  result.px01 = load_pixel_from_lds(coord, uvec2(0u, 1u));\n  result.px11 = load_pixel_from_lds(coord, uvec2(1u, 1u));\n  return result;\n}\n\n\n/* Samples base mip or the given destination coordinate. */\npixel_t filter_base_mip(uvec2 mip0_size, uvec2 dst_coord) {\n  uvec2 dst_size = compute_mip_size(mip0_size, 1u);\n  vec2 src_coord_f = (vec2(dst_coord) + 0.5f) / vec2(dst_size);\n\n  vec4 data = textureLod(sampler2DArray(s_base_mip, s_samplers[push_args.sampler_index]),\n    vec3(src_coord_f, float(gl_GlobalInvocationID.z)), 0.0f);\n  return px_set_f32(data);\n}\n\n\n/* Samples shared mip for the given destination coordinate. */\npixel_t filter_shared_mip(uvec2 mip0_size, uvec2 dst_coord) {\n  uvec2 src_size = compute_mip_size(mip0_size, MAX_MIP_COUNT);\n  uvec2 dst_size = compute_mip_size(src_size, 1u);\n\n  vec2 src_coord_f = (vec2(dst_coord) / vec2(dst_size)) * vec2(src_size) + 0.5f;\n  quad_t quad = load_quad_from_shared_mip(uvec2(src_coord_f));\n  return interpolate_quad_at(quad, src_coord_f, src_size);\n}\n\n\n/* Samples pixel data from LDS */\npixel_t filter_lds_mip(uvec2 mip0_size, uint base_mip, uvec2 dst_coord) {\n  uvec2 src_size = compute_mip_size(mip0_size, base_mip);\n  uvec2 dst_size = compute_mip_size(src_size, 1u);\n\n  vec2 src_coord_f = (vec2(dst_coord) / vec2(dst_size)) * vec2(src_size) + 0.5f;\n  quad_t quad = load_quad_from_lds(uvec2(src_coord_f));\n  return interpolate_quad_at(quad, src_coord_f, src_size);\n}\n\n\n/* Processes the next set of even-sized mips, and returns the final result. The mip\n * parameter will contain the mip index that the returned result was computed for.\n * This writes out all computed mips to the image. */\npixel_t process_even_mips(uint dst_mip, uint max_mip, uvec2 dst_coord, uvec2 max_coord, pixel_t px) {\n  uint sid = gl_SubgroupInvocationID;\n\n  /* Store given pixel data in next mip level */\n  if (all(lessThan(dst_coord, max_coord)))\n    store_pixel_to_mip(dst_mip, dst_coord, px);\n\n  /* Check if the mip level we just stored (and thus read in the next step)\n   * is odd-sized, and if so, exit so that it gets written to LDS. */\n  if (dst_mip >= max_mip)\n   return px;\n\n  /* Shuffle horizontally first, then vertically. This works because\n   * we use z-order curves to arrange threads within each subgroup. */\n  shuffle_interpolate(px, 4u);\n  dst_mip += 1u;\n\n  if (all(lessThan(dst_coord >> 1u, max_coord >> 1u)) && (sid % 4u == 0u))\n    store_pixel_to_mip(dst_mip, dst_coord >> 1u, px);\n\n  if (dst_mip >= max_mip)\n    return px;\n\n  /* Same thing as above, except one mip level deeper */\n  shuffle_interpolate(px, 16u);\n  dst_mip += 1u;\n\n  if (all(lessThan(dst_coord >> 2u, max_coord >> 2u)) && (sid % 16u == 0u))\n    store_pixel_to_mip(dst_mip, dst_coord >> 2u, px);\n\n  if (dst_mip >= max_mip)\n    return px;\n\n  /* Handle last mip that we can process in one subgroup */\n  shuffle_interpolate(px, 64u);\n  dst_mip += 1u;\n\n  if (all(lessThan(dst_coord >> 3u, max_coord >> 3u)) && (sid % 64u == 0u))\n    store_pixel_to_mip(dst_mip, dst_coord >> 3u, px);\n\n  return px;\n}\n\n\n/* Determines block properties for a given mip region to process. Returns:\n * .x : Block count of the given region in x dimension.\n * .y : Block count of the given region in y dimension.\n * .z : Number of blocks that the workgroup can process in one go. */\nuvec3 compute_block_properties(uvec4 region) {\n  uvec3 block_size = determine_block_size();\n  uvec2 region_size = (region.zw - region.xy + block_size.x - 1u) / block_size.x;\n\n  uint block_count = gl_WorkGroupSize.x / block_size.y;\n  return uvec3(region_size, block_count);\n}\n\n\n/* Computes block base coordinate (.xy) and block-relative\n * pixel coordinate (.zw) for the calling thread. */\nuvec4 compute_block_coord(uvec2 block_count, uint base_block) {\n  uvec3 block_size = determine_block_size();\n\n  uint tid = gl_SubgroupID * gl_SubgroupSize + gl_SubgroupInvocationID;\n\n  uint block_index = tid / block_size.y + base_block;\n  uint block_pixel = tid % block_size.y;\n\n  uvec2 pixel_coord = decode_morton_2d(block_pixel);\n\n  /* Block counts are small here but may not be powers of two */\n  uint block_y = uint((float(block_index) + 0.5f) / float(block_count.x));\n  uint block_x = block_index - block_y * block_count.x;\n\n  return uvec4(block_x, block_y, pixel_coord);\n}\n\n\n/* Cooperatively computes read/write areas and block properties for each\n * mip and stores them in global registers. Only works for subgroup sizes\n * of at least 16 since we process up to 12 mips in one go. */\nu16vec2 g_per_mip_base_area = {};\nu16vec2 g_per_mip_write_area = {};\nu16vec2 g_per_mip_read_area = {};\nuint32_t g_per_mip_block_info = {};\n\nvoid setup_per_mip_properties(uvec2 mip0_size) {\n#if USE_SUBGROUP_PATH\n  /* Dst mip index is 1-based, but mip counts should not really\n   * be powers of two anyway so this is fine. */\n  if (gl_SubgroupSize > MAX_MIP_COUNT + MAX_MIP_COUNT) {\n    uint mip = gl_SubgroupInvocationID;\n\n    uvec4 w_area = compute_write_rect(mip0_size, mip);\n    uvec4 r_area = compute_read_rect(mip0_size, mip);\n    uvec3 block_properties = compute_block_properties(r_area);\n\n    g_per_mip_base_area = u16vec2(w_area.xy);\n    g_per_mip_write_area = u16vec2(w_area.zw);\n    g_per_mip_read_area = u16vec2(r_area.zw);\n\n    g_per_mip_block_info = 0u;\n    g_per_mip_block_info = bitfieldInsert(g_per_mip_block_info, block_properties.x,  0,  8);\n    g_per_mip_block_info = bitfieldInsert(g_per_mip_block_info, block_properties.y,  8,  8);\n    g_per_mip_block_info = bitfieldInsert(g_per_mip_block_info, block_properties.z, 16, 16);\n  }\n#endif\n}\n\nuvec4 get_read_rect(uvec2 mip0_size, uint dst_mip) {\n#if USE_SUBGROUP_PATH\n  if (gl_SubgroupSize > MAX_MIP_COUNT + MAX_MIP_COUNT) {\n    u16vec2 xy = unpack16(subgroupBroadcast(pack32(g_per_mip_base_area), dst_mip));\n    u16vec2 zw = unpack16(subgroupBroadcast(pack32(g_per_mip_read_area), dst_mip));\n    return uvec4(xy, zw);\n  }\n#endif\n\n  return compute_read_rect(mip0_size, dst_mip);\n}\n\nuvec4 get_write_rect(uvec2 mip0_size, uint dst_mip) {\n#if USE_SUBGROUP_PATH\n  if (gl_SubgroupSize > MAX_MIP_COUNT + MAX_MIP_COUNT) {\n    u16vec2 xy = unpack16(subgroupBroadcast(pack32(g_per_mip_base_area), dst_mip));\n    u16vec2 zw = unpack16(subgroupBroadcast(pack32(g_per_mip_write_area), dst_mip));\n    return uvec4(xy, zw);\n  }\n#endif\n\n  return compute_write_rect(mip0_size, dst_mip);\n}\n\nuvec3 get_block_properties(uvec2 mip0_size, uint dst_mip) {\n#if USE_SUBGROUP_PATH\n  if (gl_SubgroupSize > MAX_MIP_COUNT + MAX_MIP_COUNT) {\n    uint32_t packed_data = subgroupBroadcast(g_per_mip_block_info, dst_mip);\n\n    return uvec3(\n      bitfieldExtract(packed_data,  0,  8),\n      bitfieldExtract(packed_data,  8,  8),\n      bitfieldExtract(packed_data, 16, 16));\n  }\n#endif\n\n  return compute_block_properties(get_read_rect(mip0_size, dst_mip));\n}\n\n\n/* Helper function to filter and process the mip stored in LDS */\npixel_t process_lds_pixels(uvec2 mip0_size, uint base_mip, uint dst_mip, uvec2 local_coord, uvec2 pixel_coord, uvec4 write_area, uint even_count) {\n  pixel_t pixel = filter_lds_mip(mip0_size, base_mip, local_coord);\n\n  return process_even_mips(dst_mip, dst_mip + even_count,\n    pixel_coord, write_area.zw, pixel);\n}\n\n\n/* Helper function to conditionally write processed pixel back to LDS */\nvoid process_lds_writeback(uvec2 local_coord, uvec2 pixel_coord, uvec4 read_area, uint even_count, pixel_t pixel) {\n  if (all(lessThan(pixel_coord, read_area.zw))) {\n    /* Only use one the first thread of each block to store the result */\n    if (bitfieldExtract(gl_SubgroupInvocationID, 0, int(even_count + even_count)) == 0u)\n      store_pixel_to_lds(local_coord >> even_count, pixel);\n  }\n}\n\n\n/* Downsamples mip from image as well as all even-sized mips following it, and\n * writes result to LDS as necessary. Returns the last mip level written. */\nuint process_base_mip(uvec2 mip0_size, uint base_mip, uint last_mip) {\n  uint odd_mask = compute_odd_mip_mask_flat(mip0_size);\n  uint dst_mip = base_mip + 1u;\n\n  /* Compute processing area of the first mip that we *write*. */\n  uvec4 dst_r_area = get_read_rect(mip0_size, dst_mip);\n  uvec4 dst_w_area = get_write_rect(mip0_size, dst_mip);\n\n  uvec3 block_size = determine_block_size();\n  uvec3 block_info = get_block_properties(mip0_size, dst_mip);\n\n  uint even_count = min(uint(findLSB(odd_mask >> dst_mip)), block_size.z);\n\n  /* Iterate over blocks */\n  for (uint i = 0u; i < block_info.x * block_info.y; i += block_info.z) {\n    uvec4 block_coord = compute_block_coord(block_info.xy, i);\n\n    if (block_coord.y < block_info.y) {\n      uvec2 local_coord = block_size.x * block_coord.xy + block_coord.zw;\n      uvec2 pixel_coord = dst_r_area.xy + local_coord;\n\n      pixel_t pixel;\n\n      if (base_mip == 0u)\n        pixel = filter_base_mip(mip0_size, pixel_coord);\n      else\n        pixel = filter_shared_mip(mip0_size, pixel_coord);\n\n      pixel = process_even_mips(dst_mip, dst_mip + even_count,\n        pixel_coord, dst_w_area.zw, pixel);\n\n      if (dst_mip + even_count < last_mip)\n        process_lds_writeback(local_coord, pixel_coord, dst_r_area, even_count, pixel);\n    }\n  }\n\n  return dst_mip + even_count;\n}\n\n\n/* Downsamples mip from LDS, as well as all mips following it. */\nuint process_lds_mip(uvec2 mip0_size, uint base_mip, uint last_mip) {\n  uint odd_mask = compute_odd_mip_mask_flat(mip0_size);\n  uint dst_mip = base_mip + 1u;\n\n  /* Compute processing area of the first mip that we *write*. */\n  uvec4 dst_r_area = get_read_rect(mip0_size, dst_mip);\n  uvec4 dst_w_area = get_write_rect(mip0_size, dst_mip);\n\n  uvec3 block_size = determine_block_size();\n  uvec3 block_info = get_block_properties(mip0_size, dst_mip);\n\n  uint even_count = min(uint(findLSB(odd_mask >> dst_mip)), block_size.z);\n\n  uint block_count = block_info.x * block_info.y;\n  uint block_count_per_wave = gl_SubgroupSize / block_size.y;\n\n  if (block_count > block_count_per_wave) {\n    /* Iterate over blocks */\n    for (uint i = 0u; i < block_count; i += block_info.z) {\n      uvec4 block_coord = compute_block_coord(block_info.xy, i);\n\n      pixel_t pixel;\n\n      uvec2 local_coord = block_size.x * block_coord.xy + block_coord.zw;\n      uvec2 pixel_coord = dst_r_area.xy + local_coord;\n\n      if (block_coord.y < block_info.y) {\n        pixel = process_lds_pixels(mip0_size, base_mip, dst_mip,\n          local_coord, pixel_coord, dst_w_area, even_count);\n      }\n\n      if (dst_mip + even_count < last_mip) {\n        /* Ensure that all threads have finished reading LDS before overriding.\n         * This is safe since subsequent iterations will not be reading any of\n         * the pixels written here. */\n        controlBarrier(gl_ScopeWorkgroup, gl_ScopeWorkgroup,\n          gl_StorageSemanticsShared, gl_SemanticsAcquireRelease);\n\n        if (block_coord.y < block_info.y)\n          process_lds_writeback(local_coord, pixel_coord, dst_r_area, even_count, pixel);\n      }\n    }\n  } else {\n    /* Special path for small, single-subgroup reductions. Basically the same\n     * as above, except we can actually avoid a full workgroup barrier and do\n     * some more constant-folding because there will only be one iteration.\n     * This is very common when downsampling typical render resolutions. */\n    uvec4 block_coord = compute_block_coord(block_info.xy, 0u);\n\n    uvec2 local_coord = block_size.x * block_coord.xy + block_coord.zw;\n    uvec2 pixel_coord = dst_r_area.xy + local_coord;\n\n    if (block_coord.y < block_info.y) {\n      pixel_t pixel = process_lds_pixels(mip0_size, base_mip, dst_mip,\n        local_coord, pixel_coord, dst_w_area, even_count);\n\n      controlBarrier(gl_ScopeSubgroup, gl_ScopeSubgroup,\n        gl_StorageSemanticsShared, gl_SemanticsAcquireRelease);\n\n      if (dst_mip + even_count < last_mip)\n        process_lds_writeback(local_coord, pixel_coord, dst_r_area, even_count, pixel);\n    }\n  }\n\n  return dst_mip + even_count;\n}\n\n\n/* Processes block of mips until there is only one active thread left. */\nvoid process_mips(uvec2 mip0_size, uint base_mip, uint last_mip) {\n  uint dst_mip = process_base_mip(mip0_size, base_mip, last_mip);\n\n  while (dst_mip < last_mip) {\n    controlBarrier(gl_ScopeWorkgroup, gl_ScopeWorkgroup,\n      gl_StorageSemanticsShared, gl_SemanticsAcquireRelease);\n\n    dst_mip = process_lds_mip(mip0_size, dst_mip, last_mip);\n  }\n}\n\n\n/* Helper to increment the atomic counter, making sure that all stores\n * from this workgroup complete before returning, and making all stores\n * from other workgroups visible. Returns true if this is the final\n * workgroup that needs to process the mip tail. */\nshared uint g_subgroups_done;\nshared uint g_workgroups_done;\n\nbool finalize_group(uvec2 mip0_size) {\n  uint tid = gl_SubgroupID * gl_SubgroupSize + gl_SubgroupInvocationID;\n  uint z = gl_GlobalInvocationID.z;\n\n  uvec2 group_count = compute_mip_size(mip0_size, MAX_MIP_COUNT);\n  uint group_count_flat = group_count.x * group_count.y;\n\n  /* Wait for all stores in the current subgroup to complete before signaling\n   * that it is done with its work, then signal the workgroup once everything\n   * is done. This way we avoid one barrier that we'd need when signaling the\n   * workgroup from one thread directly. */\n  controlBarrier(gl_ScopeSubgroup, gl_ScopeQueueFamily,\n    gl_StorageSemanticsImage, gl_SemanticsRelease);\n\n  if (subgroupElect()) {\n    uint subgroups_done = atomicAdd(g_subgroups_done, 1u) + 1u;\n\n    if (subgroups_done == gl_WorkGroupSize.x / gl_SubgroupSize)\n      g_workgroups_done = atomicAdd(push_args.ctr.counters[z], 1u) + 1u;\n  }\n\n  /* Wait for broadcast to finish and check if this workgroup\n   * is the last one active. If so, downsample the mip tail. */\n  controlBarrier(gl_ScopeWorkgroup, gl_ScopeWorkgroup,\n    gl_StorageSemanticsShared, gl_SemanticsAcquireRelease);\n\n  if (g_workgroups_done != group_count_flat)\n    return false;\n\n  /* Make sure we can read from the shared mip level */\n  memoryBarrier(gl_ScopeQueueFamily, gl_StorageSemanticsImage, gl_SemanticsAcquire);\n  return true;\n}\n\n\nvoid main() [[subgroup_uniform_control_flow]] {\n  /* There will be LDS sync at some point so this is fine */\n  g_subgroups_done = 0u;\n  g_workgroups_done = 0u;\n\n  /* Handle first set of mips */\n  uvec2 mip0_size = uvec2(textureSize(s_base_mip, 0).xy);\n  setup_per_mip_properties(mip0_size);\n\n  process_mips(mip0_size, 0, min(push_args.mip_count, MAX_MIP_COUNT));\n\n  /* Skip further processing if there are no more mips to process */\n  if (push_args.mip_count <= MAX_MIP_COUNT)\n    return;\n\n  /* Check if we're the last active workgroup and downsample mip tail */\n  if (finalize_group(mip0_size))\n    process_mips(mip0_size, MAX_MIP_COUNT, push_args.mip_count);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_present_common.glsl",
    "content": "#include \"dxvk_color_space.glsl\"\n\n#extension GL_EXT_nonuniform_qualifier : require\n#extension GL_EXT_samplerless_texture_functions : require\n\nlayout(constant_id = 0) const uint c_samples = 0u;\nlayout(constant_id = 1) const bool c_gamma = false;\n\nlayout(constant_id = 2) const uint c_src_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\nlayout(constant_id = 3) const bool c_src_is_srgb = true;\nlayout(constant_id = 4) const uint c_dst_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\nlayout(constant_id = 5) const bool c_dst_is_srgb = true;\nlayout(constant_id = 6) const bool c_composite_hud = false;\nlayout(constant_id = 7) const bool c_composite_cursor = false;\n\nlayout(set = 0, binding = 0) uniform sampler s_samplers[];\n\nlayout(set = 1, binding = 0) uniform texture2D s_image;\nlayout(set = 1, binding = 0) uniform texture2DMS s_image_ms;\nlayout(set = 1, binding = 1) uniform texture1D s_gamma;\nlayout(set = 1, binding = 2) uniform texture2D s_hud;\nlayout(set = 1, binding = 3) uniform texture2D s_cursor;\n\nlayout(push_constant)\nuniform present_info_t {\n  ivec2 src_offset;\n  ivec2 src_extent;\n  ivec2 dst_offset;\n  ivec2 cursor_offset;\n  ivec2 cursor_extent;\n  uint sampler_gamma;\n  uint sampler_cursor;\n};\n\n\nvec4 blend_sc_rgb(vec4 dst, vec4 src) {\n  return mix(dst, vec4(src.rgb, 1.0f), src.aaaa);\n}\n\n\nvec4 blend_linear_sdr(vec4 dst, vec4 src) {\n  src.rgb = nits_to_sc_rgb(src.rgb * SDR_NITS);\n  return blend_sc_rgb(dst, src);\n}\n\n\nvec4 composite_image(vec4 color) {\n  ivec2 coord = ivec2(gl_FragCoord.xy);\n\n  if (c_composite_hud)\n    color = blend_linear_sdr(color, texelFetch(s_hud, coord, 0));\n\n  if (c_composite_cursor) {\n    ivec2 rel_ofs = coord - cursor_offset;\n\n    if (max(rel_ofs.x, rel_ofs.y) >= 0 && all(lessThan(rel_ofs, cursor_extent))) {\n      color = blend_linear_sdr(color,\n        texture(sampler2D(s_cursor, s_samplers[sampler_cursor]),\n        vec2(rel_ofs) / vec2(cursor_extent)));\n    }\n  }\n\n  return color;\n}\n\n\nvec4 input_to_sc_rgb(vec4 color) {\n  switch (c_src_color_space) {\n    default:\n    case VK_COLOR_SPACE_PASS_THROUGH_EXT:\n      return color;\n\n    case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: {\n      if (!c_src_is_srgb)\n        color.rgb = srgb_to_linear(color.rgb);\n\n      color.rgb = nits_to_sc_rgb(color.rgb * SDR_NITS);\n      return color;\n    }\n\n    case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:\n      return color;\n\n    case VK_COLOR_SPACE_HDR10_ST2084_EXT:\n      color.rgb = nits_to_sc_rgb(pq_to_nits(color.rgb));\n      color.rgb = rec2020_to_rec709 * color.rgb;\n      return color;\n  }\n}\n\n\nvec4 sc_rgb_to_output(vec4 color) {\n  if (c_gamma) {\n    // If we need to apply a gamma curve, convert to sRGB, perform\n    // the lookup, and then convert back to scRGB as necessary\n    if (c_src_color_space != VK_COLOR_SPACE_PASS_THROUGH_EXT) {\n      color.rgb = sc_rgb_to_nits(color.rgb) / SDR_NITS;\n      color.rgb = linear_to_srgb(color.rgb);\n    } else if (c_src_is_srgb) {\n      color.rgb = linear_to_srgb(color.rgb);\n    }\n\n    color.rgb = vec3(\n      texture(sampler1D(s_gamma, s_samplers[sampler_gamma]), color.r).r,\n      texture(sampler1D(s_gamma, s_samplers[sampler_gamma]), color.g).g,\n      texture(sampler1D(s_gamma, s_samplers[sampler_gamma]), color.b).b);\n\n    if (c_dst_color_space != VK_COLOR_SPACE_PASS_THROUGH_EXT) {\n      color.rgb = srgb_to_linear(color.rgb);\n      color.rgb = nits_to_sc_rgb(color.rgb * SDR_NITS);\n    } else if (c_dst_is_srgb) {\n      color.rgb = srgb_to_linear(color.rgb);\n    }\n  }\n\n  switch (c_dst_color_space) {\n    default:\n    case VK_COLOR_SPACE_PASS_THROUGH_EXT:\n      // If we applied a gamma curve, the output is already correct\n      if (!c_gamma && c_src_is_srgb != c_dst_is_srgb) {\n        color.rgb = c_src_is_srgb\n          ? linear_to_srgb(color.rgb)\n          : srgb_to_linear(color.rgb);\n      }\n\n      return color;\n\n    case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: {\n      color.rgb = sc_rgb_to_nits(color.rgb) / SDR_NITS;\n\n      if (!c_dst_is_srgb)\n        color.rgb = linear_to_srgb(color.rgb);\n\n      return color;\n    }\n\n    case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:\n      return color;\n\n    case VK_COLOR_SPACE_HDR10_ST2084_EXT:\n      color.rgb = rec709_to_rec2020 * color.rgb;\n      color.rgb = nits_to_pq(sc_rgb_to_nits(color.rgb));\n      return color;\n  }\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_present_frag.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"dxvk_present_common.glsl\"\n\nlayout(location = 0) out vec4 o_color;\n\nvoid main() {\n  ivec2 coord = ivec2(gl_FragCoord.xy) + src_offset - dst_offset;\n\n  o_color = input_to_sc_rgb(texelFetch(s_image, coord, 0));\n  o_color = composite_image(o_color);\n  o_color = sc_rgb_to_output(o_color);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_present_frag_blit.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"dxvk_present_common.glsl\"\n\nlayout(location = 0) in  vec2 i_coord;\nlayout(location = 0) out vec4 o_color;\n\nvoid main() {\n  vec2 coord = vec2(src_offset) + vec2(src_extent) * i_coord;\n  vec2 delta = vec2(dFdx(coord.x), dFdy(coord.y));\n\n  ivec2 i_coord = ivec2(coord);\n  vec2  f_coord = fract(coord);\n\n  if (all(lessThan(delta, vec2(1.0f)))) {\n    // Map pixel rectangle to source image. If it is entirely contained within one\n    // source pixel, just sample that pixel, otherwise do a linear interpolation.\n    // For even scaling factors, this is essentially integer scaling.\n    vec2 lo = max(coord - 0.5f * delta, vec2(src_offset));\n    vec2 hi = min(coord + 0.5f * delta, vec2(src_offset + src_extent - 1));\n\n    i_coord = ivec2(lo);\n    f_coord = mix((hi - floor(hi)) / delta, vec2(0.0), equal(floor(lo), floor(hi)));\n  }\n\n  // Manually interpolate in the correct color space\n  o_color = mix(mix(input_to_sc_rgb(texelFetch(s_image, i_coord + ivec2(0, 0), 0)),\n                    input_to_sc_rgb(texelFetch(s_image, i_coord + ivec2(1, 0), 0)), f_coord.x),\n                mix(input_to_sc_rgb(texelFetch(s_image, i_coord + ivec2(0, 1), 0)),\n                    input_to_sc_rgb(texelFetch(s_image, i_coord + ivec2(1, 1), 0)), f_coord.x),\n                f_coord.y);\n\n  o_color = composite_image(o_color);\n  o_color = sc_rgb_to_output(o_color);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_present_frag_ms.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"dxvk_present_common.glsl\"\n\nlayout(location = 0) out vec4 o_color;\n\nvoid main() {\n  ivec2 coord = ivec2(gl_FragCoord.xy) + src_offset - dst_offset;\n  o_color = input_to_sc_rgb(texelFetch(s_image_ms, coord, 0));\n  \n  for (uint i = 1; i < c_samples; i++)\n    o_color += input_to_sc_rgb(texelFetch(s_image_ms, coord, int(i)));\n\n  o_color = composite_image(o_color / float(c_samples));\n  o_color = sc_rgb_to_output(o_color);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_present_frag_ms_blit.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : enable\n\n#include \"dxvk_present_common.glsl\"\n\nlayout(location = 0) in  vec2 i_coord;\nlayout(location = 0) out vec4 o_color;\n\nconst vec2 sample_positions[] = {\n  /* 2 samples */\n  vec2( 0.25f, 0.25f),\n  vec2(-0.25f,-0.25f),\n  /* 4 samples */\n  vec2(-0.125f,-0.375f),\n  vec2( 0.375f,-0.125f),\n  vec2(-0.375f, 0.125f),\n  vec2( 0.125f, 0.375f),\n  /* 8 samples */\n  vec2( 0.0625f,-0.1875f),\n  vec2(-0.0625f, 0.1875f),\n  vec2( 0.3125f, 0.0625f),\n  vec2(-0.1875f,-0.3125f),\n  vec2(-0.3125f, 0.3125f),\n  vec2(-0.4375f,-0.0625f),\n  vec2( 0.1875f, 0.4375f),\n  vec2( 0.4375f,-0.4375f),\n  /* 16 samples */\n  vec2( 0.0625f, 0.0625f),\n  vec2(-0.0625f,-0.1875f),\n  vec2(-0.1875f, 0.1250f),\n  vec2( 0.2500f,-0.0625f),\n  vec2(-0.3125f,-0.1250f),\n  vec2( 0.1250f, 0.3125f),\n  vec2( 0.3125f, 0.1875f),\n  vec2( 0.1875f,-0.3125f),\n  vec2(-0.1250f, 0.3750f),\n  vec2( 0.0000f,-0.4375f),\n  vec2(-0.2500f,-0.3750f),\n  vec2(-0.3750f, 0.2500f),\n  vec2(-0.5000f, 0.0000f),\n  vec2( 0.4375f,-0.2500f),\n  vec2( 0.3750f, 0.4375f),\n  vec2(-0.4375f,-0.5000f),\n};\n\nvoid main() {\n  vec2 coord = vec2(src_offset) + vec2(src_extent) * i_coord;\n\n  ivec2 cint = ivec2(coord);\n  vec2 cfrac = fract(coord) - 0.5f;\n\n  uint pos_index = c_samples - 2u;\n\n  o_color = vec4(0.0f);\n\n  for (uint i = 0; i < c_samples; i++) {\n    vec2 sample_pos = sample_positions[pos_index + i];\n\n    ivec2 coffset = ivec2(greaterThan(cfrac - sample_pos, vec2(0.5f)));\n    o_color += input_to_sc_rgb(texelFetch(s_image_ms, cint + coffset, int(i)));\n  }\n\n  o_color = composite_image(o_color / float(c_samples));\n  o_color = sc_rgb_to_output(o_color);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_present_vert.vert",
    "content": "#version 450\n\nlayout(location = 0) out vec2 o_coord;\n\nvoid main() {\n  vec2 coord = vec2(\n    float(gl_VertexIndex & 2),\n    float(gl_VertexIndex & 1) * 2.0f);\n\n  o_coord = coord;\n  gl_Position = vec4(-1.0f + 2.0f * coord, 0.0f, 1.0f);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_resolve_common.glsl",
    "content": "#define VK_RESOLVE_MODE_NONE            (0)\n#define VK_RESOLVE_MODE_SAMPLE_ZERO_BIT (1 << 0)\n#define VK_RESOLVE_MODE_AVERAGE_BIT     (1 << 1)\n#define VK_RESOLVE_MODE_MIN_BIT         (1 << 2)\n#define VK_RESOLVE_MODE_MAX_BIT         (1 << 3)\n\n#define resolve_fn(name, type, load_fn)               \\\ntype name(ivec3 coord, int samples, uint mode) {      \\\n  if (mode == VK_RESOLVE_MODE_NONE)                   \\\n    return type(0);                                   \\\n  type value = load_fn(coord, 0);                     \\\n                                                      \\\n  switch (mode) {                                     \\\n    case VK_RESOLVE_MODE_SAMPLE_ZERO_BIT:             \\\n      return value;                                   \\\n                                                      \\\n    case VK_RESOLVE_MODE_AVERAGE_BIT:                 \\\n      for (int i = 1; i < samples; i++)               \\\n        value += load_fn(coord, i);                   \\\n      value /= type(c_samples);                       \\\n      break;                                          \\\n                                                      \\\n    case VK_RESOLVE_MODE_MIN_BIT:                     \\\n      for (int i = 1; i < samples; i++)               \\\n        value = min(value, load_fn(coord, i));        \\\n      break;                                          \\\n                                                      \\\n    case VK_RESOLVE_MODE_MAX_BIT:                     \\\n      for (int i = 1; i < c_samples; i++)             \\\n        value = min(value, load_fn(coord, i));        \\\n      break;                                          \\\n  }                                                   \\\n                                                      \\\n  return value;                                       \\\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_resolve_frag_d.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : enable\n#extension GL_EXT_samplerless_texture_functions : enable\n\n#include \"dxvk_resolve_common.glsl\"\n\nlayout(constant_id = 0) const int c_samples = 1;\nlayout(constant_id = 1) const int c_mode    = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;\n\nlayout(binding = 0) uniform texture2DMSArray s_depth;\n\nfloat load_depth(ivec3 coord, int s) {\n  return texelFetch(s_depth, coord, s).r;\n}\n\nresolve_fn(resolve_depth, float, load_depth)\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  ivec3 coord = ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer);\n\n  gl_FragDepth = resolve_depth(coord, c_samples, c_mode);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_resolve_frag_ds.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : enable\n#extension GL_ARB_shader_stencil_export : enable\n#extension GL_EXT_samplerless_texture_functions : enable\n\n#include \"dxvk_resolve_common.glsl\"\n\nlayout(constant_id = 0) const int c_samples = 1;\nlayout(constant_id = 1) const int c_mode_d  = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;\nlayout(constant_id = 2) const int c_mode_s  = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;\n\nlayout(binding = 0) uniform  texture2DMSArray s_depth;\nlayout(binding = 1) uniform utexture2DMSArray s_stencil;\n\nfloat load_depth(ivec3 coord, int s) {\n  return texelFetch(s_depth, coord, s).r;\n}\n\nuint load_stencil(ivec3 coord, int s) {\n  return texelFetch(s_stencil, coord, s).r;\n}\n\nresolve_fn(resolve_depth, float, load_depth)\nresolve_fn(resolve_stencil, uint, load_stencil)\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  ivec3 coord = ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer);\n\n  gl_FragDepth = resolve_depth(coord, c_samples, c_mode_d);\n  gl_FragStencilRefARB = int(resolve_stencil(coord, c_samples, c_mode_s));\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_resolve_frag_f.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : enable\n#extension GL_EXT_samplerless_texture_functions : enable\n\n#include \"dxvk_resolve_common.glsl\"\n\nlayout(constant_id = 0) const int c_samples = 1;\nlayout(constant_id = 1) const int c_mode    = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;\n\nlayout(binding = 0) uniform texture2DMSArray s_image;\n\nlayout(location = 0) out vec4 o_color;\n\nvec4 load_color(ivec3 coord, int s) {\n  return texelFetch(s_image, coord, s);\n}\n\nresolve_fn(resolve_color, vec4, load_color)\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  ivec3 coord = ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer);\n  o_color = resolve_color(coord, c_samples, c_mode);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_resolve_frag_i.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : enable\n#extension GL_EXT_samplerless_texture_functions : enable\n\n#include \"dxvk_resolve_common.glsl\"\n\nlayout(constant_id = 0) const int c_samples = 1;\nlayout(constant_id = 1) const int c_mode    = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;\n\nlayout(binding = 0) uniform itexture2DMSArray s_image;\n\nlayout(location = 0) out ivec4 o_color;\n\nivec4 load_color(ivec3 coord, int s) {\n  return texelFetch(s_image, coord, s);\n}\n\nresolve_fn(resolve_color, ivec4, load_color)\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  ivec3 coord = ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer);\n  o_color = resolve_color(coord, c_samples, c_mode);\n}\n"
  },
  {
    "path": "src/dxvk/shaders/dxvk_resolve_frag_u.frag",
    "content": "#version 450\n\n#extension GL_GOOGLE_include_directive : enable\n#extension GL_EXT_samplerless_texture_functions : enable\n\n#include \"dxvk_resolve_common.glsl\"\n\nlayout(constant_id = 0) const int c_samples = 1;\nlayout(constant_id = 1) const int c_mode    = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;\n\nlayout(binding = 0) uniform utexture2DMSArray s_image;\n\nlayout(location = 0) out vec4 o_color;\n\nuvec4 load_color(ivec3 coord, int s) {\n  return texelFetch(s_image, coord, s);\n}\n\nresolve_fn(resolve_color, uvec4, load_color)\n\nlayout(push_constant)\nuniform u_info_t {\n  ivec2 offset;\n} u_info;\n\nvoid main() {\n  ivec3 coord = ivec3(gl_FragCoord.xy + u_info.offset, gl_Layer);\n  o_color = resolve_color(coord, c_samples, c_mode);\n}\n"
  },
  {
    "path": "src/meson.build",
    "content": "subdir('wsi')\nsubdir('util')\nsubdir('spirv')\nsubdir('vulkan')\nsubdir('dxvk')\n\nif get_option('enable_dxgi')\n  subdir('dxgi')\nendif\n\nif get_option('enable_d3d11')\n  if not get_option('enable_dxgi')\n    error('DXGI is required for D3D11.')\n  endif\n  subdir('d3d11')\nendif\n\nif get_option('enable_d3d10')\n  if not get_option('enable_d3d11')\n    error('D3D11 is required for D3D10.')\n  endif\n  subdir('d3d10')\nendif\n\nif get_option('enable_d3d9')\n  subdir('dxso')\n  subdir('d3d9')\nendif\n\nif get_option('enable_d3d8')\n  if not get_option('enable_d3d9')\n    error('D3D9 is required for D3D8.')\n  endif\n  subdir('d3d8')\nendif\n\n# Nothing selected\nif not get_option('enable_d3d8') and not get_option('enable_d3d9') and not get_option('enable_dxgi')\n  warning('Nothing selected to be built.?')\nendif\n"
  },
  {
    "path": "src/spirv/meson.build",
    "content": "spirv_src = files([\n  'spirv_code_buffer.cpp',\n  'spirv_compression.cpp',\n  'spirv_module.cpp',\n])\n\nspirv_lib = static_library('spirv', spirv_src,\n  include_directories : [ dxvk_include_path ],\n)\n"
  },
  {
    "path": "src/spirv/spirv_code_buffer.cpp",
    "content": "#include <array>\n#include <cstring>\n\n#include \"spirv_code_buffer.h\"\n\nnamespace dxvk {\n  \n  SpirvCodeBuffer:: SpirvCodeBuffer() { }\n  SpirvCodeBuffer::~SpirvCodeBuffer() { }\n  \n  \n  SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size)\n  : m_ptr(size) {\n    m_code.resize(size);\n  }\n\n\n  SpirvCodeBuffer::SpirvCodeBuffer(uint32_t size, const uint32_t* data)\n  : m_ptr(size) {\n    m_code.resize(size);\n    std::memcpy(m_code.data(), data, size * sizeof(uint32_t));\n  }\n  \n  \n  SpirvCodeBuffer::SpirvCodeBuffer(std::istream& stream) {\n    stream.ignore(std::numeric_limits<std::streamsize>::max());\n    std::streamsize length = stream.gcount();\n    stream.clear();\n    stream.seekg(0, std::ios_base::beg);\n    \n    std::vector<char> buffer(length);\n    stream.read(buffer.data(), length);\n    buffer.resize(stream.gcount());\n    \n    m_code.resize(buffer.size() / sizeof(uint32_t));\n    std::memcpy(reinterpret_cast<char*>(m_code.data()),\n      buffer.data(), m_code.size() * sizeof(uint32_t));\n    \n    m_ptr = m_code.size();\n  }\n\n\n  SpirvCodeBuffer::SpirvCodeBuffer(std::vector<uint32_t>&& data)\n  : m_code(std::move(data)), m_ptr(m_code.size()) {\n\n  }\n\n  \n  uint32_t SpirvCodeBuffer::allocId() {\n    constexpr size_t BoundIdsOffset = 3;\n\n    if (m_code.size() <= BoundIdsOffset)\n      return 0;\n\n    return m_code[BoundIdsOffset]++;\n  }\n\n\n  void SpirvCodeBuffer::append(const SpirvInstruction& ins) {\n    const size_t size = m_code.size();\n\n    m_code.resize(size + ins.length());\n\n    for (uint32_t i = 0; i < ins.length(); i++)\n      m_code[size + i] = ins.arg(i);\n\n    m_ptr += ins.length();\n  }\n\n\n  void SpirvCodeBuffer::append(const SpirvCodeBuffer& other) {\n    if (other.size() != 0) {\n      const size_t size = m_code.size();\n      m_code.resize(size + other.m_code.size());\n      \n            uint32_t* dst = this->m_code.data();\n      const uint32_t* src = other.m_code.data();\n      \n      std::memcpy(dst + size, src, other.size());\n      m_ptr += other.m_code.size();\n    }\n  }\n  \n  \n  void SpirvCodeBuffer::putWord(uint32_t word) {\n    m_code.insert(m_code.begin() + m_ptr, word);\n    m_ptr += 1;\n  }\n  \n  \n  void SpirvCodeBuffer::putIns(spv::Op opCode, uint16_t wordCount) {\n    this->putWord(\n        (static_cast<uint32_t>(opCode)    <<  0)\n      | (static_cast<uint32_t>(wordCount) << 16));\n  }\n  \n  \n  void SpirvCodeBuffer::putInt32(uint32_t word) {\n    this->putWord(word);\n  }\n  \n  \n  void SpirvCodeBuffer::putInt64(uint64_t value) {\n    this->putWord(value >>  0);\n    this->putWord(value >> 32);\n  }\n  \n  \n  void SpirvCodeBuffer::putFloat32(float value) {\n    uint32_t tmp;\n    static_assert(sizeof(tmp) == sizeof(value));\n    std::memcpy(&tmp, &value, sizeof(value));\n    this->putInt32(tmp);\n  }\n  \n  \n  void SpirvCodeBuffer::putFloat64(double value) {\n    uint64_t tmp;\n    static_assert(sizeof(tmp) == sizeof(value));\n    std::memcpy(&tmp, &value, sizeof(value));\n    this->putInt64(tmp);\n  }\n  \n  \n  void SpirvCodeBuffer::putStr(const char* str) {\n    uint32_t word = 0;\n    uint32_t nbit = 0;\n    \n    for (uint32_t i = 0; str[i] != '\\0'; str++) {\n      word |= (static_cast<uint32_t>(str[i]) & 0xFF) << nbit;\n      \n      if ((nbit += 8) == 32) {\n        this->putWord(word);\n        word = 0;\n        nbit = 0;\n      }\n    }\n    \n    // Commit current word\n    this->putWord(word);\n  }\n  \n  \n  void SpirvCodeBuffer::putHeader(uint32_t version, uint32_t boundIds) {\n    this->putWord(spv::MagicNumber);\n    this->putWord(version);\n    this->putWord(0); // Generator\n    this->putWord(boundIds);\n    this->putWord(0); // Schema\n  }\n  \n  \n  void SpirvCodeBuffer::erase(size_t size) {\n    m_code.erase(\n      m_code.begin() + m_ptr,\n      m_code.begin() + m_ptr + size);\n  }\n\n\n  uint32_t SpirvCodeBuffer::strLen(const char* str) {\n    // Null-termination plus padding\n    return (std::strlen(str) + 4) / 4;\n  }\n  \n  \n  void SpirvCodeBuffer::store(std::ostream& stream) const {\n    stream.write(\n      reinterpret_cast<const char*>(m_code.data()),\n      sizeof(uint32_t) * m_code.size());\n  }\n  \n}\n"
  },
  {
    "path": "src/spirv/spirv_code_buffer.h",
    "content": "#pragma once\n\n#include <iostream>\n#include <utility>\n#include <vector>\n\n#include \"spirv_instruction.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief SPIR-V code buffer\n   * \n   * Helper class for generating SPIR-V shaders.\n   * Stores arbitrary SPIR-V instructions in a\n   * format that can be read by Vulkan drivers.\n   */\n  class SpirvCodeBuffer {\n    \n  public:\n    \n    SpirvCodeBuffer();\n    explicit SpirvCodeBuffer(uint32_t size);\n    SpirvCodeBuffer(const SpirvCodeBuffer &) = default;\n    SpirvCodeBuffer(SpirvCodeBuffer &&) = default;\n    SpirvCodeBuffer(uint32_t size, const uint32_t* data);\n    SpirvCodeBuffer(std::istream& stream);\n    SpirvCodeBuffer(std::vector<uint32_t>&& data);\n    \n    template<size_t N>\n    SpirvCodeBuffer(const uint32_t (&data)[N])\n    : SpirvCodeBuffer(N, data) { }\n    \n    ~SpirvCodeBuffer();\n\n    SpirvCodeBuffer &operator=(const SpirvCodeBuffer &) = default;\n    SpirvCodeBuffer &operator=(SpirvCodeBuffer &&) = default;\n    \n    /**\n     * \\brief Code data\n     * \\returns Code data\n     */\n    const uint32_t* data() const { return m_code.data(); }\n          uint32_t* data()       { return m_code.data(); }\n    \n    /**\n     * \\brief Code size, in dwords\n     * \\returns Code size, in dwords\n     */\n    uint32_t dwords() const {\n      return m_code.size();\n    }\n    \n    /**\n     * \\brief Code size, in bytes\n     * \\returns Code size, in bytes\n     */\n    size_t size() const {\n      return m_code.size() * sizeof(uint32_t);\n    }\n    \n    /**\n     * \\brief Begin instruction iterator\n     * \n     * Points to the first instruction in the instruction\n     * block. The header, if any, will be skipped over.\n     * \\returns Instruction iterator\n     */\n    SpirvInstructionIterator begin() {\n      return SpirvInstructionIterator(\n        m_code.data(), 0, m_code.size());\n    }\n    \n    /**\n     * \\brief End instruction iterator\n     * \n     * Points to the end of the instruction block.\n     * \\returns Instruction iterator\n     */\n    SpirvInstructionIterator end() {\n      return SpirvInstructionIterator(nullptr, 0, 0);\n    }\n    \n    /**\n     * \\brief Allocates a new ID\n     *\n     * Returns a new valid ID and increments the\n     * maximum ID count stored in the header.\n     * \\returns The new SPIR-V ID\n     */\n    uint32_t allocId();\n    \n    /**\n     * \\brief Appends an instruction\n     *\n     * Slightly faster than individually adding words.\n     * \\param [in] ins Instruction\n     */\n    void append(const SpirvInstruction& ins);\n\n    /**\n     * \\brief Merges two code buffers\n     * \n     * This is useful to generate declarations or\n     * the SPIR-V header at the same time as the\n     * code when doing so in advance is impossible.\n     * \\param [in] other Code buffer to append\n     */\n    void append(const SpirvCodeBuffer& other);\n    \n    /**\n     * \\brief Appends an 32-bit word to the buffer\n     * \\param [in] word The word to append\n     */\n    void putWord(uint32_t word);\n    \n    /**\n     * \\brief Appends an instruction word to the buffer\n     * \n     * Adds a single word containing both the word count\n     * and the op code number for a single instruction.\n     * \\param [in] opCode Operand code\n     * \\param [in] wordCount Number of words\n     */\n    void putIns(spv::Op opCode, uint16_t wordCount);\n\n    /**\n     * \\brief Appends a 32-bit integer to the buffer\n     * \\param [in] value The number to add\n     */\n    void putInt32(uint32_t word);\n    \n    /**\n     * \\brief Appends a 64-bit integer to the buffer\n     * \n     * A 64-bit integer will take up two 32-bit words.\n     * \\param [in] value 64-bit value to add\n     */\n    void putInt64(uint64_t value);\n    \n    /**\n     * \\brief Appends a 32-bit float to the buffer\n     * \\param [in] value The number to add\n     */\n    void putFloat32(float value);\n    \n    /**\n     * \\brief Appends a 64-bit float to the buffer\n     * \\param [in] value The number to add\n     */\n    void putFloat64(double value);\n    \n    /**\n     * \\brief Appends a literal string to the buffer\n     * \\param [in] str String to append to the buffer\n     */\n    void putStr(const char* str);\n    \n    /**\n     * \\brief Adds the header to the buffer\n     *\n     * \\param [in] version SPIR-V version\n     * \\param [in] boundIds Number of bound IDs\n     */\n    void putHeader(uint32_t version, uint32_t boundIds);\n\n    /**\n     * \\brief Erases given number of dwords\n     *\n     * Removes data from the code buffer, starting\n     * at the current insertion offset.\n     * \\param [in] size Number of words to remove\n     */\n    void erase(size_t size);\n    \n    /**\n     * \\brief Computes length of a literal string\n     * \n     * \\param [in] str The string to check\n     * \\returns Number of words consumed by a string\n     */\n    uint32_t strLen(const char* str);\n    \n    /**\n     * \\brief Stores the SPIR-V module to a stream\n     * \n     * The ability to save modules to a file\n     * exists mostly for debugging purposes.\n     * \\param [in] stream Output stream\n     */\n    void store(std::ostream& stream) const;\n    \n    /**\n     * \\brief Retrieves current insertion pointer\n     * \n     * Sometimes it may be necessay to insert code into the\n     * middle of the stream rather than appending it. This\n     * retrieves the current function pointer. Note that the\n     * pointer will become invalid if any code is inserted\n     * before the current pointer location.\n     * \\returns Current instruction pointr\n     */\n    size_t getInsertionPtr() const {\n      return m_ptr;\n    }\n    \n    /**\n     * \\brief Sets insertion pointer to a specific value\n     * \n     * Sets the insertion pointer to a value that was\n     * previously retrieved by \\ref getInsertionPtr.\n     * \\returns Current instruction pointr\n     */\n    void beginInsertion(size_t ptr) {\n      m_ptr = ptr;\n    }\n    \n    /**\n     * \\brief Sets insertion pointer to the end\n     * \n     * After this call, new instructions will be\n     * appended to the stream. In other words,\n     * this will restore default behaviour.\n     * \\returns Previous instruction pointer\n     */\n    size_t endInsertion() {\n      return std::exchange(m_ptr, m_code.size());\n    }\n    \n  private:\n    \n    std::vector<uint32_t> m_code;\n    size_t m_ptr = 0;\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/spirv/spirv_compression.cpp",
    "content": "#include \"spirv_compression.h\"\n\nnamespace dxvk {\n\n  SpirvCompressedBuffer::SpirvCompressedBuffer()\n  : m_size(0) {\n\n  }\n\n\n  SpirvCompressedBuffer::SpirvCompressedBuffer(SpirvCodeBuffer& code)\n  : m_size(code.dwords()) {\n    // The compression (detailed below) achieves roughly 55% of the\n    // original size on average and is very consistent, so an initial\n    // estimate of roughly 58% will be accurate most of the time.\n    const uint32_t* data = code.data();\n    m_code.reserve((m_size * 75) / 128);\n\n    std::array<uint32_t, 16> block;\n    uint32_t blockMask = 0;\n    uint32_t blockOffset = 0;\n\n    // The algorithm used is a simple variable-to-fixed compression that\n    // encodes up to two consecutive SPIR-V tokens into one DWORD using\n    // a small number of different encodings. While not achieving great\n    // compression ratios, the main goal is to allow decompression code\n    // to be fast, with short dependency chains.\n    // Compressed tokens are stored in blocks of 16 DWORDs, each preceeded\n    // by a single DWORD which stores the layout for each DWORD, two bits\n    // each. The supported layouts, are as follows:\n    // 0x0: 1x 32-bit;  0x1: 1x 20-bit + 1x 12-bit\n    // 0x2: 2x 16-bit;  0x3: 1x 12-bit + 1x 20-bit\n    // These layouts are chosen to allow reasonably efficient encoding of\n    // opcode tokens, which usually fit into 20 bits, followed by type IDs,\n    // which tend to be low as well since most types are defined early.\n    for (size_t i = 0; i < m_size; ) {\n      if (likely(i + 1 < m_size)) {\n        uint32_t a = data[i];\n        uint32_t b = data[i + 1];\n        uint32_t schema;\n        uint32_t encode;\n\n        if (std::max(a, b) < (1u << 16)) {\n          schema = 0x2;\n          encode = a | (b << 16);\n        } else if (a < (1u << 20) && b < (1u << 12)) {\n          schema = 0x1;\n          encode = a | (b << 20);\n        } else if (a < (1u << 12) && b < (1u << 20)) {\n          schema = 0x3;\n          encode = a | (b << 12);\n        } else {\n          schema = 0x0;\n          encode = a;\n        }\n\n        block[blockOffset] = encode;\n        blockMask |= schema << (blockOffset << 1);\n        blockOffset += 1;\n\n        i += schema ? 2 : 1;\n      } else {\n        block[blockOffset] = data[i++];\n        blockOffset += 1;\n      }\n\n      if (unlikely(blockOffset == 16) || unlikely(i == m_size)) {\n        m_code.insert(m_code.end(), blockMask);\n        m_code.insert(m_code.end(), block.begin(), block.begin() + blockOffset);\n\n        blockMask = 0;\n        blockOffset = 0;\n      }\n    }\n\n    // Only shrink the array if we have lots of overhead for some reason.\n    // This should only happen on shaders where our initial estimate was\n    // too small. In general, we want to avoid reallocation here.\n    if (m_code.capacity() > (m_code.size() * 10) / 9)\n      m_code.shrink_to_fit();\n  }\n\n    \n  SpirvCompressedBuffer::~SpirvCompressedBuffer() {\n\n  }\n\n\n  SpirvCodeBuffer SpirvCompressedBuffer::decompress() const {\n    SpirvCodeBuffer code(m_size);\n    uint32_t* data = code.data();\n\n    uint32_t srcOffset = 0;\n    uint32_t dstOffset = 0;\n\n    constexpr uint32_t shiftAmounts = 0x0c101420;\n\n    while (dstOffset < m_size) {\n      uint32_t blockMask = m_code[srcOffset];\n\n      for (uint32_t i = 0; i < 16 && dstOffset < m_size; i++) {\n        // Use 64-bit integers for some of the operands so we can\n        // shift by 32 bits and not handle it as a special cases\n        uint32_t schema = (blockMask >> (i << 1)) & 0x3;\n        uint32_t shift  = (shiftAmounts >> (schema << 3)) & 0xff;\n        uint64_t mask   = ~(~0ull << shift);\n        uint64_t encode = m_code[srcOffset + i + 1];\n\n        data[dstOffset] = encode & mask;\n\n        if (likely(schema))\n          data[dstOffset + 1] = encode >> shift;\n\n        dstOffset += schema ? 2 : 1;\n      }\n\n      srcOffset += 17;\n    }\n\n    return code;\n  }\n\n}"
  },
  {
    "path": "src/spirv/spirv_compression.h",
    "content": "#pragma once\n\n#include <vector>\n\n#include \"spirv_code_buffer.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Compressed SPIR-V code buffer\n   *\n   * Implements a fast in-memory compression\n   * to keep memory footprint low.\n   */\n  class SpirvCompressedBuffer {\n\n  public:\n\n    SpirvCompressedBuffer();\n\n    SpirvCompressedBuffer(SpirvCodeBuffer& code);\n    \n    ~SpirvCompressedBuffer();\n    \n    SpirvCodeBuffer decompress() const;\n\n  private:\n\n    size_t                m_size;\n    std::vector<uint32_t> m_code;\n\n    void encodeDword(uint32_t dw);\n\n    uint32_t decodeDword(size_t& offset) const;\n\n  };\n\n}"
  },
  {
    "path": "src/spirv/spirv_include.h",
    "content": "#pragma once\n\n#include <spirv/unified1/spirv.hpp>\n#include <spirv/unified1/GLSL.std.450.h>\n\n#include \"../util/log/log.h\"\n#include \"../util/log/log_debug.h\"\n\n#include \"../util/util_error.h\"\n#include \"../util/util_flags.h\"\n#include \"../util/util_likely.h\"\n#include \"../util/util_string.h\"\n\n#include \"../util/rc/util_rc.h\"\n#include \"../util/rc/util_rc_ptr.h\"\n"
  },
  {
    "path": "src/spirv/spirv_instruction.h",
    "content": "#pragma once\n\n#include \"spirv_include.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief SPIR-V instruction\n   * \n   * Helps parsing a single instruction, providing\n   * access to the op code, instruction length and\n   * instruction arguments.\n   */\n  class SpirvInstruction {\n    \n  public:\n    \n    SpirvInstruction() { }\n    SpirvInstruction(uint32_t* code, uint32_t offset, uint32_t length)\n    : m_code(code), m_offset(offset), m_length(length) { }\n    \n    /**\n     * \\brief SPIR-V Op code\n     * \\returns The op code\n     */\n    spv::Op opCode() const {\n      return static_cast<spv::Op>(\n        this->arg(0) & spv::OpCodeMask);\n    }\n    \n    /**\n     * \\brief Instruction length\n     * \\returns Number of DWORDs\n     */\n    uint32_t length() const {\n      return this->arg(0) >> spv::WordCountShift;\n    }\n    \n    /**\n     * \\brief Instruction offset\n     * \\returns Offset in DWORDs\n     */\n    uint32_t offset() const {\n      return m_offset;\n    }\n    \n    /**\n     * \\brief Argument value\n     * \n     * Retrieves an argument DWORD. Note that some instructions\n     * take 64-bit arguments which require more than one DWORD.\n     * Arguments start at index 1. Calling this method with an\n     * argument ID of 0 will return the opcode token.\n     * \\param [in] idx Argument index, starting at 1\n     * \\returns The argument value\n     */\n    uint32_t arg(uint32_t idx) const {\n      const uint32_t index = m_offset + idx;\n      return index < m_length ? m_code[index] : 0;\n    }\n\n    /**\n     * \\brief Argument string\n     *\n     * Retrieves a pointer to a UTF-8-encoded string.\n     * \\param [in] idx Argument index, starting at 1\n     * \\returns Pointer to the literal string\n     */\n    const char* chr(uint32_t idx) const {\n      const uint32_t index = m_offset + idx;\n      return index < m_length ? reinterpret_cast<const char*>(&m_code[index]) : nullptr;\n    }\n    \n    /**\n     * \\brief Changes the value of an argument\n     * \n     * \\param [in] idx Argument index, starting at 1\n     * \\param [in] word New argument word\n     */\n    void setArg(uint32_t idx, uint32_t word) const {\n      if (m_offset + idx < m_length)\n        m_code[m_offset + idx] = word;\n    }\n    \n  private:\n    \n    uint32_t* m_code   = nullptr;\n    uint32_t  m_offset = 0;\n    uint32_t  m_length = 0;\n    \n  };\n  \n  \n  /**\n   * \\brief SPIR-V instruction iterator\n   * \n   * Convenient iterator that can be used\n   * to process raw SPIR-V shader code.\n   */\n  class SpirvInstructionIterator {\n    \n  public:\n    \n    SpirvInstructionIterator() { }\n    SpirvInstructionIterator(uint32_t* code, uint32_t offset, uint32_t length)\n    : m_code  (length != 0 ? code   : nullptr),\n      m_offset(length != 0 ? offset : 0),\n      m_length(length) {\n      if ((length >= 5) && (offset == 0) && (m_code[0] == spv::MagicNumber))\n        this->advance(5);\n    }\n    \n    SpirvInstructionIterator& operator ++ () {\n      this->advance(SpirvInstruction(m_code, m_offset, m_length).length());\n      return *this;\n    }\n    \n    SpirvInstructionIterator operator ++ (int) {\n      SpirvInstructionIterator result = *this;\n      this->advance(SpirvInstruction(m_code, m_offset, m_length).length());\n      return result;\n    }\n    \n    SpirvInstruction operator * () const {\n      return SpirvInstruction(m_code, m_offset, m_length);\n    }\n    \n    bool operator == (const SpirvInstructionIterator& other) const {\n      return this->m_code   == other.m_code\n          && this->m_offset == other.m_offset\n          && this->m_length == other.m_length;\n    }\n    \n    bool operator != (const SpirvInstructionIterator& other) const {\n      return this->m_code   != other.m_code\n          || this->m_offset != other.m_offset\n          || this->m_length != other.m_length;\n    }\n    \n  private:\n    \n    uint32_t* m_code   = nullptr;\n    uint32_t  m_offset = 0;\n    uint32_t  m_length = 0;\n    \n    void advance(uint32_t n) {\n      if (m_offset + n < m_length) {\n        m_offset += n;\n      } else {\n        m_code   = nullptr;\n        m_offset = 0;\n        m_length = 0;\n      }\n    }\n    \n  };\n  \n}"
  },
  {
    "path": "src/spirv/spirv_module.cpp",
    "content": "#include <cstring>\n\n#include \"spirv_module.h\"\n\nnamespace dxvk {\n  \n  SpirvModule::SpirvModule(uint32_t version)\n  : m_version(version) {\n    this->instImportGlsl450();\n  }\n  \n  \n  SpirvModule::~SpirvModule() {\n    \n  }\n  \n  \n  SpirvCodeBuffer SpirvModule::compile() {\n    SpirvCodeBuffer result;\n    result.putHeader(m_version, m_id);\n    result.append(m_capabilities);\n    result.append(m_extensions);\n    result.append(m_instExt);\n    result.append(m_memoryModel);\n    result.append(m_entryPoints);\n    result.append(m_execModeInfo);\n    result.append(m_debugNames);\n    result.append(m_annotations);\n    result.append(m_typeConstDefs);\n    result.append(m_variables);\n\n    // Perform some crude dead code elimination. In some cases, our compilers\n    // may emit invalid code, such as an unreachable block branching to a loop's\n    // continue block, but those cases cannot be reasonably detected up-front.\n    std::unordered_set<uint32_t> reachableBlocks;\n    std::unordered_set<uint32_t> mergeBlocks;\n\n    classifyBlocks(reachableBlocks, mergeBlocks);\n\n    bool reachable = true;\n\n    for (auto ins : m_code) {\n      if (ins.opCode() == spv::OpFunctionEnd) {\n        reachable = true;\n        result.append(ins);\n      } else if (ins.opCode() == spv::OpLabel) {\n        uint32_t labelId = ins.arg(1);\n\n        if ((reachable = reachableBlocks.find(labelId) != reachableBlocks.end())) {\n          result.append(ins);\n        } else if (mergeBlocks.find(labelId) != mergeBlocks.end()) {\n          result.append(ins);\n          result.putIns(spv::OpUnreachable, 1);\n        }\n      } else if (reachable) {\n        result.append(ins);\n      }\n    }\n\n    return result;\n  }\n  \n  \n  uint32_t SpirvModule::allocateId() {\n    return m_id++;\n  }\n  \n  \n  bool SpirvModule::hasCapability(\n          spv::Capability         capability) {\n    for (auto ins : m_capabilities) {\n      if (ins.opCode() == spv::OpCapability && ins.arg(1) == capability)\n        return true;\n    }\n\n    return false;\n  }\n\n  void SpirvModule::enableCapability(\n          spv::Capability         capability) {\n    // Scan the generated instructions to check\n    // whether we already enabled the capability.\n    if (!hasCapability(capability)) {\n      m_capabilities.putIns (spv::OpCapability, 2);\n      m_capabilities.putWord(capability);\n    }\n  }\n  \n  \n  void SpirvModule::enableExtension(\n    const char*                   extensionName) {\n    m_extensions.putIns (spv::OpExtension, 1 + m_extensions.strLen(extensionName));\n    m_extensions.putStr (extensionName);\n  }\n  \n  \n  void SpirvModule::addEntryPoint(\n          uint32_t                entryPointId,\n          spv::ExecutionModel     executionModel,\n    const char*                   name) {\n    m_entryPoints.putIns  (spv::OpEntryPoint, 3 + m_entryPoints.strLen(name) + m_interfaceVars.size());\n    m_entryPoints.putWord (executionModel);\n    m_entryPoints.putWord (entryPointId);\n    m_entryPoints.putStr  (name);\n    \n    for (uint32_t varId : m_interfaceVars)\n      m_entryPoints.putWord(varId);\n  }\n  \n  \n  void SpirvModule::setMemoryModel(\n          spv::AddressingModel    addressModel,\n          spv::MemoryModel        memoryModel) {\n    m_memoryModel.putIns  (spv::OpMemoryModel, 3);\n    m_memoryModel.putWord (addressModel);\n    m_memoryModel.putWord (memoryModel);\n  }\n  \n    \n  void SpirvModule::setExecutionMode(\n          uint32_t                entryPointId,\n          spv::ExecutionMode      executionMode) {\n    m_execModeInfo.putIns (spv::OpExecutionMode, 3);\n    m_execModeInfo.putWord(entryPointId);\n    m_execModeInfo.putWord(executionMode);\n  }\n  \n  \n  void SpirvModule::setExecutionMode(\n          uint32_t                entryPointId,\n          spv::ExecutionMode      executionMode,\n          uint32_t                argCount,\n    const uint32_t*               args) {\n    m_execModeInfo.putIns (spv::OpExecutionMode, 3 + argCount);\n    m_execModeInfo.putWord(entryPointId);\n    m_execModeInfo.putWord(executionMode);\n\n    for (uint32_t i = 0; i < argCount; i++)\n      m_execModeInfo.putWord(args[i]);\n  }\n\n\n  void SpirvModule::setInvocations(\n          uint32_t                entryPointId,\n          uint32_t                invocations) {\n    m_execModeInfo.putIns  (spv::OpExecutionMode, 4);\n    m_execModeInfo.putWord (entryPointId);\n    m_execModeInfo.putWord (spv::ExecutionModeInvocations);\n    m_execModeInfo.putInt32(invocations);\n  }\n  \n  \n  void SpirvModule::setLocalSize(\n          uint32_t                entryPointId,\n          uint32_t                x,\n          uint32_t                y,\n          uint32_t                z) {\n    m_execModeInfo.putIns  (spv::OpExecutionMode, 6);\n    m_execModeInfo.putWord (entryPointId);\n    m_execModeInfo.putWord (spv::ExecutionModeLocalSize);\n    m_execModeInfo.putInt32(x);\n    m_execModeInfo.putInt32(y);\n    m_execModeInfo.putInt32(z);\n  }\n  \n  \n  void SpirvModule::setOutputVertices(\n          uint32_t                entryPointId,\n          uint32_t                vertexCount) {\n    m_execModeInfo.putIns (spv::OpExecutionMode, 4);\n    m_execModeInfo.putWord(entryPointId);\n    m_execModeInfo.putWord(spv::ExecutionModeOutputVertices);\n    m_execModeInfo.putWord(vertexCount);\n  }\n  \n  \n  uint32_t SpirvModule::addDebugString(\n    const char*                   string) {\n    uint32_t resultId = this->allocateId();\n    \n    m_debugNames.putIns (spv::OpString,\n      2 + m_debugNames.strLen(string));\n    m_debugNames.putWord(resultId);\n    m_debugNames.putStr (string);\n    return resultId;\n  }\n  \n  \n  void SpirvModule::setDebugSource(\n          spv::SourceLanguage     language,\n          uint32_t                version,\n          uint32_t                file,\n    const char*                   source) {\n    uint32_t strLen = source != nullptr\n      ? m_debugNames.strLen(source) : 0;\n    \n    m_debugNames.putIns (spv::OpSource, 4 + strLen);\n    m_debugNames.putWord(language);\n    m_debugNames.putWord(version);\n    m_debugNames.putWord(file);\n    \n    if (source != nullptr)\n      m_debugNames.putStr(source);\n  }\n  \n  void SpirvModule::setDebugName(\n          uint32_t                expressionId,\n    const char*                   debugName) {\n    m_debugNames.putIns (spv::OpName, 2 + m_debugNames.strLen(debugName));\n    m_debugNames.putWord(expressionId);\n    m_debugNames.putStr (debugName);\n  }\n  \n  \n  void SpirvModule::setDebugMemberName(\n          uint32_t                structId,\n          uint32_t                memberId,\n    const char*                   debugName) {\n    m_debugNames.putIns (spv::OpMemberName, 3 + m_debugNames.strLen(debugName));\n    m_debugNames.putWord(structId);\n    m_debugNames.putWord(memberId);\n    m_debugNames.putStr (debugName);\n  }\n  \n  \n  uint32_t SpirvModule::constBool(\n          bool                    v) {\n    return this->defConst(v\n        ? spv::OpConstantTrue\n        : spv::OpConstantFalse,\n      this->defBoolType(),\n      0, nullptr);\n  }\n  \n  \n  uint32_t SpirvModule::consti32(\n          int32_t                 v) {\n    std::array<uint32_t, 1> data;\n    std::memcpy(data.data(), &v, sizeof(v));\n    \n    return this->defConst(\n      spv::OpConstant,\n      this->defIntType(32, 1),\n      data.size(),\n      data.data());\n  }\n  \n  \n  uint32_t SpirvModule::consti64(\n          int64_t                 v) {\n    std::array<uint32_t, 2> data;\n    std::memcpy(data.data(), &v, sizeof(v));\n    \n    return this->defConst(\n      spv::OpConstant,\n      this->defIntType(64, 1),\n      data.size(),\n      data.data());\n  }\n  \n  \n  uint32_t SpirvModule::constu32(\n          uint32_t                v) {\n    std::array<uint32_t, 1> data;\n    std::memcpy(data.data(), &v, sizeof(v));\n    \n    return this->defConst(\n      spv::OpConstant,\n      this->defIntType(32, 0),\n      data.size(),\n      data.data());\n  }\n  \n  \n  uint32_t SpirvModule::constu64(\n          uint64_t                v) {\n    std::array<uint32_t, 2> data;\n    std::memcpy(data.data(), &v, sizeof(v));\n    \n    return this->defConst(\n      spv::OpConstant,\n      this->defIntType(64, 0),\n      data.size(),\n      data.data());\n  }\n  \n  \n  uint32_t SpirvModule::constf32(\n          float                   v) {\n    std::array<uint32_t, 1> data;\n    std::memcpy(data.data(), &v, sizeof(v));\n    \n    return this->defConst(\n      spv::OpConstant,\n      this->defFloatType(32),\n      data.size(),\n      data.data());\n  }\n  \n  \n  uint32_t SpirvModule::constf64(\n          double                  v) {\n    std::array<uint32_t, 2> data;\n    std::memcpy(data.data(), &v, sizeof(v));\n    \n    return this->defConst(\n      spv::OpConstant,\n      this->defFloatType(64),\n      data.size(),\n      data.data());\n  }\n  \n  \n  uint32_t SpirvModule::constvec4i32(\n          int32_t                 x,\n          int32_t                 y,\n          int32_t                 z,\n          int32_t                 w) {\n    std::array<uint32_t, 4> args = {{\n      this->consti32(x), this->consti32(y),\n      this->consti32(z), this->consti32(w),\n    }};\n    \n    uint32_t scalarTypeId = this->defIntType(32, 1);\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 4);\n    \n    return this->constComposite(vectorTypeId, args.size(), args.data());\n  }\n\n\n  uint32_t SpirvModule::constvec4b32(\n          bool                    x,\n          bool                    y,\n          bool                    z,\n          bool                    w) {\n    std::array<uint32_t, 4> args = {{\n      this->constBool(x), this->constBool(y),\n      this->constBool(z), this->constBool(w),\n    }};\n    \n    uint32_t scalarTypeId = this->defBoolType();\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 4);\n    \n    return this->constComposite(vectorTypeId, args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::constvec2u32(\n          uint32_t                x,\n          uint32_t                y) {\n    std::array<uint32_t, 2> args = {{\n      this->constu32(x), this->constu32(y),\n    }};\n\n    uint32_t scalarTypeId = this->defIntType(32, 0);\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 2);\n\n    return this->constComposite(vectorTypeId, args.size(), args.data());\n  }\n\n\n  uint32_t SpirvModule::constvec4u32(\n          uint32_t                x,\n          uint32_t                y,\n          uint32_t                z,\n          uint32_t                w) {\n    std::array<uint32_t, 4> args = {{\n      this->constu32(x), this->constu32(y),\n      this->constu32(z), this->constu32(w),\n    }};\n    \n    uint32_t scalarTypeId = this->defIntType(32, 0);\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 4);\n    \n    return this->constComposite(vectorTypeId, args.size(), args.data());\n  }\n  \n\n  uint32_t SpirvModule::constvec2f32(\n          float                   x,\n          float                   y) {\n    std::array<uint32_t, 2> args = {{\n      this->constf32(x), this->constf32(y),\n    }};\n    \n    uint32_t scalarTypeId = this->defFloatType(32);\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 2);\n    \n    return this->constComposite(vectorTypeId, args.size(), args.data());\n  }\n  \n\n  uint32_t SpirvModule::constvec3f32(\n          float                   x,\n          float                   y,\n          float                   z) {\n    std::array<uint32_t, 3> args = {{\n      this->constf32(x), this->constf32(y),\n      this->constf32(z),\n    }};\n    \n    uint32_t scalarTypeId = this->defFloatType(32);\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 3);\n    \n    return this->constComposite(vectorTypeId, args.size(), args.data());\n  }\n\n  \n  uint32_t SpirvModule::constvec4f32(\n          float                   x,\n          float                   y,\n          float                   z,\n          float                   w) {\n    std::array<uint32_t, 4> args = {{\n      this->constf32(x), this->constf32(y),\n      this->constf32(z), this->constf32(w),\n    }};\n    \n    uint32_t scalarTypeId = this->defFloatType(32);\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, 4);\n    \n    return this->constComposite(vectorTypeId, args.size(), args.data());\n  }\n\n\n  uint32_t SpirvModule::constfReplicant(\n          float                   replicant,\n          uint32_t                count) {\n    uint32_t value = this->constf32(replicant);\n\n    std::array<uint32_t, 4> args = { value, value, value, value };\n\n    // Can't make a scalar composite.\n    if (count == 1)\n      return args[0];\n    \n    uint32_t scalarTypeId = this->defFloatType(32);\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, count);\n    \n    return this->constComposite(vectorTypeId, count, args.data());\n  }\n\n\n  uint32_t SpirvModule::constbReplicant(\n          bool                    replicant,\n          uint32_t                count) {\n    uint32_t value = this->constBool(replicant);\n\n    std::array<uint32_t, 4> args = { value, value, value, value };\n\n    // Can't make a scalar composite.\n    if (count == 1)\n      return args[0];\n    \n    uint32_t scalarTypeId = this->defBoolType();\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, count);\n    \n    return this->constComposite(vectorTypeId, count, args.data());\n  }\n\n\n  uint32_t SpirvModule::constiReplicant(\n          int32_t                 replicant,\n          uint32_t                count) {\n    uint32_t value = this->consti32(replicant);\n\n    std::array<uint32_t, 4> args = { value, value, value, value };\n\n    // Can't make a scalar composite.\n    if (count == 1)\n      return args[0];\n    \n    uint32_t scalarTypeId = this->defIntType(32, 1);\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, count);\n    \n    return this->constComposite(vectorTypeId, count, args.data());\n  }\n\n\n  uint32_t SpirvModule::constuReplicant(\n          int32_t                 replicant,\n          uint32_t                count) {\n    uint32_t value = this->constu32(replicant);\n\n    std::array<uint32_t, 4> args = { value, value, value, value };\n\n    // Can't make a scalar composite.\n    if (count == 1)\n      return args[0];\n    \n    uint32_t scalarTypeId = this->defIntType(32, 0);\n    uint32_t vectorTypeId = this->defVectorType(scalarTypeId, count);\n    \n    return this->constComposite(vectorTypeId, count, args.data());\n  }\n  \n  \n  uint32_t SpirvModule::constComposite(\n          uint32_t                typeId,\n          uint32_t                constCount,\n    const uint32_t*               constIds) {\n    return this->defConst(\n      spv::OpConstantComposite,\n      typeId, constCount, constIds);\n  }\n  \n  \n  uint32_t SpirvModule::constUndef(\n          uint32_t                typeId) {\n    return this->defConst(spv::OpUndef,\n      typeId, 0, nullptr);\n  }\n\n\n  uint32_t SpirvModule::constNull(\n          uint32_t                typeId) {\n    return this->defConst(spv::OpConstantNull,\n      typeId, 0, nullptr);\n  }\n\n\n  uint32_t SpirvModule::lateConst32(\n          uint32_t                typeId) {\n    uint32_t resultId = this->allocateId();\n    m_lateConsts.insert(resultId);\n\n    m_typeConstDefs.putIns (spv::OpConstant, 4);\n    m_typeConstDefs.putWord(typeId);\n    m_typeConstDefs.putWord(resultId);\n    m_typeConstDefs.putWord(0);\n    return resultId;\n  }\n\n\n  void SpirvModule::setLateConst(\n            uint32_t                constId,\n      const uint32_t*               argIds) {\n    for (auto ins : m_typeConstDefs) {\n      if (ins.opCode() != spv::OpConstant\n       && ins.opCode() != spv::OpConstantComposite)\n        continue;\n      \n      if (ins.arg(2) != constId)\n        continue;\n\n      for (uint32_t i = 3; i < ins.length(); i++)\n        ins.setArg(i, argIds[i - 3]);\n\n      return;\n    }\n  }\n\n\n  uint32_t SpirvModule::specConstBool(\n          bool                    v) {\n    uint32_t typeId   = this->defBoolType();\n    uint32_t resultId = this->allocateId();\n    \n    const spv::Op op = v\n      ? spv::OpSpecConstantTrue\n      : spv::OpSpecConstantFalse;\n    \n    m_typeConstDefs.putIns  (op, 3);\n    m_typeConstDefs.putWord (typeId);\n    m_typeConstDefs.putWord (resultId);\n    return resultId;\n  }\n    \n  \n  uint32_t SpirvModule::specConst32(\n          uint32_t                typeId,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_typeConstDefs.putIns  (spv::OpSpecConstant, 4);\n    m_typeConstDefs.putWord (typeId);\n    m_typeConstDefs.putWord (resultId);\n    m_typeConstDefs.putWord (value);\n    return resultId;\n  }\n  \n  \n  void SpirvModule::decorate(\n          uint32_t                object,\n          spv::Decoration         decoration) {\n    m_annotations.putIns  (spv::OpDecorate, 3);\n    m_annotations.putWord (object);\n    m_annotations.putWord (decoration);\n  }\n  \n  \n  void SpirvModule::decorateArrayStride(\n          uint32_t                object,\n          uint32_t                stride) {\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationArrayStride);\n    m_annotations.putInt32(stride);\n  }\n  \n  \n  void SpirvModule::decorateBinding(\n          uint32_t                object,\n          uint32_t                binding) {\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationBinding);\n    m_annotations.putInt32(binding);\n  }\n  \n  \n  void SpirvModule::decorateBlock(uint32_t object) {\n    m_annotations.putIns  (spv::OpDecorate, 3);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationBlock);\n  }\n  \n  \n  void SpirvModule::decorateBuiltIn(\n          uint32_t                object,\n          spv::BuiltIn            builtIn) {\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationBuiltIn);\n    m_annotations.putWord (builtIn);\n  }\n  \n  \n  void SpirvModule::decorateComponent(\n          uint32_t                object,\n          uint32_t                location) {\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationComponent);\n    m_annotations.putInt32(location);\n  }\n  \n  \n  void SpirvModule::decorateDescriptorSet(\n          uint32_t                object,\n          uint32_t                set) {\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationDescriptorSet);\n    m_annotations.putInt32(set);\n  }\n  \n  \n  void SpirvModule::decorateIndex(\n          uint32_t                object,\n          uint32_t                index) {\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationIndex);\n    m_annotations.putInt32(index);\n  }\n\n\n  void SpirvModule::decorateLocation(\n          uint32_t                object,\n          uint32_t                location) {\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationLocation);\n    m_annotations.putInt32(location);\n  }\n  \n  \n  void SpirvModule::decorateSpecId(\n          uint32_t                object,\n          uint32_t                specId) {\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationSpecId);\n    m_annotations.putInt32(specId);\n  }\n  \n\n  void SpirvModule::decorateXfb(\n          uint32_t                object,\n          uint32_t                streamId,\n          uint32_t                bufferId,\n          uint32_t                offset,\n          uint32_t                stride) {\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationStream);\n    m_annotations.putInt32(streamId);\n\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationXfbBuffer);\n    m_annotations.putInt32(bufferId);\n\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationXfbStride);\n    m_annotations.putInt32(stride);\n\n    m_annotations.putIns  (spv::OpDecorate, 4);\n    m_annotations.putWord (object);\n    m_annotations.putWord (spv::DecorationOffset);\n    m_annotations.putInt32(offset);\n  }\n  \n  \n  void SpirvModule::memberDecorateBuiltIn(\n          uint32_t                structId,\n          uint32_t                memberId,\n          spv::BuiltIn            builtIn) {\n    m_annotations.putIns  (spv::OpMemberDecorate, 5);\n    m_annotations.putWord (structId);\n    m_annotations.putWord (memberId);\n    m_annotations.putWord (spv::DecorationBuiltIn);\n    m_annotations.putWord (builtIn);\n  }\n\n\n  void SpirvModule::memberDecorate(\n          uint32_t                structId,\n          uint32_t                memberId,\n          spv::Decoration         decoration) {\n    m_annotations.putIns  (spv::OpMemberDecorate, 4);\n    m_annotations.putWord (structId);\n    m_annotations.putWord (memberId);\n    m_annotations.putWord (decoration);\n  }\n\n\n  void SpirvModule::memberDecorateMatrixStride(\n          uint32_t                structId,\n          uint32_t                memberId,\n          uint32_t                stride) {\n    m_annotations.putIns  (spv::OpMemberDecorate, 5);\n    m_annotations.putWord (structId);\n    m_annotations.putWord (memberId);\n    m_annotations.putWord (spv::DecorationMatrixStride);\n    m_annotations.putWord (stride);\n  }\n  \n  \n  void SpirvModule::memberDecorateOffset(\n          uint32_t                structId,\n          uint32_t                memberId,\n          uint32_t                offset) {\n    m_annotations.putIns  (spv::OpMemberDecorate, 5);\n    m_annotations.putWord (structId);\n    m_annotations.putWord (memberId);\n    m_annotations.putWord (spv::DecorationOffset);\n    m_annotations.putWord (offset);\n  }\n  \n  \n  uint32_t SpirvModule::defVoidType() {\n    return this->defType(spv::OpTypeVoid, 0, nullptr);\n  }\n  \n  \n  uint32_t SpirvModule::defBoolType() {\n    return this->defType(spv::OpTypeBool, 0, nullptr);\n  }\n  \n  \n  uint32_t SpirvModule::defIntType(\n          uint32_t                width,\n          uint32_t                isSigned) {\n    std::array<uint32_t, 2> args = {{ width, isSigned }};\n    return this->defType(spv::OpTypeInt,\n      args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::defFloatType(\n          uint32_t                width) {\n    std::array<uint32_t, 1> args = {{ width }};\n    return this->defType(spv::OpTypeFloat,\n      args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::defVectorType(\n          uint32_t                elementType,\n          uint32_t                elementCount) {\n    std::array<uint32_t, 2> args =\n      {{ elementType, elementCount }};\n    \n    return this->defType(spv::OpTypeVector,\n      args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::defMatrixType(\n          uint32_t                columnType,\n          uint32_t                columnCount) {\n    std::array<uint32_t, 2> args =\n      {{ columnType, columnCount }};\n    \n    return this->defType(spv::OpTypeMatrix,\n      args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::defArrayType(\n          uint32_t                typeId,\n          uint32_t                length) {\n    std::array<uint32_t, 2> args = {{ typeId, length }};\n    \n    return this->defType(spv::OpTypeArray,\n      args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::defArrayTypeUnique(\n          uint32_t                typeId,\n          uint32_t                length) {\n    uint32_t resultId = this->allocateId();\n    \n    m_typeConstDefs.putIns (spv::OpTypeArray, 4);\n    m_typeConstDefs.putWord(resultId);\n    m_typeConstDefs.putWord(typeId);\n    m_typeConstDefs.putWord(length);\n\n    m_uniqueTypes.insert(resultId);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::defRuntimeArrayType(\n          uint32_t                typeId) {\n    std::array<uint32_t, 1> args = { typeId };\n    \n    return this->defType(spv::OpTypeRuntimeArray,\n      args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::defRuntimeArrayTypeUnique(\n          uint32_t                typeId) {\n    uint32_t resultId = this->allocateId();\n    \n    m_typeConstDefs.putIns (spv::OpTypeRuntimeArray, 3);\n    m_typeConstDefs.putWord(resultId);\n    m_typeConstDefs.putWord(typeId);\n\n    m_uniqueTypes.insert(resultId);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::defFunctionType(\n          uint32_t                returnType,\n          uint32_t                argCount,\n    const uint32_t*               argTypes) {\n    std::vector<uint32_t> args;\n    args.push_back(returnType);\n    \n    for (uint32_t i = 0; i < argCount; i++)\n      args.push_back(argTypes[i]);\n    \n    return this->defType(spv::OpTypeFunction,\n      args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::defStructType(\n          uint32_t                memberCount,\n    const uint32_t*               memberTypes) {\n    return this->defType(spv::OpTypeStruct,\n      memberCount, memberTypes);\n  }\n  \n  \n  uint32_t SpirvModule::defStructTypeUnique(\n          uint32_t                memberCount,\n    const uint32_t*               memberTypes) {\n    uint32_t resultId = this->allocateId();\n    \n    m_typeConstDefs.putIns (spv::OpTypeStruct, 2 + memberCount);\n    m_typeConstDefs.putWord(resultId);\n    \n    for (uint32_t i = 0; i < memberCount; i++)\n      m_typeConstDefs.putWord(memberTypes[i]);\n\n    m_uniqueTypes.insert(resultId);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::defPointerType(\n          uint32_t                variableType,\n          spv::StorageClass       storageClass) {\n    std::array<uint32_t, 2> args = {{\n      static_cast<uint32_t>(storageClass),\n      variableType,\n    }};\n    \n    return this->defType(spv::OpTypePointer,\n      args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::defSamplerType() {\n    return this->defType(spv::OpTypeSampler, 0, nullptr);\n  }\n  \n  \n  uint32_t SpirvModule::defImageType(\n          uint32_t                sampledType,\n          spv::Dim                dimensionality,\n          uint32_t                depth,\n          uint32_t                arrayed,\n          uint32_t                multisample,\n          uint32_t                sampled,\n          spv::ImageFormat        format) {\n    std::array<uint32_t, 7> args = {{\n      sampledType,\n    static_cast<uint32_t>(dimensionality),\n      depth, arrayed,\n      multisample,\n      sampled,\n    static_cast<uint32_t>(format)\n    }};\n    \n    return this->defType(spv::OpTypeImage,\n      args.size(), args.data());\n  }\n  \n  \n  uint32_t SpirvModule::defSampledImageType(\n          uint32_t                imageType) {\n    return this->defType(spv::OpTypeSampledImage, 1, &imageType);\n  }\n  \n  \n  uint32_t SpirvModule::newVar(\n          uint32_t                pointerType,\n          spv::StorageClass       storageClass) {\n    return newVarInit(pointerType, storageClass, 0u);\n  }\n  \n  \n  uint32_t SpirvModule::newVarInit(\n          uint32_t                pointerType,\n          spv::StorageClass       storageClass,\n          uint32_t                initialValue) {\n    uint32_t resultId = this->allocateId();\n    \n    if (isInterfaceVar(storageClass))\n      m_interfaceVars.push_back(resultId);\n\n    auto& code = storageClass != spv::StorageClassFunction\n      ? m_variables : m_code;\n    \n    code.putIns  (spv::OpVariable, 4u + (initialValue ? 1u : 0u));\n    code.putWord (pointerType);\n    code.putWord (resultId);\n    code.putWord (storageClass);\n\n    if (initialValue)\n      code.putWord(initialValue);\n\n    return resultId;\n  }\n  \n  \n  void SpirvModule::functionBegin(\n          uint32_t                returnType,\n          uint32_t                functionId,\n          uint32_t                functionType,\n    spv::FunctionControlMask      functionControl) {\n    m_code.putIns (spv::OpFunction, 5);\n    m_code.putWord(returnType);\n    m_code.putWord(functionId);\n    m_code.putWord(functionControl);\n    m_code.putWord(functionType);\n  }\n  \n  \n  uint32_t SpirvModule::functionParameter(\n          uint32_t                parameterType) {\n    uint32_t parameterId = this->allocateId();\n    \n    m_code.putIns (spv::OpFunctionParameter, 3);\n    m_code.putWord(parameterType);\n    m_code.putWord(parameterId);\n    return parameterId;\n  }\n  \n  \n  void SpirvModule::functionEnd() {\n    m_code.putIns (spv::OpFunctionEnd, 1);\n  }\n\n\n  uint32_t SpirvModule::opAccessChain(\n          uint32_t                resultType,\n          uint32_t                composite,\n          uint32_t                indexCount,\n    const uint32_t*               indexArray) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAccessChain, 4 + indexCount);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(composite);\n    \n    for (uint32_t i = 0; i < indexCount; i++)\n      m_code.putInt32(indexArray[i]);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opArrayLength(\n          uint32_t                resultType,\n          uint32_t                structure,\n          uint32_t                memberId) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpArrayLength, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(structure);\n    m_code.putWord(memberId);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAny(\n          uint32_t                resultType,\n          uint32_t                vector) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAny, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAll(\n          uint32_t                resultType,\n          uint32_t                vector) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAll, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector);\n    return resultId;\n  }\n  \n    \n  uint32_t SpirvModule::opAtomicLoad(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicLoad, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    return resultId;\n  }\n  \n  \n  void SpirvModule::opAtomicStore(\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    m_code.putIns (spv::OpAtomicStore, 5);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicExchange(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicExchange, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicCompareExchange(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                equal,\n          uint32_t                unequal,\n          uint32_t                value,\n          uint32_t                comparator) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicCompareExchange, 9);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(equal);\n    m_code.putWord(unequal);\n    m_code.putWord(value);\n    m_code.putWord(comparator);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicIIncrement(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicIIncrement, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicIDecrement(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicIDecrement, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicIAdd(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicIAdd, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicISub(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicISub, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicSMin(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicSMin, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicSMax(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicSMax, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicUMin(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicUMin, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicUMax(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicUMax, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicAnd(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicAnd, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicOr(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicOr, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opAtomicXor(\n          uint32_t                resultType,\n          uint32_t                pointer,\n          uint32_t                scope,\n          uint32_t                semantics,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpAtomicXor, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(pointer);\n    m_code.putWord(scope);\n    m_code.putWord(semantics);\n    m_code.putWord(value);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opBitcast(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpBitcast, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opBitCount(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpBitCount, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opBitReverse(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpBitReverse, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFindILsb(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450FindILsb);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFindUMsb(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450FindUMsb);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFindSMsb(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450FindSMsb);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n            \n  uint32_t SpirvModule::opBitFieldInsert(\n          uint32_t                resultType,\n          uint32_t                base,\n          uint32_t                insert,\n          uint32_t                offset,\n          uint32_t                count) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpBitFieldInsert, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(base);\n    m_code.putWord(insert);\n    m_code.putWord(offset);\n    m_code.putWord(count);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opBitFieldSExtract(\n          uint32_t                resultType,\n          uint32_t                base,\n          uint32_t                offset,\n          uint32_t                count) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpBitFieldSExtract, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(base);\n    m_code.putWord(offset);\n    m_code.putWord(count);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opBitFieldUExtract(\n          uint32_t                resultType,\n          uint32_t                base,\n          uint32_t                offset,\n          uint32_t                count) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpBitFieldUExtract, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(base);\n    m_code.putWord(offset);\n    m_code.putWord(count);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opBitwiseAnd(\n          uint32_t                resultType,\n          uint32_t                operand1,\n          uint32_t                operand2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpBitwiseAnd, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand1);\n    m_code.putWord(operand2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opBitwiseOr(\n          uint32_t                resultType,\n          uint32_t                operand1,\n          uint32_t                operand2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpBitwiseOr, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand1);\n    m_code.putWord(operand2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opBitwiseXor(\n          uint32_t                resultType,\n          uint32_t                operand1,\n          uint32_t                operand2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpBitwiseXor, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand1);\n    m_code.putWord(operand2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opNot(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpNot, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opShiftLeftLogical(\n          uint32_t                resultType,\n          uint32_t                base,\n          uint32_t                shift) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpShiftLeftLogical, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(base);\n    m_code.putWord(shift);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opShiftRightArithmetic(\n          uint32_t                resultType,\n          uint32_t                base,\n          uint32_t                shift) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpShiftRightArithmetic, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(base);\n    m_code.putWord(shift);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opShiftRightLogical(\n          uint32_t                resultType,\n          uint32_t                base,\n          uint32_t                shift) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpShiftRightLogical, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(base);\n    m_code.putWord(shift);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opConvertFtoS(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpConvertFToS, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opConvertFtoU(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpConvertFToU, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opConvertStoF(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpConvertSToF, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opConvertUtoF(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpConvertUToF, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opUConvert(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns (spv::OpUConvert, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n  \n  uint32_t SpirvModule::opCompositeConstruct(\n          uint32_t                resultType,\n          uint32_t                valueCount,\n    const uint32_t*               valueArray) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpCompositeConstruct, 3 + valueCount);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    \n    for (uint32_t i = 0; i < valueCount; i++)\n      m_code.putWord(valueArray[i]);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opCompositeExtract(\n          uint32_t                resultType,\n          uint32_t                composite,\n          uint32_t                indexCount,\n    const uint32_t*               indexArray) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpCompositeExtract, 4 + indexCount);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(composite);\n    \n    for (uint32_t i = 0; i < indexCount; i++)\n      m_code.putInt32(indexArray[i]);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opCompositeInsert(\n          uint32_t                resultType,\n          uint32_t                object,\n          uint32_t                composite,\n          uint32_t                indexCount,\n    const uint32_t*               indexArray) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpCompositeInsert, 5 + indexCount);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(object);\n    m_code.putWord(composite);\n    \n    for (uint32_t i = 0; i < indexCount; i++)\n      m_code.putInt32(indexArray[i]);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opDpdx(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpDPdx, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opDpdy(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpDPdy, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opDpdxCoarse(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpDPdxCoarse, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opDpdyCoarse(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpDPdyCoarse, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opDpdxFine(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpDPdxFine, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opDpdyFine(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpDPdyFine, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opVectorExtractDynamic(\n          uint32_t                resultType,\n          uint32_t                vector,\n          uint32_t                index) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpVectorExtractDynamic, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector);\n    m_code.putWord(index);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opVectorShuffle(\n          uint32_t                resultType,\n          uint32_t                vectorLeft,\n          uint32_t                vectorRight,\n          uint32_t                indexCount,\n    const uint32_t*               indexArray) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpVectorShuffle, 5 + indexCount);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vectorLeft);\n    m_code.putWord(vectorRight);\n    \n    for (uint32_t i = 0; i < indexCount; i++)\n      m_code.putInt32(indexArray[i]);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSNegate(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpSNegate, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFNegate(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFNegate, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSAbs(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450SAbs);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFAbs(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450FAbs);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n\n    uint32_t SpirvModule::opFSign(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450FSign);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opFMix(\n          uint32_t                resultType,\n          uint32_t                x,\n          uint32_t                y,\n          uint32_t                a) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 8);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450FMix);\n    m_code.putWord(x);\n    m_code.putWord(y);\n    m_code.putWord(a);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opCross(\n          uint32_t                resultType,\n          uint32_t                x,\n          uint32_t                y) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Cross);\n    m_code.putWord(x);\n    m_code.putWord(y);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opIAdd(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpIAdd, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opISub(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpISub, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFAdd(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFAdd, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFSub(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFSub, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSDiv(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpSDiv, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opUDiv(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpUDiv, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSRem(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpSRem, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opUMod(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpUMod, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFDiv(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFDiv, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opIMul(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpIMul, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n    \n  uint32_t SpirvModule::opFMul(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFMul, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opVectorTimesScalar(\n    uint32_t                resultType,\n    uint32_t                vector,\n    uint32_t                scalar) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpVectorTimesScalar, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector);\n    m_code.putWord(scalar);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opMatrixTimesMatrix(\n    uint32_t                resultType,\n    uint32_t                a,\n    uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpMatrixTimesMatrix, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opMatrixTimesVector(\n    uint32_t                resultType,\n    uint32_t                matrix,\n    uint32_t                vector) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpMatrixTimesVector, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(matrix);\n    m_code.putWord(vector);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opVectorTimesMatrix(\n    uint32_t                resultType,\n    uint32_t                vector,\n    uint32_t                matrix) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpVectorTimesMatrix, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector);\n    m_code.putWord(matrix);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opTranspose(\n    uint32_t                resultType,\n    uint32_t                matrix) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpTranspose, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(matrix);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opInverse(\n    uint32_t                resultType,\n    uint32_t                matrix) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450MatrixInverse);\n    m_code.putWord(matrix);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opFFma(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b,\n          uint32_t                c) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 8);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Fma);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    m_code.putWord(c);\n    return resultId;\n  }\n    \n  \n  uint32_t SpirvModule::opFMax(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450FMax);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFMin(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450FMin);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n    \n  \n  uint32_t SpirvModule::opNMax(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450NMax);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opNMin(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450NMin);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSMax(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450SMax);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSMin(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450SMin);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opUMax(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450UMax);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opUMin(\n          uint32_t                resultType,\n          uint32_t                a,\n          uint32_t                b) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450UMin);\n    m_code.putWord(a);\n    m_code.putWord(b);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFClamp(\n          uint32_t                resultType,\n          uint32_t                x,\n          uint32_t                minVal,\n          uint32_t                maxVal) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 8);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450FClamp);\n    m_code.putWord(x);\n    m_code.putWord(minVal);\n    m_code.putWord(maxVal);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opNClamp(\n          uint32_t                resultType,\n          uint32_t                x,\n          uint32_t                minVal,\n          uint32_t                maxVal) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 8);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450NClamp);\n    m_code.putWord(x);\n    m_code.putWord(minVal);\n    m_code.putWord(maxVal);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opIEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpIEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opINotEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpINotEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSLessThan(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpSLessThan, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSLessThanEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpSLessThanEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSGreaterThan(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpSGreaterThan, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSGreaterThanEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpSGreaterThanEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opULessThan(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpULessThan, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opULessThanEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpULessThanEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opUGreaterThan(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpUGreaterThan, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opUGreaterThanEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpUGreaterThanEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFOrdEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFOrdEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFUnordNotEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFUnordNotEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFOrdLessThan(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFOrdLessThan, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFOrdLessThanEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFOrdLessThanEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFOrdGreaterThan(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFOrdGreaterThan, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFOrdGreaterThanEqual(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFOrdGreaterThanEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opLogicalEqual(\n          uint32_t                resultType,\n          uint32_t                operand1,\n          uint32_t                operand2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpLogicalEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand1);\n    m_code.putWord(operand2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opLogicalNotEqual(\n          uint32_t                resultType,\n          uint32_t                operand1,\n          uint32_t                operand2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpLogicalNotEqual, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand1);\n    m_code.putWord(operand2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opLogicalAnd(\n          uint32_t                resultType,\n          uint32_t                operand1,\n          uint32_t                operand2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpLogicalAnd, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand1);\n    m_code.putWord(operand2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opLogicalOr(\n          uint32_t                resultType,\n          uint32_t                operand1,\n          uint32_t                operand2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpLogicalOr, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand1);\n    m_code.putWord(operand2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opLogicalNot(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpLogicalNot, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opDot(\n          uint32_t                resultType,\n          uint32_t                vector1,\n          uint32_t                vector2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpDot, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(vector1);\n    m_code.putWord(vector2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSin(\n          uint32_t                resultType,\n          uint32_t                vector) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Sin);\n    m_code.putWord(vector);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opCos(\n          uint32_t                resultType,\n          uint32_t                vector) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Cos);\n    m_code.putWord(vector);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSqrt(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Sqrt);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opInverseSqrt(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450InverseSqrt);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opNormalize(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Normalize);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opRawAccessChain(\n          uint32_t                resultType,\n          uint32_t                base,\n          uint32_t                stride,\n          uint32_t                index,\n          uint32_t                offset,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns (spv::OpRawAccessChainNV, operand ? 8 : 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(base);\n    m_code.putWord(stride);\n    m_code.putWord(index);\n    m_code.putWord(offset);\n\n    if (operand)\n      m_code.putWord(operand);\n\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opReflect(\n          uint32_t                resultType,\n          uint32_t                incident,\n          uint32_t                normal) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Reflect);\n    m_code.putWord(incident);\n    m_code.putWord(normal);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opLength(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Length);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opExp2(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Exp2);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opExp(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Exp);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opLog2(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Log2);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n  uint32_t SpirvModule::opPow(\n    uint32_t                resultType,\n    uint32_t                base,\n    uint32_t                exponent) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Pow);\n    m_code.putWord(base);\n    m_code.putWord(exponent);\n    return resultId;\n  }\n  \n  uint32_t SpirvModule::opFract(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Fract);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opCeil(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Ceil);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFloor(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Floor);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opRound(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Round);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opRoundEven(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450RoundEven);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opTrunc(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450Trunc);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opFConvert(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns (spv::OpFConvert, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opPackHalf2x16(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450PackHalf2x16);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opUnpackHalf2x16(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450UnpackHalf2x16);\n    m_code.putWord(operand);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opSelect(\n          uint32_t                resultType,\n          uint32_t                condition,\n          uint32_t                operand1,\n          uint32_t                operand2) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpSelect, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(condition);\n    m_code.putWord(operand1);\n    m_code.putWord(operand2);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opIsNan(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpIsNan, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opIsInf(\n          uint32_t                resultType,\n          uint32_t                operand) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpIsInf, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(operand);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opFunctionCall(\n          uint32_t                resultType,\n          uint32_t                functionId,\n          uint32_t                argCount,\n    const uint32_t*               argIds) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpFunctionCall, 4 + argCount);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(functionId);\n    \n    for (uint32_t i = 0; i < argCount; i++)\n      m_code.putWord(argIds[i]);\n    return resultId;\n  }\n  \n  \n  void SpirvModule::opLabel(uint32_t labelId) {\n    m_code.putIns (spv::OpLabel, 2);\n    m_code.putWord(labelId);\n\n    m_blockId = labelId;\n  }\n  \n  \n  uint32_t SpirvModule::opLoad(\n          uint32_t                typeId,\n          uint32_t                pointerId) {\n    return opLoad(typeId, pointerId, SpirvMemoryOperands());\n  }\n\n\n  uint32_t SpirvModule::opLoad(\n          uint32_t                typeId,\n          uint32_t                pointerId,\n    const SpirvMemoryOperands&    operands) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpLoad, 4 + getMemoryOperandWordCount(operands));\n    m_code.putWord(typeId);\n    m_code.putWord(resultId);\n    m_code.putWord(pointerId);\n\n    putMemoryOperands(operands);\n    return resultId;\n  }\n  \n  \n  void SpirvModule::opStore(\n          uint32_t                pointerId,\n          uint32_t                valueId) {\n    opStore(pointerId, valueId, SpirvMemoryOperands());\n  }\n\n\n  void SpirvModule::opStore(\n          uint32_t                pointerId,\n          uint32_t                valueId,\n    const SpirvMemoryOperands&    operands) {\n    m_code.putIns (spv::OpStore, 3 + getMemoryOperandWordCount(operands));\n    m_code.putWord(pointerId);\n    m_code.putWord(valueId);\n\n    putMemoryOperands(operands);\n  }\n\n\n  uint32_t SpirvModule::opInterpolateAtCentroid(\n          uint32_t                resultType,\n          uint32_t                interpolant) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450InterpolateAtCentroid);\n    m_code.putWord(interpolant);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opInterpolateAtSample(\n          uint32_t                resultType,\n          uint32_t                interpolant,\n          uint32_t                sample) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450InterpolateAtSample);\n    m_code.putWord(interpolant);\n    m_code.putWord(sample);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opInterpolateAtOffset(\n          uint32_t                resultType,\n          uint32_t                interpolant,\n          uint32_t                offset) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpExtInst, 7);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(m_instExtGlsl450);\n    m_code.putWord(GLSLstd450InterpolateAtOffset);\n    m_code.putWord(interpolant);\n    m_code.putWord(offset);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opImage(\n          uint32_t                resultType,\n          uint32_t                sampledImage) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpImage, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageRead(\n          uint32_t                resultType,\n          uint32_t                image,\n          uint32_t                coordinates,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseRead\n      : spv::OpImageRead;\n\n    m_code.putIns(op, 5 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(image);\n    m_code.putWord(coordinates);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n  \n  \n  void SpirvModule::opImageWrite(\n          uint32_t                image,\n          uint32_t                coordinates,\n          uint32_t                texel,\n    const SpirvImageOperands&     operands) {\n    m_code.putIns (spv::OpImageWrite,\n      4 + getImageOperandWordCount(operands));\n    m_code.putWord(image);\n    m_code.putWord(coordinates);\n    m_code.putWord(texel);\n    \n    putImageOperands(operands);\n  }\n  \n  \n  uint32_t SpirvModule::opImageSparseTexelsResident(\n          uint32_t                resultType,\n          uint32_t                residentCode) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns (spv::OpImageSparseTexelsResident, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(residentCode);\n\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opSampledImage(\n          uint32_t                resultType,\n          uint32_t                image,\n          uint32_t                sampler) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpSampledImage, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(image);\n    m_code.putWord(sampler);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageTexelPointer(\n          uint32_t                resultType,\n          uint32_t                image,\n          uint32_t                coordinates,\n          uint32_t                sample) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpImageTexelPointer, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(image);\n    m_code.putWord(coordinates);\n    m_code.putWord(sample);\n    return resultId;\n  }\n  \n    \n  uint32_t SpirvModule::opImageQuerySizeLod(\n          uint32_t                resultType,\n          uint32_t                image,\n          uint32_t                lod) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpImageQuerySizeLod, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(image);\n    m_code.putWord(lod);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageQuerySize(\n          uint32_t                resultType,\n          uint32_t                image) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpImageQuerySize, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(image);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageQueryLevels(\n          uint32_t                resultType,\n          uint32_t                image) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpImageQueryLevels, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(image);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageQueryLod(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpImageQueryLod, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageQuerySamples(\n          uint32_t                resultType,\n          uint32_t                image) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpImageQuerySamples, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(image);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageFetch(\n          uint32_t                resultType,\n          uint32_t                image,\n          uint32_t                coordinates,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n\n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseFetch\n      : spv::OpImageFetch;\n\n    m_code.putIns(op, 5 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(image);\n    m_code.putWord(coordinates);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageGather(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n          uint32_t                component,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseGather\n      : spv::OpImageGather;\n\n    m_code.putIns(op, 6 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    m_code.putWord(component);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageDrefGather(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n          uint32_t                reference,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseDrefGather\n      : spv::OpImageDrefGather;\n\n    m_code.putIns(op, 6 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    m_code.putWord(reference);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n  \n    \n  uint32_t SpirvModule::opImageSampleImplicitLod(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseSampleImplicitLod\n      : spv::OpImageSampleImplicitLod;\n\n    m_code.putIns(op, 5 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageSampleExplicitLod(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseSampleExplicitLod\n      : spv::OpImageSampleExplicitLod;\n\n    m_code.putIns(op, 5 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opImageSampleProjImplicitLod(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseSampleProjImplicitLod\n      : spv::OpImageSampleProjImplicitLod;\n\n    m_code.putIns(op, 5 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageSampleProjExplicitLod(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseSampleProjExplicitLod\n      : spv::OpImageSampleProjExplicitLod;\n\n    m_code.putIns(op, 5 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageSampleDrefImplicitLod(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n          uint32_t                reference,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseSampleDrefImplicitLod\n      : spv::OpImageSampleDrefImplicitLod;\n\n    m_code.putIns(op, 6 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    m_code.putWord(reference);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::opImageSampleDrefExplicitLod(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n          uint32_t                reference,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseSampleDrefExplicitLod\n      : spv::OpImageSampleDrefExplicitLod;\n\n    m_code.putIns(op, 6 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    m_code.putWord(reference);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n  \n\n  uint32_t SpirvModule::opImageSampleProjDrefImplicitLod(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n          uint32_t                reference,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseSampleProjDrefImplicitLod\n      : spv::OpImageSampleProjDrefImplicitLod;\n\n    m_code.putIns(op, 6 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    m_code.putWord(reference);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opImageSampleProjDrefExplicitLod(\n          uint32_t                resultType,\n          uint32_t                sampledImage,\n          uint32_t                coordinates,\n          uint32_t                reference,\n    const SpirvImageOperands&     operands) {\n    uint32_t resultId = this->allocateId();\n    \n    spv::Op op = operands.sparse\n      ? spv::OpImageSparseSampleProjDrefExplicitLod\n      : spv::OpImageSampleProjDrefExplicitLod;\n\n    m_code.putIns(op, 6 + getImageOperandWordCount(operands));\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(sampledImage);\n    m_code.putWord(coordinates);\n    m_code.putWord(reference);\n    \n    putImageOperands(operands);\n    return resultId;\n  }\n\n  \n  uint32_t SpirvModule::opGroupNonUniformBallot(\n          uint32_t                resultType,\n          uint32_t                execution,\n          uint32_t                predicate) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpGroupNonUniformBallot, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(execution);\n    m_code.putWord(predicate);\n    return resultId;\n  }\n\n  \n  uint32_t SpirvModule::opGroupNonUniformBallotBitCount(\n          uint32_t                resultType,\n          uint32_t                execution,\n          uint32_t                operation,\n          uint32_t                ballot) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpGroupNonUniformBallotBitCount, 6);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(execution);\n    m_code.putWord(operation);\n    m_code.putWord(ballot);\n    return resultId;\n  }\n\n\n  uint32_t SpirvModule::opGroupNonUniformElect(\n          uint32_t                resultType,\n          uint32_t                execution) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpGroupNonUniformElect, 4);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(execution);\n    return resultId;\n  }\n\n  \n  uint32_t SpirvModule::opGroupNonUniformBroadcastFirst(\n          uint32_t                resultType,\n          uint32_t                execution,\n          uint32_t                value) {\n    uint32_t resultId = this->allocateId();\n\n    m_code.putIns(spv::OpGroupNonUniformBroadcastFirst, 5);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    m_code.putWord(execution);\n    m_code.putWord(value);\n    return resultId;\n  }\n\n\n  void SpirvModule::opControlBarrier(\n          uint32_t                execution,\n          uint32_t                memory,\n          uint32_t                semantics) {\n    m_code.putIns (spv::OpControlBarrier, 4);\n    m_code.putWord(execution);\n    m_code.putWord(memory);\n    m_code.putWord(semantics);\n  }\n  \n  \n  void SpirvModule::opMemoryBarrier(\n          uint32_t                memory,\n          uint32_t                semantics) {\n    m_code.putIns (spv::OpMemoryBarrier, 3);\n    m_code.putWord(memory);\n    m_code.putWord(semantics);\n  }\n  \n  \n  void SpirvModule::opLoopMerge(\n          uint32_t                mergeBlock,\n          uint32_t                continueTarget,\n          uint32_t                loopControl) {\n    m_code.putIns (spv::OpLoopMerge, 4);\n    m_code.putWord(mergeBlock);\n    m_code.putWord(continueTarget);\n    m_code.putWord(loopControl);\n  }\n  \n  \n  void SpirvModule::opSelectionMerge(\n          uint32_t                mergeBlock,\n          uint32_t                selectionControl) {\n    m_code.putIns (spv::OpSelectionMerge, 3);\n    m_code.putWord(mergeBlock);\n    m_code.putWord(selectionControl);\n  }\n  \n  \n  void SpirvModule::opBranch(\n          uint32_t                label) {\n    m_code.putIns (spv::OpBranch, 2);\n    m_code.putWord(label);\n\n    m_blockId = 0;\n  }\n  \n  \n  void SpirvModule::opBranchConditional(\n          uint32_t                condition,\n          uint32_t                trueLabel,\n          uint32_t                falseLabel) {\n    m_code.putIns (spv::OpBranchConditional, 4);\n    m_code.putWord(condition);\n    m_code.putWord(trueLabel);\n    m_code.putWord(falseLabel);\n\n    m_blockId = 0;\n  }\n  \n  \n  void SpirvModule::opSwitch(\n          uint32_t                selector,\n          uint32_t                jumpDefault,\n          uint32_t                caseCount,\n    const SpirvSwitchCaseLabel*   caseLabels) {\n    m_code.putIns (spv::OpSwitch, 3 + 2 * caseCount);\n    m_code.putWord(selector);\n    m_code.putWord(jumpDefault);\n    \n    for (uint32_t i = 0; i < caseCount; i++) {\n      m_code.putWord(caseLabels[i].literal);\n      m_code.putWord(caseLabels[i].labelId);\n    }\n\n    m_blockId = 0;\n  }\n  \n  \n  uint32_t SpirvModule::opPhi(\n          uint32_t                resultType,\n          uint32_t                sourceCount,\n    const SpirvPhiLabel*          sourceLabels) {\n    uint32_t resultId = this->allocateId();\n    \n    m_code.putIns (spv::OpPhi, 3 + 2 * sourceCount);\n    m_code.putWord(resultType);\n    m_code.putWord(resultId);\n    \n    for (uint32_t i = 0; i < sourceCount; i++) {\n      m_code.putWord(sourceLabels[i].varId);\n      m_code.putWord(sourceLabels[i].labelId);\n    }\n    \n    return resultId;\n  }\n  \n    \n  void SpirvModule::opReturn() {\n    m_code.putIns (spv::OpReturn, 1);\n    m_blockId = 0;\n  }\n  \n  \n  void SpirvModule::opDemoteToHelperInvocation() {\n    m_code.putIns (spv::OpDemoteToHelperInvocation, 1);\n  }\n  \n  \n  void SpirvModule::opEmitVertex(\n          uint32_t                streamId) {\n    if (streamId == 0) {\n      m_code.putIns (spv::OpEmitVertex, 1);\n    } else {\n      m_code.putIns (spv::OpEmitStreamVertex, 2);\n      m_code.putWord(streamId);\n    }\n  }\n  \n  \n  void SpirvModule::opEndPrimitive(\n          uint32_t                streamId) {\n    if (streamId == 0) {\n      m_code.putIns (spv::OpEndPrimitive, 1);\n    } else {\n      m_code.putIns (spv::OpEndStreamPrimitive, 2);\n      m_code.putWord(streamId);\n    }\n  }\n  \n  \n  void SpirvModule::opBeginInvocationInterlock() {\n    m_code.putIns(spv::OpBeginInvocationInterlockEXT, 1);\n  }\n\n\n  void SpirvModule::opEndInvocationInterlock() {\n    m_code.putIns(spv::OpEndInvocationInterlockEXT, 1);\n  }\n\n\n  uint32_t SpirvModule::opSinCos(\n          uint32_t                x,\n          bool                    useBuiltIn) {\n    // We only operate on 32-bit floats here\n    uint32_t floatType = defFloatType(32);\n    uint32_t resultType = defVectorType(floatType, 2u);\n\n    if (useBuiltIn) {\n      std::array<uint32_t, 2> members = { opSin(floatType, x), opCos(floatType, x) };\n      return opCompositeConstruct(resultType, members.size(), members.data());\n    } else {\n      uint32_t uintType = defIntType(32, false);\n      uint32_t sintType = defIntType(32, true);\n      uint32_t boolType = defBoolType();\n\n      // Normalize input to multiple of pi/4\n      uint32_t xNorm = opFMul(floatType, opFAbs(floatType, x), constf32(4.0 / pi));\n\n      uint32_t xTrunc = opTrunc(floatType, xNorm);\n      uint32_t xFract = opFSub(floatType, xNorm, xTrunc);\n\n      uint32_t xInt = opConvertFtoU(uintType, xTrunc);\n\n      // Mirror input along x axis as necessary\n      uint32_t mirror = opINotEqual(boolType, opBitwiseAnd(uintType, xInt, constu32(1u)), constu32(0u));\n      xFract = opSelect(floatType, mirror, opFSub(floatType, constf32(1.0f), xFract), xFract);\n\n      // Compute taylor series for fractional part\n      uint32_t xFract_2 = opFMul(floatType, xFract, xFract);\n      uint32_t xFract_4 = opFMul(floatType, xFract_2, xFract_2);\n      uint32_t xFract_6 = opFMul(floatType, xFract_4, xFract_2);\n\n      uint32_t taylor = opFMul(floatType, xFract_6, constf32(-sincosTaylorFactor(7)));\n      decorate(taylor, spv::DecorationNoContraction);\n\n      taylor = opFFma(floatType, xFract_4, constf32(sincosTaylorFactor(5)), taylor);\n      decorate(taylor, spv::DecorationNoContraction);\n\n      taylor = opFFma(floatType, xFract_2, constf32(-sincosTaylorFactor(3)), taylor);\n      decorate(taylor, spv::DecorationNoContraction);\n\n      taylor = opFAdd(floatType, constf32(sincosTaylorFactor(1)), taylor);\n      decorate(taylor, spv::DecorationNoContraction);\n\n      taylor = opFMul(floatType, taylor, xFract);\n      decorate(taylor, spv::DecorationNoContraction);\n\n      // Compute co-function based on sin^2 + cos^2 = 1\n      uint32_t coFunc = opSqrt(floatType, opFSub(floatType, constf32(1.0f), opFMul(floatType, taylor, taylor)));\n\n      // Determine whether the taylor series was used for sine or cosine and assign the correct result\n      uint32_t funcIsSin = opIEqual(boolType, opBitwiseAnd(uintType, opIAdd(uintType, xInt, constu32(1u)), constu32(2u)), constu32(0u));\n\n      uint32_t sin = opSelect(floatType, funcIsSin, taylor, coFunc);\n      uint32_t cos = opSelect(floatType, funcIsSin, coFunc, taylor);\n\n      // Determine whether sine is negative. Interpret the input as a\n      // signed integer in order to propagate signed zeroes properly.\n      uint32_t inputNeg = opSLessThan(boolType, opBitcast(sintType, x), consti32(0));\n\n      uint32_t sinNeg = opINotEqual(boolType, opBitwiseAnd(uintType, xInt, constu32(4u)), constu32(0u));\n               sinNeg = opLogicalNotEqual(boolType, sinNeg, inputNeg);\n\n      // Determine whether cosine is negative\n      uint32_t cosNeg = opINotEqual(boolType, opBitwiseAnd(uintType, opIAdd(uintType, xInt, constu32(2u)), constu32(4u)), constu32(0u));\n\n      sin = opSelect(floatType, sinNeg, opFNegate(floatType, sin), sin);\n      cos = opSelect(floatType, cosNeg, opFNegate(floatType, cos), cos);\n\n      std::array<uint32_t, 2> members = { sin, cos };\n      return opCompositeConstruct(resultType, members.size(), members.data());\n    }\n  }\n\n\n  uint32_t SpirvModule::defType(\n          spv::Op                 op, \n          uint32_t                argCount,\n    const uint32_t*               argIds) {\n    // Since the type info is stored in the code buffer,\n    // we can use the code buffer to look up type IDs as\n    // well. Result IDs are always stored as argument 1.\n    for (auto ins : m_typeConstDefs) {\n      bool match = ins.opCode() == op\n                && ins.length() == 2 + argCount;\n      \n      for (uint32_t i = 0; i < argCount && match; i++)\n        match &= ins.arg(2 + i) == argIds[i];\n      \n      if (match && m_uniqueTypes.find(ins.arg(1)) == m_uniqueTypes.end())\n        return ins.arg(1);\n    }\n    \n    // Type not yet declared, create a new one.\n    uint32_t resultId = this->allocateId();\n    m_typeConstDefs.putIns (op, 2 + argCount);\n    m_typeConstDefs.putWord(resultId);\n    \n    for (uint32_t i = 0; i < argCount; i++)\n      m_typeConstDefs.putWord(argIds[i]);\n    return resultId;\n  }\n  \n  \n  uint32_t SpirvModule::defConst(\n          spv::Op                 op,\n          uint32_t                typeId,\n          uint32_t                argCount,\n    const uint32_t*               argIds) {\n    // Avoid declaring constants multiple times\n    for (auto ins : m_typeConstDefs) {\n      bool match = ins.opCode() == op\n                && ins.length() == 3 + argCount\n                && ins.arg(1)   == typeId;\n      \n      for (uint32_t i = 0; i < argCount && match; i++)\n        match &= ins.arg(3 + i) == argIds[i];\n      \n      if (!match)\n        continue;\n      \n      uint32_t id = ins.arg(2);\n\n      if (m_lateConsts.find(id) == m_lateConsts.end())\n        return id;\n    }\n    \n    // Constant not yet declared, make a new one\n    uint32_t resultId = this->allocateId();\n    m_typeConstDefs.putIns (op, 3 + argCount);\n    m_typeConstDefs.putWord(typeId);\n    m_typeConstDefs.putWord(resultId);\n    \n    for (uint32_t i = 0; i < argCount; i++)\n      m_typeConstDefs.putWord(argIds[i]);\n    return resultId;\n  }\n  \n  \n  void SpirvModule::instImportGlsl450() {\n    m_instExtGlsl450 = this->allocateId();\n    const char* name = \"GLSL.std.450\";\n    \n    m_instExt.putIns (spv::OpExtInstImport, 2 + m_instExt.strLen(name));\n    m_instExt.putWord(m_instExtGlsl450);\n    m_instExt.putStr (name);\n  }\n  \n  \n  uint32_t SpirvModule::getMemoryOperandWordCount(\n    const SpirvMemoryOperands&    op) const {\n    const uint32_t result\n      = ((op.flags & spv::MemoryAccessAlignedMask)              ? 1 : 0)\n      + ((op.flags & spv::MemoryAccessMakePointerAvailableMask) ? 1 : 0)\n      + ((op.flags & spv::MemoryAccessMakePointerVisibleMask)   ? 1 : 0);\n\n    return op.flags ? result + 1 : 0;\n  }\n\n\n  void SpirvModule::putMemoryOperands(\n    const SpirvMemoryOperands&    op) {\n    if (op.flags) {\n      m_code.putWord(op.flags);\n\n      if (op.flags & spv::MemoryAccessAlignedMask)\n        m_code.putWord(op.alignment);\n\n      if (op.flags & spv::MemoryAccessMakePointerAvailableMask)\n        m_code.putWord(op.makeAvailable);\n\n      if (op.flags & spv::MemoryAccessMakePointerVisibleMask)\n        m_code.putWord(op.makeVisible);\n    }\n  }\n\n\n  uint32_t SpirvModule::getImageOperandWordCount(const SpirvImageOperands& op) const {\n    // Each flag may add one or more operands\n    const uint32_t result\n      = ((op.flags & spv::ImageOperandsBiasMask)                ? 1 : 0)\n      + ((op.flags & spv::ImageOperandsLodMask)                 ? 1 : 0)\n      + ((op.flags & spv::ImageOperandsConstOffsetMask)         ? 1 : 0)\n      + ((op.flags & spv::ImageOperandsGradMask)                ? 2 : 0)\n      + ((op.flags & spv::ImageOperandsOffsetMask)              ? 1 : 0)\n      + ((op.flags & spv::ImageOperandsConstOffsetsMask)        ? 1 : 0)\n      + ((op.flags & spv::ImageOperandsSampleMask)              ? 1 : 0)\n      + ((op.flags & spv::ImageOperandsMinLodMask)              ? 1 : 0)\n      + ((op.flags & spv::ImageOperandsMakeTexelAvailableMask)  ? 1 : 0)\n      + ((op.flags & spv::ImageOperandsMakeTexelVisibleMask)    ? 1 : 0);\n    \n    // Add a DWORD for the operand mask if it is non-zero\n    return op.flags ? result + 1 : 0;\n  }\n  \n  \n  void SpirvModule::putImageOperands(const SpirvImageOperands& op) {\n    if (op.flags) {\n      m_code.putWord(op.flags);\n      \n      if (op.flags & spv::ImageOperandsBiasMask)\n        m_code.putWord(op.sLodBias);\n      \n      if (op.flags & spv::ImageOperandsLodMask)\n        m_code.putWord(op.sLod);\n\n      if (op.flags & spv::ImageOperandsGradMask) {\n        m_code.putWord(op.sGradX);\n        m_code.putWord(op.sGradY);\n      }\n\n      if (op.flags & spv::ImageOperandsConstOffsetMask)\n        m_code.putWord(op.sConstOffset);\n\n      if (op.flags & spv::ImageOperandsOffsetMask)\n        m_code.putWord(op.gOffset);\n      \n      if (op.flags & spv::ImageOperandsConstOffsetsMask)\n        m_code.putWord(op.gConstOffsets);\n      \n      if (op.flags & spv::ImageOperandsSampleMask)\n        m_code.putWord(op.sSampleId);\n      \n      if (op.flags & spv::ImageOperandsMinLodMask)\n        m_code.putWord(op.sMinLod);\n\n      if (op.flags & spv::ImageOperandsMakeTexelAvailableMask)\n        m_code.putWord(op.makeAvailable);\n\n      if (op.flags & spv::ImageOperandsMakeTexelVisibleMask)\n        m_code.putWord(op.makeVisible);\n    }\n  }\n  \n\n  bool SpirvModule::isInterfaceVar(\n          spv::StorageClass       sclass) const {\n    if (m_version < spvVersion(1, 4)) {\n      return sclass == spv::StorageClassInput\n          || sclass == spv::StorageClassOutput;\n    } else {\n      // All global variables need to be declared\n      return sclass != spv::StorageClassFunction;\n    }\n  }\n\n\n  void SpirvModule::classifyBlocks(\n          std::unordered_set<uint32_t>& reachableBlocks,\n          std::unordered_set<uint32_t>& mergeBlocks) {\n    std::unordered_multimap<uint32_t, uint32_t> branches;\n    std::queue<uint32_t> blockQueue;\n\n    uint32_t blockId = 0;\n\n    for (auto ins : m_code) {\n      switch (ins.opCode()) {\n        case spv::OpLabel: {\n          uint32_t id = ins.arg(1);\n\n          if (!blockId)\n            branches.insert({ 0u, id });\n\n          blockId = id;\n        } break;\n\n        case spv::OpFunction: {\n          blockId = 0u;\n        } break;\n\n        case spv::OpBranch: {\n          branches.insert({ blockId, ins.arg(1) });\n        } break;\n\n        case spv::OpBranchConditional: {\n          branches.insert({ blockId, ins.arg(2) });\n          branches.insert({ blockId, ins.arg(3) });\n        } break;\n\n        case spv::OpSwitch: {\n          branches.insert({ blockId, ins.arg(2) });\n\n          for (uint32_t i = 4; i < ins.length(); i += 2)\n            branches.insert({ blockId, ins.arg(i) });\n        } break;\n\n        case spv::OpSelectionMerge: {\n          mergeBlocks.insert(ins.arg(1));\n        } break;\n\n        case spv::OpLoopMerge: {\n          mergeBlocks.insert(ins.arg(1));\n\n          // It is possible for the continue block to be unreachable in\n          // practice, but we still need to emit it if we are not going\n          // to eliminate this loop. Since the current block dominates\n          // the loop, use it to keep the continue block intact.\n          branches.insert({ blockId, ins.arg(2) });\n        } break;\n\n        default:;\n      }\n    }\n\n    blockQueue.push(0);\n\n    while (!blockQueue.empty()) {\n      uint32_t id = blockQueue.front();\n\n      auto range = branches.equal_range(id);\n\n      for (auto i = range.first; i != range.second; i++) {\n        if (reachableBlocks.insert(i->second).second)\n          blockQueue.push(i->second);\n      }\n\n      blockQueue.pop();\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/spirv/spirv_module.h",
    "content": "#pragma once\n\n#include <queue>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"spirv_code_buffer.h\"\n\nnamespace dxvk {\n  \n  struct SpirvPhiLabel {\n    uint32_t varId         = 0;\n    uint32_t labelId       = 0;\n  };\n  \n  struct SpirvSwitchCaseLabel {\n    uint32_t literal       = 0;\n    uint32_t labelId       = 0;\n  };\n\n  struct SpirvMemoryOperands {\n    uint32_t flags         = 0;\n    uint32_t alignment     = 0;\n    uint32_t makeAvailable = 0;\n    uint32_t makeVisible   = 0;\n  };\n  \n  struct SpirvImageOperands {\n    uint32_t flags         = 0;\n    uint32_t sLodBias      = 0;\n    uint32_t sLod          = 0;\n    uint32_t sConstOffset  = 0;\n    uint32_t sGradX        = 0;\n    uint32_t sGradY        = 0;\n    uint32_t gOffset       = 0;\n    uint32_t gConstOffsets = 0;\n    uint32_t sSampleId     = 0;\n    uint32_t sMinLod       = 0;\n    uint32_t makeAvailable = 0;\n    uint32_t makeVisible   = 0;\n    bool     sparse        = false;\n  };\n\n  constexpr uint32_t spvVersion(uint32_t major, uint32_t minor) {\n    return (major << 16) | (minor << 8);\n  }\n  \n  /**\n   * \\brief SPIR-V module\n   * \n   * This class generates a code buffer containing a full\n   * SPIR-V shader module. Ensures that the module layout\n   * is valid, as defined in the SPIR-V 1.0 specification,\n   * section 2.4 \"Logical Layout of a Module\".\n   */\n  class SpirvModule {\n    \n  public:\n    \n    explicit SpirvModule(uint32_t version);\n\n    ~SpirvModule();\n    \n    SpirvCodeBuffer compile();\n\n    size_t getInsertionPtr() {\n      return m_code.getInsertionPtr();\n    }\n    \n    void beginInsertion(size_t ptr) {\n      m_code.beginInsertion(ptr);\n    }\n    \n    void endInsertion() {\n      m_code.endInsertion();\n    }\n    \n    uint32_t getBlockId() const {\n      return m_blockId;\n    }\n\n    uint32_t allocateId();\n    \n    bool hasCapability(\n            spv::Capability         capability);\n\n    void enableCapability(\n            spv::Capability         capability);\n    \n    void enableExtension(\n      const char*                   extensionName);\n    \n    void addEntryPoint(\n            uint32_t                entryPointId,\n            spv::ExecutionModel     executionModel,\n      const char*                   name);\n    \n    void setMemoryModel(\n            spv::AddressingModel    addressModel,\n            spv::MemoryModel        memoryModel);\n    \n    void setExecutionMode(\n            uint32_t                entryPointId,\n            spv::ExecutionMode      executionMode);\n    \n    void setExecutionMode(\n            uint32_t                entryPointId,\n            spv::ExecutionMode      executionMode,\n            uint32_t                argCount,\n      const uint32_t*               args);\n    \n    void setInvocations(\n            uint32_t                entryPointId,\n            uint32_t                invocations);\n    \n    void setLocalSize(\n            uint32_t                entryPointId,\n            uint32_t                x,\n            uint32_t                y,\n            uint32_t                z);\n    \n    void setOutputVertices(\n            uint32_t                entryPointId,\n            uint32_t                vertexCount);\n    \n    uint32_t addDebugString(\n      const char*                   string);\n    \n    void setDebugSource(\n            spv::SourceLanguage     language,\n            uint32_t                version,\n            uint32_t                file,\n      const char*                   source);\n    \n    void setDebugName(\n            uint32_t                expressionId,\n      const char*                   debugName);\n    \n    void setDebugMemberName(\n            uint32_t                structId,\n            uint32_t                memberId,\n      const char*                   debugName);\n    \n    uint32_t constBool(\n            bool                    v);\n    \n    uint32_t consti32(\n            int32_t                 v);\n    \n    uint32_t consti64(\n            int64_t                 v);\n    \n    uint32_t constu32(\n            uint32_t                v);\n    \n    uint32_t constu64(\n            uint64_t                v);\n    \n    uint32_t constf32(\n            float                   v);\n    \n    uint32_t constf64(\n            double                  v);\n    \n    uint32_t constvec4i32(\n            int32_t                 x,\n            int32_t                 y,\n            int32_t                 z,\n            int32_t                 w);\n\n    uint32_t constvec4b32(\n            bool                    x,\n            bool                    y,\n            bool                    z,\n            bool                    w);\n    \n    uint32_t constvec2u32(\n            uint32_t                x,\n            uint32_t                y);\n\n    uint32_t constvec4u32(\n            uint32_t                x,\n            uint32_t                y,\n            uint32_t                z,\n            uint32_t                w);\n\n    uint32_t constvec2f32(\n            float                   x,\n            float                   y);\n\n    uint32_t constvec3f32(\n            float                   x,\n            float                   y,\n            float                   z);\n\n    uint32_t constvec4f32(\n            float                   x,\n            float                   y,\n            float                   z,\n            float                   w);\n\n    uint32_t constfReplicant(\n            float                   replicant,\n            uint32_t                count);\n\n    uint32_t constbReplicant(\n            bool                    replicant,\n            uint32_t                count);\n\n    uint32_t constiReplicant(\n            int32_t                 replicant,\n            uint32_t                count);\n\n    uint32_t constuReplicant(\n            int32_t                 replicant,\n            uint32_t                count);\n    \n    uint32_t constComposite(\n            uint32_t                typeId,\n            uint32_t                constCount,\n      const uint32_t*               constIds);\n    \n    uint32_t constUndef(\n            uint32_t                typeId);\n    \n    uint32_t constNull(\n            uint32_t                typeId);\n    \n    uint32_t lateConst32(\n            uint32_t                typeId);\n\n    void setLateConst(\n            uint32_t                constId,\n      const uint32_t*               argIds);\n\n    uint32_t specConstBool(\n            bool                    v);\n    \n    uint32_t specConst32(\n            uint32_t                typeId,\n            uint32_t                value);\n    \n    void decorate(\n            uint32_t                object,\n            spv::Decoration         decoration);\n    \n    void decorateArrayStride(\n            uint32_t                object,\n            uint32_t                stride);\n    \n    void decorateBinding(\n            uint32_t                object,\n            uint32_t                binding);\n    \n    void decorateBlock(\n            uint32_t                object);\n    \n    void decorateBuiltIn(\n            uint32_t                object,\n            spv::BuiltIn            builtIn);\n    \n    void decorateComponent(\n            uint32_t                object,\n            uint32_t                location);\n    \n    void decorateDescriptorSet(\n            uint32_t                object,\n            uint32_t                set);\n    \n    void decorateIndex(\n            uint32_t                object,\n            uint32_t                index);\n    \n    void decorateLocation(\n            uint32_t                object,\n            uint32_t                location);\n    \n    void decorateSpecId(\n            uint32_t                object,\n            uint32_t                specId);\n    \n    void decorateXfb(\n            uint32_t                object,\n            uint32_t                streamId,\n            uint32_t                bufferId,\n            uint32_t                offset,\n            uint32_t                stride);\n    \n    void memberDecorateBuiltIn(\n            uint32_t                structId,\n            uint32_t                memberId,\n            spv::BuiltIn            builtIn);\n\n    void memberDecorate(\n            uint32_t                structId,\n            uint32_t                memberId,\n            spv::Decoration         decoration);\n\n    void memberDecorateMatrixStride(\n            uint32_t                structId,\n            uint32_t                memberId,\n            uint32_t                stride);\n    \n    void memberDecorateOffset(\n            uint32_t                structId,\n            uint32_t                memberId,\n            uint32_t                offset);\n    \n    uint32_t defVoidType();\n    \n    uint32_t defBoolType();\n    \n    uint32_t defIntType(\n            uint32_t                width,\n            uint32_t                isSigned);\n    \n    uint32_t defFloatType(\n            uint32_t                width);\n    \n    uint32_t defVectorType(\n            uint32_t                elementType,\n            uint32_t                elementCount);\n    \n    uint32_t defMatrixType(\n            uint32_t                columnType,\n            uint32_t                columnCount);\n    \n    uint32_t defArrayType(\n            uint32_t                typeId,\n            uint32_t                length);\n    \n    uint32_t defArrayTypeUnique(\n            uint32_t                typeId,\n            uint32_t                length);\n    \n    uint32_t defRuntimeArrayType(\n            uint32_t                typeId);\n    \n    uint32_t defRuntimeArrayTypeUnique(\n            uint32_t                typeId);\n    \n    uint32_t defFunctionType(\n            uint32_t                returnType,\n            uint32_t                argCount,\n      const uint32_t*               argTypes);\n    \n    uint32_t defStructType(\n            uint32_t                memberCount,\n      const uint32_t*               memberTypes);\n    \n    uint32_t defStructTypeUnique(\n            uint32_t                memberCount,\n      const uint32_t*               memberTypes);\n    \n    uint32_t defPointerType(\n            uint32_t                variableType,\n            spv::StorageClass       storageClass);\n    \n    uint32_t defSamplerType();\n    \n    uint32_t defImageType(\n            uint32_t                sampledType,\n            spv::Dim                dimensionality,\n            uint32_t                depth,\n            uint32_t                arrayed,\n            uint32_t                multisample,\n            uint32_t                sampled,\n            spv::ImageFormat        format);\n    \n    uint32_t defSampledImageType(\n            uint32_t                imageType);\n    \n    uint32_t newVar(\n            uint32_t                pointerType,\n            spv::StorageClass       storageClass);\n    \n    uint32_t newVarInit(\n            uint32_t                pointerType,\n            spv::StorageClass       storageClass,\n            uint32_t                initialValue);\n    \n    void functionBegin(\n            uint32_t                returnType,\n            uint32_t                functionId,\n            uint32_t                functionType,\n      spv::FunctionControlMask      functionControl);\n    \n    uint32_t functionParameter(\n            uint32_t                parameterType);\n    \n    void functionEnd();\n\n    uint32_t opAccessChain(\n            uint32_t                resultType,\n            uint32_t                composite,\n            uint32_t                indexCount,\n      const uint32_t*               indexArray);\n    \n    uint32_t opArrayLength(\n            uint32_t                resultType,\n            uint32_t                structure,\n            uint32_t                memberId);\n    \n    uint32_t opAny(\n            uint32_t                resultType,\n            uint32_t                vector);\n    \n    uint32_t opAll(\n            uint32_t                resultType,\n            uint32_t                vector);\n    \n    uint32_t opAtomicLoad(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics);\n            \n    void opAtomicStore(\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n            \n    uint32_t opAtomicExchange(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n            \n    uint32_t opAtomicCompareExchange(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                equal,\n            uint32_t                unequal,\n            uint32_t                value,\n            uint32_t                comparator);\n            \n    uint32_t opAtomicIIncrement(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics);\n            \n    uint32_t opAtomicIDecrement(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics);\n            \n    uint32_t opAtomicIAdd(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n    \n    uint32_t opAtomicISub(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n    \n    uint32_t opAtomicSMin(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n    \n    uint32_t opAtomicSMax(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n    \n    uint32_t opAtomicUMin(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n    \n    uint32_t opAtomicUMax(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n    \n    uint32_t opAtomicAnd(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n    \n    uint32_t opAtomicOr(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n    \n    uint32_t opAtomicXor(\n            uint32_t                resultType,\n            uint32_t                pointer,\n            uint32_t                scope,\n            uint32_t                semantics,\n            uint32_t                value);\n    \n    uint32_t opBitcast(\n            uint32_t                resultType,\n            uint32_t                operand);\n            \n    uint32_t opBitCount(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opBitReverse(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opFindILsb(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opFindUMsb(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opFindSMsb(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opBitFieldInsert(\n            uint32_t                resultType,\n            uint32_t                base,\n            uint32_t                insert,\n            uint32_t                offset,\n            uint32_t                count);\n    \n    uint32_t opBitFieldSExtract(\n            uint32_t                resultType,\n            uint32_t                base,\n            uint32_t                offset,\n            uint32_t                count);\n    \n    uint32_t opBitFieldUExtract(\n            uint32_t                resultType,\n            uint32_t                base,\n            uint32_t                offset,\n            uint32_t                count);\n    \n    uint32_t opBitwiseAnd(\n            uint32_t                resultType,\n            uint32_t                operand1,\n            uint32_t                operand2);\n    \n    uint32_t opBitwiseOr(\n            uint32_t                resultType,\n            uint32_t                operand1,\n            uint32_t                operand2);\n    \n    uint32_t opBitwiseXor(\n            uint32_t                resultType,\n            uint32_t                operand1,\n            uint32_t                operand2);\n    \n    uint32_t opNot(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opShiftLeftLogical(\n            uint32_t                resultType,\n            uint32_t                base,\n            uint32_t                shift);\n    \n    uint32_t opShiftRightArithmetic(\n            uint32_t                resultType,\n            uint32_t                base,\n            uint32_t                shift);\n    \n    uint32_t opShiftRightLogical(\n            uint32_t                resultType,\n            uint32_t                base,\n            uint32_t                shift);\n    \n    uint32_t opConvertFtoS(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opConvertFtoU(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opConvertStoF(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opConvertUtoF(\n            uint32_t                resultType,\n            uint32_t                operand);\n\n    uint32_t opUConvert(\n            uint32_t                resultType,\n            uint32_t                operand);\n\n    uint32_t opCompositeConstruct(\n            uint32_t                resultType,\n            uint32_t                valueCount,\n      const uint32_t*               valueArray);\n    \n    uint32_t opCompositeExtract(\n            uint32_t                resultType,\n            uint32_t                composite,\n            uint32_t                indexCount,\n      const uint32_t*               indexArray);\n    \n    uint32_t opCompositeInsert(\n            uint32_t                resultType,\n            uint32_t                object,\n            uint32_t                composite,\n            uint32_t                indexCount,\n      const uint32_t*               indexArray);\n    \n    uint32_t opDpdx(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opDpdy(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opDpdxCoarse(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opDpdyCoarse(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opDpdxFine(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opDpdyFine(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opVectorExtractDynamic(\n            uint32_t                resultType,\n            uint32_t                vector,\n            uint32_t                index);\n    \n    uint32_t opVectorShuffle(\n            uint32_t                resultType,\n            uint32_t                vectorLeft,\n            uint32_t                vectorRight,\n            uint32_t                indexCount,\n      const uint32_t*               indexArray);\n    \n    uint32_t opSNegate(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opFNegate(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opSAbs(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opFAbs(\n            uint32_t                resultType,\n            uint32_t                operand);\n\n    uint32_t opFSign(\n            uint32_t                resultType,\n            uint32_t                operand);\n\n    uint32_t opFMix(\n            uint32_t                resultType,\n            uint32_t                x,\n            uint32_t                y,\n            uint32_t                a);\n\n  uint32_t opCross(\n            uint32_t                resultType,\n            uint32_t                x,\n            uint32_t                y);\n    \n    uint32_t opIAdd(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opISub(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opFAdd(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opFSub(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opSDiv(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opUDiv(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opSRem(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opUMod(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opFDiv(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opIMul(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opFMul(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n\n    uint32_t opVectorTimesScalar(\n            uint32_t                resultType,\n            uint32_t                vector,\n            uint32_t                scalar);\n\n    uint32_t opMatrixTimesMatrix(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n\n    uint32_t opMatrixTimesVector(\n            uint32_t                resultType,\n            uint32_t                matrix,\n            uint32_t                vector);\n\n    uint32_t opVectorTimesMatrix(\n            uint32_t                resultType,\n            uint32_t                vector,\n            uint32_t                matrix);\n\n    uint32_t opTranspose(\n            uint32_t                resultType,\n            uint32_t                matrix);\n\n    uint32_t opInverse(\n            uint32_t                resultType,\n            uint32_t                matrix);\n    \n    uint32_t opFFma(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b,\n            uint32_t                c);\n    \n    uint32_t opFMax(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opFMin(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opNMax(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opNMin(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opSMax(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opSMin(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opUMax(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opUMin(\n            uint32_t                resultType,\n            uint32_t                a,\n            uint32_t                b);\n    \n    uint32_t opFClamp(\n            uint32_t                resultType,\n            uint32_t                x,\n            uint32_t                minVal,\n            uint32_t                maxVal);\n    \n    uint32_t opNClamp(\n            uint32_t                resultType,\n            uint32_t                x,\n            uint32_t                minVal,\n            uint32_t                maxVal);\n    \n    uint32_t opIEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opINotEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opSLessThan(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opSLessThanEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opSGreaterThan(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opSGreaterThanEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opULessThan(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opULessThanEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opUGreaterThan(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opUGreaterThanEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opFOrdEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opFUnordNotEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opFOrdLessThan(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opFOrdLessThanEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opFOrdGreaterThan(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opFOrdGreaterThanEqual(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opLogicalEqual(\n            uint32_t                resultType,\n            uint32_t                operand1,\n            uint32_t                operand2);\n    \n    uint32_t opLogicalNotEqual(\n            uint32_t                resultType,\n            uint32_t                operand1,\n            uint32_t                operand2);\n    \n    uint32_t opLogicalAnd(\n            uint32_t                resultType,\n            uint32_t                operand1,\n            uint32_t                operand2);\n    \n    uint32_t opLogicalOr(\n            uint32_t                resultType,\n            uint32_t                operand1,\n            uint32_t                operand2);\n    \n    uint32_t opLogicalNot(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opDot(\n            uint32_t                resultType,\n            uint32_t                vector1,\n            uint32_t                vector2);\n    \n    uint32_t opSin(\n            uint32_t                resultType,\n            uint32_t                vector);\n    \n    uint32_t opCos(\n            uint32_t                resultType,\n            uint32_t                vector);\n    \n    uint32_t opSqrt(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opInverseSqrt(\n            uint32_t                resultType,\n            uint32_t                operand);\n\n    uint32_t opNormalize(\n            uint32_t                resultType,\n            uint32_t                operand);\n\n    uint32_t opRawAccessChain(\n            uint32_t                resultType,\n            uint32_t                base,\n            uint32_t                stride,\n            uint32_t                index,\n            uint32_t                offset,\n            uint32_t                operand);\n\n    uint32_t opReflect(\n            uint32_t                resultType,\n            uint32_t                incident,\n            uint32_t                normal);\n\n    uint32_t opLength(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opExp2(\n            uint32_t                resultType,\n            uint32_t                operand);\n\n    uint32_t opExp(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opLog2(\n            uint32_t                resultType,\n            uint32_t                operand);\n\n    uint32_t opPow(\n            uint32_t                resultType,\n            uint32_t                base,\n            uint32_t                exponent);\n    \n    uint32_t opFract(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opCeil(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opFloor(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opRound(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opRoundEven(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opTrunc(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opFConvert(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opPackHalf2x16(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opUnpackHalf2x16(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opSelect(\n            uint32_t                resultType,\n            uint32_t                condition,\n            uint32_t                operand1,\n            uint32_t                operand2);\n\n    uint32_t opIsNan(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opIsInf(\n            uint32_t                resultType,\n            uint32_t                operand);\n    \n    uint32_t opFunctionCall(\n            uint32_t                resultType,\n            uint32_t                functionId,\n            uint32_t                argCount,\n      const uint32_t*               argIds);\n    \n    void opLabel(\n            uint32_t                labelId);\n    \n    uint32_t opLoad(\n            uint32_t                typeId,\n            uint32_t                pointerId);\n\n    uint32_t opLoad(\n            uint32_t                typeId,\n            uint32_t                pointerId,\n      const SpirvMemoryOperands&    operands);\n\n    void opStore(\n            uint32_t                pointerId,\n            uint32_t                valueId);\n\n    void opStore(\n            uint32_t                pointerId,\n            uint32_t                valueId,\n      const SpirvMemoryOperands&    operands);\n\n    uint32_t opInterpolateAtCentroid(\n            uint32_t                resultType,\n            uint32_t                interpolant);\n    \n    uint32_t opInterpolateAtSample(\n            uint32_t                resultType,\n            uint32_t                interpolant,\n            uint32_t                sample);\n    \n    uint32_t opInterpolateAtOffset(\n            uint32_t                resultType,\n            uint32_t                interpolant,\n            uint32_t                offset);\n\n    uint32_t opImage(\n            uint32_t                resultType,\n            uint32_t                sampledImage);\n    \n    uint32_t opImageRead(\n            uint32_t                resultType,\n            uint32_t                image,\n            uint32_t                coordinates,\n      const SpirvImageOperands&     operands);\n    \n    void opImageWrite(\n            uint32_t                image,\n            uint32_t                coordinates,\n            uint32_t                texel,\n      const SpirvImageOperands&     operands);\n\n    uint32_t opImageSparseTexelsResident(\n            uint32_t                resultType,\n            uint32_t                residentCode);\n\n    uint32_t opImageTexelPointer(\n            uint32_t                resultType,\n            uint32_t                image,\n            uint32_t                coordinates,\n            uint32_t                sample);\n    \n    uint32_t opSampledImage(\n            uint32_t                resultType,\n            uint32_t                image,\n            uint32_t                sampler);\n    \n    uint32_t opImageQuerySizeLod(\n            uint32_t                resultType,\n            uint32_t                image,\n            uint32_t                lod);\n    \n    uint32_t opImageQuerySize(\n            uint32_t                resultType,\n            uint32_t                image);\n    \n    uint32_t opImageQueryLevels(\n            uint32_t                resultType,\n            uint32_t                image);\n    \n    uint32_t opImageQueryLod(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates);\n    \n    uint32_t opImageQuerySamples(\n            uint32_t                resultType,\n            uint32_t                image);\n    \n    uint32_t opImageFetch(\n            uint32_t                resultType,\n            uint32_t                image,\n            uint32_t                coordinates,\n      const SpirvImageOperands&     operands);\n    \n    uint32_t opImageGather(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n            uint32_t                component,\n      const SpirvImageOperands&     operands);\n    \n    uint32_t opImageDrefGather(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n            uint32_t                reference,\n      const SpirvImageOperands&     operands);\n    \n    uint32_t opImageSampleImplicitLod(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n      const SpirvImageOperands&     operands);\n    \n    uint32_t opImageSampleExplicitLod(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n      const SpirvImageOperands&     operands);\n\n    uint32_t opImageSampleProjImplicitLod(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n      const SpirvImageOperands&     operands);\n\n    uint32_t opImageSampleProjExplicitLod(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n      const SpirvImageOperands&     operands);\n    \n    uint32_t opImageSampleDrefImplicitLod(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n            uint32_t                reference,\n      const SpirvImageOperands&     operands);\n    \n    uint32_t opImageSampleDrefExplicitLod(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n            uint32_t                reference,\n      const SpirvImageOperands&     operands);\n\n    uint32_t opImageSampleProjDrefImplicitLod(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n            uint32_t                reference,\n      const SpirvImageOperands&     operands);\n\n    uint32_t opImageSampleProjDrefExplicitLod(\n            uint32_t                resultType,\n            uint32_t                sampledImage,\n            uint32_t                coordinates,\n            uint32_t                reference,\n      const SpirvImageOperands&     operands);\n\n    uint32_t opGroupNonUniformBallot(\n            uint32_t                resultType,\n            uint32_t                execution,\n            uint32_t                predicate);\n    \n    uint32_t opGroupNonUniformBallotBitCount(\n            uint32_t                resultType,\n            uint32_t                execution,\n            uint32_t                operation,\n            uint32_t                ballot);\n    \n    uint32_t opGroupNonUniformElect(\n            uint32_t                resultType,\n            uint32_t                execution);\n    \n    uint32_t opGroupNonUniformBroadcastFirst(\n            uint32_t                resultType,\n            uint32_t                execution,\n            uint32_t                value);\n    \n    void opControlBarrier(\n            uint32_t                execution,\n            uint32_t                memory,\n            uint32_t                semantics);\n    \n    void opMemoryBarrier(\n            uint32_t                memory,\n            uint32_t                semantics);\n    \n    void opLoopMerge(\n            uint32_t                mergeBlock,\n            uint32_t                continueTarget,\n            uint32_t                loopControl);\n    \n    void opSelectionMerge(\n            uint32_t                mergeBlock,\n            uint32_t                selectionControl);\n    \n    void opBranch(\n            uint32_t                label);\n    \n    void opBranchConditional(\n            uint32_t                condition,\n            uint32_t                trueLabel,\n            uint32_t                falseLabel);\n    \n    void opSwitch(\n            uint32_t                selector,\n            uint32_t                jumpDefault,\n            uint32_t                caseCount,\n      const SpirvSwitchCaseLabel*   caseLabels);\n    \n    uint32_t opPhi(\n            uint32_t                resultType,\n            uint32_t                sourceCount,\n      const SpirvPhiLabel*          sourceLabels);\n    \n    void opReturn();\n    \n    void opDemoteToHelperInvocation();\n    \n    void opEmitVertex(\n            uint32_t                streamId);\n    \n    void opEndPrimitive(\n            uint32_t                streamId);\n\n    void opBeginInvocationInterlock();\n\n    void opEndInvocationInterlock();\n\n    uint32_t opSinCos(\n            uint32_t                x,\n            bool                    useBuiltIn);\n\n  private:\n    \n    uint32_t m_version;\n    uint32_t m_id             = 1;\n    uint32_t m_instExtGlsl450 = 0;\n    uint32_t m_blockId        = 0;\n    \n    SpirvCodeBuffer m_capabilities;\n    SpirvCodeBuffer m_extensions;\n    SpirvCodeBuffer m_instExt;\n    SpirvCodeBuffer m_memoryModel;\n    SpirvCodeBuffer m_entryPoints;\n    SpirvCodeBuffer m_execModeInfo;\n    SpirvCodeBuffer m_debugNames;\n    SpirvCodeBuffer m_annotations;\n    SpirvCodeBuffer m_typeConstDefs;\n    SpirvCodeBuffer m_variables;\n    SpirvCodeBuffer m_code;\n\n    std::unordered_set<uint32_t> m_uniqueTypes;\n    std::unordered_set<uint32_t> m_lateConsts;\n\n    std::vector<uint32_t> m_interfaceVars;\n\n    uint32_t defType(\n            spv::Op                 op, \n            uint32_t                argCount,\n      const uint32_t*               argIds);\n    \n    uint32_t defConst(\n            spv::Op                 op,\n            uint32_t                typeId,\n            uint32_t                argCount,\n      const uint32_t*               argIds);\n    \n    void instImportGlsl450();\n    \n    uint32_t getMemoryOperandWordCount(\n      const SpirvMemoryOperands&    op) const;\n\n    void putMemoryOperands(\n      const SpirvMemoryOperands&    op);\n\n    uint32_t getImageOperandWordCount(\n      const SpirvImageOperands&     op) const;\n    \n    void putImageOperands(\n      const SpirvImageOperands&     op);\n\n    bool isInterfaceVar(\n            spv::StorageClass       sclass) const;\n\n    void classifyBlocks(\n            std::unordered_set<uint32_t>& reachableBlocks,\n            std::unordered_set<uint32_t>& mergeBlocks);\n\n    static constexpr double sincosTaylorFactor(uint32_t power) {\n      double result = 1.0;\n\n      for (uint32_t i = 1; i <= power; i++)\n        result *= pi * 0.25f / double(i);\n\n      return result;\n    }\n\n  };\n  \n}\n"
  },
  {
    "path": "src/util/com/com_destruction_notifier.cpp",
    "content": "#include \"com_destruction_notifier.h\"\n\nnamespace dxvk {\n\n  D3DDestructionNotifier::D3DDestructionNotifier(IUnknown* pParent)\n  : m_parent(pParent) {\n\n  }\n\n\n  D3DDestructionNotifier::~D3DDestructionNotifier() {\n    Notify();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3DDestructionNotifier::AddRef() {\n    return m_parent->AddRef();\n  }\n\n\n  ULONG STDMETHODCALLTYPE D3DDestructionNotifier::Release() {\n    return m_parent->Release();\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3DDestructionNotifier::QueryInterface(\n          REFIID                    iid,\n          void**                    ppvObject) {\n    return m_parent->QueryInterface(iid, ppvObject);\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3DDestructionNotifier::RegisterDestructionCallback(\n          PFN_DESTRUCTION_CALLBACK  pCallback,\n          void*                     pData,\n          UINT*                     pCallbackId) {\n    std::lock_guard lock(m_mutex);\n\n    if (!pCallback)\n      return DXGI_ERROR_INVALID_CALL;\n\n    auto& cb = m_callbacks.emplace_back();\n    cb.cb = pCallback;\n    cb.data = pData;\n\n    if (pCallbackId) {\n      cb.id = ++m_nextId;\n      *pCallbackId = cb.id;\n    }\n\n    return S_OK;\n  }\n\n\n  HRESULT STDMETHODCALLTYPE D3DDestructionNotifier::UnregisterDestructionCallback(\n          UINT                      CallbackId) {\n    std::lock_guard lock(m_mutex);\n\n    if (!CallbackId)\n      return DXGI_ERROR_NOT_FOUND;\n\n    for (size_t i = 0; i < m_callbacks.size(); i++) {\n      if (m_callbacks[i].id == CallbackId) {\n        m_callbacks[i] = std::move(m_callbacks.back());\n        m_callbacks.pop_back();\n        return S_OK;\n      }\n    }\n\n    return DXGI_ERROR_NOT_FOUND;\n  }\n\n\n  void D3DDestructionNotifier::Notify() {\n    for (size_t i = 0; i < m_callbacks.size(); i++)\n      m_callbacks[i].cb(m_callbacks[i].data);\n\n    m_callbacks.clear();\n  }\n\n}\n"
  },
  {
    "path": "src/util/com/com_destruction_notifier.h",
    "content": "#pragma once\n\n#include <d3dcommon.h>\n\n#include \"../util/thread.h\"\n#include \"../util/util_small_vector.h\"\n\nnamespace dxvk {\n\n  class D3DDestructionNotifier : public ID3DDestructionNotifier {\n\n  public:\n\n    D3DDestructionNotifier(IUnknown* pParent);\n\n    ~D3DDestructionNotifier();\n\n    ULONG STDMETHODCALLTYPE AddRef();\n\n    ULONG STDMETHODCALLTYPE Release();\n\n    HRESULT STDMETHODCALLTYPE QueryInterface(\n            REFIID                    iid,\n            void**                    ppvObject);\n\n    HRESULT STDMETHODCALLTYPE RegisterDestructionCallback(\n            PFN_DESTRUCTION_CALLBACK  pCallback,\n            void*                     pData,\n            UINT*                     pCallbackId);\n\n    HRESULT STDMETHODCALLTYPE UnregisterDestructionCallback(\n            UINT                      CallbackId);\n\n    void Notify();\n\n  private:\n\n    struct Entry {\n      uint32_t id = 0u;\n      PFN_DESTRUCTION_CALLBACK cb = nullptr;\n      void* data = nullptr;\n    };\n\n    IUnknown*   m_parent = nullptr;\n\n    dxvk::mutex m_mutex;\n    uint32_t    m_nextId = 0u;\n\n    small_vector<Entry, 2> m_callbacks;\n\n  };\n\n}\n"
  },
  {
    "path": "src/util/com/com_guid.cpp",
    "content": "#include <mutex>\n#include <unordered_set>\n\n#include \"com_guid.h\"\n\n#include \"../../d3d11/d3d11_interfaces.h\"\n\n#include \"../../dxgi/dxgi_interfaces.h\"\n\n#include \"../../dxvk/dxvk_hash.h\"\n\n#include \"../thread.h\"\n\nnamespace dxvk {\n\n  struct GuidPair {\n    GuidPair() { };\n    GuidPair(IID a_, IID b_)\n    : a(a_), b(b_) { }\n\n    IID a, b;\n\n    size_t hash() const {\n      return size_t(a.Data1) ^ size_t(b.Data1);\n    }\n\n    bool eq(const GuidPair& other) const {\n      return a == other.a && b == other.b;\n    }\n  };\n\n  dxvk::mutex                                    g_loggedQueryInterfaceErrorMutex;\n  std::unordered_set<GuidPair, DxvkHash, DxvkEq> g_loggedQueryInterfaceErrors;\n\n  bool logQueryInterfaceError(REFIID objectGuid, REFIID requestedGuid) {\n    if (Logger::logLevel() > LogLevel::Warn)\n      return false;\n\n    std::lock_guard lock(g_loggedQueryInterfaceErrorMutex);\n    return g_loggedQueryInterfaceErrors.emplace(objectGuid, requestedGuid).second;\n  }\n\n}\n\nstd::ostream& operator << (std::ostream& os, REFIID guid) {\n  os << std::hex << std::setfill('0')\n     << std::setw(8) << guid.Data1 << '-';\n\n  os << std::hex << std::setfill('0')\n     << std::setw(4) << guid.Data2 << '-';\n\n  os << std::hex << std::setfill('0')\n     << std::setw(4) << guid.Data3 << '-';\n\n  os << std::hex << std::setfill('0')\n     << std::setw(2) << static_cast<short>(guid.Data4[0])\n     << std::setw(2) << static_cast<short>(guid.Data4[1])\n     << '-'\n     << std::setw(2) << static_cast<short>(guid.Data4[2])\n     << std::setw(2) << static_cast<short>(guid.Data4[3])\n     << std::setw(2) << static_cast<short>(guid.Data4[4])\n     << std::setw(2) << static_cast<short>(guid.Data4[5])\n     << std::setw(2) << static_cast<short>(guid.Data4[6])\n     << std::setw(2) << static_cast<short>(guid.Data4[7]);\n  return os;\n}\n"
  },
  {
    "path": "src/util/com/com_guid.h",
    "content": "#pragma once\n\n#include <ostream>\n#include <iomanip>\n\n#include \"com_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Checks whether an unknown GUID should be logged\n   *\n   * \\param [in] objectGuid GUID of the object that QueryInterface is called on\n   * \\param [in] requestGuid Requested unsupported GUID\n   * \\returns \\c true if the error should be logged\n   */\n  bool logQueryInterfaceError(REFIID objectGuid, REFIID requestedGuid);\n\n};\n\nstd::ostream& operator << (std::ostream& os, REFIID guid);\n"
  },
  {
    "path": "src/util/com/com_include.h",
    "content": "#pragma once\n\n// GCC complains about the COM interfaces\n// not having virtual destructors\n#ifdef __GNUC__\n#pragma GCC diagnostic ignored \"-Wnon-virtual-dtor\"\n#endif // __GNUC__\n\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#include <unknwn.h>\n\n// GCC: -std options disable certain keywords\n// https://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html\n#if defined(__WINE__) && !defined(typeof)\n#define typeof __typeof\n#endif\n"
  },
  {
    "path": "src/util/com/com_object.h",
    "content": "#pragma once\n\n#include <atomic>\n\n#include \"com_include.h\"\n\n#include \"../util_likely.h\"\n  \nnamespace dxvk {\n  \n  template<typename T>\n  class NoWrapper : public T {\n\n  public:\n\n    virtual ~NoWrapper() { }\n\n  };\n  \n  /**\n   * \\brief Reference-counted COM object\n   *\n   * This can serve as a templated base class for most\n   * COM objects. It implements AddRef and Release from\n   * \\c IUnknown, and provides methods to increment and\n   * decrement private references which are not visible\n   * to the application.\n   * \n   * Having two reference counters is sadly necessary\n   * in order to not break games which steal internal\n   * references if the refefence count of an object is\n   + greater than they expect. DXVK sometimes requires\n   * holding on to objects which the application wants\n   * to delete.\n   */\n  template<typename Base>\n  class ComObject : public Base {\n    \n  public:\n    \n    virtual ~ComObject() { }\n    \n    ULONG STDMETHODCALLTYPE AddRef() {\n      uint32_t refCount = m_refCount++;\n      if (unlikely(!refCount))\n        AddRefPrivate();\n      return refCount + 1;\n    }\n    \n    ULONG STDMETHODCALLTYPE Release() {\n      uint32_t refCount = --m_refCount;\n      if (unlikely(!refCount))\n        ReleasePrivate();\n      return refCount;\n    }\n\n\n    void AddRefPrivate() {\n      ++m_refPrivate;\n    }\n\n\n    void ReleasePrivate() {\n      uint32_t refPrivate = --m_refPrivate;\n      if (unlikely(!refPrivate)) {\n        m_refPrivate += 0x80000000;\n        delete this;\n      }\n    }\n\n    ULONG GetPrivateRefCount() const {\n      return m_refPrivate.load();\n    }\n\n    bool HasLiveReferences() const {\n      return bool(m_refCount.load() | (m_refPrivate.load() & 0x7FFFFFFF));\n    }\n\n  protected:\n    \n    std::atomic<uint32_t> m_refCount   = { 0ul };\n    std::atomic<uint32_t> m_refPrivate = { 0ul };\n    \n  };\n\n  /**\n   * \\brief Clamped, reference-counted COM object\n   *\n   * This version of ComObject ensures that the reference\n   * count does not wrap around if a release happens at zero.\n   * eg. [m_refCount = 0]\n   *     Release()\n   *     [m_refCount = 0]\n   * This is a notable quirk of D3D9's COM implementation\n   * and is relied upon by some games.\n   */\n  template<typename Base>\n  class ComObjectClamp : public ComObject<Base> {\n\n  public:\n\n    ULONG STDMETHODCALLTYPE Release() {\n      ULONG refCount = this->m_refCount;\n      if (likely(refCount != 0ul)) {\n        this->m_refCount--;\n        refCount--;\n\n        if (refCount == 0ul)\n          this->ReleasePrivate();\n      }\n\n      return refCount;\n    }\n\n  };\n  \n  template<typename T>\n  inline void InitReturnPtr(T** ptr) {\n    if (ptr != nullptr)\n      *ptr = nullptr;\n  }\n  \n}\n"
  },
  {
    "path": "src/util/com/com_pointer.h",
    "content": "#pragma once\n\n#include \"com_include.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Increment public ref count\n   * \n   * If the pointer is not \\c nullptr, this \n   * calls \\c AddRef for the given object.\n   * \\returns Pointer to the object\n   */\n  template<typename T>\n  T* ref(T* object) {\n    if (object != nullptr)\n      object->AddRef();\n    return object;\n  }\n\n\n  /**\n   * \\brief Ref count methods for public references\n   */\n  template<typename T, bool Public>\n  struct ComRef_ {\n    static void incRef(T* ptr) { ptr->AddRef(); }\n    static void decRef(T* ptr) { ptr->Release(); }\n  };\n\n\n  /**\n   * \\brief Ref count methods for private references\n   */\n  template<typename T>\n  struct ComRef_<T, false> {\n    static void incRef(T* ptr) { ptr->AddRefPrivate(); }\n    static void decRef(T* ptr) { ptr->ReleasePrivate(); }\n  };\n\n  \n  /**\n   * \\brief COM pointer\n   * \n   * Implements automatic reference\n   * counting for COM objects.\n   */\n  template<typename T, bool Public = true>\n  class Com {\n    using ComRef = ComRef_<T, Public>;\n  public:\n    \n    Com() { }\n    Com(std::nullptr_t) { }\n    Com(T* object)\n    : m_ptr(object) {\n      this->incRef();\n    }\n    \n    Com(const Com& other)\n    : m_ptr(other.m_ptr) {\n      this->incRef();\n    }\n    \n    Com(Com&& other)\n    : m_ptr(other.m_ptr) {\n      other.m_ptr = nullptr;\n    }\n    \n    Com& operator = (T* object) {\n      // prevents the destruction of private\n      // Com objects during self-assignment\n      if (likely(m_ptr != object)) {\n        this->decRef();\n        m_ptr = object;\n        this->incRef();\n      }\n      return *this;\n    }\n    \n    Com& operator = (const Com& other) {\n      other.incRef();\n      this->decRef();\n      m_ptr = other.m_ptr;\n      return *this;\n    }\n    \n    Com& operator = (Com&& other) {\n      this->decRef();\n      this->m_ptr = other.m_ptr;\n      other.m_ptr = nullptr;\n      return *this;\n    }\n    \n    Com& operator = (std::nullptr_t) {\n      this->decRef();\n      m_ptr = nullptr;\n      return *this;\n    }\n    \n    ~Com() {\n      this->decRef();\n      m_ptr = nullptr;\n    }\n    \n    T* operator -> () const {\n      return m_ptr;\n    }\n    \n    T**       operator & ()       { return &m_ptr; }\n    T* const* operator & () const { return &m_ptr; }\n    \n    template<bool Public_>\n    bool operator == (const Com<T, Public_>& other) const { return m_ptr == other.m_ptr; }\n    template<bool Public_>\n    bool operator != (const Com<T, Public_>& other) const { return m_ptr != other.m_ptr; }\n    \n    bool operator == (const T* other) const { return m_ptr == other; }\n    bool operator != (const T* other) const { return m_ptr != other; }\n    \n    bool operator == (std::nullptr_t) const { return m_ptr == nullptr; }\n    bool operator != (std::nullptr_t) const { return m_ptr != nullptr; }\n    \n    T* ref() const {\n      return dxvk::ref(m_ptr);\n    }\n    \n    T* ptr() const {\n      return m_ptr;\n    }\n\n    Com<T, true>  pubRef() const { return m_ptr; }\n    Com<T, false> prvRef() const { return m_ptr; }\n\n    explicit operator bool () const {\n      return m_ptr != nullptr;\n    }\n    \n  private:\n    \n    T* m_ptr = nullptr;\n    \n    void incRef() const {\n      if (m_ptr != nullptr)\n        ComRef::incRef(m_ptr);\n    }\n    \n    void decRef() const {\n      if (m_ptr != nullptr)\n        ComRef::decRef(m_ptr);\n    }\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/util/com/com_private_data.cpp",
    "content": "#include <cmath>\n#include <cstring>\n#include <cstdlib>\n\n#include \"com_private_data.h\"\n\nnamespace dxvk {\n  \n  ComPrivateDataEntry::ComPrivateDataEntry() { }\n  ComPrivateDataEntry::ComPrivateDataEntry(\n          REFGUID   guid,\n          UINT      size,\n    const void*     data)\n  : m_guid(guid),\n    m_type(ComPrivateDataType::Data),\n    m_size(size),\n    m_data(std::malloc(size)) {\n    std::memcpy(m_data, data, size);\n  }\n  \n  \n  ComPrivateDataEntry::ComPrivateDataEntry(\n          REFGUID   guid,\n    const IUnknown* iface)\n  : m_guid  (guid),\n    m_type(ComPrivateDataType::Iface),\n    m_iface (const_cast<IUnknown*>(iface)) {\n    if (m_iface)\n      m_iface->AddRef();\n  }\n  \n  \n  ComPrivateDataEntry::~ComPrivateDataEntry() {\n    this->destroy();\n  }\n  \n  \n  ComPrivateDataEntry::ComPrivateDataEntry(ComPrivateDataEntry&& other)\n  : m_guid  (other.m_guid),\n    m_type  (other.m_type),\n    m_size  (other.m_size),\n    m_data  (other.m_data),\n    m_iface (other.m_iface) {\n    other.m_guid  = __uuidof(IUnknown);\n    other.m_type  = ComPrivateDataType::None;\n    other.m_size  = 0;\n    other.m_data  = nullptr;\n    other.m_iface = nullptr;\n  }\n  \n  \n  ComPrivateDataEntry& ComPrivateDataEntry::operator = (ComPrivateDataEntry&& other) {\n    this->destroy();\n    this->m_guid  = other.m_guid;\n    this->m_type  = other.m_type;\n    this->m_size  = other.m_size;\n    this->m_data  = other.m_data;\n    this->m_iface = other.m_iface;\n    \n    other.m_guid  = __uuidof(IUnknown);\n    other.m_type  = ComPrivateDataType::None;\n    other.m_size  = 0;\n    other.m_data  = nullptr;\n    other.m_iface = nullptr;\n    return *this;\n  }\n  \n  \n  HRESULT ComPrivateDataEntry::get(UINT& size, void* data) const {\n    UINT minSize = 0;\n\n    if (m_type == ComPrivateDataType::Iface) minSize = sizeof(IUnknown*);\n    if (m_type == ComPrivateDataType::Data)  minSize = m_size;\n    \n    if (!data) {\n      size = minSize;\n      return S_OK;\n    }\n\n    HRESULT result = size < minSize\n      ? DXGI_ERROR_MORE_DATA\n      : S_OK;\n    \n    if (size >= minSize) {\n      if (m_type == ComPrivateDataType::Iface) {\n        if (m_iface)\n          m_iface->AddRef();\n        std::memcpy(data, &m_iface, minSize);\n      } else {\n        std::memcpy(data, m_data, minSize);\n      }\n    }\n    \n    size = minSize;\n    return result;\n  }\n  \n  \n  void ComPrivateDataEntry::destroy() {\n    if (m_data)\n      std::free(m_data);\n    if (m_iface)\n      m_iface->Release();\n  }\n  \n  \n  HRESULT ComPrivateData::setData(\n          REFGUID   guid,\n          UINT      size,\n    const void*     data) {\n    if (!data) {\n      for (auto it = m_entries.begin(); it != m_entries.end(); ++it) {\n        if (it->hasGuid(guid)) {\n          m_entries.erase(it);\n          return S_OK;\n        }\n      }\n      return S_FALSE;\n    }\n    this->insertEntry(ComPrivateDataEntry(guid, size, data));\n    return S_OK;\n  }\n  \n  \n  HRESULT ComPrivateData::setInterface(\n          REFGUID   guid,\n    const IUnknown* iface) {\n    this->insertEntry(ComPrivateDataEntry(guid, iface));\n    return S_OK;\n  }\n  \n  \n  HRESULT ComPrivateData::getData(\n          REFGUID   guid,\n          UINT*     size,\n          void*     data) {\n    if (!size)\n      return E_INVALIDARG;\n    \n    auto entry = this->findEntry(guid);\n    \n    if (!entry) {\n      *size = 0;\n      return DXGI_ERROR_NOT_FOUND;\n    }\n    \n    return entry->get(*size, data);\n  }\n  \n  \n  ComPrivateDataEntry* ComPrivateData::findEntry(REFGUID guid) {\n    for (ComPrivateDataEntry& e : m_entries) {\n      if (e.hasGuid(guid))\n        return &e;\n    }\n    \n    return nullptr;\n  }\n  \n  \n  void ComPrivateData::insertEntry(ComPrivateDataEntry&& entry) {\n    ComPrivateDataEntry  srcEntry = std::move(entry);\n    ComPrivateDataEntry* dstEntry = this->findEntry(srcEntry.guid());\n    \n    if (dstEntry)\n      *dstEntry = std::move(srcEntry);\n    else\n      m_entries.push_back(std::move(srcEntry));\n  }\n  \n}\n"
  },
  {
    "path": "src/util/com/com_private_data.h",
    "content": "#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\n#include \"com_include.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief COM private data entry type\n   */\n  enum class ComPrivateDataType : uint32_t {\n    None,\n    Data,\n    Iface,\n  };\n  \n  /**\n   * \\brief Data entry for private storage\n   * Stores a single private storage item.\n   */\n  class ComPrivateDataEntry {\n    \n  public:\n    \n    ComPrivateDataEntry();\n    ComPrivateDataEntry(\n            REFGUID   guid,\n            UINT      size,\n      const void*     data);\n    ComPrivateDataEntry(\n            REFGUID   guid,\n      const IUnknown* iface);\n    ~ComPrivateDataEntry();\n    \n    ComPrivateDataEntry             (ComPrivateDataEntry&& other);\n    ComPrivateDataEntry& operator = (ComPrivateDataEntry&& other);\n    \n    /**\n     * \\brief The entry's GUID\n     * \\returns The GUID\n     */\n    REFGUID guid() const {\n      return m_guid;\n    }\n    \n    /**\n     * \\brief Checks whether the GUID matches another one\n     * \n     * GUIDs are used to identify private data entries.\n     * \\param [in] guid The GUID to compare to\n     * \\returns \\c true if this entry holds the same GUID\n     */\n    bool hasGuid(REFGUID guid) const {\n      return m_guid == guid;\n    }\n    \n    /**\n     * \\brief Retrieves stored data\n     * \n     * \\param [in,out] size Destination buffer size\n     * \\param [in] data Appliaction-provided buffer\n     * \\returns \\c S_OK on success, or \\c DXGI_ERROR_MORE_DATA\n     *          if the destination buffer is too small\n     */\n    HRESULT get(UINT& size, void* data) const;\n    \n  private:\n    \n    GUID                m_guid  = __uuidof(IUnknown);\n    ComPrivateDataType  m_type  = ComPrivateDataType::None;\n    UINT                m_size  = 0;\n    void*               m_data  = nullptr;\n    IUnknown*           m_iface = nullptr;\n    \n    void destroy();\n    \n  };\n  \n  \n  /**\n   * \\brief Private storage for DXGI objects\n   * \n   * Provides storage for application-defined\n   * byte arrays or COM interfaces that can be\n   * retrieved using GUIDs.\n   */\n  class ComPrivateData {\n    \n  public:\n    \n    HRESULT setData(\n            REFGUID   guid,\n            UINT      size,\n      const void*     data);\n    \n    HRESULT setInterface(\n            REFGUID   guid,\n      const IUnknown* iface);\n    \n    HRESULT getData(\n            REFGUID   guid,\n            UINT*     size,\n            void*     data);\n    \n  private:\n    \n    std::vector<ComPrivateDataEntry> m_entries;\n    \n    ComPrivateDataEntry* findEntry(REFGUID guid);\n    void insertEntry(ComPrivateDataEntry&& entry);\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/util/config/config.cpp",
    "content": "#include <array>\n#include <fstream>\n#include <sstream>\n#include <iostream>\n#include <regex>\n#include <utility>\n\n#include \"config.h\"\n\n#include \"../log/log.h\"\n\n#include \"../util_env.h\"\n\n#include \"../sha1/sha1_util.h\"\n\nnamespace dxvk {\n\n  using ProfileList = std::vector<std::pair<const char*, Config>>;\n\n\n  const static ProfileList g_profiles = {\n    /**********************************************/\n    /* D3D12 GAMES (vkd3d-proton with dxvk dxgi)  */\n    /**********************************************/\n\n    /* Diablo 4 - Will complain about missing     *\n     * GPU unless dxgi Id match actual GPU Id.    *\n     * Bugs out on some multi-gpu systems.        */\n    { R\"(\\\\Diablo IV\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n      { \"dxvk.hideIntegratedGraphics\",      \"True\" },\n    }} },\n    /* Ratchet & Clank: Rift Apart - does not allow *\n     * enabling ray tracing if it sees an AMD GPU.  */\n    { R\"(\\\\RiftApart\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n    }} },\n    /* Persona 3 Reload - disables vsync by default and *\n     * runs into severe frame latency issues on Deck.   */\n    { R\"(\\\\P3R\\.exe$)\", {{\n      { \"dxgi.syncInterval\",                   \"1\" },\n    }} },\n    /* World of Warcraft                           *\n     * Bugs out on some multi-gpu systems.         */\n    { R\"(\\\\Wow(Classic)?\\.exe$)\", {{\n      { \"dxvk.hideIntegratedGraphics\",      \"True\" },\n    }} },\n    /* Bright Memory - Will choose other vendors   *\n     * over Intel even if Intel is the only dGPU   */\n    { R\"(\\\\BrightMemory_EP1-Win64-Shipping\\.exe$)\", {{\n      { \"dxvk.hideIntegratedGraphics\",      \"True\" },\n    }} },\n    /* AC Shadows: Uses composition swapchain to   *\n     * check for HDR support                       */\n    { R\"(\\\\ACShadows\\.exe$)\", {{\n      { \"dxgi.enableDummyCompositionSwapchain\", \"True\" }\n    }} },\n    /* Anno 117: Uses composition swapchain        */\n    { R\"(\\\\Anno117\\.exe$)\", {{\n      { \"dxgi.enableDummyCompositionSwapchain\", \"True\" }\n    }} },\n\n    /**********************************************/\n    /* D3D11 GAMES                                */\n    /**********************************************/\n\n    /* Batman Arkham Knight - doesn't like intel vendor id *\n     *(refuses to boot if vendor isn't 0x10de or 0x1002)   */\n    { R\"(\\\\BatmanAK\\.exe$)\", {{\n      { \"dxgi.hideIntelGpu\",                \"True\" },\n    }} },\n    /* Assassin's Creed Syndicate: amdags issues  */\n    { R\"(\\\\ACS\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* Dissidia Final Fantasy NT Free Edition */\n    { R\"(\\\\dffnt\\.exe$)\", {{\n      { \"dxgi.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Elite Dangerous: Compiles weird shaders    *\n     * when running on AMD hardware               */\n    { R\"(\\\\EliteDangerous64\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* EVE Online: Needs this to expose D3D12     *\n     * otherwise D3D12 option on launcher is      *\n     * greyed out                                 */\n    { R\"(\\\\evelauncher\\.exe$)\", {{\n      { \"d3d11.maxFeatureLevel\",            \"12_1\" },\n    }} },\n    /* The Evil Within: Submits command lists     *\n     * multiple times                             */\n    { R\"(\\\\EvilWithin(Demo)?\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",       \"vi\" },\n    }} },\n    /* Far Cry 3: Assumes clear(0.5) on an UNORM  *\n     * format to result in 128 on AMD and 127 on  *\n     * Nvidia. We assume that the Vulkan drivers  *\n     * match the clear behaviour of D3D11.        *\n     * Intel needs to match the AMD result        */\n    { R\"(\\\\(farcry3|fc3_blooddragon)_d3d11\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n      { \"dxgi.hideIntelGpu\",                \"True\" },\n    }} },\n    /* Far Cry 4 and Primal: Same as Far Cry 3    */\n    { R\"(\\\\(FarCry4|FCPrimal)\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n      { \"dxgi.hideIntelGpu\",                \"True\" },\n    }} },\n    /* Far Cry 5 and New Dawn: Unsynchronized     *\n     * write-after-read in some compute shaders   *\n     * cause invisible terrain on some GPUs.      */\n    { R\"(\\\\FarCry(5|NewDawn)\\.exe$)\", {{\n      { \"d3d11.forceComputeLdsBarriers\",    \"True\" },\n    }} },\n    /* Frostpunk: Renders one frame with D3D9     *\n     * after creating the DXGI swap chain         */\n    { R\"(\\\\Frostpunk\\.exe$)\", {{\n      { \"dxgi.deferSurfaceCreation\",        \"True\" },\n      { \"d3d11.cachedDynamicResources\",        \"c\" },\n    }} },\n    /* Nioh: Apparently the same as the Atelier games */\n    { R\"(\\\\nioh\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Anno 1800: Poor performance without this   */\n    { R\"(\\\\Anno1800\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"c\" },\n    }} },\n    /* Fifa '19+: Binds typed buffer SRV to shader *\n     * that expects raw/structured buffer SRV      */\n    { R\"(\\\\FIFA(19|20|21|22)(_demo)?\\.exe$)\", {{\n      { \"dxvk.useRawSsbo\",                  \"True\" },\n    }} },\n    /* Resident Evil 2/3: Ignore WaW hazards      */\n    { R\"(\\\\re(2|3|3demo)\\.exe$)\", {{\n      { \"d3d11.relaxedBarriers\",            \"True\" },\n    }} },\n    /* Devil May Cry 5                            */\n    { R\"(\\\\DevilMayCry5\\.exe$)\", {{\n      { \"d3d11.relaxedBarriers\",            \"True\" },\n    }} },\n    /* Call of Duty WW2                           */\n    { R\"(\\\\s2_sp64_ship\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n    }} },\n    /* Need for Speed 2015                        */\n    { R\"(\\\\NFS16\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n    }} },\n    /* Mass Effect Andromeda                      */\n    { R\"(\\\\MassEffectAndromeda\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n    }} },\n    /* Mirror`s Edge Catalyst: Crashes on AMD     */\n    { R\"(\\\\MirrorsEdgeCatalyst(Trial)?\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* Star Wars Battlefront (2015)               */\n    { R\"(\\\\starwarsbattlefront(trial)?\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* NieR Replicant                             */\n    { R\"(\\\\NieR Replicant ver\\.1\\.22474487139\\.exe)\", {{\n      { \"d3d11.cachedDynamicResources\",       \"vi\" },\n    }} },\n    /* Hitman 2 - requires AGS library            */\n    { R\"(\\\\HITMAN2\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n      { \"d3d11.cachedDynamicResources\",        \"c\" },\n    }} },\n    /* Modern Warfare Remastered                  */\n    { R\"(\\\\h1(_[ms]p64_ship|-mod)\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* H2M-Mod - Modern Warfare Remastered        */\n    { R\"(\\\\h2m-mod\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* HMW-Mod - Modern Warfare Remastered        *\n     * AMD AGS crash                              */\n    { R\"(\\\\hmw-mod\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* Infinite Warfare                           *\n     * AMD AGS crash                              */\n    { R\"(\\\\iw7(_ship|-mod)\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* Modern Warfare 2 Campaign Remastered       *\n     * AMD AGS crash same as above                */\n    { R\"(\\\\MW2CR\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* Crysis 3 - slower if it notices AMD card     *\n     * but apparently no longer works when spoofing *\n     * vendor IDs. Cached dynamic buffers help      *\n     * massively in CPU bound game parts            */\n    { R\"(\\\\Crysis3\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Crysis 3 Remastered                        *\n     * Cached dynamic buffers help massively      *\n     * in CPU bound game parts                    */\n    { R\"(\\\\Crysis3Remastered\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Atelier series - games try to render video *\n     * with a D3D9 swap chain over the DXGI swap  *\n     * chain, which breaks D3D11 presentation     */\n    { R\"(\\\\Atelier_(Ayesha|Escha_and_Logy|Shallie)(_EN)?\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Atelier Firis                              */\n    { R\"(\\\\A18\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Atelier Rorona/Totori/Meruru               */\n    { R\"(\\\\A(11R|12V|13V)_x64_Release(_en)?\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Just how many of these games are there?    */\n    { R\"(\\\\Atelier_(Lulua|Lydie_and_Suelle|Ryza(_2|_3)?|Sophie_2)\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* ...                                        */\n    { R\"(\\\\Atelier_(Lydie_and_Suelle|Firis|Sophie)_DX\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Fairy Tail                                 */\n    { R\"(\\\\FAIRY_TAIL\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Nights of Azure                            */\n    { R\"(\\\\CNN\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Star Wars Battlefront II: amdags issues    */\n    { R\"(\\\\starwarsbattlefrontii\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* F1 games - do not synchronize TGSM access  *\n     * in a compute shader, causing artifacts     */\n    { R\"(\\\\F1_20(1[89]|[2-9][0-9])\\.exe$)\", {{\n      { \"d3d11.forceComputeLdsBarriers\",    \"True\" },\n    }} },\n    /* Darksiders Warmastered - apparently reads  *\n     * from write-only mapped buffers             */\n    { R\"(\\\\darksiders1\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Monster Hunter World                       */\n    { R\"(\\\\MonsterHunterWorld\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Kingdome Come: Deliverance                 */\n    { R\"(\\\\KingdomCome\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Homefront: The Revolution                  */\n    { R\"(\\\\Homefront2_Release\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Sniper Ghost Warrior Contracts             */\n    { R\"(\\\\SGWContracts\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Armored Warfare             */\n    { R\"(\\\\armoredwarfare\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"c\" },\n    }} },\n    /* Nioh 2 - Fixes some fires looking glitchy  */\n    { R\"(\\\\nioh2\\.exe$)\", {{\n      { \"dxgi.deferSurfaceCreation\",        \"True\" },\n      { \"dxvk.zeroMappedMemory\",            \"True\" },\n    }} },\n    /* Crazy Machines 3 - crashes on long device  *\n     * descriptions                               */\n    { R\"(\\\\cm3\\.exe$)\", {{\n      { \"dxgi.customDeviceDesc\",            \"DXVK Device\" },\n    }} },\n    /* World of Final Fantasy: Broken and useless use     *\n     * of 4x MSAA throughout the renderer. Water doesn't  *\n     * render if the GPU name contains \"Radeon\", clearly  *\n     * us plebs aren't worthy of the divine pixel liquid. */\n    { R\"(\\\\WOFF\\.exe$)\", {{\n      { \"d3d11.disableMsaa\",                \"True\" },\n      { \"dxgi.customDeviceDesc\",            \"DXVK Device\" },\n    }} },\n    /* Mary Skelter 2 - Broken MSAA               */\n    { R\"(\\\\MarySkelter2\\.exe$)\", {{\n      { \"d3d11.disableMsaa\",                \"True\" },\n    }} },\n    /* Final Fantasy XIV: Uses lots of HVV and    *\n     * also reads some uncached memory.           */\n    { R\"(\\\\ffxiv_dx11\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",       \"vi\" },\n    }} },\n    /* Final Fantasy XV: VXAO does thousands of   *\n     * draw calls with the same UAV bound         */\n    { R\"(\\\\ffxv_s\\.exe$)\", {{\n      { \"d3d11.relaxedGraphicsBarriers\",    \"True\" },\n    }} },\n    /* God of War - relies on NVAPI/AMDAGS for    *\n     * barrier stuff, needs nvapi for DLSS        */\n    { R\"(\\\\GoW\\.exe$)\", {{\n      { \"d3d11.relaxedBarriers\",            \"True\" },\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n      { \"dxgi.maxFrameLatency\",                \"1\" },\n    }} },\n    /* AoE 2 DE - runs poorly for some users      */\n    { R\"(\\\\AoE2DE_s\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Assassin's Creed 3 and 4                   */\n    { R\"(\\\\ac(3|4bf)[sm]p\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Stranger of Paradise - FF Origin           */\n    { R\"(\\\\SOPFFO\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    /* Small Radios Big Televisions               */\n    }} },\n    { R\"(\\\\SRBT\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* A Way Out: fix for stuttering and low fps  */\n    { R\"(\\\\AWayOut(_friend)?\\.exe$)\", {{\n      { \"dxgi.maxFrameLatency\",                \"1\" },\n    }} },\n    /* Garden Warfare 2                           *\n     * Won't start on amd Id without atiadlxx     */\n    { R\"(\\\\GW2\\.Main_Win64_Retail\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n    }} },\n    /* DayZ */\n    { R\"(\\\\DayZ_x64\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",       \"cr\" },\n    }} },\n    /* Stray - writes to the same UAV every draw, *\n     * presumably for culling, which doesn't play *\n     * nicely with D3D11 without vendor libraries */\n    { R\"(\\\\Stray-Win64-Shipping\\.exe$)\", {{\n      { \"d3d11.relaxedGraphicsBarriers\",    \"True\" },\n    }} },\n    /* Metal Gear Solid V: Ground Zeroes          *\n     * Texture quality can break at high vram     */\n    { R\"(\\\\MgsGroundZeroes\\.exe$)\", {{\n      { \"dxgi.maxDeviceMemory\",             \"4095\" },\n    }} },\n    /* Shantae and the Pirate's Curse             *\n     * Game speeds up above 60 fps                */\n    { R\"(\\\\ShantaeCurse\\.exe$)\", {{\n      { \"dxgi.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Mighty Switch Force! Collection            *\n     * Games speed up above 60 fps                */\n    { R\"(\\\\MSFC\\.exe$)\", {{\n      { \"dxgi.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* The Evil Within 2 *\n    * Game speeds up above 120 fps */\n    { R\"(\\\\TEW2\\.exe$)\", {{\n      { \"dxgi.maxFrameRate\",                 \"-120\" },\n    }} },\n    /* Sonic Frontiers - flickering shadows and   *\n     * vegetation when GPU-bound                  */\n    { R\"(\\\\SonicFrontiers\\.exe$)\", {{\n      { \"dxgi.maxFrameLatency\",                \"1\" },\n    }} },\n    /* SpellForce 3 Reforced & expansions         *\n     * Greatly improves CPU bound performance     */\n    { R\"(\\\\SF3ClientFinal\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"v\" },\n    }} },\n    /* Tom Clancy's Ghost Recon Breakpoint        */\n    { R\"(\\\\GRB\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n    }} },\n    /* GTA V performance issues                   */\n    { R\"(\\\\GTA5\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",       \"vi\" },\n    }} },\n    /* Crash Bandicoot N. Sane Trilogy            *\n     * Work around some vsync funkiness           */\n    { R\"(\\\\CrashBandicootNSaneTrilogy\\.exe$)\", {{\n      { \"dxgi.syncInterval\",                   \"1\" },\n    }} },\n    /* Fallout 76                                      *\n     * Game tries to be too \"smart\" and changes sync   *\n     * interval based on performance (in fullscreen)   *\n     * or tries to match (or ratio below) 60fps        *\n     * (in windowed).                                  *\n     * Ends up getting in a loop where it will switch  *\n     * and start stuttering, or get stuck at targeting *\n     * 30Hz in fullscreen.                             *\n     * Windowed mode being locked to 60fps as well is  *\n     * pretty suboptimal...                            */\n    { R\"(\\\\Fallout76\\.exe$)\", {{\n      { \"dxgi.syncInterval\",                   \"1\" },\n    }} },\n    /* Bladestorm Nightmare                       *\n     * Game speed increases when above 60 fps in  *\n     * the tavern area                            */\n    { R\"(\\\\BLADESTORM Nightmare\\\\Launch_(EA|JP)\\.exe$)\", {{\n      { \"dxgi.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Vindictus d3d11 CPU bound perf, and work   *\n     * around the game not properly initializing  *\n     * some of its constant buffers after discard */\n    { R\"(\\\\Vindictus(_x64)?\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",       \"cr\" },\n      { \"dxvk.zeroMappedMemory\",            \"True\" },\n    }} },\n    /* Riders Republic - Statically linked AMDAGS */\n    { R\"(\\\\RidersRepublic(_BE)?\\.exe$)\", {{\n      { \"dxgi.hideAmdGpu\",                  \"True\" },\n    }} },\n    /* Kenshi                                     *\n     * Helps CPU bound performance                */\n    { R\"(\\\\kenshi_x64\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"v\" },\n    }} },\n    /* Granblue Relink: Spams pixel shader UAVs   *\n     * and assumes that AMD GPUs do not expose    *\n     * native command lists for AGS usage         */\n    { R\"(\\\\granblue_fantasy_relink\\.exe$)\", {{\n      { \"d3d11.relaxedGraphicsBarriers\",    \"True\" },\n      { \"d3d11.exposeDriverCommandLists\",  \"False\" },\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n    }} },\n    /* Crysis 1/Warhead - Game bug in d3d10 makes *\n     * it select lowest supported refresh rate    */\n    { R\"(\\\\Crysis(64)?\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                  \"-1\" },\n      { \"dxgi.maxFrameRate\",                  \"-1\" },\n    }} },\n    /* EDF6 - possible race condition?            */\n    { R\"(\\\\EDF6\\.exe$)\", {{\n      { \"d3d11.enableContextLock\",          \"True\" },\n    }} },\n    /* Kena: Bridge of Spirits: intel water       *\n     * flickering issues                          */\n    { R\"(\\\\Kena-Win64-Shipping\\.exe$)\", {{\n      { \"dxgi.hideIntelGpu\",                \"True\" },\n    }} },\n    /* GTA Definitive Edition trilogy             *\n     * Static ags crash with HDR support          */\n    { R\"(\\\\(LibertyCity|ViceCity|SanAndreas)\\.exe$)\", {{\n      { \"dxgi.enableUe4Workarounds\",        \"True\" },\n    }} },\n    /* Warcraft 3 Reforged                        *\n     * Bugs out on some multi-gpu systems.        */\n    { R\"(\\\\x86_64\\\\Warcraft III\\.exe$)\", {{\n      { \"dxvk.hideIntegratedGraphics\",      \"True\" },\n    }} },\n    /* Earth Defense Force 5                      */\n    { R\"(\\\\EDF5\\.exe$)\", {{\n      { \"dxgi.tearFree\",                   \"False\" },\n      { \"dxgi.syncInterval\",                   \"1\" },\n    }} },\n    /* The Hurricane of the Varstray              *\n     * Too fast above 60fps                       */\n    { R\"(\\\\Varstray_steam(_demo)?\\.exe$)\", {{\n      { \"dxgi.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Watch Dogs 2 - ships broken compute shaders *\n     * with no barriers when they are needed       */\n    { R\"(\\\\WatchDogs2\\.exe$)\", {{\n      { \"d3d11.forceComputeUavBarriers\",    \"True\" },\n    }} },\n    /* Rocketbirds 2: Ignores row pitch for       *\n     * mapped images, corrupting intro video      */\n    { R\"(\\\\Rocketbirds 2\\\\Game\\.exe$)\", {{\n      { \"d3d11.disableDirectImageMapping\",  \"True\" },\n    }} },\n    /* LEGO City Undercover                       *\n     * Fixes graphical corruption on cutscenes    */\n    { R\"(\\\\LEGOLCUR_DX11\\.exe$)\", {{\n      { \"d3d11.disableDirectImageMapping\",  \"True\" },\n    }} },\n    /* Wargame: European Escalation: Broken gamma   *\n     * ramp when nvapi is available for some reason */\n    { R\"(\\\\Wargame European Escalation\\\\WarGame\\.exe$)\", {{\n      { \"dxgi.hideNvidiaGpu\",               \"True\" },\n    }} },\n    /* Guilty Gear - Speeds up above 60 fps         */\n    { R\"(\\\\GuiltyGear\\.exe$)\", {{\n      { \"dxgi.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Everybody's Gone to the Rapture - CPU perf   */\n    { R\"(\\\\Rapture_Release\\.exe$)\", {{\n      { \"d3d11.cachedDynamicResources\",        \"a\" },\n    }} },\n    /* Total War Pharaoh Dynasties: Broken menu   *\n     * because the game doesn't manage to respect *\n     * pitch for an A8_UNORM image. Resolves are  *\n     * not needed because the game dynamically    *\n     * checks sample count in affected shaders.   */\n    { R\"(\\\\Pharaoh\\.exe$)\", {{\n      { \"d3d11.disableDirectImageMapping\",  \"True\" },\n      { \"dxvk.enableImplicitResolves\",     \"False\" },\n    }} },\n\n    /**********************************************/\n    /* D3D9 GAMES                                 */\n    /**********************************************/\n\n    /* A Hat in Time                              */\n    { R\"(\\\\HatinTimeGame\\.exe$)\", {{\n      { \"d3d9.lenientClear\",                \"True\" },\n    }} },\n    /* Anarchy Online                             */\n    { R\"(\\\\anarchyonline\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* Borderlands                                */\n    { R\"(\\\\Borderlands\\.exe$)\", {{\n      { \"d3d9.lenientClear\",                \"True\" },\n    }} },\n    /* Borderlands 2                              *\n     * Missing lava in Vault of the Warrior       *\n     * without Strict floats                      */\n    { R\"(\\\\Borderlands2\\.exe$)\", {{\n      { \"d3d9.lenientClear\",                \"True\" },\n      { \"d3d9.supportDFFormats\",           \"False\" },\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Borderlands: The Pre-Sequel                */\n    { R\"(\\\\BorderlandsPreSequel\\.exe$)\", {{\n      { \"d3d9.lenientClear\",                \"True\" },\n      { \"d3d9.supportDFFormats\",           \"False\" },\n    }} },\n    /* Gothic 3                                   */\n    { R\"(\\\\Gothic(3|3Final| III Forsaken Gods)\\.exe$)\", {{\n      { \"d3d9.supportDFFormats\",           \"False\" },\n    }} },\n    /* Sonic Adventure 2                          */\n    { R\"(\\\\Sonic Adventure 2\\\\(launcher|sonic2app)\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* The Sims 2,                                *\n     * Body Shop,                                 *\n     * The Sims Life Stories,                     *\n     * The Sims Pet Stories,                      *\n     * and The Sims Castaway Stories              */\n    { R\"(\\\\(Sims2.*|TS2BodyShop|SimsLS|SimsPS|SimsCS)\"\n      R\"(|The Sims 2 Content Manager|TS2HomeCrafterPlus)\\.exe$)\", {{\n      { \"d3d9.customVendorId\",              \"10de\" },\n      { \"d3d9.customDeviceId\",              \"0091\" },\n      { \"d3d9.customDeviceDesc\", \"GeForce 7800 GTX\" },\n      { \"d3d9.disableA8RT\",                 \"True\" },\n      { \"d3d9.supportX4R4G4B4\",            \"False\" },\n      { \"d3d9.maxAvailableMemory\",          \"2048\" },\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Dead Space                                 *\n     * Uses the a NULL render target instead      *\n     * of a 1x1 one if DF24 is NOT supported      *\n     * Mouse and physics issues above 60 FPS      *\n     * Built-in Vsync Locks the game to 30 FPS    */\n    { R\"(\\\\Dead Space\\.exe$)\", {{\n      { \"d3d9.supportDFFormats\",           \"False\" },\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n      { \"d3d9.presentInterval\",                \"1\" },\n    }} },\n    /* Dead Space 2                               *\n     * Physics issues above 60 FPS                *\n     * Built-in Vsync Locks the game to 30 FPS    */\n    { R\"(\\\\deadspace2\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n      { \"d3d9.presentInterval\",                \"1\" },\n    }} },\n    /* Halo CE/HaloPC                             */\n    { R\"(\\\\halo(ce)?\\.exe$)\", {{\n    /* Game enables minor decal layering fixes     *\n     * specifically when it detects AMD.           *\n     * Avoids chip being detected as unsupported   *\n     * when on intel. Avoids possible path towards *\n     * invalid texture addressing methods.         */\n      { \"d3d9.customVendorId\",              \"1002\" },\n    /* Avoids card not recognized error.          *\n     * Keeps game's rendering methods             *\n     * consistent for optimal compatibility.      */\n      { \"d3d9.customDeviceId\",              \"4172\" },\n    /* The game uses incorrect sampler types in   *\n     * the shaders for glass rendering which      *\n     * breaks it on native + us if we don't       *\n     * spec-constantly chose the sampler type     *\n     * automagically.                             */\n      { \"d3d9.forceSamplerTypeSpecConstants\", \"True\" },\n    }} },\n    /* Counter Strike: Global Offensive\n       Needs NVAPI to avoid a forced AO + Smoke\n       exploit so we must force AMD vendor ID.    */\n    { R\"(\\\\csgo\\.exe$)\", {{\n      { \"d3d9.hideNvidiaGpu\",               \"True\" },\n    }} },\n    /* Vampire - The Masquerade Bloodlines        */\n    { R\"(\\\\vampire\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n      { \"d3d9.maxAvailableMemory\",          \"1024\" },\n    }} },\n    /* Senran Kagura Shinovi Versus               */\n    { R\"(\\\\SKShinoviVersus\\.exe$)\", {{\n      { \"d3d9.forceAspectRatio\",            \"16:9\" },\n    }} },\n    /* Skyrim (NVAPI)                             */\n    { R\"(\\\\TESV\\.exe$)\", {{\n      { \"d3d9.hideNvidiaGpu\",               \"True\" },\n    }} },\n    /* Hyperdimension Neptunia U: Action Unleashed */\n    { R\"(\\\\Neptunia\\.exe$)\", {{\n      { \"d3d9.forceAspectRatio\",            \"16:9\" },\n    }} },\n    /* GTA IV (NVAPI)                             *\n     * Also thinks we're always on Intel          *\n     * and will report/use bad amounts of VRAM    *\n     * if we report more than 128 MB of VRAM.     *\n     * Disabling support for DF texture formats   *\n     * makes the game use a better looking render *\n     * path for mirrors.                          *\n     * Also runs into issues after alt-tabbing.   */\n    { R\"(\\\\(GTAIV|EFLC)\\.exe$)\", {{\n      { \"d3d9.hideNvidiaGpu\",               \"True\" },\n      { \"dxgi.maxDeviceMemory\",              \"128\" },\n      { \"d3d9.supportDFFormats\",           \"False\" },\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n    }} },\n    /* Battlefield 2 & Battlefield 2142           *\n     * Bad z-pass and ingame GUI loss on alt tab  *\n     * Also hang when alt tabbing which seems     *\n     * like a game bug that d3d9 drivers work     *\n     * around.                                    */\n    { R\"(\\\\(BF2|BF2142|PRBF2)\\.exe$)\", {{\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n      { \"d3d9.countLosableResources\",      \"False\" },\n    }} },\n    /* SpellForce 2 Series                        */\n    { R\"(\\\\SpellForce2.*\\.exe$)\", {{\n      { \"d3d9.forceSamplerTypeSpecConstants\", \"True\" },\n    }} },\n    /* Tomb Raider: Legend, Anniversary, Underworld  *\n     * Read from a buffer created with:              *\n     * D3DPOOL_DEFAULT,                              *\n     * D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY buffer. *\n     * Legend flickers with next gen content option. */\n    { R\"(\\\\(trl|tra|tru)\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Everquest                                  */\n    { R\"(\\\\eqgame\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Dark Messiah of Might & Magic              */\n    { R\"(\\\\mm\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* Mafia 2                                    */\n    { R\"(\\\\mafia2\\.exe$)\", {{\n      { \"d3d9.customVendorId\",              \"10de\" },\n      { \"d3d9.customDeviceId\",              \"0402\" },\n    }} },\n    /* Warhammer: Online                          *\n     * Overly bright ground textures on Nvidia    */\n    { R\"(\\\\(WAR(-64)?|WARTEST(-64)?)\\.exe$)\", {{\n      { \"d3d9.hideNvidiaGpu\",               \"True\" },\n    }} },\n    /* Dragon Nest                                */\n    { R\"(\\\\DragonNest_x64\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* Dal Segno                                  */\n    { R\"(\\\\DST\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Kohan II                                   */\n    { R\"(\\\\k2\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* Time Leap Paradise SUPER LIVE              */\n    { R\"(\\\\tlpsl\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Ninja Gaiden Sigma 1/2                     */\n    { R\"(\\\\NINJA GAIDEN SIGMA(2)?\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Demon Stone breaks at frame rates > 60fps  */\n    { R\"(\\\\Demonstone\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Far Cry 1                                  *\n     * Has worse water rendering on AMD GPUs      */\n    { R\"(\\\\FarCry\\.exe$)\", {{\n      { \"d3d9.hideAmdGpu\",                  \"True\" },\n    }} },\n    /* Sine Mora EX                               */\n    { R\"(\\\\SineMoraEX\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Red Orchestra 2                            */\n    { R\"(\\\\ROGame\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Dark Souls II                              */\n    { R\"(\\\\DarkSoulsII\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Dogfight 1942                              */\n    { R\"(\\\\Dogfight1942\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Bayonetta                                  */\n    { R\"(\\\\Bayonetta\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Rayman Origins                             */\n    { R\"(\\\\Rayman Origins\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Guilty Gear Xrd -Relevator-                */\n    { R\"(\\\\GuiltyGearXrd\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Richard Burns Rally                        */\n    { R\"(\\\\RichardBurnsRally_SSE\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* BlazBlue Centralfiction                    */\n    { R\"(\\\\BBCF\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Limbo                                      */\n    { R\"(\\\\limbo\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Escape from Tarkov launcher                *\n     * Work around partial presentation issues    */\n    { R\"(\\\\BsgLauncher\\.exe$)\", {{\n      { \"d3d9.shaderModel\",                    \"1\" },\n    }} },\n    /* Star Wars The Force Unleashed 2            *\n     * Black particles because it tries to bind   *\n     * a 2D texture for a shader that             *\n     * declares a 3d texture.                     */\n    { R\"(\\\\SWTFU2\\.exe$)\", {{\n      { \"d3d9.forceSamplerTypeSpecConstants\", \"True\" },\n    }} },\n    /* Myst V End of Ages                         *\n     * Resolution change crash and cached         *\n     * dynamic buffers for performance reasons    */\n    { R\"(\\\\eoa\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",        \"True\" },\n      { \"d3d9.countLosableResources\",         \"False\" },\n    }} },\n    /* Supreme Commander & Forged Alliance Forever */\n    { R\"(\\\\(SupremeCommander|ForgedAlliance)\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Bionic Commando                            *\n     * Physics break at high fps                  */\n    { R\"(\\\\bionic_commando\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Beyond Good And Evil                       *\n     * UI breaks at high fps                      */\n    { R\"(\\\\BGE\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* King Of Fighters XIII                      *\n     * In-game speed increases on high FPS        */\n    { R\"(\\\\kof(xiii|13_win32_Release)\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* YS Origin                                  *\n     * Helps very bad frametimes in some areas    */\n    { R\"(\\\\yso_win\\.exe$)\", {{\n      { \"d3d9.maxFrameLatency\",                \"1\" },\n    }} },\n    /* Saints Row 2 - Prevents unmap crash        */\n    { R\"(\\\\SR2_pc\\.exe$)\", {{\n      { \"d3d9.textureMemory\",                  \"0\" },\n    }} },\n    /* The Witcher (2007)                         *\n     * Inventory hair explosion at very high fps  */\n    { R\"(\\\\witcher\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                \"-300\" },\n    }} },\n    /* Guitar Hero World Tour                     *\n     * Very prone to address space crashes        */\n    { R\"(\\\\(GHWT|GHWT_Definitive)\\.exe$)\", {{\n      { \"d3d9.textureMemory\",                 \"16\" },\n    }} },\n    /* The Ship (2004)                            */\n    { R\"(\\\\ship\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* SiN Episodes Emergence                     */\n    { R\"(\\\\SinEpisodes\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* Hammer World Editor                        */\n    { R\"(\\\\(hammer(plusplus)?|mallet|wc)\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Sonic & All-Stars Racing Transformed       *\n     * Helps performance when Resizable BAR       *\n     * is enabled                                 */\n    { R\"(\\\\ASN_App_PcDx9_Final\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Final Fantasy XIV - Direct3D 9 mode        *\n     * Can crash with unmapping                   */\n    { R\"(\\\\ffxiv\\.exe$)\", {{\n      { \"d3d9.textureMemory\",                  \"0\" },\n    }} },\n    /* Alien Rage                                 *\n     * GTX 295 & disable Hack to fix shadows      */\n    { R\"(\\\\(ShippingPC-AFEARGame|ARageMP)\\.exe$)\", {{\n      { \"d3d9.customVendorId\",              \"10de\" },\n      { \"d3d9.customDeviceId\",              \"05E0\" },\n      { \"dxgi.hideNvidiaGpu\",              \"False\" },\n    }} },\n    /* Battle Fantasia Revised Edition            *\n     * Speedup above 60fps                        */\n    { R\"(\\\\bf10\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Codename Panzers Phase One/Two             *\n     * Main menu won't render after intros        *\n     * and CPU bound performance                  */\n    { R\"(\\\\(PANZERS|PANZERS_Phase_2)\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* DC Universe Online                         *\n     * Freezes after alt tabbing                  */\n    { R\"(\\\\DCGAME\\.EXE$)\", {{\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n    }} },\n    /* Halo Online                                *\n     * Black textures                             */\n    { R\"(\\\\eldorado\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n      { \"d3d9.allowDirectBufferMapping\",   \"False\" },\n    }} },\n    /* Injustice: Gods Among Us                   *\n     * Locks a buffer that's still in use         */\n    { R\"(\\\\injustice\\.exe$)\", {{\n      { \"d3d9.allowDirectBufferMapping\",   \"False\" },\n    }} },\n    /* STEINS;GATE ELITE                          */\n    { R\"(\\\\SG_ELITE\\\\Game\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* The Incredibles                            */\n    { R\"(\\\\IncPC\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                  \"59\" },\n    }} },\n    /* Conflict Vietnam                           */\n    { R\"(\\\\Vietnam\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Project: Snowblind                         */\n    { R\"(\\\\Snowblind\\.(SP|MP|exe)$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Aviary Attorney                            */\n    { R\"(\\\\Aviary Attorney\\\\nw\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Drakensang: The Dark Eye                   */\n    { R\"(\\\\drakensang\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Age of Empires 2 - janky frame timing      */\n    { R\"(\\\\AoK HD\\.exe$)\", {{\n      { \"d3d9.maxFrameLatency\",                \"1\" },\n    }} },\n    /* Battlestations Midway                      */\n    { R\"(\\\\Battlestationsmidway\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Assassin's Creed 2                         *\n     * Helps alt tab crash on Linux               */\n    { R\"(\\\\AssassinsCreedIIGame\\.exe$)\" , {{\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n    }} },\n    /* Sonic CD                                   */\n    { R\"(\\\\soniccd\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* UK Truck Simulator 1                       */\n    { R\"(\\\\UK Truck Simulator\\\\bin\\\\win_x86\\\\game\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* d3d9 Supreme Ruler game series             *\n     * Leaks a state block leading                *\n     * to Reset calls failing                     */\n    { R\"(\\\\SupremeRuler(Ultimate|GreatWar|1936|CW)\\.exe$)\", {{\n      { \"d3d9.countLosableResources\",      \"False\" },\n    }} },\n    /* Operation Flashpoint: Red River            *\n     * Flickering issues                          */\n    { R\"(\\\\RedRiver\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Dark Void - Crashes above 60fps in places  */\n    { R\"(\\\\ShippingPC-SkyGame\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* 9th Dawn II                                *\n     * OpenGL game that also spins up d3d9        *\n     * Black screens without config               */\n    { R\"(\\\\ninthdawnii\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* Delta Force: Xtreme 1 & 2 - Performance    */\n    { R\"(\\\\(DFX|dfx2)\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Prototype                                  *\n     * Incorrect shadows on AMD & Intel.          *\n     * AA 4x can not be selected above 2GB vram   */\n    { R\"(\\\\prototypef\\.exe$)\", {{\n      { \"d3d9.hideAmdGpu\",                  \"True\" },\n      { \"dxgi.maxDeviceMemory\",             \"2047\" },\n    }} },\n    /* STAR WARS: The Force Unleashed             *\n     * Prevents black screen on each alt-tab      */\n    { R\"(\\\\SWTFU\\.exe$)\", {{\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n    }} },\n    /* Fallout New Vegas - Various visual issues  *\n     * in mod New Vegas Reloaded. Nvidia path in  *\n     * same mod use NvAPI_D3D9_StretchRectEx for  *\n     * depth buffer resolves                      */\n    { R\"(\\\\FalloutNV(Launcher)?\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n      { \"d3d9.hideNvidiaGpu\",               \"True\" },\n    }} },\n    /* Dungeons and Dragons: Dragonshard          *\n     * Massive FPS decreases in some scenes       */\n    { R\"(\\\\Dragonshard\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Battle for Middle-earth 2 and expansion    *\n     * Slowdowns in certain scenarios             */\n    { R\"(\\\\(The Battle for Middle-earth( \\(tm\\))? II( Demo)?)\"\n      R\"(|The Lord of the Rings, The Rise of the Witch-king)\\\\game\\.dat$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* WRC4 - Audio breaks above 60fps            */\n    { R\"(\\\\WRC4\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Splinter Cell Conviction                   *\n     * Unsupported GPU complaint                  */\n    { R\"(\\\\conviction_game\\.exe$)\", {{\n      { \"dxgi.customVendorId\",              \"10de\" },\n      { \"dxgi.customDeviceId\",              \"05e0\" },\n      { \"dxgi.customDeviceDesc\", \"GeForce GTX 295\" },\n    }} },\n    /* Resident Evil: Operation Raccoon City      */\n    { R\"(\\\\RaccoonCity\\.exe$)\", {{\n      { \"d3d9.textureMemory\",                  \"0\" },\n    }} },\n    /* APB: Reloaded                              *\n     * Fixes frametime jumps when shooting        */\n    { R\"(\\\\APB\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Battle Mages - helps CPU bound perf        */\n    { R\"(\\\\Battle Mages\\\\mages\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Prince of Persia (2008) - Can get stuck    *\n     * during loading at very high fps            */\n    { R\"(\\\\Prince( of Persia|OfPersia_Launcher)\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"240\" },\n    }} },\n    /* F.E.A.R 1 & expansions                     *\n     * Graphics glitches at very high fps         */\n    { R\"(\\\\FEAR(MP|XP|XP2)?\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                \"-360\" },\n    }} },\n    /* Secret World Legends - d3d9 mode only sees *\n     * 512MB vram locking higher graphics presets */\n    { R\"(\\\\SecretWorldLegends\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* Far Cry 2:                                 *\n     * Set cached dynamic buffers to True to      *\n     * improve perf on all hardware.              */\n    { R\"(\\\\(FarCry2|farcry2game)\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Dark Sector - Crashes in places            */\n    { R\"(\\\\DS\\.exe$)\", {{\n      { \"d3d9.textureMemory\",                  \"0\" },\n    }} },\n    /* Arcana Heart 3 Love Max + Xtend version    *\n     * Game speed is too fast above 60 fps        */\n    { R\"(\\\\(AH3LM|AALib)\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* May Payne 3 - Visual issues on drivers     *\n     * such as ANV (and amdvlk when set to True)  */\n    { R\"(\\\\MaxPayne3\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Star Wars Empire at War & expansion        *\n     * In base game the AA option dissapears at   *\n     * 2075MB vram and above                      */\n    { R\"(\\\\(StarWarsG|sweaw|swfoc)\\.exe$)\", {{\n      { \"d3d9.maxAvailableMemory\",          \"2048\" },\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* CivCity: Rome                              *\n     * Enables soft real-time shadows             */\n    { R\"(\\\\CivCity Rome\\.exe$)\", {{\n      { \"d3d9.customVendorId\",              \"10de\" },\n    }} },\n    /* Silent Hill 2 (2001)                       *\n     * The Enhancements mod configures the        *\n     * swapchain to only have a single backbuffer *\n     * and then calls GetFrontBufferData after    *\n     * rendering to the backbuffer. This causes   *\n     * it to get the wrong data from              *\n     * GetFrontBufferData().                      */\n    { R\"(\\\\sh2pc\\.exe$)\", {{\n      { \"d3d9.extraFrontbuffer\",            \"True\" },\n    }} },\n    /* Lego Indiana Jones: The Original Adventures *\n     * Fix UI performance                          */\n    { R\"(\\\\LEGOIndy\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Lego Batman: The Videogame                 *\n     * Fix UI performance                         */\n    { R\"((\\\\LEGOBatman|LegoBatman\\\\Game)\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Thumper - Fixes missing track              */\n    { R\"(\\\\THUMPER_dx9\\.exe$)\", {{\n      { \"d3d9.floatEmulation\",            \"Strict\" },\n    }} },\n    /* Red Orchestra: Ostfront 41-45 - Blinks,    *\n     * freeze, crash or goes Windowed on alt-tab  */\n    { R\"(\\\\RedOrchestra\\.exe$)\", {{\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n      { \"d3d9.countLosableResources\",      \"False\" },\n    }} },\n    /* Pirate Hunter - Prevents crash             */\n    { R\"(\\\\PH\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n      { \"d3d9.maxAvailableMemory\",          \"2048\" },\n    }} },\n    /* Battle Engine Aquila - Enables additional  *\n     * graphical features and Nvidia particle fog */\n    { R\"(\\\\BEA\\.exe$)\", {{\n      { \"d3d9.customVendorId\",              \"10de\" },\n      { \"d3d9.customDeviceId\",              \"0330\" },\n      { \"d3d9.customDeviceDesc\", \"NVIDIA GeForce FX 5900 Ultra\" },\n    }} },\n    /* Astebreed - Will crash on any graphical    *\n     * options or preset changes otherwise. Also, *\n     * game speed is too fast above 60 fps.       */\n    { R\"(\\\\Astebreed\\.exe$)\", {{\n      { \"d3d9.countLosableResources\",      \"False\" },\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Dungeon Lords - Crash when saving game     */\n    { R\"(\\\\(DLSteamEdition|dlords)\\.exe$)\", {{\n      { \"d3d9.textureMemory\",                  \"0\" },\n    }} },\n    /* Test Drive Unlimited 2                     *\n     * Loss of input on alt-tab                   */\n    { R\"(\\\\TestDrive2\\.exe$)\", {{\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n    }} },\n    /* Perilous Warp - Nvidia path depends on     *\n     * unimplemented NvAPI_D3D9_StretchRectEx.    *\n     * Without it screen effects such as blood    *\n     * splatter are bugged.                       */\n    { R\"(\\\\Perilous Warp\\\\system(64)?\\\\game\\.exe$)\", {{\n      { \"d3d9.hideNvidiaGpu\",               \"True\" },\n    }} },\n    /* AquaNox 2: Does not start if too many      *\n     * modes/resolutions are advertised           */\n    { R\"(\\\\AN2\\.dat$)\", {{\n      { \"d3d9.modeCountCompatibility\",      \"True\" },\n    }} },\n    /* Psi-Ops: The Mindgate Conspiracy           *\n     * Broken input and physics above 60 fps      */\n    { R\"(\\\\PsiOps\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Alone in the Dark (2008)                   *\n     * Crashes when selecting the graphics menu   *\n     * option without memory tracking in place    */\n    { R\"(\\\\Alone\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* Scarface - Rendering issues when the       *\n     * D3DLOCK_DISCARD flag is respected          */\n    { R\"(\\\\Scarface\\.exe$)\", {{\n      { \"d3d9.allowDiscard\",               \"False\" },\n    }} },\n    /* Heroes of Annihilated Empires              *\n     * Cursor and other animations play back too  *\n     * fast without a frame cap in place.         */\n    { R\"(\\\\Heroes of Annihilated Empires.*\\\\engine\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* RaceRoom Racing Experience                 *\n     * Game depends on NvAPI_D3D9_StretchRectEx   */\n    { R\"(\\\\RRRE(64)?\\.exe$)\", {{\n      { \"d3d9.hideNvidiaGpu\",               \"True\" },\n    }} },\n    /* Blitzkrieg 2 - Startup crash               */\n    { R\"(\\\\Blitzkrieg 2( - Fall of the Reich| - Liberation)?\\\\bin\\\\game\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n    }} },\n    /* Core Awaken series (Jilelen and LittleSnow/The Yuka) & *\n     * Mine Dungeon 2 ~Rurumu's trip~                         *\n     * Freezes on boot                                        */\n    { R\"(\\\\(BioDungeon|BioLab2|MineDungeon2)\\.exe$)\", {{\n      { \"d3d9.countLosableResources\",      \"False\" },\n    }} },\n    /* Ridge Racer Unbounded - Vertex explosions  */\n    { R\"(\\\\RRU(_demo)?\\.exe$)\", {{\n      { \"dxvk.zeroMappedMemory\",            \"True\" },\n    }} },\n    /* Sims 3                                     *\n     * Worse shadow quality on unknown AMD cards  */\n    { R\"(\\\\TS3(W)?\\.exe$)\", {{\n      { \"d3d9.customVendorId\",              \"10de\" },\n      { \"d3d9.customDeviceId\",              \"1080\" },\n      { \"d3d9.customDeviceDesc\", \"Geforce GTX 580\" },\n    }} },\n    /* Sid Meier's Pirates!: Live the Life        *\n     * Fixes a crash on resolution change         */\n    { R\"(\\\\Pirates!\\.exe$)\", {{\n      { \"d3d9.countLosableResources\",      \"False\" },\n    }} },\n    /* Warhammer 40,000: Dawn of War DE           *\n     * Fixes occasional vertex explosions         */\n    { R\"(\\\\W40k(_gog)?\\.exe$)\", {{\n      { \"dxvk.zeroMappedMemory\",            \"True\" },\n    }} },\n\n    /**********************************************/\n    /* D3D8 GAMES                                 */\n    /**********************************************/\n\n    /* Duke Nukem Forever (2001)                  */\n    { R\"(\\\\DukeForever\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Anito: Defend a Land Enraged               */\n    { R\"(\\\\Anito\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n      { \"d3d9.maxAvailableMemory\",          \"1024\" },\n    }} },\n    /* Motor City Online                          */\n    { R\"(\\\\MCity_d\\.exe$)\", {{\n      { \"d3d8.batching\",                    \"True\" },\n    }} },\n    /* Railroad Tycoon 3                          */\n    { R\"(\\\\RT3\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Pure Pinball 2.0 REDUX                     *\n     * This game reads from undeclared vs inputs  *\n     * but somehow works on native. Let's just    *\n     * change its declaration to make them work.  */\n    { R\"(\\\\Pure Pinball 2\\.0 REDUX\\.exe$)\", {{\n      { \"d3d8.forceVsDecl\",  \"0:2,4:2,7:4,9:1,8:1\" },\n    }} },\n    /* Need for Speed III: Hot Pursuit            *\n     * (with the \"Modern Patch\")                  */\n    { R\"(\\\\nfs3\\.exe$)\", {{\n      { \"d3d8.batching\",                    \"True\" },\n    }} },\n    /* Need for Speed: High Stakes / Road         *\n     * Challenge (with the \"Modern Patch\") -      *\n     * Won't actually render anything in game     *\n     * without a memory limit in place            */\n    { R\"(\\\\nfs4\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n      { \"d3d9.maxAvailableMemory\",          \"1024\" },\n      { \"d3d8.batching\",                    \"True\" },\n    }} },\n    /* Need for Speed: Hot Pursuit 2              */\n    { R\"(\\\\NFSHP2\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Project I.G.I. 2: Covert Strike            *\n     * Very stuttery frametime with own framecap  */\n    { R\"(\\\\igi2\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Treasure Planet: Battle at Procyon         *\n     * Declares v5 as color but shader uses v6    */\n    { R\"(\\\\TP_Win32\\.exe$)\", {{\n      { \"d3d8.forceVsDecl\",      \"0:2,3:2,6:4,7:1\" },\n    }} },\n    /* Scrapland (Remastered)                     */\n    { R\"(\\\\Scrap\\.exe$)\", {{\n      { \"d3d9.deferSurfaceCreation\",        \"True\" },\n    }} },\n    /* V-Rally 3                                  */\n    { R\"(\\\\VRally3(Demo)?\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Soldiers: Heroes Of World War II           *\n     * Fills up all available memory and hangs    *\n     * while loading the main menu otherwise      */\n    { R\"(\\\\Soldiers\\.exe$)\", {{\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n      { \"d3d9.maxAvailableMemory\",           \"512\" },\n    }} },\n    /* Cossacks II: Napoleonic Wars &             *\n     * Battle for Europe                          */\n    { R\"(\\\\Cossacks II.*\\\\engine\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Alexander                                  */\n    { R\"(\\\\Alexander\\\\Data\\\\engine\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* 3DMark2001 (SE)                            *\n     * Fixes a drastic performance drop in the    *\n     * \"Car Chase - High Detail\" benchmark        */\n    { R\"(\\\\3DMark2001(SE)?\\.exe$)\", {{\n      { \"d3d9.allowDirectBufferMapping\",   \"False\" },\n    }} },\n    /* Delta Force: Black Hawk Down               */\n    { R\"(\\\\dfbhd\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* X2: The Threat                             */\n    { R\"(\\\\X2\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* The Lord of the Rings:                     *\n     * The Fellowship of the Ring                 */\n    { R\"(\\\\Fellowship\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n      { \"d3d8.placeP8InScratch\",            \"True\" },\n    }} },\n    /* Inquisitor (2009)                          *\n     * Leaks a resource when alt-tabbing          */\n    { R\"(\\\\Inquisitor\\.exe$)\", {{\n      { \"d3d9.countLosableResources\",      \"False\" },\n    }} },\n    /* Art of Murder FBI Confidential - CPU perf  */\n    { R\"(\\\\Art of Murder - FBI Confidential\\\\game\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Max Payne 1 - Stalls waiting for an index buffer */\n    { R\"(\\\\MaxPayne\\.exe$)\", {{\n      { \"d3d9.allowDirectBufferMapping\",   \"False\" },\n    }} },\n    /* FIFA Football 2003                         */\n    { R\"(\\\\fifa2003(demo)?\\.exe$)\", {{\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Splinter Cell: Pandora Tomorrow (Retail)   *\n     * Missing shadows without dref scaling and   *\n     * broken inputs and physics above 60 FPS     */\n    { R\"(\\\\offline\\\\system\\\\SplinterCell2\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n      { \"d3d8.scaleDref\",                     \"24\" },\n    }} },\n    /* Splinter Cell: Pandora Tomorrow (Steam)    *\n     * Broken inputs and physics above 60 FPS     */\n    { R\"(\\\\Splinter Cell Pandora Tomorrow\\\\system\\\\SplinterCell2\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Chrome: Gold Edition                       *\n     * Broken character model motion at high FPS  */\n    { R\"(\\\\Chrome(Single|Net)\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n    }} },\n    /* Rayman 3: Hoodlum Havoc                    *\n     * Missing geometry and textures without      *\n     * legacy DISCARD behavior                    */\n    { R\"(\\\\Rayman3\\.exe$)\", {{\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n      { \"d3d8.forceLegacyDiscard\",          \"True\" },\n    }} },\n    /* Tom Clancy's Splinter Cell                 *\n     * Fixes shadow buffers, broken physics       *\n     * above 60 FPS and game freezing on alt-tab  */\n    { R\"(\\\\splintercell\\.exe$)\", {{\n      { \"d3d9.hideAmdGpu\",                  \"True\" },\n      { \"d3d9.maxFrameRate\",                 \"-60\" },\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n      { \"d3d8.scaleDref\",                     \"24\" },\n      { \"d3d8.shadowPerspectiveDivide\",     \"True\" },\n    }} },\n    /* Trainz v1.3 (2001)                         *\n     * Fixes black screen after alt-tab           */\n    { R\"(\\\\bin\\\\trainz\\.exe$)\", {{\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n    }} },\n    /* A.I.M.: Artificial Intelligence Machine    *\n     * Fixes black screen after the options       *\n     * window is closed or on alt-tab             */\n    { R\"(\\\\AIM\\.exe$)\", {{\n      { \"d3d9.deviceLossOnFocusLoss\",       \"True\" },\n    }} },\n    /* Star Trek: Starfleet Command III           *\n     * The GOG release ships with a D3D8 to D3D9  *\n     * wrapper that leaks several surfaces.       */\n    { R\"(\\\\SFC3\\.exe$)\", {{\n      { \"d3d9.countLosableResources\",      \"False\" },\n    }} },\n    /* GTR - FIA GT Racing Game                   *\n     * Vram complaint & restricted resolutions    *\n     * Performance                                */\n    { R\"(\\\\GTR (- FIA GT Rac(e)?ing Game|Demo)\\\\(GTR(Demo)?|(3D)?Config)\\.exe$)\", {{\n      { \"d3d9.maxAvailableMemory\",          \"1024\" },\n      { \"d3d9.memoryTrackTest\",             \"True\" },\n      { \"d3d9.cachedWriteOnlyBuffers\",      \"True\" },\n    }} },\n    /* Comanche 4 - Only enables the FSAA option  *\n     * if it detects a device ID of 0x025x.       */\n     { R\"(\\\\c4(lan)?\\.exe$)\", {{\n      { \"d3d9.customVendorId\",              \"10de\" },\n      { \"d3d9.customDeviceId\",              \"0250\" },\n      { \"d3d9.customDeviceDesc\", \"NVIDIA GeForce4 Ti 4600\" },\n    }} },\n    /* AquaNox: Does not start if too many        *\n     * modes/resolutions are advertised           */\n    { R\"(\\\\Aqua\\.exe$)\", {{\n      { \"d3d9.modeCountCompatibility\",      \"True\" },\n    }} },\n    /* Top Spin (2005)                            *\n     * Missing geometry and textures without      *\n     * legacy DISCARD behavior                    */\n    { R\"(\\\\TopSpin\\.exe$)\", {{\n      { \"d3d8.forceLegacyDiscard\",          \"True\" },\n    }} },\n    /* Lego Racers 2 - Hits an incredible amount  *\n     * of queue syncs with direct buffer mapping  */\n    { R\"(\\\\LEGO Racers 2\\.exe$)\", {{\n      { \"d3d9.allowDirectBufferMapping\",   \"False\" },\n    }} },\n    /* Smash Up Derby - Poor performance on Intel *\n     * due to queue syncs on certain race tracks  */\n    { R\"(\\\\Smash up Derby\\\\cars\\.exe$)\", {{\n      { \"d3d9.allowDirectBufferMapping\",   \"False\" },\n    }} },\n  };\n\n\n  const static ProfileList g_deckProfiles = {\n    /* Fallout 4: Defaults to 45 FPS on OLED, but also breaks above 60 FPS */\n    { R\"(\\\\Fallout4\\.exe$)\", {{\n      { \"dxgi.syncInterval\",                   \"1\" },\n      { \"dxgi.maxFrameRate\",                 \"-60\" },\n    }} },\n  };\n\n\n  const static ProfileList g_hashedProfiles = {\n    /* Nothing to see here */\n  };\n\n\n  const Config* findProfile(const ProfileList& profiles, const std::string& appName) {\n    auto appConfig = std::find_if(profiles.begin(), profiles.end(),\n      [&appName] (const std::pair<const char*, Config>& pair) {\n        // With certain locales, regex parsing will simply crash. Using regex::imbue\n        // does not resolve this; only the global locale seems to matter here. Catch\n        // bad_alloc errors to work around this for now.\n        try {\n          std::regex expr(pair.first, std::regex::extended | std::regex::icase);\n          return std::regex_search(appName, expr);\n        } catch (const std::bad_alloc& e) {\n          Logger::err(str::format(\"Failed to parse regular expression: \", pair.first));\n          return false;\n        }\n      });\n\n    return appConfig != profiles.end()\n      ? &appConfig->second\n      : nullptr;\n  }\n\n\n  const Config* findHashedProfile(const ProfileList& profiles, const std::string& appName) {\n    // Don't bother hashing exe names if we don't have\n    // any top-secret app profiles to begin with\n    if (profiles.empty())\n      return nullptr;\n\n    auto n = appName.find_last_of('\\\\') + 1u;\n\n    if (n >= appName.size())\n      return nullptr;\n\n    auto hash = Sha1Hash::compute(&appName[n], appName.size() - n).toString();\n\n    auto appConfig = std::find_if(profiles.begin(), profiles.end(),\n      [&hash] (const std::pair<const char*, Config>& pair) {\n        return hash == pair.first;\n      });\n\n    return appConfig != profiles.end()\n      ? &appConfig->second\n      : nullptr;\n  }\n\n\n  static bool isWhitespace(char ch) {\n    return ch == ' ' || ch == '\\x9' || ch == '\\r';\n  }\n\n\n  static bool isValidKeyChar(char ch) {\n    return (ch >= '0' && ch <= '9')\n        || (ch >= 'A' && ch <= 'Z')\n        || (ch >= 'a' && ch <= 'z')\n        || (ch == '.' || ch == '_');\n  }\n\n\n  static size_t skipWhitespace(const std::string& line, size_t n) {\n    while (n < line.size() && isWhitespace(line[n]))\n      n += 1;\n    return n;\n  }\n\n\n  struct ConfigContext {\n    bool active;\n  };\n\n\n  static void parseUserConfigLine(Config& config, ConfigContext& ctx, const std::string& line) {\n    std::stringstream key;\n    std::stringstream value;\n\n    // Extract the key\n    size_t n = skipWhitespace(line, 0);\n\n    if (n < line.size() && line[n] == '[') {\n      n += 1;\n\n      size_t e = line.size() - 1;\n      while (e > n && line[e] != ']')\n        e -= 1;\n\n      while (n < e)\n        key << line[n++];\n\n      ctx.active = key.str() == env::getExeName();\n    } else {\n      while (n < line.size() && isValidKeyChar(line[n]))\n        key << line[n++];\n\n      // Check whether the next char is a '='\n      n = skipWhitespace(line, n);\n      if (n >= line.size() || line[n] != '=')\n        return;\n\n      // Extract the value\n      bool insideString = false;\n      n = skipWhitespace(line, n + 1);\n\n      while (n < line.size()) {\n        if (!insideString && isWhitespace(line[n]))\n          break;\n\n        if (line[n] == '\"') {\n          insideString = !insideString;\n          n++;\n        } else\n          value << line[n++];\n      }\n\n      if (ctx.active)\n        config.setOption(key.str(), value.str());\n    }\n  }\n\n\n  Config::Config() { }\n  Config::~Config() { }\n\n\n  Config::Config(OptionMap&& options)\n  : m_options(std::move(options)) { }\n\n\n  void Config::merge(const Config& other) {\n    for (auto& pair : other.m_options)\n      m_options.insert(pair);\n  }\n\n\n  void Config::setOption(const std::string& key, const std::string& value) {\n    m_options.insert_or_assign(key, value);\n  }\n\n\n  std::string Config::getOptionValue(const char* option) const {\n    auto iter = m_options.find(option);\n\n    return iter != m_options.end()\n      ? iter->second : std::string();\n  }\n\n\n  bool Config::parseOptionValue(\n    const std::string&  value,\n          std::string&  result) {\n    result = value;\n    return true;\n  }\n\n\n  bool Config::parseOptionValue(\n    const std::string&  value,\n          bool&         result) {\n    static const std::array<std::pair<const char*, bool>, 2> s_lookup = {{\n      { \"true\",  true  },\n      { \"false\", false },\n    }};\n\n    return parseStringOption(value,\n      s_lookup.begin(), s_lookup.end(), result);\n  }\n\n\n  bool Config::parseOptionValue(\n    const std::string&  value,\n          int32_t&      result) {\n    if (value.size() == 0)\n      return false;\n\n    // Parse sign, don't allow '+'\n    int32_t sign = 1;\n    size_t start = 0;\n\n    if (value[0] == '-') {\n      sign = -1;\n      start = 1;\n    }\n\n    // Parse absolute number\n    int32_t intval = 0;\n\n    for (size_t i = start; i < value.size(); i++) {\n      if (value[i] < '0' || value[i] > '9')\n        return false;\n\n      intval *= 10;\n      intval += value[i] - '0';\n    }\n\n    // Apply sign and return\n    result = sign * intval;\n    return true;\n  }\n\n\n  bool Config::parseOptionValue(\n    const std::string&  value,\n          float&        result) {\n    if (value.size() == 0)\n      return false;\n\n    // Parse sign\n    size_t pos = 0;\n    bool negate = false;\n\n    if (value[0] == '-') {\n      negate = true;\n\n      if (++pos == value.size())\n        return false;\n    }\n\n    // Parse integer part\n    uint64_t intPart = 0;\n\n    if (value[pos] == '.')\n      return false;\n\n    while (pos < value.size()) {\n      if (value[pos] == '.') {\n        if (++pos == value.size())\n          return false;\n        break;\n      }\n\n      if (value[pos] < '0' || value[pos] > '9')\n        return false;\n\n      intPart *= 10;\n      intPart += value[pos] - '0';\n      pos += 1;\n    }\n\n    // Parse fractional part\n    uint64_t fractPart = 0;\n    uint64_t fractDivisor = 1;\n\n    while (pos < value.size()) {\n      if (value[pos] < '0' || value[pos] > '9')\n        return false;\n\n      fractDivisor *= 10;\n      fractPart *= 10;\n      fractPart += value[pos] - '0';\n      pos += 1;\n    }\n\n    // Compute final number, not super accurate but this should do\n    result = float((double(fractPart) / double(fractDivisor)) + double(intPart));\n\n    if (negate)\n      result = -result;\n\n    return std::isfinite(result);\n  }\n\n\n  bool Config::parseOptionValue(\n    const std::string&  value,\n          Tristate&     result) {\n    static const std::array<std::pair<const char*, Tristate>, 3> s_lookup = {{\n      { \"true\",  Tristate::True  },\n      { \"false\", Tristate::False },\n      { \"auto\",  Tristate::Auto  },\n    }};\n\n    return parseStringOption(value,\n      s_lookup.begin(), s_lookup.end(), result);\n  }\n\n\n  template<typename I, typename V>\n  bool Config::parseStringOption(\n          std::string   str,\n          I             begin,\n          I             end,\n          V&            value) {\n    str = Config::toLower(str);\n\n    for (auto i = begin; i != end; i++) {\n      if (str == i->first) {\n        value = i->second;\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n\n  Config Config::getAppConfig(const std::string& appName) {\n    const Config* config = nullptr;\n\n    if (env::getEnvVar(\"SteamDeck\") == \"1\")\n      config = findProfile(g_deckProfiles, appName);\n\n    if (!config)\n      config = findProfile(g_profiles, appName);\n\n    if (!config)\n      config = findHashedProfile(g_hashedProfiles, appName);\n\n    if (config) {\n      // Inform the user that we loaded a default config\n      Logger::info(str::format(\"Found built-in config:\"));\n\n      for (auto& pair : config->m_options)\n        Logger::info(str::format(\"  \", pair.first, \" = \", pair.second));\n\n      return *config;\n    }\n\n    return Config();\n  }\n\n\n  Config Config::getUserConfig() {\n    Config config;\n\n    // Load either $DXVK_CONFIG_FILE or $PWD/dxvk.conf\n    std::string filePath = env::getEnvVar(\"DXVK_CONFIG_FILE\");\n    std::string confLine = env::getEnvVar(\"DXVK_CONFIG\");\n\n    if (filePath == \"\")\n      filePath = \"dxvk.conf\";\n\n    // Open the file if it exists\n    std::ifstream stream(str::topath(filePath.c_str()).c_str());\n\n    if (!stream && confLine.empty())\n      return config;\n\n    // Initialize parser context\n    ConfigContext ctx;\n    ctx.active = true;\n\n    if (stream) {\n      // Inform the user that we loaded a file, might\n      // help when debugging configuration issues\n      Logger::info(str::format(\"Found config file: \", filePath));\n\n      // Parse the file line by line\n      std::string line;\n\n      while (std::getline(stream, line))\n        parseUserConfigLine(config, ctx, line);\n    }\n\n    if (!confLine.empty()) {\n      ctx.active = true;\n\n      // Inform the user that we parsing config from environment, might\n      // help when debugging configuration issues\n      Logger::info(str::format(\"Found config env: \", confLine));\n\n      for(auto l : str::split(confLine, \";\"))\n        parseUserConfigLine(config, ctx, std::string(l.data(), l.size()));\n    }\n\n    return config;\n  }\n\n\n  void Config::logOptions() const {\n    if (!m_options.empty()) {\n      Logger::info(\"Effective configuration:\");\n\n      for (auto& pair : m_options)\n        Logger::info(str::format(\"  \", pair.first, \" = \", pair.second));\n    }\n  }\n\n  std::string Config::toLower(std::string str) {\n    std::transform(str.begin(), str.end(), str.begin(),\n      [] (unsigned char c) { return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c; });\n    return str;\n  }\n\n}\n"
  },
  {
    "path": "src/util/config/config.h",
    "content": "#pragma once\n\n#include <cstdint>\n#include <string>\n#include <unordered_map>\n\nnamespace dxvk {\n\n  /**\n   * \\brief Tri-state\n   * \n   * Used to conditionally override\n   * booleans if desired.\n   */\n  enum class Tristate : int32_t {\n    Auto  = -1,\n    False =  0,\n    True  =  1,\n  };\n\n  /**\n   * \\brief Config option set\n   * \n   * Stores configuration options\n   * as a set of key-value pairs.\n   */\n  class Config {\n    using OptionMap = std::unordered_map<std::string, std::string>;\n  public:\n\n    Config();\n    Config(OptionMap&& options);\n    ~Config();\n\n    /**\n     * \\brief Merges two configuration sets\n     * \n     * Options specified in this config object will\n     * not be overridden if they are specified in\n     * the second config object.\n     * \\param [in] other Config set to merge.\n     */\n    void merge(const Config& other);\n\n    /**\n     * \\brief Sets an option\n     * \n     * \\param [in] key Option name\n     * \\param [in] value Option value\n     */\n    void setOption(\n      const std::string& key,\n      const std::string& value);\n\n    /**\n     * \\brief Parses an option value\n     *\n     * Retrieves the option value as a string, and then\n     * tries to convert that string to the given type.\n     * If parsing the string fails because it is either\n     * invalid or if the option is not defined, this\n     * method will return a fallback value.\n     * \n     * Currently, this supports the types \\c bool,\n     * \\c int32_t, and \\c std::string.\n     * \\tparam T Return value type\n     * \\param [in] option Option name\n     * \\param [in] fallback Fallback value\n     * \\returns Parsed option value\n     * \\returns The parsed option value\n     */\n    template<typename T>\n    T getOption(const char* option, T fallback = T()) const {\n      const std::string& value = getOptionValue(option);\n\n      T result = fallback;\n      parseOptionValue(value, result);\n      return result;\n    }\n\n    /**\n     * \\brief Logs option values\n     * \n     * Prints the effective configuration\n     * to the log for debugging purposes. \n     */\n    void logOptions() const;\n\n    /**\n     * \\brief Retrieves default options for an app\n     * \n     * \\param [in] appName Name of the application\n     * \\returns Default options for the application\n     */\n    static Config getAppConfig(const std::string& appName);\n\n    /**\n     * \\brief Retrieves user configuration\n     * \n     * Reads options from the configuration file,\n     * if it can be found, or an empty option set.\n     * \\returns User-defined configuration options\n     */\n    static Config getUserConfig();\n\n    static std::string toLower(std::string str);\n\n  private:\n\n    OptionMap m_options;\n\n    std::string getOptionValue(\n      const char*         option) const;\n\n    static bool parseOptionValue(\n      const std::string&  value,\n            std::string&  result);\n\n    static bool parseOptionValue(\n      const std::string&  value,\n            bool&         result);\n\n    static bool parseOptionValue(\n      const std::string&  value,\n            int32_t&      result);\n    \n    static bool parseOptionValue(\n      const std::string&  value,\n            float&        result);\n\n    static bool parseOptionValue(\n      const std::string&  value,\n            Tristate&     result);\n\n    template<typename I, typename V>\n    static bool parseStringOption(\n            std::string   str,\n            I             begin,\n            I             end,\n            V&            value);\n\n  };\n\n\n  /**\n   * \\brief Applies tristate option\n   * \n   * Overrides the given value if \\c state is\n   * \\c True or \\c False, and leaves it intact\n   * otherwise.\n   * \\param [out] option The value to override\n   * \\param [in] state Tristate to apply\n   */\n  inline void applyTristate(bool& option, Tristate state) {\n    option &= state != Tristate::False;\n    option |= state == Tristate::True;\n  }\n\n}\n"
  },
  {
    "path": "src/util/log/log.cpp",
    "content": "#include <utility>\n\n#include \"log.h\"\n\n#include \"../util_env.h\"\n\nnamespace dxvk {\n  \n  Logger::Logger(const std::string& fileName)\n  : m_minLevel(getMinLogLevel()), m_fileName(fileName) {\n\n  }\n  \n  \n  Logger::~Logger() { }\n  \n  \n  void Logger::trace(const std::string& message) {\n    s_instance.emitMsg(LogLevel::Trace, message);\n  }\n  \n  \n  void Logger::debug(const std::string& message) {\n    s_instance.emitMsg(LogLevel::Debug, message);\n  }\n  \n  \n  void Logger::info(const std::string& message) {\n    s_instance.emitMsg(LogLevel::Info, message);\n  }\n  \n  \n  void Logger::warn(const std::string& message) {\n    s_instance.emitMsg(LogLevel::Warn, message);\n  }\n  \n  \n  void Logger::err(const std::string& message) {\n    s_instance.emitMsg(LogLevel::Error, message);\n  }\n  \n  \n  void Logger::log(LogLevel level, const std::string& message) {\n    s_instance.emitMsg(level, message);\n  }\n  \n  \n  void Logger::emitMsg(LogLevel level, const std::string& message) {\n    if (level >= m_minLevel) {\n      std::lock_guard<dxvk::mutex> lock(m_mutex);\n      \n      static std::array<const char*, 5> s_prefixes\n        = {{ \"trace: \", \"debug: \", \"info:  \", \"warn:  \", \"err:   \" }};\n      \n      const char* prefix = s_prefixes.at(static_cast<uint32_t>(level));\n\n      if (!std::exchange(m_initialized, true)) {\n#ifdef _WIN32\n        HMODULE ntdll = GetModuleHandleA(\"ntdll.dll\");\n\n        if (ntdll)\n          m_wineLogOutput = reinterpret_cast<PFN_wineLogOutput>(GetProcAddress(ntdll, \"__wine_dbg_output\"));\n#endif\n        auto path = getFileName(m_fileName);\n\n        if (!path.empty())\n          m_fileStream = std::ofstream(str::topath(path.c_str()).c_str());\n      }\n\n      std::stringstream stream(message);\n      std::string line;\n\n      while (std::getline(stream, line, '\\n')) {\n        std::stringstream outstream;\n        outstream << prefix << line << std::endl;\n\n        std::string adjusted = outstream.str();\n\n        if (!adjusted.empty()) {\n#ifdef _WIN32\n          if (m_wineLogOutput) {\n            // __wine_dbg_output tries to buffer lines up to 1020 characters\n            // including null terminator, and will cause a hang if we submit\n            // anything longer than that even in consecutive calls. Work\n            // around this by splitting long lines into multiple lines.\n            constexpr size_t MaxDebugBufferLength = 1018;\n\n            if (adjusted.size() <= MaxDebugBufferLength) {\n              m_wineLogOutput(adjusted.c_str());\n            } else {\n              std::array<char, MaxDebugBufferLength + 2u> buffer;\n\n              for (size_t i = 0; i < adjusted.size(); i += MaxDebugBufferLength) {\n                size_t size = std::min(adjusted.size() - i, MaxDebugBufferLength);\n\n                std::strncpy(buffer.data(), &adjusted[i], size);\n                if (buffer[size - 1u] != '\\n')\n                  buffer[size++] = '\\n';\n\n                buffer[size] = '\\0';\n                m_wineLogOutput(buffer.data());\n              }\n            }\n          }\n\n          // Don't log anything to stderr if we're not on wine. Usually games are\n          // compiled as gui apps anyway, and emitting anything to the standard\n          // output streams can crash certain games.\n#else\n          // For native builds, logging to stderr should be fine.\n          std::cerr << adjusted;\n#endif\n        }\n\n        if (m_fileStream) {\n          m_fileStream << adjusted;\n          m_fileStream.flush();\n        }\n      }\n    }\n  }\n  \n  \n  std::string Logger::getFileName(const std::string& base) {\n    std::string path = env::getEnvVar(\"DXVK_LOG_PATH\");\n    \n    if (path == \"none\")\n      return std::string();\n\n#ifdef _WIN32\n    // Don't create a log file if we're writing to wine's console output\n    if (path.empty() && m_wineLogOutput)\n      return std::string();\n#endif\n\n    if (!path.empty() && *path.rbegin() != '/')\n      path += '/';\n\n    std::string exeName = env::getExeBaseName();\n    path += exeName + \"_\" + base;\n    return path;\n  }\n\n\n  LogLevel Logger::getMinLogLevel() {\n    const std::array<std::pair<const char*, LogLevel>, 6> logLevels = {{\n      { \"trace\", LogLevel::Trace },\n      { \"debug\", LogLevel::Debug },\n      { \"info\",  LogLevel::Info  },\n      { \"warn\",  LogLevel::Warn  },\n      { \"error\", LogLevel::Error },\n      { \"none\",  LogLevel::None  },\n    }};\n    \n    const std::string logLevelStr = env::getEnvVar(\"DXVK_LOG_LEVEL\");\n    \n    for (const auto& pair : logLevels) {\n      if (logLevelStr == pair.first)\n        return pair.second;\n    }\n    \n    return LogLevel::Info;\n  }\n  \n}\n"
  },
  {
    "path": "src/util/log/log.h",
    "content": "#pragma once\n\n#include <array>\n#include <fstream>\n#include <iostream>\n#include <string>\n\n#include \"../thread.h\"\n\nnamespace dxvk {\n  \n  enum class LogLevel : uint32_t {\n    Trace = 0,\n    Debug = 1,\n    Info  = 2,\n    Warn  = 3,\n    Error = 4,\n    None  = 5,\n  };\n  \n#ifdef _WIN32\n  using PFN_wineLogOutput = int (__cdecl *)(const char *);\n#endif\n\n  /**\n   * \\brief Logger\n   * \n   * Logger for one DLL. Creates a text file and\n   * writes all log messages to that file.\n   */\n  class Logger {\n    \n  public:\n    \n    Logger(const std::string& file_name);\n    ~Logger();\n    \n    static void trace(const std::string& message);\n    static void debug(const std::string& message);\n    static void info (const std::string& message);\n    static void warn (const std::string& message);\n    static void err  (const std::string& message);\n    static void log  (LogLevel level, const std::string& message);\n    \n    static LogLevel logLevel() {\n      return s_instance.m_minLevel;\n    }\n    \n  private:\n    \n    static Logger     s_instance;\n    \n    const LogLevel    m_minLevel;\n    const std::string m_fileName;\n    \n    dxvk::mutex       m_mutex;\n    std::ofstream     m_fileStream;\n\n    bool              m_initialized = false;\n#ifdef _WIN32\n    PFN_wineLogOutput m_wineLogOutput = nullptr;\n#endif\n\n    void emitMsg(LogLevel level, const std::string& message);\n    \n    std::string getFileName(\n      const std::string& base);\n\n    static LogLevel getMinLogLevel();\n\n  };\n  \n}\n"
  },
  {
    "path": "src/util/log/log_debug.cpp",
    "content": "#include \"log_debug.h\"\n\nnamespace dxvk::debug {\n  \n  std::string methodName(const std::string& prettyName) {\n    size_t end = prettyName.find(\"(\");\n    size_t begin = prettyName.substr(0, end).rfind(\" \") + 1;\n    return prettyName.substr(begin,end - begin);\n  }\n  \n}\n"
  },
  {
    "path": "src/util/log/log_debug.h",
    "content": "#pragma once\n\n#include <sstream>\n\n#include \"log.h\"\n\n#ifdef _MSC_VER\n#define METHOD_NAME __FUNCSIG__\n#else\n#define METHOD_NAME __PRETTY_FUNCTION__\n#endif\n\n#define TRACE_ENABLED\n\n#ifdef TRACE_ENABLED\n#define TRACE(...) \\\n  do { dxvk::debug::trace(METHOD_NAME, ##__VA_ARGS__); } while (0)\n#else\n#define TRACE(...) \\\n  do { } while (0)\n#endif\n\nnamespace dxvk::debug {\n  \n  std::string methodName(const std::string& prettyName);\n  \n  inline void traceArgs(std::stringstream& stream) { }\n    \n  template<typename Arg1>\n  void traceArgs(std::stringstream& stream, const Arg1& arg1) {\n    stream << arg1;\n  }\n  \n  template<typename Arg1, typename Arg2, typename... Args>\n  void traceArgs(std::stringstream& stream, const Arg1& arg1, const Arg2& arg2, const Args&... args) {\n    stream << arg1 << \",\";\n    traceArgs(stream, arg2, args...);\n  }\n  \n  template<typename... Args>\n  void trace(const std::string& funcName, const Args&... args) {\n    std::stringstream stream;\n    stream << methodName(funcName) << \"(\";\n    traceArgs(stream, args...);\n    stream << \")\";\n    Logger::trace(stream.str());\n  }\n  \n}\n"
  },
  {
    "path": "src/util/meson.build",
    "content": "util_src = files([\n  'util_env.cpp',\n  'util_string.cpp',\n  'util_fps_limiter.cpp',\n  'util_file.cpp',\n  'util_flush.cpp',\n  'util_gdi.cpp',\n  'util_luid.cpp',\n  'util_matrix.cpp',\n  'util_shared_res.cpp',\n  'util_sleep.cpp',\n\n  'thread.cpp',\n\n  'com/com_destruction_notifier.cpp',\n  'com/com_guid.cpp',\n  'com/com_private_data.cpp',\n\n  'config/config.cpp',\n\n  'log/log.cpp',\n  'log/log_debug.cpp',\n\n  'sha1/sha1.c',\n  'sha1/sha1_util.cpp',\n\n  'sync/sync_recursive.cpp',\n])\n\nutil_lib = static_library('util', util_src,\n  include_directories : [ dxvk_include_path ],\n)\n\nutil_dep = declare_dependency(\n  link_with           : [ util_lib ],\n)\n"
  },
  {
    "path": "src/util/rc/util_rc.h",
    "content": "#pragma once\n\n#include <atomic>\n\n#include \"../util_likely.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Reference-counted object\n   */\n  class RcObject {\n    \n  public:\n    \n    /**\n     * \\brief Increments reference count\n     * \\returns New reference count\n     */\n    force_inline uint32_t incRef() {\n      return ++m_refCount;\n    }\n    \n    /**\n     * \\brief Decrements reference count\n     * \\returns New reference count\n     */\n    force_inline uint32_t decRef() {\n      return --m_refCount;\n    }\n    \n  private:\n    \n    std::atomic<uint32_t> m_refCount = { 0u };\n    \n  };\n  \n}"
  },
  {
    "path": "src/util/rc/util_rc_ptr.h",
    "content": "#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <ostream>\n#include <utility>\n\nnamespace dxvk {\n\n  /**\n   * \\brief Pointer for reference-counted objects\n   * \n   * This only requires the given type to implement \\c incRef\n   * and \\c decRef methods that adjust the reference count.\n   * \\tparam T Object type\n   */\n  template<typename T>\n  class Rc {\n    template<typename Tx>\n    friend class Rc;\n  public:\n\n    Rc() = default;\n    Rc(std::nullptr_t) { }\n\n    Rc(T* object)\n    : m_object(object) {\n      this->incRef();\n    }\n\n    Rc(const Rc& other)\n    : m_object(other.m_object) {\n      this->incRef();\n    }\n\n    template<typename Tx>\n    Rc(const Rc<Tx>& other)\n    : m_object(other.m_object) {\n      this->incRef();\n    }\n\n    Rc(Rc&& other)\n    : m_object(other.m_object) {\n      other.m_object = nullptr;\n    }\n\n    template<typename Tx>\n    Rc(Rc<Tx>&& other)\n    : m_object(other.m_object) {\n      other.m_object = nullptr;\n    }\n\n    Rc& operator = (std::nullptr_t) {\n      this->decRef();\n      m_object = nullptr;\n      return *this;\n    }\n\n    Rc& operator = (const Rc& other) {\n      other.incRef();\n      this->decRef();\n      m_object = other.m_object;\n      return *this;\n    }\n\n    template<typename Tx>\n    Rc& operator = (const Rc<Tx>& other) {\n      other.incRef();\n      this->decRef();\n      m_object = other.m_object;\n      return *this;\n    }\n\n    Rc& operator = (Rc&& other) {\n      this->decRef();\n      this->m_object = other.m_object;\n      other.m_object = nullptr;\n      return *this;\n    }\n\n    template<typename Tx>\n    Rc& operator = (Rc<Tx>&& other) {\n      this->decRef();\n      this->m_object = other.m_object;\n      other.m_object = nullptr;\n      return *this;\n    }\n\n    ~Rc() {\n      this->decRef();\n    }\n\n    T& operator *  () const { return *m_object; }\n    T* operator -> () const { return  m_object; }\n    T* ptr() const { return m_object; }\n\n    template<typename Tx> bool operator == (const Rc<Tx>& other) const { return m_object == other.m_object; }\n    template<typename Tx> bool operator != (const Rc<Tx>& other) const { return m_object != other.m_object; }\n\n    template<typename Tx> bool operator == (Tx* other) const { return m_object == other; }\n    template<typename Tx> bool operator != (Tx* other) const { return m_object != other; }\n\n    bool operator == (std::nullptr_t) const { return m_object == nullptr; }\n    bool operator != (std::nullptr_t) const { return m_object != nullptr; }\n    \n    explicit operator bool () const {\n      return m_object != nullptr;\n    }\n\n    /**\n     * \\brief Sets pointer without acquiring a reference\n     *\n     * Must only be use when a reference has been taken via\n     * other means.\n     * \\param [in] object Object pointer\n     */\n    void unsafeInsert(T* object) {\n      this->decRef();\n      m_object = object;\n    }\n\n    /**\n     * \\brief Extracts raw pointer\n     *\n     * Sets the smart pointer to null without decrementing the\n     * reference count. Must only be used when the reference\n     * count is decremented in some other way.\n     * \\returns Pointer to owned object\n     */\n    T* unsafeExtract() {\n      return std::exchange(m_object, nullptr);\n    }\n\n    /**\n     * \\brief Creates smart pointer without taking reference\n     *\n     * Must only be used when a refernece has been obtained via other means.\n     * \\param [in] object Pointer to object to take ownership of\n     */\n    static Rc<T> unsafeCreate(T* object) {\n      return Rc<T>(object, false);\n    }\n\n  private:\n\n    T* m_object = nullptr;\n\n    explicit Rc(T* object, bool)\n    : m_object(object) { }\n\n    force_inline void incRef() const {\n      if (m_object != nullptr)\n        m_object->incRef();\n    }\n\n    force_inline void decRef() const {\n      if (m_object != nullptr) {\n        if constexpr (std::is_void_v<decltype(m_object->decRef())>) {\n          m_object->decRef();\n        } else {\n          // Deprecated, objects should manage themselves now.\n          if (!m_object->decRef())\n            delete m_object;\n        }\n      }\n    }\n\n  };\n\n  template<typename Tx, typename Ty>\n  bool operator == (Tx* a, const Rc<Ty>& b) { return b == a; }\n\n  template<typename Tx, typename Ty>\n  bool operator != (Tx* a, const Rc<Ty>& b) { return b != a; }\n\n  struct RcHash {\n    template<typename T>\n    size_t operator () (const Rc<T>& rc) const {\n      return reinterpret_cast<uintptr_t>(rc.ptr()) / sizeof(T);\n    }\n  };\n\n}\n\ntemplate<typename T>\nstd::ostream& operator << (std::ostream& os, const dxvk::Rc<T>& rc) {\n  return os << rc.ptr();\n}\n"
  },
  {
    "path": "src/util/sha1/sha1.c",
    "content": "/*\t$OpenBSD: sha1.c,v 1.26 2015/09/11 09:18:27 guenther Exp $\t*/\n\n/*\n * SHA-1 in C\n * By Steve Reid <steve@edmweb.com>\n * 100% Public Domain\n *\n * Test Vectors (from FIPS PUB 180-1)\n * \"abc\"\n *   A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D\n * \"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\"\n *   84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1\n * A million repetitions of \"a\"\n *   34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F\n */\n\n#include <stdint.h>\n#include <string.h>\n#include \"sha1.h\"\n\n#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))\n\n/*\n * blk0() and blk() perform the initial expand.\n * I got the idea of expanding during the round function from SSLeay\n */\n# define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \\\n    |(rol(block->l[i],8)&0x00FF00FF))\n    \n#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \\\n    ^block->l[(i+2)&15]^block->l[i&15],1))\n\n/*\n * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1\n */\n#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);\n#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);\n#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);\n#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);\n#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);\n\ntypedef union {\n\tuint8_t c[64];\n\tuint32_t l[16];\n} CHAR64LONG16;\n\n/*\n * Hash a single 512-bit block. This is the core of the algorithm.\n */\nvoid\nSHA1Transform(uint32_t state[5], const uint8_t* buffer)\n{\n\tuint32_t a, b, c, d, e;\n\tuint8_t workspace[SHA1_BLOCK_LENGTH];\n\tCHAR64LONG16 *block = (CHAR64LONG16 *)workspace;\n\n\t(void)memcpy(block, buffer, SHA1_BLOCK_LENGTH);\n\n\t/* Copy context->state[] to working vars */\n\ta = state[0];\n\tb = state[1];\n\tc = state[2];\n\td = state[3];\n\te = state[4];\n\n\t/* 4 rounds of 20 operations each. Loop unrolled. */\n\tR0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);\n\tR0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);\n\tR0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);\n\tR0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);\n\tR1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);\n\tR2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);\n\tR2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);\n\tR2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);\n\tR2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);\n\tR2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);\n\tR3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);\n\tR3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);\n\tR3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);\n\tR3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);\n\tR3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);\n\tR4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);\n\tR4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);\n\tR4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);\n\tR4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);\n\tR4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);\n\n\t/* Add the working vars back into context.state[] */\n\tstate[0] += a;\n\tstate[1] += b;\n\tstate[2] += c;\n\tstate[3] += d;\n\tstate[4] += e;\n\n\t/* Wipe variables */\n\ta = b = c = d = e = 0;\n}\n\n\n/*\n * SHA1Init - Initialize new context\n */\nvoid\nSHA1Init(SHA1_CTX *context)\n{\n\n\t/* SHA1 initialization constants */\n\tcontext->count = 0;\n\tcontext->state[0] = 0x67452301;\n\tcontext->state[1] = 0xEFCDAB89;\n\tcontext->state[2] = 0x98BADCFE;\n\tcontext->state[3] = 0x10325476;\n\tcontext->state[4] = 0xC3D2E1F0;\n}\n\n\n/*\n * Run your data through this.\n */\nvoid\nSHA1Update(SHA1_CTX *context, const uint8_t *data, size_t len)\n{\n\tsize_t i, j;\n\n\tj = (size_t)((context->count >> 3) & 63);\n\tcontext->count += (len << 3);\n\tif ((j + len) > 63) {\n\t\t(void)memcpy(&context->buffer[j], data, (i = 64-j));\n\t\tSHA1Transform(context->state, context->buffer);\n\t\tfor ( ; i + 63 < len; i += 64)\n\t\t\tSHA1Transform(context->state, (uint8_t *)&data[i]);\n\t\tj = 0;\n\t} else {\n\t\ti = 0;\n\t}\n\t(void)memcpy(&context->buffer[j], &data[i], len - i);\n}\n\n\n/*\n * Add padding and return the message digest.\n */\nvoid\nSHA1Pad(SHA1_CTX *context)\n{\n\tuint8_t finalcount[8];\n\tuint32_t i;\n\n\tfor (i = 0; i < 8; i++) {\n\t\tfinalcount[i] = (uint8_t)((context->count >>\n\t\t    ((7 - (i & 7)) * 8)) & 255);\t/* Endian independent */\n\t}\n\tSHA1Update(context, (uint8_t *)\"\\200\", 1);\n\twhile ((context->count & 504) != 448)\n\t\tSHA1Update(context, (uint8_t *)\"\\0\", 1);\n\tSHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */\n}\n\nvoid\nSHA1Final(uint8_t digest[SHA1_DIGEST_LENGTH], SHA1_CTX *context)\n{\n\tuint32_t i;\n\n\tSHA1Pad(context);\n\tfor (i = 0; i < SHA1_DIGEST_LENGTH; i++) {\n\t\tdigest[i] = (uint8_t)\n\t\t   ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);\n\t}\n\tmemset(context, 0, sizeof(*context));\n}\n"
  },
  {
    "path": "src/util/sha1/sha1.h",
    "content": "/*\t$OpenBSD: sha1.h,v 1.24 2012/12/05 23:19:57 deraadt Exp $\t*/\n\n/*\n * SHA-1 in C\n * By Steve Reid <steve@edmweb.com>\n * 100% Public Domain\n */\n\n#ifndef _SHA1_H\n#define _SHA1_H\n\n#include <stddef.h>\n#include <stdint.h>\n\n#define\tSHA1_BLOCK_LENGTH\t\t64\n#define\tSHA1_DIGEST_LENGTH\t\t20\n#define\tSHA1_DIGEST_STRING_LENGTH\t(SHA1_DIGEST_LENGTH * 2 + 1)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct _SHA1_CTX {\n    uint32_t state[5];\n    uint64_t count;\n    uint8_t buffer[SHA1_BLOCK_LENGTH];\n} SHA1_CTX;\n\nvoid SHA1Init(SHA1_CTX *);\nvoid SHA1Pad(SHA1_CTX *);\nvoid SHA1Transform(uint32_t [5], const uint8_t*);\nvoid SHA1Update(SHA1_CTX *, const uint8_t *, size_t);\nvoid SHA1Final(uint8_t [SHA1_DIGEST_LENGTH], SHA1_CTX *);\n\n#define HTONDIGEST(x) do {                                              \\\n        x[0] = htonl(x[0]);                                             \\\n        x[1] = htonl(x[1]);                                             \\\n        x[2] = htonl(x[2]);                                             \\\n        x[3] = htonl(x[3]);                                             \\\n        x[4] = htonl(x[4]); } while (0)\n\n#define NTOHDIGEST(x) do {                                              \\\n        x[0] = ntohl(x[0]);                                             \\\n        x[1] = ntohl(x[1]);                                             \\\n        x[2] = ntohl(x[2]);                                             \\\n        x[3] = ntohl(x[3]);                                             \\\n        x[4] = ntohl(x[4]); } while (0)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _SHA1_H */\n"
  },
  {
    "path": "src/util/sha1/sha1_util.cpp",
    "content": "#include \"sha1.h\"\n#include \"sha1_util.h\"\n\nnamespace dxvk {\n  \n  std::string Sha1Hash::toString() const {\n    static const char nibbles[]\n      = { '0', '1', '2', '3', '4', '5', '6', '7',\n          '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };\n    \n    std::string result;\n    result.resize(2 * m_digest.size());\n    \n    for (uint32_t i = 0; i < m_digest.size(); i++) {\n      result.at(2 * i + 0) = nibbles[(m_digest[i] >> 4) & 0xF];\n      result.at(2 * i + 1) = nibbles[(m_digest[i] >> 0) & 0xF];\n    }\n    \n    return result;\n  }\n  \n  \n  Sha1Hash Sha1Hash::compute(\n    const void*     data,\n          size_t    size) {\n    Sha1Data chunk = { data, size };\n    return compute(1, &chunk);\n  }\n  \n  \n  Sha1Hash Sha1Hash::compute(\n          size_t    numChunks,\n    const Sha1Data* chunks) {\n    Sha1Digest digest;\n    \n    SHA1_CTX ctx;\n    SHA1Init(&ctx);\n    \n    for (size_t i = 0; i < numChunks; i++) {\n      auto ptr = reinterpret_cast<const uint8_t*>(chunks[i].data);\n      SHA1Update(&ctx, ptr, chunks[i].size);\n    }\n\n    SHA1Final(digest.data(), &ctx);\n    return Sha1Hash(digest);\n  }\n  \n}"
  },
  {
    "path": "src/util/sha1/sha1_util.h",
    "content": "#pragma once\n\n#include <array>\n#include <cstring>\n#include <string>\n\nnamespace dxvk {\n  \n  using Sha1Digest = std::array<uint8_t, 20>;\n\n  struct Sha1Data {\n    const void* data;\n    size_t      size;\n  };\n  \n  class Sha1Hash {\n    \n  public:\n    \n    Sha1Hash() { }\n    Sha1Hash(const Sha1Digest& digest)\n    : m_digest(digest) { }\n    \n    std::string toString() const;\n    \n    uint32_t dword(uint32_t id) const {\n      return uint32_t(m_digest[4 * id + 0]) <<  0\n           | uint32_t(m_digest[4 * id + 1]) <<  8\n           | uint32_t(m_digest[4 * id + 2]) << 16\n           | uint32_t(m_digest[4 * id + 3]) << 24;\n    }\n    \n    bool operator == (const Sha1Hash& other) const {\n      return !std::memcmp(\n        this->m_digest.data(),\n        other.m_digest.data(),\n        other.m_digest.size());\n    }\n    \n    bool operator != (const Sha1Hash& other) const {\n      return !this->operator == (other);\n    }\n    \n    static Sha1Hash compute(\n      const void*     data,\n            size_t    size);\n    \n    static Sha1Hash compute(\n            size_t    numChunks,\n      const Sha1Data* chunks);\n    \n    template<typename T>\n    static Sha1Hash compute(const T& data) {\n      return compute(&data, sizeof(T));\n    }\n\n    const uint8_t* digest() const {\n      return m_digest.data();\n    }\n\n    size_t digestLength() const {\n      return m_digest.size();\n    }\n\n  private:\n    \n    Sha1Digest m_digest;\n    \n  };\n  \n}"
  },
  {
    "path": "src/util/sync/sync_recursive.cpp",
    "content": "#include \"sync_recursive.h\"\n#include \"sync_spinlock.h\"\n\nnamespace dxvk::sync {\n\n  void RecursiveSpinlock::lock() {\n    spin(2000, [this] { return try_lock(); });\n  }\n\n\n  void RecursiveSpinlock::unlock() {\n    if (likely(m_counter == 0))\n      m_owner.store(0, std::memory_order_release);\n    else\n      m_counter -= 1;\n  }\n\n\n  bool RecursiveSpinlock::try_lock() {\n    uint32_t threadId = dxvk::this_thread::get_id();\n    uint32_t expected = 0;\n\n    bool status = m_owner.compare_exchange_weak(\n      expected, threadId, std::memory_order_acquire);\n    \n    if (status)\n      return true;\n    \n    if (expected != threadId)\n      return false;\n    \n    m_counter += 1;\n    return true;\n  }\n\n}\n"
  },
  {
    "path": "src/util/sync/sync_recursive.h",
    "content": "#pragma once\n\n#include <atomic>\n\n#include \"../com/com_include.h\"\n\nnamespace dxvk::sync {\n\n  /**\n   * \\brief Recursive spinlock\n   * \n   * Implements a spinlock that can be acquired\n   * by the same thread multiple times.\n   */\n  class RecursiveSpinlock {\n\n  public:\n\n    void lock();\n\n    void unlock();\n\n    bool try_lock();\n\n  private:\n\n    std::atomic<uint32_t> m_owner   = { 0u };\n    uint32_t              m_counter = { 0u };\n    \n  };\n\n}\n"
  },
  {
    "path": "src/util/sync/sync_signal.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <functional>\n#include <list>\n\n#include \"../rc/util_rc.h\"\n\n#include \"../thread.h\"\n\nnamespace dxvk::sync {\n  \n  /**\n   * \\brief Signal\n   * \n   * Interface for a CPU-side fence. Can be signaled\n   * to a given value, and any thread waiting for a\n   * lower value will be woken up.\n   */\n  class Signal : public RcObject {\n    \n  public:\n    \n    virtual ~Signal() { }\n\n    /**\n     * \\brief Last signaled value\n     * \\returns Last signaled value\n     */\n    virtual uint64_t value() const = 0;\n    \n    /**\n     * \\brief Notifies signal\n     *\n     * Wakes up all threads currently waiting for\n     * a value lower than \\c value. Note that\n     * \\c value must monotonically increase.\n     * \\param [in] value Value to signal to\n     */\n    virtual void signal(uint64_t value) = 0;\n    \n    /**\n     * \\brief Waits for signal\n     * \n     * Blocks the calling thread until another\n     * thread signals it with a value equal to\n     * or greater than \\c value.\n     * \\param [in] value The value to wait for\n     */\n    virtual void wait(uint64_t value) = 0;\n    \n  };\n\n\n  /**\n   * \\brief Sync point\n   *\n   * Convenience class that stores a sync\n   * object and a value to wait on.\n   */\n  class SyncPoint {\n\n  public:\n\n    SyncPoint() = default;\n    SyncPoint(Rc<Signal> signal, uint64_t value)\n    : m_signal(std::move(signal)), m_value(value) { }\n\n    void synchronize() {\n      if (m_signal) {\n        m_signal->wait(m_value);\n        m_signal = nullptr;\n      }\n    }\n\n  private:\n\n    Rc<Signal>  m_signal;\n    uint64_t    m_value = 0u;\n\n  };\n\n\n  /**\n   * \\brief Fence\n   *\n   * Simple CPU-side fence.\n   */\n  class Fence final : public Signal {\n\n  public:\n\n    Fence()\n    : m_value(0ull) { }\n\n    explicit Fence(uint64_t value)\n    : m_value(value) { }\n\n    uint64_t value() const {\n      return m_value.load(std::memory_order_acquire);  \n    }\n\n    void signal(uint64_t value) {\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n      m_value.store(value, std::memory_order_release);\n      m_cond.notify_all();\n    }\n    \n    void wait(uint64_t value) {\n      if (value <= m_value.load(std::memory_order_acquire))\n        return;\n\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n      m_cond.wait(lock, [this, value] {\n        return value <= m_value.load(std::memory_order_acquire);\n      });\n    }\n\n  private:\n\n    std::atomic<uint64_t>    m_value;\n    dxvk::mutex              m_mutex;\n    dxvk::condition_variable m_cond;\n\n  };\n\n\n  /**\n   * \\brief Callback signal\n   *\n   * CPU-side fence with the ability to call a\n   * function when signaled to a given value.\n   */\n  class CallbackFence final : public Signal {\n\n  public:\n\n    CallbackFence()\n    : m_value(0ull) { }\n\n    explicit CallbackFence(uint64_t value)\n    : m_value(value) { }\n\n    uint64_t value() const {\n      return m_value.load(std::memory_order_acquire);\n    }\n\n    void signal(uint64_t value) {\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n      m_value.store(value, std::memory_order_release);\n      m_cond.notify_all();\n\n      for (auto i = m_callbacks.begin(); i != m_callbacks.end(); ) {\n        if (value >= i->first) {\n          i->second();\n          i = m_callbacks.erase(i);\n        } else {\n          i++;\n        }\n      }\n    }\n\n    void wait(uint64_t value) {\n      if (value <= m_value.load(std::memory_order_acquire))\n        return;\n\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n      m_cond.wait(lock, [this, value] {\n        return value <= m_value.load(std::memory_order_acquire);\n      });\n    }\n\n    template<typename Fn>\n    void setCallback(uint64_t value, Fn&& proc) {\n      if (value <= this->value()) {\n        proc();\n        return;\n      }\n\n      std::unique_lock<dxvk::mutex> lock(m_mutex);\n\n      // Verify value is still in the future upon lock.\n      if (value > this->value())\n        m_callbacks.emplace_back(std::piecewise_construct,\n          std::make_tuple(value),\n          std::make_tuple(proc));\n      else {\n        lock.unlock();\n        proc();\n      }\n    }\n\n  private:\n\n    std::atomic<uint64_t>    m_value;\n    dxvk::mutex              m_mutex;\n    dxvk::condition_variable m_cond;\n\n    std::list<std::pair<uint64_t, std::function<void ()>>> m_callbacks;\n\n  };\n  \n}\n"
  },
  {
    "path": "src/util/sync/sync_spinlock.h",
    "content": "#pragma once\n\n#include <atomic>\n\n#include \"../thread.h\"\n\n#include \"../util_bit.h\"\n#include \"../util_likely.h\"\n\nnamespace dxvk::sync {\n\n  /**\n   * \\brief Generic spin function\n   *\n   * Blocks calling thread until a condition becomes\n   * \\c true, calling \\c yield every few iterations.\n   * \\param [in] spinCount Number of probes between each yield\n   * \\param [in] fn Condition to test\n   */\n  template<typename Fn>\n  void spin(uint32_t spinCount, const Fn& fn) {\n    while (unlikely(!fn())) {\n      for (uint32_t i = 1; i < spinCount; i++) {\n        #if defined(DXVK_ARCH_X86)\n        _mm_pause();\n        #elif defined(DXVK_ARCH_ARM64)\n        __asm__ __volatile__ (\"yield\");\n        #else\n        /* Do nothing (busy-loop). Please add more #elif above here if\n         * your CPU architecture has a suitable pause/yield instruction */\n        #endif\n        if (fn())\n          return;\n      }\n\n      dxvk::this_thread::yield();\n    }\n  }\n  \n  /**\n   * \\brief Spin lock\n   * \n   * A low-overhead spin lock which can be used to\n   * protect data structures for a short duration\n   * in case the structure is not likely contested.\n   */\n  class Spinlock {\n\n  public:\n    \n    Spinlock() { }\n    ~Spinlock() { }\n    \n    Spinlock             (const Spinlock&) = delete;\n    Spinlock& operator = (const Spinlock&) = delete;\n    \n    void lock() {\n      spin(200, [this] { return try_lock(); });\n    }\n    \n    void unlock() {\n      m_lock.store(0, std::memory_order_release);\n    }\n    \n    bool try_lock() {\n      return likely(!m_lock.load())\n          && likely(!m_lock.exchange(1, std::memory_order_acquire));\n    }\n    \n  private:\n    \n    std::atomic<uint32_t> m_lock = { 0 };\n    \n  };\n  \n}\n"
  },
  {
    "path": "src/util/sync/sync_ticketlock.h",
    "content": "#pragma once\n\n#include <atomic>\n\n#include \"../thread.h\"\n\nnamespace dxvk::sync {\n  \n  /**\n   * \\brief Ticket spinlock\n   * \n   * A fair spinlock implementation that should\n   * be preferred over \\ref Spinlock when one of\n   * the threads accessing the lock is likely to\n   * starve another.\n   */\n  class TicketLock {\n\n  public:\n\n    void lock() {\n      uint32_t ticket = m_counter.fetch_add(1);\n\n      while (m_serving.load(std::memory_order_acquire) != ticket)\n        continue;\n    }\n\n    void unlock() {\n      uint32_t serveNext = m_serving.load() + 1;\n      m_serving.store(serveNext, std::memory_order_release);\n    }\n\n  private:\n\n    std::atomic<uint32_t> m_counter = { 0 };\n    std::atomic<uint32_t> m_serving = { 0 };\n\n  };\n  \n}\n"
  },
  {
    "path": "src/util/thread.cpp",
    "content": "#include <atomic>\n\n#include \"thread.h\"\n#include \"util_likely.h\"\n\n#ifdef _WIN32\n\nnamespace dxvk {\n\n  thread::thread(ThreadProc&& proc)\n  : m_data(new ThreadData(std::move(proc))) {\n    m_data->handle = ::CreateThread(nullptr, 0x100000,\n      thread::threadProc, m_data, STACK_SIZE_PARAM_IS_A_RESERVATION,\n      &m_data->id);\n\n    if (!m_data->handle) {\n      delete m_data;\n      throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again), \"Failed to create thread\");\n    }\n  }\n\n\n  thread::~thread() {\n    if (joinable())\n      std::terminate();\n  }\n\n\n  void thread::join() {\n    if (!joinable())\n      throw std::system_error(std::make_error_code(std::errc::invalid_argument), \"Thread not joinable\");\n\n    if (get_id() == this_thread::get_id())\n      throw std::system_error(std::make_error_code(std::errc::resource_deadlock_would_occur), \"Cannot join current thread\");\n\n    if(::WaitForSingleObjectEx(m_data->handle, INFINITE, FALSE) == WAIT_FAILED)\n      throw std::system_error(std::make_error_code(std::errc::invalid_argument), \"Joining thread failed\");\n\n    detach();\n  }\n\n\n  void thread::set_priority(ThreadPriority priority) {\n    int32_t value;\n    switch (priority) {\n      default:\n      case ThreadPriority::Normal: value = THREAD_PRIORITY_NORMAL; break;\n      case ThreadPriority::Lowest: value = THREAD_PRIORITY_LOWEST; break;\n    }\n\n    if (m_data)\n      ::SetThreadPriority(m_data->handle, int32_t(value));\n  }\n\n\n  uint32_t thread::hardware_concurrency() {\n    SYSTEM_INFO info = { };\n    ::GetSystemInfo(&info);\n    return info.dwNumberOfProcessors;\n  }\n\n\n  DWORD WINAPI thread::threadProc(void* arg) {\n    auto data = reinterpret_cast<ThreadData*>(arg);\n    DWORD exitCode = 0;\n\n    try {\n      data->proc();\n    } catch (...) {\n      exitCode = 1;\n    }\n\n    data->decRef();\n    return exitCode;\n  }\n\n}\n\n\nnamespace dxvk::this_thread {\n\n  bool isInModuleDetachment() {\n    using PFN_RtlDllShutdownInProgress = BOOLEAN (NTAPI *)();\n\n    static auto RtlDllShutdownInProgress = reinterpret_cast<PFN_RtlDllShutdownInProgress>(\n      ::GetProcAddress(::GetModuleHandleW(L\"ntdll.dll\"), \"RtlDllShutdownInProgress\"));\n\n    return RtlDllShutdownInProgress();\n  }\n\n}\n\n#else\n\nnamespace dxvk::this_thread {\n  \n  static std::atomic<uint32_t> g_threadCtr = { 0u };\n  static thread_local uint32_t g_threadId  = 0u;\n  \n  // This implementation returns thread ids unique to the current instance.\n  // ie. if you use this across multiple .so's then you might get conflicting ids.\n  //\n  // This isn't an issue for us, as it is only used by the spinlock implementation,\n  // but may be for you if you use this elsewhere.\n  uint32_t get_id() {\n    if (unlikely(!g_threadId))\n      g_threadId = ++g_threadCtr;\n\n    return g_threadId;\n  }\n\n}\n\n#endif\n"
  },
  {
    "path": "src/util/thread.h",
    "content": "#pragma once\n\n#include <chrono>\n#include <condition_variable>\n#include <functional>\n#include <mutex>\n#include <shared_mutex>\n#include <thread>\n#include <utility>\n\n#include \"util_error.h\"\n\n#include \"./com/com_include.h\"\n\n#include \"./rc/util_rc.h\"\n#include \"./rc/util_rc_ptr.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Thread priority\n   */\n  enum class ThreadPriority : int32_t {\n    Normal,\n    Lowest,\n  };\n\n#ifdef _WIN32\n\n  using ThreadProc = std::function<void()>;\n\n\n  /**\n   * \\brief Thread object\n   */\n  struct ThreadData {\n    ThreadData(ThreadProc&& proc_)\n    : proc(std::move(proc_)) { }\n\n    ~ThreadData() {\n      if (handle)\n        CloseHandle(handle);\n    }\n\n    HANDLE                handle = nullptr;\n    DWORD                 id     = 0;\n    std::atomic<uint32_t> refs   = { 2u };\n    ThreadProc            proc;\n\n    void decRef() {\n      if (refs.fetch_sub(1, std::memory_order_release) == 1)\n        delete this;\n    }\n  };\n\n\n  /**\n   * \\brief Thread wrapper\n   *\n   * Drop-in replacement for std::thread\n   * using plain win32 threads.\n   */\n  class thread {\n\n  public:\n\n    using id = uint32_t;\n    using native_handle_type = HANDLE;\n\n    thread() { }\n\n    explicit thread(ThreadProc&& proc);\n\n    ~thread();\n\n    thread(thread&& other)\n    : m_data(std::exchange(other.m_data, nullptr)) { }\n\n    thread& operator = (thread&& other) {\n      if (m_data)\n        m_data->decRef();\n\n      m_data = std::exchange(other.m_data, nullptr);\n      return *this;\n    }\n\n    void detach() {\n      m_data->decRef();\n      m_data = nullptr;\n    }\n\n    bool joinable() const {\n      return m_data != nullptr;\n    }\n\n    id get_id() const {\n      return joinable() ? m_data->id : id();\n    }\n\n    native_handle_type native_handle() const {\n      return joinable() ? m_data->handle : native_handle_type();\n    }\n\n    void swap(thread& other) {\n      std::swap(m_data, other.m_data);\n    }\n\n    void join();\n\n    void set_priority(ThreadPriority priority);\n\n    static uint32_t hardware_concurrency();\n\n  private:\n\n    ThreadData* m_data = nullptr;\n\n    static DWORD WINAPI threadProc(void* arg);\n\n  };\n\n\n  namespace this_thread {\n    inline void yield() {\n      SwitchToThread();\n    }\n\n    inline thread::id get_id() {\n      return thread::id(GetCurrentThreadId());\n    }\n\n    bool isInModuleDetachment();\n  }\n\n\n  /**\n   * \\brief SRW-based mutex implementation\n   *\n   * Drop-in replacement for \\c std::mutex that uses Win32\n   * SRW locks, which are implemented with \\c futex in wine.\n   */\n  class mutex {\n\n  public:\n\n    using native_handle_type = PSRWLOCK;\n\n    mutex() { }\n\n    mutex(const mutex&) = delete;\n    mutex& operator = (const mutex&) = delete;\n\n    void lock() {\n      AcquireSRWLockExclusive(&m_lock);\n    }\n\n    void unlock() {\n      ReleaseSRWLockExclusive(&m_lock);\n    }\n\n    bool try_lock() {\n      return TryAcquireSRWLockExclusive(&m_lock);\n    }\n\n    native_handle_type native_handle() {\n      return &m_lock;\n    }\n\n  private:\n\n    SRWLOCK m_lock = SRWLOCK_INIT;\n\n  };\n\n\n  /**\n   * \\brief SRW-based shared mutex implementation\n   */\n  class shared_mutex {\n\n  public:\n\n    using native_handle_type = PSRWLOCK;\n\n    shared_mutex() { }\n\n    shared_mutex(const shared_mutex&) = delete;\n    shared_mutex& operator = (const shared_mutex&) = delete;\n\n    void lock() {\n      AcquireSRWLockExclusive(&m_lock);\n    }\n\n    void lock_shared() {\n      AcquireSRWLockShared(&m_lock);\n    }\n\n    void unlock() {\n      ReleaseSRWLockExclusive(&m_lock);\n    }\n\n    void unlock_shared() {\n      ReleaseSRWLockShared(&m_lock);\n    }\n\n    bool try_lock() {\n      return TryAcquireSRWLockExclusive(&m_lock);\n    }\n\n    bool try_lock_shared() {\n      return TryAcquireSRWLockShared(&m_lock);\n    }\n\n    native_handle_type native_handle() {\n      return &m_lock;\n    }\n\n  private:\n\n    SRWLOCK m_lock = SRWLOCK_INIT;\n\n  };\n\n\n  /**\n   * \\brief Recursive mutex implementation\n   *\n   * Drop-in replacement for \\c std::recursive_mutex that\n   * uses Win32 critical sections.\n   */\n  class recursive_mutex {\n\n  public:\n\n    using native_handle_type = PCRITICAL_SECTION;\n\n    recursive_mutex() {\n      InitializeCriticalSection(&m_lock);\n    }\n\n    ~recursive_mutex() {\n      DeleteCriticalSection(&m_lock);\n    }\n\n    recursive_mutex(const recursive_mutex&) = delete;\n    recursive_mutex& operator = (const recursive_mutex&) = delete;\n\n    void lock() {\n      EnterCriticalSection(&m_lock);\n    }\n\n    void unlock() {\n      LeaveCriticalSection(&m_lock);\n    }\n\n    bool try_lock() {\n      return TryEnterCriticalSection(&m_lock);\n    }\n\n    native_handle_type native_handle() {\n      return &m_lock;\n    }\n\n  private:\n\n    CRITICAL_SECTION m_lock;\n\n  };\n\n\n  /**\n   * \\brief SRW-based condition variable implementation\n   *\n   * Drop-in replacement for \\c std::condition_variable that\n   * uses Win32 condition variables on SRW locks.\n   */\n  class condition_variable {\n\n  public:\n\n    using native_handle_type = PCONDITION_VARIABLE;\n\n    condition_variable() {\n      InitializeConditionVariable(&m_cond);\n    }\n\n    condition_variable(condition_variable&) = delete;\n\n    condition_variable& operator = (condition_variable&) = delete;\n\n    void notify_one() {\n      WakeConditionVariable(&m_cond);\n    }\n\n    void notify_all() {\n      WakeAllConditionVariable(&m_cond);\n    }\n\n    void wait(std::unique_lock<dxvk::mutex>& lock) {\n      auto srw = lock.mutex()->native_handle();\n      SleepConditionVariableSRW(&m_cond, srw, INFINITE, 0);\n    }\n\n    template<typename Predicate>\n    void wait(std::unique_lock<dxvk::mutex>& lock, Predicate pred) {\n      while (!pred())\n        wait(lock);\n    }\n\n    template<typename Clock, typename Duration>\n    std::cv_status wait_until(std::unique_lock<dxvk::mutex>& lock, const std::chrono::time_point<Clock, Duration>& time) {\n      auto now = Clock::now();\n\n      return (now < time)\n        ? wait_for(lock, now - time)\n        : std::cv_status::timeout;\n    }\n\n    template<typename Clock, typename Duration, typename Predicate>\n    bool wait_until(std::unique_lock<dxvk::mutex>& lock, const std::chrono::time_point<Clock, Duration>& time, Predicate pred) {\n      if (pred())\n        return true;\n\n      auto now = Clock::now();\n      return now < time && wait_for(lock, now - time, pred);\n    }\n\n    template<typename Rep, typename Period>\n    std::cv_status wait_for(std::unique_lock<dxvk::mutex>& lock, const std::chrono::duration<Rep, Period>& timeout) {\n      auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(timeout);\n      auto srw = lock.mutex()->native_handle();\n\n      return SleepConditionVariableSRW(&m_cond, srw, ms.count(), 0)\n        ? std::cv_status::no_timeout\n        : std::cv_status::timeout;\n    }\n\n    template<typename Rep, typename Period, typename Predicate>\n    bool wait_for(std::unique_lock<dxvk::mutex>& lock, const std::chrono::duration<Rep, Period>& timeout, Predicate pred) {\n      bool result = pred();\n\n      if (!result && wait_for(lock, timeout) == std::cv_status::no_timeout)\n        result = pred();\n\n      return result;\n    }\n\n    native_handle_type native_handle() {\n      return &m_cond;\n    }\n\n  private:\n\n    CONDITION_VARIABLE m_cond;\n\n  };\n\n#else\n  class thread : public std::thread {\n  public:\n    using std::thread::thread;\n\n    void set_priority(ThreadPriority priority) {\n      ::sched_param param = {};\n      int32_t policy;\n      switch (priority) {\n        default:\n        case ThreadPriority::Normal: policy = SCHED_OTHER; break;\n#ifndef __linux__\n        case ThreadPriority::Lowest: policy = SCHED_OTHER; break;\n#else\n        case ThreadPriority::Lowest: policy = SCHED_IDLE;  break;\n#endif\n      }\n      ::pthread_setschedparam(this->native_handle(), policy, &param);\n    }\n  };\n\n  using mutex              = std::mutex;\n  using shared_mutex       = std::shared_mutex;\n  using recursive_mutex    = std::recursive_mutex;\n  using condition_variable = std::condition_variable;\n\n  namespace this_thread {\n    inline void yield() {\n      std::this_thread::yield();\n    }\n\n    uint32_t get_id();\n\n    inline bool isInModuleDetachment() {\n      return false;\n    }\n  }\n#endif\n\n}\n"
  },
  {
    "path": "src/util/util_bit.h",
    "content": "#pragma once\n\n#if (defined(__x86_64__) && !defined(__arm64ec__)) || (defined(_M_X64) && !defined(_M_ARM64EC)) \\\n    || defined(__i386__) || defined(_M_IX86) || defined(__e2k__)\n  #define DXVK_ARCH_X86\n  #if defined(__x86_64__) || defined(_M_X64) || defined(__e2k__)\n    #define DXVK_ARCH_X86_64\n  #endif\n#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)\n  #define DXVK_ARCH_ARM64\n#endif\n\n#ifdef DXVK_ARCH_X86\n  #ifndef _MSC_VER\n    #if defined(_WIN32) && (defined(__AVX__) || defined(__AVX2__))\n      #error \"AVX-enabled builds not supported due to stack alignment issues.\"\n    #endif\n    #if defined(__WINE__) && defined(__clang__)\n      #pragma push_macro(\"_WIN32\")\n      #undef _WIN32\n    #endif\n    #include <x86intrin.h>\n    #if defined(__WINE__) && defined(__clang__)\n      #pragma pop_macro(\"_WIN32\")\n    #endif\n  #else\n    #include <intrin.h>\n  #endif\n#endif\n\n#include \"util_likely.h\"\n#include \"util_math.h\"\n\n#include <cstddef>\n#include <cstdint>\n#include <cstring>\n#include <iterator>\n#include <type_traits>\n#include <vector>\n\nnamespace dxvk::bit {\n\n  template<typename T, typename J>\n  T cast(const J& src) {\n    static_assert(sizeof(T) == sizeof(J));\n    static_assert(std::is_trivially_copyable<J>::value && std::is_trivial<T>::value);\n\n    T dst;\n    std::memcpy(&dst, &src, sizeof(T));\n    return dst;\n  }\n  \n  template<typename T>\n  T extract(T value, uint32_t fst, uint32_t lst) {\n    return (value >> fst) & ~(~T(0) << (lst - fst + 1));\n  }\n\n  template<typename T>\n  T popcnt(T n) {\n    n -= ((n >> 1u) & T(0x5555555555555555ull));\n    n = (n & T(0x3333333333333333ull)) + ((n >> 2u) & T(0x3333333333333333ull));\n    n = (n + (n >> 4u)) & T(0x0f0f0f0f0f0f0f0full);\n    n *= T(0x0101010101010101ull);\n    return n >> (8u * (sizeof(T) - 1u));\n  }\n\n  inline uint32_t tzcnt(uint32_t n) {\n    #if defined(_MSC_VER) && !defined(__clang__)\n    if(n == 0)\n      return 32;\n    return _tzcnt_u32(n);\n    #elif defined(__BMI__)\n    return __tzcnt_u32(n);\n    #elif defined(DXVK_ARCH_X86) && (defined(__GNUC__) || defined(__clang__))\n    // tzcnt is encoded as rep bsf, so we can use it on all\n    // processors, but the behaviour of zero inputs differs:\n    // - bsf:   zf = 1, cf = ?, result = ?\n    // - tzcnt: zf = 0, cf = 1, result = 32\n    // We'll have to handle this case manually.\n    uint32_t res;\n    uint32_t tmp;\n    asm (\n      \"tzcnt %2, %0;\"\n      \"mov  $32, %1;\"\n      \"test  %2, %2;\"\n      \"cmovz %1, %0;\"\n      : \"=&r\" (res), \"=&r\" (tmp)\n      : \"r\" (n)\n      : \"cc\");\n    return res;\n    #elif defined(__GNUC__) || defined(__clang__)\n    return n != 0 ? __builtin_ctz(n) : 32;\n    #else\n    uint32_t r = 31;\n    n &= -n;\n    r -= (n & 0x0000FFFF) ? 16 : 0;\n    r -= (n & 0x00FF00FF) ?  8 : 0;\n    r -= (n & 0x0F0F0F0F) ?  4 : 0;\n    r -= (n & 0x33333333) ?  2 : 0;\n    r -= (n & 0x55555555) ?  1 : 0;\n    return n != 0 ? r : 32;\n    #endif\n  }\n\n  inline uint32_t tzcnt(uint64_t n) {\n    #if defined(DXVK_ARCH_X86_64) && defined(_MSC_VER) && !defined(__clang__)\n    if(n == 0)\n      return 64;\n    return (uint32_t)_tzcnt_u64(n);\n    #elif defined(DXVK_ARCH_X86_64) && defined(__BMI__)\n    return __tzcnt_u64(n);\n    #elif defined(DXVK_ARCH_X86_64) && (defined(__GNUC__) || defined(__clang__))\n    uint64_t res;\n    uint64_t tmp;\n    asm (\n      \"tzcnt %2, %0;\"\n      \"mov  $64, %1;\"\n      \"test  %2, %2;\"\n      \"cmovz %1, %0;\"\n      : \"=&r\" (res), \"=&r\" (tmp)\n      : \"r\" (n)\n      : \"cc\");\n    return res;\n    #elif defined(__GNUC__) || defined(__clang__)\n    return n != 0 ? __builtin_ctzll(n) : 64;\n    #else\n    uint32_t lo = uint32_t(n);\n    if (lo) {\n      return tzcnt(lo);\n    } else {\n      uint32_t hi = uint32_t(n >> 32);\n      return tzcnt(hi) + 32;\n    }\n    #endif\n  }\n\n  inline uint32_t bsf(uint32_t n) {\n    #if (defined(__GNUC__) || defined(__clang__)) && !defined(__BMI__) && defined(DXVK_ARCH_X86)\n    uint32_t res;\n    asm (\"tzcnt %1,%0\"\n    : \"=r\" (res)\n    : \"r\" (n)\n    : \"cc\");\n    return res;\n    #else\n    return tzcnt(n);\n    #endif\n  }\n\n  inline uint32_t bsf(uint64_t n) {\n    #if (defined(__GNUC__) || defined(__clang__)) && !defined(__BMI__) && defined(DXVK_ARCH_X86_64)\n    uint64_t res;\n    asm (\"tzcnt %1,%0\"\n    : \"=r\" (res)\n    : \"r\" (n)\n    : \"cc\");\n    return res;\n    #else\n    return tzcnt(n);\n    #endif\n  }\n\n  inline uint32_t lzcnt(uint32_t n) {\n    #if defined(_MSC_VER) && !defined(__clang__) && !defined(__LZCNT__)\n    unsigned long bsr;\n    if(n == 0)\n      return 32;\n    _BitScanReverse(&bsr, n);\n    return 31-bsr;\n    #elif (defined(_MSC_VER) && !defined(__clang__)) || defined(__LZCNT__)\n    return _lzcnt_u32(n);\n    #elif defined(__GNUC__) || defined(__clang__)\n    return n != 0 ? __builtin_clz(n) : 32;\n    #else\n    uint32_t r = 0;\n\n    if (n == 0)\treturn 32;\n\n    if (n <= 0x0000FFFF) { r += 16; n <<= 16; }\n    if (n <= 0x00FFFFFF) { r += 8;  n <<= 8; }\n    if (n <= 0x0FFFFFFF) { r += 4;  n <<= 4; }\n    if (n <= 0x3FFFFFFF) { r += 2;  n <<= 2; }\n    if (n <= 0x7FFFFFFF) { r += 1;  n <<= 1; }\n\n    return r;\n    #endif\n  }\n\n  inline uint32_t lzcnt(uint64_t n) {\n    #if defined(_MSC_VER) && !defined(__clang__) && !defined(__LZCNT__) && defined(DXVK_ARCH_X86_64)\n    unsigned long bsr;\n    if(n == 0)\n      return 64;\n    _BitScanReverse64(&bsr, n);\n    return 63-bsr;\n    #elif defined(DXVK_ARCH_X86_64) && ((defined(_MSC_VER) && !defined(__clang__)) && defined(__LZCNT__))\n    return _lzcnt_u64(n);\n    #elif defined(DXVK_ARCH_X86_64) && (defined(__GNUC__) || defined(__clang__))\n    return n != 0 ? __builtin_clzll(n) : 64;\n    #else\n    uint32_t lo = uint32_t(n);\n    uint32_t hi = uint32_t(n >> 32u);\n    return hi ? lzcnt(hi) : lzcnt(lo) + 32u;\n    #endif\n  }\n\n  template<typename T>\n  uint32_t pack(T& dst, uint32_t& shift, T src, uint32_t count) {\n    constexpr uint32_t Bits = 8 * sizeof(T);\n    if (likely(shift < Bits))\n      dst |= src << shift;\n    shift += count;\n    return shift > Bits ? shift - Bits : 0;\n  }\n\n  template<typename T>\n  uint32_t unpack(T& dst, T src, uint32_t& shift, uint32_t count) {\n    constexpr uint32_t Bits = 8 * sizeof(T);\n    if (likely(shift < Bits))\n      dst = (src >> shift) & ((T(1) << count) - 1);\n    shift += count;\n    return shift > Bits ? shift - Bits : 0;\n  }\n\n\n  /**\n   * \\brief Clears cache lines of memory\n   *\n   * Uses non-temporal stores. The memory region offset\n   * and size are assumed to be aligned to 64 bytes.\n   * \\param [in] mem Memory region to clear\n   * \\param [in] size Number of bytes to clear\n   */\n  inline void bclear(void* mem, size_t size) {\n    #if defined(DXVK_ARCH_X86) && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER))\n    auto zero = _mm_setzero_si128();\n\n    #if defined(__clang__)\n    #pragma nounroll\n    #elif defined(__GNUC__)\n    #pragma GCC unroll 0\n    #endif\n    for (size_t i = 0; i < size; i += 64u) {\n      auto* ptr = reinterpret_cast<__m128i*>(mem) + i / sizeof(zero);\n      _mm_stream_si128(ptr + 0u, zero);\n      _mm_stream_si128(ptr + 1u, zero);\n      _mm_stream_si128(ptr + 2u, zero);\n      _mm_stream_si128(ptr + 3u, zero);\n    }\n    #else\n    std::memset(mem, 0, size);\n    #endif\n  }\n\n\n  /**\n   * \\brief Compares two aligned structs bit by bit\n   *\n   * \\param [in] a First struct\n   * \\param [in] b Second struct\n   * \\returns \\c true if the structs are equal\n   */\n  template<typename T>\n  bool bcmpeq(const T* a, const T* b) {\n    static_assert(alignof(T) >= 16);\n    #if defined(DXVK_ARCH_X86) && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER))\n    auto ai = reinterpret_cast<const __m128i*>(a);\n    auto bi = reinterpret_cast<const __m128i*>(b);\n\n    size_t i = 0;\n\n    #if defined(__clang__)\n    #pragma nounroll\n    #elif defined(__GNUC__)\n    #pragma GCC unroll 0\n    #endif\n\n    for ( ; i < 2 * (sizeof(T) / 32); i += 2) {\n      __m128i eq0 = _mm_cmpeq_epi8(\n        _mm_load_si128(ai + i),\n        _mm_load_si128(bi + i));\n      __m128i eq1 = _mm_cmpeq_epi8(\n        _mm_load_si128(ai + i + 1),\n        _mm_load_si128(bi + i + 1));\n      __m128i eq = _mm_and_si128(eq0, eq1);\n\n      int mask = _mm_movemask_epi8(eq);\n      if (mask != 0xFFFF)\n        return false;\n    }\n\n    for ( ; i < sizeof(T) / 16; i++) {\n      __m128i eq = _mm_cmpeq_epi8(\n        _mm_load_si128(ai + i),\n        _mm_load_si128(bi + i));\n\n      int mask = _mm_movemask_epi8(eq);\n      if (mask != 0xFFFF)\n        return false;\n    }\n\n    return true;\n    #else\n    return !std::memcmp(a, b, sizeof(T));\n    #endif\n  }\n\n  template <size_t Bits>\n  class bitset {\n    static constexpr size_t Dwords = align(Bits, 32) / 32;\n  public:\n\n    constexpr bitset()\n      : m_dwords() {\n\n    }\n\n    constexpr bool get(uint32_t idx) const {\n      uint32_t dword = 0;\n      uint32_t bit   = idx;\n\n      // Compiler doesn't remove this otherwise.\n      if constexpr (Dwords > 1) {\n        dword = idx / 32;\n        bit   = idx % 32;\n      }\n\n      return m_dwords[dword] & (1u << bit);\n    }\n\n    constexpr void set(uint32_t idx, bool value) {\n      uint32_t dword = 0;\n      uint32_t bit   = idx;\n\n      // Compiler doesn't remove this otherwise.\n      if constexpr (Dwords > 1) {\n        dword = idx / 32;\n        bit   = idx % 32;\n      }\n\n      if (value)\n        m_dwords[dword] |= 1u << bit;\n      else\n        m_dwords[dword] &= ~(1u << bit);\n    }\n\n    constexpr bool exchange(uint32_t idx, bool value) {\n      bool oldValue = get(idx);\n      set(idx, value);\n      return oldValue;\n    }\n\n    constexpr void flip(uint32_t idx) {\n      uint32_t dword = 0;\n      uint32_t bit   = idx;\n\n      // Compiler doesn't remove this otherwise.\n      if constexpr (Dwords > 1) {\n        dword = idx / 32;\n        bit   = idx % 32;\n      }\n\n      m_dwords[dword] ^= 1u << bit;\n    }\n\n    constexpr void setAll() {\n      if constexpr (Bits % 32 == 0) {\n        for (size_t i = 0; i < Dwords; i++)\n          m_dwords[i] = std::numeric_limits<uint32_t>::max();\n      }\n      else {\n        for (size_t i = 0; i < Dwords - 1; i++)\n          m_dwords[i] = std::numeric_limits<uint32_t>::max();\n\n        m_dwords[Dwords - 1] = (1u << (Bits % 32)) - 1;\n      }\n    }\n\n    constexpr void clearAll() {\n      for (size_t i = 0; i < Dwords; i++)\n        m_dwords[i] = 0;\n    }\n\n    constexpr bool any() const {\n      for (size_t i = 0; i < Dwords; i++) {\n        if (m_dwords[i] != 0)\n          return true;\n      }\n\n      return false;\n    }\n\n    constexpr uint32_t& dword(uint32_t idx) {\n      return m_dwords[idx];\n    }\n\n    constexpr size_t bitCount() {\n      return Bits;\n    }\n\n    constexpr size_t dwordCount() {\n      return Dwords;\n    }\n\n    constexpr bool operator [] (uint32_t idx) const {\n      return get(idx);\n    }\n\n    constexpr void setN(uint32_t bits) {\n      uint32_t fullDwords = bits / 32;\n      uint32_t offset = bits % 32;\n\n      for (size_t i = 0; i < fullDwords; i++)\n        m_dwords[i] = std::numeric_limits<uint32_t>::max();\n     \n      if (offset > 0)\n        m_dwords[fullDwords] = (1u << offset) - 1;\n    }\n\n  private:\n\n    uint32_t m_dwords[Dwords];\n\n  };\n\n  class bitvector {\n  public:\n\n    bool get(uint32_t idx) const {\n      uint32_t dword = idx / 32;\n      uint32_t bit   = idx % 32;\n\n      return m_dwords[dword] & (1u << bit);\n    }\n\n    void ensureSize(uint32_t bitCount) {\n      uint32_t dword = bitCount / 32;\n      if (unlikely(dword >= m_dwords.size())) {\n        m_dwords.resize(dword + 1);\n      }\n      m_bitCount = std::max(m_bitCount, bitCount);\n    }\n\n    void set(uint32_t idx, bool value) {\n      ensureSize(idx + 1);\n\n      uint32_t dword = 0;\n      uint32_t bit   = idx;\n\n      if (value)\n        m_dwords[dword] |= 1u << bit;\n      else\n        m_dwords[dword] &= ~(1u << bit);\n    }\n\n    bool exchange(uint32_t idx, bool value) {\n      ensureSize(idx + 1);\n\n      bool oldValue = get(idx);\n      set(idx, value);\n      return oldValue;\n    }\n\n    void flip(uint32_t idx) {\n      ensureSize(idx + 1);\n\n      uint32_t dword = idx / 32;\n      uint32_t bit   = idx % 32;\n\n      m_dwords[dword] ^= 1u << bit;\n    }\n\n    void setAll() {\n      if (m_bitCount % 32 == 0) {\n        for (size_t i = 0; i < m_dwords.size(); i++)\n          m_dwords[i] = std::numeric_limits<uint32_t>::max();\n      }\n      else {\n        for (size_t i = 0; i < m_dwords.size() - 1; i++)\n          m_dwords[i] = std::numeric_limits<uint32_t>::max();\n\n        m_dwords[m_dwords.size() - 1] = (1u << (m_bitCount % 32)) - 1;\n      }\n    }\n\n    void clearAll() {\n      for (size_t i = 0; i < m_dwords.size(); i++)\n        m_dwords[i] = 0;\n    }\n\n    bool any() const {\n      for (size_t i = 0; i < m_dwords.size(); i++) {\n        if (m_dwords[i] != 0)\n          return true;\n      }\n\n      return false;\n    }\n\n    uint32_t& dword(uint32_t idx) {\n      return m_dwords[idx];\n    }\n\n    size_t bitCount() const {\n      return m_bitCount;\n    }\n\n    size_t dwordCount() const {\n      return m_dwords.size();\n    }\n\n    bool operator [] (uint32_t idx) const {\n      return get(idx);\n    }\n\n    void setN(uint32_t bits) {\n      ensureSize(bits);\n\n      uint32_t fullDwords = bits / 32;\n      uint32_t offset = bits % 32;\n\n      for (size_t i = 0; i < fullDwords; i++)\n        m_dwords[i] = std::numeric_limits<uint32_t>::max();\n\n      if (offset > 0)\n        m_dwords[fullDwords] = (1u << offset) - 1;\n    }\n\n  private:\n\n    std::vector<uint32_t> m_dwords;\n    uint32_t              m_bitCount = 0;\n\n  };\n\n  template<typename T>\n  class BitMask {\n\n  public:\n\n    class iterator {\n    public:\n      using iterator_category = std::input_iterator_tag;\n      using value_type = T;\n      using difference_type = T;\n      using pointer = const T*;\n      using reference = T;\n\n      explicit iterator(T flags)\n        : m_mask(flags) { }\n\n      iterator& operator ++ () {\n        m_mask &= m_mask - 1;\n        return *this;\n      }\n\n      iterator operator ++ (int) {\n        iterator retval = *this;\n        m_mask &= m_mask - 1;\n        return retval;\n      }\n\n      T operator * () const {\n        return bsf(m_mask);\n      }\n\n      bool operator == (iterator other) const { return m_mask == other.m_mask; }\n      bool operator != (iterator other) const { return m_mask != other.m_mask; }\n\n    private:\n\n      T m_mask;\n\n    };\n\n    BitMask()\n      : m_mask(0) { }\n\n    explicit BitMask(T n)\n      : m_mask(n) { }\n\n    iterator begin() {\n      return iterator(m_mask);\n    }\n\n    iterator end() {\n      return iterator(0);\n    }\n\n  private:\n\n    T m_mask;\n\n  };\n\n\n  /**\n   * \\brief Encodes float as fixed point\n   *\n   * Rounds away from zero. If this is not suitable for\n   * certain use cases, implement round to nearest even.\n   * \\tparam T Integer type, may be signed\n   * \\tparam I Integer bits\n   * \\tparam F Fractional bits\n   * \\param n Float to encode\n   * \\returns Encoded fixed-point value\n   */\n  template<typename T, int32_t I, int32_t F>\n  T encodeFixed(float n) {\n    if (n != n)\n      return 0u;\n\n    n *= float(1u << F);\n\n    if constexpr (std::is_signed_v<T>) {\n      n = std::max(n, -float(1u << (I + F - 1u)));\n      n = std::min(n,  float(1u << (I + F - 1u)) - 1.0f);\n      n += n < 0.0f ? -0.5f : 0.5f;\n    } else {\n      n = std::max(n, 0.0f);\n      n = std::min(n, float(1u << (I + F)) - 1.0f);\n      n += 0.5f;\n    }\n\n    T result = T(n);\n\n    if constexpr (std::is_signed_v<T>)\n      result &= ((T(1u) << (I + F)) - 1u);\n\n    return result;\n  }\n\n\n  /**\n   * \\brief Decodes fixed-point integer to float\n   *\n   * \\tparam T Integer type, may be signed\n   * \\tparam I Integer bits\n   * \\tparam F Fractional bits\n   * \\param n Number to decode\n   * \\returns Decoded  number\n   */\n  template<typename T, int32_t I, int32_t F>\n  float decodeFixed(T n) {\n    // Sign-extend as necessary\n    if constexpr (std::is_signed_v<T>)\n      n -= (n & (T(1u) << (I + F - 1u))) << 1u;\n\n    return float(n) / float(1u << F);\n  }\n\n\n  /**\n   * \\brief Inserts one null bit after each bit\n   */\n  inline uint32_t split2(uint32_t c) {\n    c = (c ^ (c << 8u)) & 0x00ff00ffu;\n    c = (c ^ (c << 4u)) & 0x0f0f0f0fu;\n    c = (c ^ (c << 2u)) & 0x33333333u;\n    c = (c ^ (c << 1u)) & 0x55555555u;\n    return c;\n  }\n\n\n  /**\n   * \\brief Inserts two null bits after each bit\n   */\n  inline uint64_t split3(uint64_t c) {\n    c = (c | c << 32u) & 0x001f00000000ffffull;\n    c = (c | c << 16u) & 0x001f0000ff0000ffull;\n    c = (c | c <<  8u) & 0x100f00f00f00f00full;\n    c = (c | c <<  4u) & 0x10c30c30c30c30c3ull;\n    c = (c | c <<  2u) & 0x1249249249249249ull;\n    return c;\n  }\n\n\n  /**\n   * \\brief Interleaves bits from two integers\n   *\n   * Both numbers must fit into 16 bits.\n   * \\param [in] x X coordinate\n   * \\param [in] y Y coordinate\n   * \\returns Morton code of x and y\n   */\n  inline uint32_t interleave(uint16_t x, uint16_t y) {\n    return split2(x) | (split2(y) << 1u);\n  }\n\n\n  /**\n   * \\brief Interleaves bits from three integers\n   *\n   * All three numbers must fit into 16 bits.\n   */\n  inline uint64_t interleave(uint16_t x, uint16_t y, uint16_t z) {\n    return split3(x) | (split3(y) << 1u) | (split3(z) << 2u);\n  }\n\n\n  /**\n   * \\brief 48-bit integer storage type\n   */\n  struct uint48_t {\n    explicit uint48_t(uint64_t n)\n    : a(uint16_t(n)), b(uint16_t(n >> 16)), c(uint16_t(n >> 32)) { }\n\n    uint16_t a;\n    uint16_t b;\n    uint16_t c;\n\n    explicit operator uint64_t () const {\n      // GCC generates worse code if we promote to uint64 directly\n      uint32_t lo = uint32_t(a) | (uint32_t(b) << 16);\n      return uint64_t(lo) | (uint64_t(c) << 32);\n    }\n  };\n\n\n  /**\n   * \\brief FNV-1a hash implementation\n   */\n  inline uint64_t fnv1a_init() {\n    return 0xcbf29ce484222325ull;\n  }\n\n  template<typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>\n  uint64_t fnv1a_iter(uint64_t hash, T value) {\n    return (hash ^ uint64_t(value)) * 0x100000001b3ull;\n  }\n\n  inline uint64_t fnv1a_hash(const unsigned char* data, size_t size) {\n    uint64_t hash = fnv1a_init();\n    size_t idx = 0u;\n\n    while (idx + sizeof(hash) <= size) {\n      uint64_t v = (uint64_t(data[idx + 0u]) <<  0u)\n                 | (uint64_t(data[idx + 1u]) <<  8u)\n                 | (uint64_t(data[idx + 2u]) << 16u)\n                 | (uint64_t(data[idx + 3u]) << 24u)\n                 | (uint64_t(data[idx + 4u]) << 32u)\n                 | (uint64_t(data[idx + 5u]) << 40u)\n                 | (uint64_t(data[idx + 6u]) << 48u)\n                 | (uint64_t(data[idx + 7u]) << 56u);\n\n      hash = fnv1a_iter(hash, v);\n      idx += sizeof(hash);\n    }\n\n    if (idx < size) {\n      uint64_t v = 0u;\n\n      while (idx < size) {\n        v |= uint64_t(data[idx]) << (8u * (idx % sizeof(hash)));\n        idx++;\n      }\n\n      hash = fnv1a_iter(hash, v);\n    }\n\n    hash = fnv1a_iter(hash, size);\n    return hash;\n  }\n\n  inline uint64_t fnv1a_hash(const char* data, size_t size) {\n    return fnv1a_hash(reinterpret_cast<const unsigned char*>(data), size);\n  }\n\n}\n"
  },
  {
    "path": "src/util/util_enum.h",
    "content": "#pragma once\n\n#define ENUM_NAME(name) \\\n  case name: return os << #name\n\n#define ENUM_DEFAULT(name) \\\n  default: return os << static_cast<int32_t>(e)\n"
  },
  {
    "path": "src/util/util_env.cpp",
    "content": "#include <array>\n#include <cstdlib>\n#include <filesystem>\n#include <numeric>\n\n#ifdef __linux__\n#include <unistd.h>\n#include <limits.h>\n#elif defined(__FreeBSD__)\n#include <sys/sysctl.h>\n#include <unistd.h>\n#include <limits.h>\n#endif\n\n#include \"util_env.h\"\n\n#include \"./com/com_include.h\"\n\nnamespace dxvk::env {\n\n  std::string getEnvVar(const char* name) {\n#ifdef _WIN32\n    std::vector<WCHAR> result;\n    result.resize(MAX_PATH + 1);\n\n    DWORD len = ::GetEnvironmentVariableW(str::tows(name).c_str(), result.data(), MAX_PATH);\n    if (!len || len >= MAX_PATH)\n      return \"\";\n    result.resize(len + 1);\n\n    return str::fromws(result.data());\n#else\n    const char* result = std::getenv(name);\n    return result ? result : \"\";\n#endif\n  }\n\n\n  size_t matchFileExtension(const std::string& name, const char* ext) {\n    auto pos = name.find_last_of('.');\n\n    if (pos == std::string::npos)\n      return pos;\n\n    bool matches = std::accumulate(name.begin() + pos + 1, name.end(), true,\n      [&ext] (bool current, char a) {\n        if (a >= 'A' && a <= 'Z')\n          a += 'a' - 'A';\n        return current && *ext && a == *(ext++);\n      });\n\n    return matches ? pos : std::string::npos;\n  }\n\n\n  std::string getExeName() {\n    std::string fullPath = getExePath();\n    auto n = fullPath.find_last_of(env::PlatformDirSlash);\n    \n    return (n != std::string::npos)\n      ? fullPath.substr(n + 1)\n      : fullPath;\n  }\n\n\n  std::string getExeBaseName() {\n    auto exeName = getExeName();\n#ifdef _WIN32\n    auto extp = matchFileExtension(exeName, \"exe\");\n\n    if (extp != std::string::npos)\n      exeName.erase(extp);\n#endif\n\n    return exeName;\n  }\n\n\n  std::string getExePath() {\n#if defined(_WIN32)\n    std::vector<WCHAR> exePath;\n    exePath.resize(MAX_PATH + 1);\n\n    DWORD len = ::GetModuleFileNameW(NULL, exePath.data(), MAX_PATH);\n    if (!len || len == MAX_PATH)\n      return \"\";\n    exePath.resize(len + 1);\n\n    return str::fromws(exePath.data());\n#elif defined(__linux__)\n    std::array<char, PATH_MAX> exePath = {};\n\n    size_t count = readlink(\"/proc/self/exe\", exePath.data(), exePath.size());\n\n    return std::string(exePath.begin(), exePath.begin() + count);\n#elif defined(__FreeBSD__)\n    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, getpid()};\n    char exePath[PATH_MAX] = {};\n    size_t size = PATH_MAX;\n\n    if (sysctl(mib, 4, exePath, &size, NULL, 0) != 0) {\n        // throw error here?\n        return \"\";\n    }\n\n    return std::string(exePath);\n#endif\n  }\n  \n  \n  void setThreadName(const std::string& name) {\n#ifdef _WIN32\n    using SetThreadDescriptionProc = HRESULT (WINAPI *) (HANDLE, PCWSTR);\n\n    static auto SetThreadDescription = reinterpret_cast<SetThreadDescriptionProc>(\n      ::GetProcAddress(::GetModuleHandleW(L\"kernel32.dll\"), \"SetThreadDescription\"));\n\n    if (SetThreadDescription) {\n      std::array<wchar_t, 16> wideName = { };\n\n      str::transcodeString(\n        wideName.data(), wideName.size() - 1,\n        name.data(), name.size());\n\n      SetThreadDescription(::GetCurrentThread(), wideName.data());\n    }\n#else\n    std::array<char, 16> posixName = {};\n    dxvk::str::strlcpy(posixName.data(), name.c_str(), 16);\n    ::pthread_setname_np(pthread_self(), posixName.data());\n#endif\n  }\n\n\n  bool createDirectory(const std::string& path) {\n#ifdef _WIN32\n    std::array<WCHAR, MAX_PATH + 1> widePath;\n\n    size_t length = str::transcodeString(\n      widePath.data(), widePath.size() - 1,\n      path.data(), path.size());\n\n    widePath[length] = L'\\0';\n\n    if (!CreateDirectoryW(widePath.data(), nullptr))\n      return GetLastError() == ERROR_ALREADY_EXISTS;\n\n    return true;\n#else\n    return std::filesystem::is_directory(path) || std::filesystem::create_directories(path);\n#endif\n  }\n  \n}\n"
  },
  {
    "path": "src/util/util_env.h",
    "content": "#pragma once\n\n#include \"util_string.h\"\n\nnamespace dxvk::env {\n  \n#ifdef _WIN32\n  constexpr char PlatformDirSlash = '\\\\';\n#else\n  constexpr char PlatformDirSlash = '/';\n#endif\n\n  /**\n   * \\brief Checks whether the host platform is 32-bit\n   */\n  constexpr bool is32BitHostPlatform() {\n    return sizeof(void*) == 4;\n  }\n\n  /**\n   * \\brief Gets environment variable\n   * \n   * If the variable is not defined, this will return\n   * an empty string. Note that environment variables\n   * may be defined with an empty value.\n   * \\param [in] name Name of the variable\n   * \\returns Value of the variable\n   */\n  std::string getEnvVar(const char* name);\n  \n  /**\n   * \\brief Checks whether a file name has a given extension\n   *\n   * \\param [in] name File name\n   * \\param [in] ext Extension to match, in lowercase letters\n   * \\returns Position of the extension within the file name, or\n   *    \\c std::string::npos if the file has a different extension\n   */\n  size_t matchFileExtension(const std::string& name, const char* ext);\n\n  /**\n   * \\brief Gets the executable name\n   * \n   * Returns the base name (not the full path) of the\n   * program executable, including the file extension.\n   * This function should be used to identify programs.\n   * \\returns Executable name\n   */\n  std::string getExeName();\n  \n  /**\n   * \\brief Gets the executable name without extension\n   *\n   * Same as \\ref getExeName but without the file extension.\n   * \\returns Executable name\n   */\n  std::string getExeBaseName();\n\n  /**\n   * \\brief Gets full path to executable\n   * \\returns Path to executable\n   */\n  std::string getExePath();\n  \n  /**\n   * \\brief Sets name of the calling thread\n   * \\param [in] name Thread name\n   */\n  void setThreadName(const std::string& name);\n\n  /**\n   * \\brief Creates a directory\n   * \n   * \\param [in] path Path to directory\n   * \\returns \\c true on success\n   */\n  bool createDirectory(const std::string& path);\n  \n}\n"
  },
  {
    "path": "src/util/util_error.h",
    "content": "#pragma once\n\n#include <string>\n\nnamespace dxvk {\n  \n  /**\n   * \\brief DXVK error\n   * \n   * A generic exception class that stores a\n   * message. Exceptions should be logged.\n   */\n  class DxvkError {\n    \n  public:\n    \n    DxvkError() { }\n    DxvkError(std::string&& message)\n    : m_message(std::move(message)) { }\n    \n    const std::string& message() const {\n      return m_message;\n    }\n    \n  private:\n    \n    std::string m_message;\n    \n  };\n  \n}"
  },
  {
    "path": "src/util/util_file.cpp",
    "content": "#include <fstream>\n\n#include \"./com/com_include.h\"\n\n#include \"./log/log.h\"\n\n#include \"util_file.h\"\n#include \"util_string.h\"\n\nnamespace dxvk::util {\n\n#ifdef _WIN32\n  class Win32File : public FileIface {\n\n  public:\n\n    Win32File(const std::string& path, FileFlags flags)\n    : m_flags(flags) {\n      DWORD access = 0u;\n      DWORD share = 0u;\n      DWORD mode = 0u;\n\n      if (flags.test(FileFlag::AllowRead)) {\n        access |= GENERIC_READ;\n        share |= FILE_SHARE_READ;\n        mode = OPEN_EXISTING;\n      }\n\n      if (flags.test(FileFlag::AllowWrite)) {\n        access |= GENERIC_WRITE;\n        share |= FILE_SHARE_WRITE;\n        mode = OPEN_EXISTING;\n\n        if (flags.test(FileFlag::Truncate))\n          mode = CREATE_ALWAYS;\n      }\n\n      if (flags.test(FileFlag::Exclusive))\n        share = 0u;\n\n      std::array<WCHAR, MAX_PATH + 1u> pathCvt;\n\n      size_t len = str::transcodeString(\n        pathCvt.data(), pathCvt.size(),\n        path.data(), path.size());\n\n      pathCvt[len] = '\\0';\n\n      m_file = CreateFileW(pathCvt.data(),\n        access, share, nullptr, mode, FILE_ATTRIBUTE_NORMAL, nullptr);\n\n      if (!m_file)\n        m_file = INVALID_HANDLE_VALUE;\n    }\n\n    ~Win32File() {\n      CloseHandle(m_file);\n    }\n\n    bool read(size_t offset, size_t size, void* data) {\n      if (!seek(offset, FILE_BEGIN))\n        return false;\n\n      return read(size, data);\n    }\n\n    bool write(size_t offset, size_t size, const void* data) {\n      if (!seek(offset, FILE_BEGIN))\n        return false;\n\n      return write(size, data);\n    }\n\n    bool append(size_t size, const void* data) {\n      if (!seek(0, FILE_END))\n        return false;\n\n      return write(size, data);\n    }\n\n    size_t size() {\n      if (m_file == INVALID_HANDLE_VALUE)\n        return 0u;\n\n      LARGE_INTEGER size = { };\n\n      if (!GetFileSizeEx(m_file, &size))\n        return 0u;\n\n      return size.QuadPart;\n    }\n\n    bool status() const {\n      return m_file != INVALID_HANDLE_VALUE;\n    }\n\n    bool flush() {\n      return FlushFileBuffers(m_file);\n    }\n\n  private:\n\n    FileFlags m_flags = { };\n    HANDLE    m_file  = INVALID_HANDLE_VALUE;\n\n    bool seek(size_t offset, DWORD method) {\n      if (!m_file)\n        return false;\n\n      LARGE_INTEGER address = { };\n      address.QuadPart = offset;\n\n      return SetFilePointerEx(m_file, address, nullptr, method);\n    }\n\n    bool read(size_t size, void* data) {\n      auto buffer = reinterpret_cast<char*>(data);\n\n      while (size) {\n        DWORD read = 0u;\n\n        if (!ReadFile(m_file, buffer, size, &read, nullptr) || !read)\n          return false;\n\n        buffer += read;\n        size -= read;\n      }\n\n      return true;\n    }\n\n    bool write(size_t size, const void* data) {\n      auto buffer = reinterpret_cast<const char*>(data);\n\n      while (size) {\n        DWORD written = 0u;\n\n        if (!WriteFile(m_file, buffer, size, &written, nullptr) || !written)\n          return false;\n\n        buffer += written;\n        size -= written;\n      }\n\n      return true;\n    }\n\n  };\n\n  using FileImpl = Win32File;\n#else\n\n  class StlFile : public FileIface {\n\n  public:\n\n    StlFile(const std::string& path, FileFlags flags) {\n      std::ios_base::openmode mode = std::ios_base::binary;\n\n      if (flags.test(FileFlag::AllowRead))\n        mode |= std::ios_base::in;\n\n      if (flags.test(FileFlag::AllowWrite))\n        mode |= std::ios_base::out;\n\n      if (flags.test(FileFlag::Truncate))\n        mode |= std::ios_base::trunc;\n\n      m_file.open(path, mode);\n    }\n\n    ~StlFile() {\n\n    }\n\n    bool read(size_t offset, size_t size, void* data) {\n      if (!status())\n        return false;\n\n      if (!m_file.seekg(offset, std::ios_base::beg))\n        return false;\n\n      return bool(m_file.read(reinterpret_cast<char*>(data), size));\n    }\n\n    bool write(size_t offset, size_t size, const void* data) {\n      if (!status())\n        return false;\n\n      if (!m_file.seekp(offset, std::ios_base::beg))\n        return false;\n\n      return bool(m_file.write(reinterpret_cast<const char*>(data), size));\n    }\n\n    bool append(size_t size, const void* data) {\n      if (!status())\n        return false;\n\n      if (!m_file.seekp(0, std::ios_base::end))\n        return false;\n\n      return bool(m_file.write(reinterpret_cast<const char*>(data), size));\n    }\n\n    size_t size() {\n      if (status()) {\n        if (m_flags.test(FileFlag::AllowWrite)) {\n          if (m_file.seekp(0, std::ios_base::end))\n            return size_t(m_file.tellp());\n        } else {\n          if (m_file.seekg(0, std::ios_base::end))\n            return size_t(m_file.tellg());\n        }\n      }\n\n      return 0u;\n    }\n\n    bool status() const {\n      return m_file.is_open() && m_file;\n    }\n\n    bool flush() {\n      if (!status())\n        return false;\n\n      m_file.flush();\n      return true;\n    }\n\n  private:\n\n    FileFlags     m_flags = { };\n    std::fstream  m_file;\n\n  };\n\n  using FileImpl = StlFile;\n#endif\n\n  FileIface::~FileIface() {\n\n  }\n\n\n  File::File() {\n\n  }\n\n  File::File(const std::string& path, FileFlags flags)\n  : m_impl(new FileImpl(path, flags)) {\n\n  }\n\n  File::File(File&& other)\n  : m_impl(std::move(other.m_impl)) {\n\n  }\n\n  File& File::operator = (File&& other) {\n    if (&other != this) {\n      m_impl = nullptr;\n      m_impl = std::move(other.m_impl);\n    }\n\n    return *this;\n  }\n\n  File::~File() {\n\n  }\n\n  bool File::open(const std::string& path, FileFlags flags) {\n    m_impl = nullptr;\n    m_impl = new FileImpl(path, flags);\n    return m_impl->status();\n  }\n\n  bool File::read(size_t offset, size_t size, void* data) {\n    return m_impl && m_impl->read(offset, size, data);\n  }\n\n  bool File::write(size_t offset, size_t size, const void* data) {\n    return m_impl && m_impl->write(offset, size, data);\n  }\n\n  bool File::append(size_t size, const void* data) {\n    return m_impl && m_impl->append(size, data);\n  }\n\n  size_t File::size() {\n    if (!m_impl)\n      return 0u;\n\n    return m_impl->size();\n  }\n\n  bool File::flush() {\n    return m_impl && m_impl->flush();\n  }\n\n  File::operator bool () const {\n    return m_impl && m_impl->status();\n  }\n\n}\n"
  },
  {
    "path": "src/util/util_file.h",
    "content": "#pragma once\n\n#include <atomic>\n#include <cstddef>\n#include <cstdint>\n\n#include \"util_flags.h\"\n#include \"util_likely.h\"\n\n#include \"./rc/util_rc_ptr.h\"\n\nnamespace dxvk::util {\n\n  /**\n   * \\brief File flags\n   */\n  enum class FileFlag : uint32_t {\n    AllowRead       = 0,\n    AllowWrite      = 1,\n    Truncate        = 2,\n    Exclusive       = 3,\n  };\n\n  using FileFlags = Flags<FileFlag>;\n\n\n  /**\n   * \\brief Platform-specific file interface\n   */\n  class FileIface {\n\n  public:\n\n    virtual ~FileIface();\n\n    virtual bool read(size_t offset, size_t size, void* data) = 0;\n\n    virtual bool write(size_t offset, size_t size, const void* data) = 0;\n\n    virtual bool append(size_t size, const void* data) = 0;\n\n    virtual size_t size() = 0;\n\n    virtual bool status() const = 0;\n\n    virtual bool flush() = 0;\n\n    force_inline void incRef() {\n      m_refCount.fetch_add(1u, std::memory_order_acquire);\n    }\n\n    force_inline void decRef() {\n      if (m_refCount.fetch_sub(1u, std::memory_order_acquire) == 1u)\n        delete this;\n    }\n\n  private:\n\n    std::atomic<uint32_t> m_refCount = { 0u };\n\n  };\n\n\n  /**\n   * \\brief Generic file interface\n   *\n   * Provides a basic API for exclusive file I/O, which is\n   * not (yet) available in the cpp standard library.\n   * Note that this file API is not thread-safe.\n   */\n  class File {\n\n  public:\n\n    File();\n\n    File(const std::string& path, FileFlags flags);\n\n    File(File&& other);\n\n    File& operator = (File&& other);\n\n    ~File();\n\n    bool open(const std::string& path, FileFlags flags);\n\n    bool read(size_t offset, size_t size, void* data);\n\n    bool write(size_t offset, size_t size, const void* data);\n\n    bool append(size_t size, const void* data);\n\n    size_t size();\n\n    bool flush();\n\n    explicit operator bool () const;\n\n  private:\n\n    Rc<FileIface> m_impl;\n\n  };\n\n}\n"
  },
  {
    "path": "src/util/util_flags.h",
    "content": "#pragma once\n\n#include <type_traits>\n\n#include \"util_bit.h\"\n\nnamespace dxvk {\n  \n  template<typename T>\n  class Flags {\n    \n  public:\n    \n    using IntType = std::underlying_type_t<T>;\n    \n    Flags() { }\n    \n    Flags(IntType t)\n    : m_bits(t) { }\n    \n    template<typename... Tx>\n    Flags(T f, Tx... fx) {\n      this->set(f, fx...);\n    }\n    \n    template<typename... Tx>\n    void set(Tx... fx) {\n      m_bits |= bits(fx...);\n    }\n    \n    void set(Flags flags) {\n      m_bits |= flags.m_bits;\n    }\n    \n    template<typename... Tx>\n    void clr(Tx... fx) {\n      m_bits &= ~bits(fx...);\n    }\n    \n    void clr(Flags flags) {\n      m_bits &= ~flags.m_bits;\n    }\n    \n    template<typename... Tx>\n    bool any(Tx... fx) const {\n      return (m_bits & bits(fx...)) != 0;\n    }\n    \n    template<typename... Tx>\n    bool all(Tx... fx) const {\n      const IntType mask = bits(fx...);\n      return (m_bits & mask) == mask;\n    }\n    \n    bool test(T f) const {\n      return this->any(f);\n    }\n    \n    bool isClear() const {\n      return m_bits == 0;\n    }\n    \n    void clrAll() {\n      m_bits = 0;\n    }\n    \n    IntType raw() const {\n      return m_bits;\n    }\n    \n    Flags operator & (const Flags& other) const {\n      return Flags(m_bits & other.m_bits);\n    }\n    \n    Flags operator | (const Flags& other) const {\n      return Flags(m_bits | other.m_bits);\n    }\n    \n    Flags operator ^ (const Flags& other) const {\n      return Flags(m_bits ^ other.m_bits);\n    }\n\n    bool operator == (const Flags& other) const {\n      return m_bits == other.m_bits;\n    }\n    \n    bool operator != (const Flags& other) const {\n      return m_bits != other.m_bits;\n    }\n    \n  private:\n    \n    IntType m_bits = 0;\n    \n    static IntType bit(T f) {\n      return IntType(1) << static_cast<IntType>(f);\n    }\n    \n    template<typename... Tx>\n    static IntType bits(T f, Tx... fx) {\n      return bit(f) | bits(fx...);\n    }\n    \n    static IntType bits() {\n      return 0;\n    }\n    \n  };\n  \n}"
  },
  {
    "path": "src/util/util_flush.cpp",
    "content": "#include \"util_flush.h\"\n#include \"util_string.h\"\n#include \"log/log.h\"\n\nnamespace dxvk {\n\n  GpuFlushTracker::GpuFlushTracker(GpuFlushType maxType)\n  : m_maxType(maxType) {\n\n  }\n\n  bool GpuFlushTracker::considerFlush(\n          GpuFlushType          flushType,\n          uint64_t              chunkId,\n          uint32_t              lastCompleteSubmissionId,\n          uint64_t              estimatedCost) {\n    constexpr uint32_t minPendingSubmissions = 2;\n\n    constexpr uint32_t minChunkCount =  3u;\n    constexpr uint32_t maxChunkCount = 20u;\n\n    // Do not flush if there is nothing to flush\n    uint32_t chunkCount = uint32_t(chunkId - m_lastFlushChunkId);\n\n    if (!chunkCount)\n      return false;\n\n    // Deliberately ignore cost heuristic if we're not categorically ignoring\n    // submission requests anyway, since we should never submit enough to time\n    // out with the chunk-based heuristic.\n    if (flushType > m_maxType)\n      return estimatedCost >= GpuCostEstimate::MaxCostPerSubmission;\n\n    // Take any earlier missed flush with a stronger hint into account, so\n    // that we still flush those as soon as possible. Ignore synchronization\n    // commands since they will either perform a flush or not need it at all.\n    flushType = std::min(flushType, m_lastMissedType);\n\n    if (flushType != GpuFlushType::ImplicitSynchronization)\n      m_lastMissedType = flushType;\n\n    switch (flushType) {\n      case GpuFlushType::ExplicitFlush: {\n        // This shouldn't really be called for explicit flushes,\n        // but handle them anyway for the sake of completeness\n        return true;\n      }\n\n      case GpuFlushType::ImplicitStrongHint: {\n        // Flush aggressively with a strong hint to reduce readback latency.\n        return chunkCount >= minChunkCount;\n      }\n\n      case GpuFlushType::ImplicitWeakHint: {\n        // Aim for a higher number of chunks per submission with\n        // a weak hint in order to avoid submitting too often.\n        if (chunkCount < 2 * minChunkCount)\n          return false;\n\n        // Actual heuristic is shared with synchronization commands\n      } [[fallthrough]];\n\n      case GpuFlushType::ImplicitSynchronization: {\n        // If the GPU is about to go idle, flush aggressively. This may be\n        // required if the application is spinning on a query or resource.\n        uint32_t pendingSubmissions = uint32_t(m_lastFlushSubmissionId - lastCompleteSubmissionId);\n\n        if (pendingSubmissions < minPendingSubmissions)\n          return true;\n\n        // Use the number of pending submissions to decide whether to flush. Other\n        // than ignoring the minimum chunk count condition, we should treat this\n        // the same as weak hints to avoid unnecessary synchronization.\n        uint32_t threshold = std::min(maxChunkCount, pendingSubmissions * minChunkCount);\n        return chunkCount >= threshold;\n      }\n\n      case GpuFlushType::None:\n        return false;\n    }\n\n    // Should be unreachable\n    return false;\n  }\n\n\n  void GpuFlushTracker::notifyFlush(\n          uint64_t              chunkId,\n          uint64_t              submissionId) {\n    m_lastMissedType = GpuFlushType::None;\n\n    m_lastFlushChunkId = chunkId;\n    m_lastFlushSubmissionId = submissionId;\n  }\n\n}\n"
  },
  {
    "path": "src/util/util_flush.h",
    "content": "#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\nnamespace dxvk {\n\n  /**\n   * \\brief GPU cost estimate for various operations\n   *\n   * These provide only a very rough estimate for GPU execution times,\n   * which can be useful to avoid GPU time-outs in some situations.\n   */\n  struct GpuCostEstimate {\n    /** Assume that compute dispatches are much more expensive than draws\n     *  regardless of workgroup counts. This is not always true, but may\n     *  help account for immediate synchronization or complex shaders that\n     *  we do not generally have any up-front knowledge about. */\n    static constexpr uint64_t Dispatch              = 4u;\n    static constexpr uint64_t DispatchIndirect      = 5u;\n    /** Assume a high base cost per render pass. We're not counting draws\n     *  in order to avoid splitting passes on tiling GPUs, and draw costs\n     *  can vary wildly anyway. */\n    static constexpr uint64_t RenderPass            = 10u;\n    /** Transfer cost can vary wildly, but so do use cases. Just assume\n     *  a low cost, especially since synchronization on back-to-back\n     *  transfers is unlikely to be necessary. */\n    static constexpr uint64_t Transfer              = 2u;\n\n    /** Cost threshold at which submissions are always preferred */\n    static constexpr uint64_t MaxCostPerSubmission  = 1'500u;\n  };\n\n\n  /**\n   * \\brief GPU context flush type\n   */\n  enum class GpuFlushType : uint32_t {\n    /** Flush or Present called by application */\n    ExplicitFlush           = 0,\n    /** Function that requires GPU synchronization and\n     *  may require a flush called by application */\n    ImplicitSynchronization = 1,\n    /** GPU command that applications are likely to synchronize\n     *  with soon has been recorded into the command list */\n    ImplicitStrongHint      = 2,\n    /** GPU commands have been recorded and a flush should be\n     *  performed if the current command list is large enough. */\n    ImplicitWeakHint        = 3,\n\n    /** No flush. Must be the highest enum value. */\n    None                    = ~0u\n  };\n\n\n  /**\n   * \\brief GPU flush tracker\n   *\n   * Helper class that implements a context flush\n   * heuristic for various scenarios.\n   */\n  class GpuFlushTracker {\n\n  public:\n\n    GpuFlushTracker(GpuFlushType maxAllowed);\n\n    /**\n     * \\brief Queries type of last missed submission request\n     *\n     * If \\c considerFlush has returned \\c false, the strongest request type\n     * will be tracked so that a submission can be performed as soon as the\n     * corresponding heuristic allows it.\n     * \\returns Missed submission request type, or \\c GpuFlushType::None if\n     *    no submission request has been missed.\n     */\n    GpuFlushType getPendingType() const {\n      return m_lastMissedType;\n    }\n\n    /**\n     * \\brief Checks whether a context flush should be performed\n     *\n     * Note that this modifies internal state, and depending on the\n     * flush type, this may influence the decision for future flushes.\n     * \\param [in] flushType Flush type\n     * \\param [in] chunkId GPU command sequence number\n     * \\param [in] lastCompleteSubmissionId Last completed command submission ID\n     * \\param [in] estimatedCost Estimated submission cost\n     * \\returns \\c true if a flush should be performed\n     */\n    bool considerFlush(\n            GpuFlushType          flushType,\n            uint64_t              chunkId,\n            uint32_t              lastCompleteSubmissionId,\n            uint64_t              estimatedCost);\n\n    /**\n     * \\brief Notifies tracker about a context flush\n     *\n     * \\param [in] chunkId GPU command sequence number\n     * \\param [in] submissionId Command submission ID\n     */\n    void notifyFlush(\n            uint64_t              chunkId,\n            uint64_t              submissionId);\n\n  private:\n\n    GpuFlushType  m_maxType               = GpuFlushType::ImplicitWeakHint;\n    GpuFlushType  m_lastMissedType        = GpuFlushType::None;\n\n    uint64_t      m_lastFlushChunkId      = 0ull;\n    uint64_t      m_lastFlushSubmissionId = 0ull;\n\n  };\n\n}\n"
  },
  {
    "path": "src/util/util_fps_limiter.cpp",
    "content": "#include <thread>\n\n#include \"thread.h\"\n#include \"util_env.h\"\n#include \"util_fps_limiter.h\"\n#include \"util_sleep.h\"\n#include \"util_string.h\"\n\n#include \"./log/log.h\"\n\nusing namespace std::chrono_literals;\n\nnamespace dxvk {\n  \n  FpsLimiter::FpsLimiter() {\n\n  }\n\n\n  FpsLimiter::~FpsLimiter() {\n\n  }\n\n\n  void FpsLimiter::setTargetFrameRate(double frameRate, uint32_t maxLatency) {\n    std::lock_guard<dxvk::mutex> lock(m_mutex);\n\n    TimerDuration interval = frameRate != 0.0\n      ? TimerDuration(int64_t(double(TimerDuration::period::den) / frameRate))\n      : TimerDuration::zero();\n\n    if (m_targetInterval != interval) {\n      m_targetInterval = interval;\n\n      m_heuristicFrameTime = TimePoint();\n      m_heuristicFrameCount = 0;\n      m_heuristicEnable = false;\n\n      m_maxLatency = maxLatency;\n    }\n  }\n\n\n  void FpsLimiter::delay() {\n    std::unique_lock<dxvk::mutex> lock(m_mutex);\n    auto interval = m_targetInterval;\n    auto latency = m_maxLatency;\n\n    if (interval == TimerDuration::zero()) {\n      m_nextFrame = TimePoint();\n      return;\n    }\n\n    auto t1 = dxvk::high_resolution_clock::now();\n\n    if (interval < TimerDuration::zero()) {\n      interval = -interval;\n\n      if (!testRefreshHeuristic(interval, t1, latency))\n        return;\n    }\n\n    // Subsequent code must not access any class members\n    // that can be written by setTargetFrameRate\n    lock.unlock();\n\n    if (t1 < m_nextFrame)\n      Sleep::sleepUntil(t1, m_nextFrame);\n\n    m_nextFrame = (t1 < m_nextFrame + interval)\n      ? m_nextFrame + interval\n      : t1 + interval;\n  }\n\n\n  bool FpsLimiter::testRefreshHeuristic(TimerDuration interval, TimePoint now, uint32_t maxLatency) {\n    if (m_heuristicEnable)\n      return true;\n\n    constexpr static uint32_t MinWindowSize = 8;\n    constexpr static uint32_t MaxWindowSize = 128;\n\n    if (m_heuristicFrameCount >= MinWindowSize) {\n      TimerDuration windowTotalTime = now - m_heuristicFrameTime;\n      TimerDuration windowExpectedTime = m_heuristicFrameCount * interval;\n\n      uint32_t minFrameCount = m_heuristicFrameCount - 1;\n      uint32_t maxFrameCount = m_heuristicFrameCount + maxLatency;\n\n      // Enable frame rate limiter if frames have been delivered faster than\n      // the desired refresh rate even accounting for swap chain buffering.\n      if ((maxFrameCount * windowTotalTime) < (m_heuristicFrameCount * windowExpectedTime)) {\n        double got = (double(m_heuristicFrameCount) * double(TimerDuration::period::den))\n                   / (double(windowTotalTime.count()) * double(TimerDuration::period::num));\n        double refresh = double(TimerDuration::period::den) / (double(TimerDuration::period::num) * double(interval.count()));\n\n        Logger::info(str::format(\"Detected frame rate (~\", uint32_t(got), \") higher than selected refresh rate of ~\",\n          uint32_t(refresh), \" Hz.\\n\", \"Engaging frame rate limiter.\"));\n\n        m_heuristicEnable = true;\n        return true;\n      }\n\n      // Reset heuristics if frames have been delivered slower than the refresh rate.\n      if (((minFrameCount * windowTotalTime) > (m_heuristicFrameCount * windowExpectedTime))\n       || (m_heuristicFrameCount >= MaxWindowSize)) {\n        m_heuristicFrameCount = 1;\n        m_heuristicFrameTime = now;\n        return false;\n      }\n    }\n\n    if (!m_heuristicFrameCount)\n      m_heuristicFrameTime = now;\n\n    m_heuristicFrameCount += 1;\n    return false;\n  }\n\n}\n"
  },
  {
    "path": "src/util/util_fps_limiter.h",
    "content": "#pragma once\n\n#include <array>\n#include <optional>\n\n#include \"thread.h\"\n#include \"util_time.h\"\n\nnamespace dxvk {\n  \n  /**\n   * \\brief Frame rate limiter\n   *\n   * Provides functionality to stall an application\n   * thread in order to maintain a given frame rate.\n   */\n  class FpsLimiter {\n\n  public:\n\n    /**\n     * \\brief Creates frame rate limiter\n     */\n    FpsLimiter();\n\n    ~FpsLimiter();\n\n    /**\n     * \\brief Sets target frame rate\n     * \\param [in] frameRate Target frame rate\n     */\n    void setTargetFrameRate(double frameRate, uint32_t maxLatency);\n\n    /**\n     * \\brief Stalls calling thread as necessary\n     *\n     * Blocks the calling thread if the limiter is enabled\n     * and the time since the last call to \\ref delay is\n     * shorter than the target interval.\n     */\n    void delay();\n\n  private:\n\n    using TimePoint = dxvk::high_resolution_clock::time_point;\n    using TimerDuration = std::chrono::nanoseconds;\n\n    dxvk::mutex     m_mutex;\n\n    TimerDuration   m_targetInterval  = TimerDuration::zero();\n    TimePoint       m_nextFrame       = TimePoint();\n    uint32_t        m_maxLatency      = 0;\n\n    uint32_t        m_heuristicFrameCount = 0;\n    TimePoint       m_heuristicFrameTime  = TimePoint();\n    bool            m_heuristicEnable     = false;\n\n    bool testRefreshHeuristic(TimerDuration interval, TimePoint now, uint32_t maxLatency);\n\n  };\n\n}\n"
  },
  {
    "path": "src/util/util_gdi.cpp",
    "content": "#include \"util_gdi.h\"\n#include \"log/log.h\"\n\nnamespace dxvk {\n\n#ifndef _WIN32\n  NTSTATUS WINAPI D3DKMTAcquireKeyedMutex(D3DKMT_ACQUIREKEYEDMUTEX *desc) {\n    Logger::warn(\"D3DKMTAcquireKeyedMutex: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTCloseAdapter(const D3DKMT_CLOSEADAPTER *desc) {\n    Logger::warn(\"D3DKMTCloseAdapter: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTCreateDCFromMemory(D3DKMT_CREATEDCFROMMEMORY *desc) {\n    Logger::warn(\"D3DKMTCreateDCFromMemory: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTCreateDevice(D3DKMT_CREATEDEVICE *desc) {\n    Logger::warn(\"D3DKMTCreateDevice: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTCreateKeyedMutex2(D3DKMT_CREATEKEYEDMUTEX2 *desc) {\n    Logger::warn(\"D3DKMTCreateKeyedMutex2: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTDestroyAllocation(const D3DKMT_DESTROYALLOCATION *desc) {\n    Logger::warn(\"D3DKMTDestroyAllocation: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTDestroyDCFromMemory(const D3DKMT_DESTROYDCFROMMEMORY *desc) {\n    Logger::warn(\"D3DKMTDestroyDCFromMemory: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTDestroyDevice(const D3DKMT_DESTROYDEVICE *desc) {\n    Logger::warn(\"D3DKMTDestroyDevice: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTDestroyKeyedMutex(const D3DKMT_DESTROYKEYEDMUTEX *desc) {\n    Logger::warn(\"D3DKMTDestroyKeyedMutex: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS WINAPI D3DKMTDestroySynchronizationObject(const D3DKMT_DESTROYSYNCHRONIZATIONOBJECT *desc) {\n    Logger::warn(\"D3DKMTDestroySynchronizationObject: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTEscape(const D3DKMT_ESCAPE *desc) {\n    Logger::warn(\"D3DKMTEscape: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTOpenAdapterFromLuid(D3DKMT_OPENADAPTERFROMLUID *desc) {\n    Logger::warn(\"D3DKMTOpenAdapterFromLuid: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTOpenKeyedMutex(D3DKMT_OPENKEYEDMUTEX *desc) {\n    Logger::warn(\"D3DKMTOpenKeyedMutex: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTOpenResource2(D3DKMT_OPENRESOURCE *desc) {\n    Logger::warn(\"D3DKMTOpenResource2: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTOpenResourceFromNtHandle(D3DKMT_OPENRESOURCEFROMNTHANDLE *desc) {\n    Logger::warn(\"D3DKMTOpenResourceFromNtHandle: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS WINAPI D3DKMTOpenSynchronizationObject(D3DKMT_OPENSYNCHRONIZATIONOBJECT *desc) {\n    Logger::warn(\"D3DKMTOpenSynchronizationObject: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS WINAPI D3DKMTOpenSyncObjectFromNtHandle(D3DKMT_OPENSYNCOBJECTFROMNTHANDLE *desc) {\n    Logger::warn(\"D3DKMTOpenSyncObjectFromNtHandle: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTQueryResourceInfo(D3DKMT_QUERYRESOURCEINFO *desc) {\n    Logger::warn(\"D3DKMTQueryResourceInfo: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS D3DKMTQueryResourceInfoFromNtHandle(D3DKMT_QUERYRESOURCEINFOFROMNTHANDLE *desc) {\n    Logger::warn(\"D3DKMTQueryResourceInfoFromNtHandle: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS WINAPI D3DKMTReleaseKeyedMutex(D3DKMT_RELEASEKEYEDMUTEX *desc) {\n    Logger::warn(\"D3DKMTReleaseKeyedMutex: Not available on this platform.\");\n    return -1;\n  }\n\n  NTSTATUS WINAPI D3DKMTShareObjects(UINT count, const D3DKMT_HANDLE *handles, OBJECT_ATTRIBUTES *attr, UINT access, HANDLE *handle) {\n    Logger::warn(\"D3DKMTShareObjects: Not available on this platform.\");\n    return -1;\n  }\n#else\n  static NTSTATUS WINAPI NoD3DKMTAcquireKeyedMutex(D3DKMT_ACQUIREKEYEDMUTEX *desc) {\n    return -1;\n  }\n\n  NTSTATUS WINAPI D3DKMTAcquireKeyedMutex(D3DKMT_ACQUIREKEYEDMUTEX *desc) {\n    static decltype(D3DKMTAcquireKeyedMutex) *func;\n    if (!func) {\n      InterlockedCompareExchangePointer((void **)&func, (void *)GetProcAddress(GetModuleHandle(\"gdi32\"), \"D3DKMTAcquireKeyedMutex\"), NULL);\n      InterlockedCompareExchangePointer((void **)&func, (void *)NoD3DKMTAcquireKeyedMutex, NULL);\n    }\n    return func(desc);\n  }\n\n  static NTSTATUS WINAPI NoD3DKMTReleaseKeyedMutex(D3DKMT_RELEASEKEYEDMUTEX *desc) {\n    return -1;\n  }\n\n  NTSTATUS WINAPI D3DKMTReleaseKeyedMutex(D3DKMT_RELEASEKEYEDMUTEX *desc) {\n    static decltype(D3DKMTReleaseKeyedMutex) *func;\n    if (!func) {\n      InterlockedCompareExchangePointer((void **)&func, (void *)GetProcAddress(GetModuleHandle(\"gdi32\"), \"D3DKMTReleaseKeyedMutex\"), NULL);\n      InterlockedCompareExchangePointer((void **)&func, (void *)NoD3DKMTReleaseKeyedMutex, NULL);\n    }\n    return func(desc);\n  }\n#endif\n}\n"
  },
  {
    "path": "src/util/util_gdi.h",
    "content": "#pragma once\n\n#include <d3d9.h>\n#include <dxgi.h>\n#include <d3d11_4.h>\n#include <d3d12.h>\n\n#ifndef _WIN32\n#define EXTERN_C\n#define WINBASEAPI\n#endif\n\nnamespace dxvk {\n  using NTSTATUS = LONG;\n  using D3DDDIFORMAT = D3DFORMAT;\n  using D3DKMT_HANDLE = UINT;\n  using D3DGPU_VIRTUAL_ADDRESS = ULONGLONG;\n\n  typedef struct _D3DKMT_ACQUIREKEYEDMUTEX\n  {\n      D3DKMT_HANDLE hKeyedMutex;\n      UINT64 Key;\n      LARGE_INTEGER *pTimeout;\n      UINT64 FenceValue;\n  } D3DKMT_ACQUIREKEYEDMUTEX;\n\n  typedef struct _D3DKMT_CLOSEADAPTER\n  {\n      D3DKMT_HANDLE hAdapter;\n  } D3DKMT_CLOSEADAPTER;\n\n  typedef struct _D3DKMT_CREATEDCFROMMEMORY\n  {\n      void *pMemory;\n      D3DDDIFORMAT Format;\n      UINT Width;\n      UINT Height;\n      UINT Pitch;\n      HDC hDeviceDc;\n      PALETTEENTRY *pColorTable;\n      HDC hDc;\n      HANDLE hBitmap;\n  } D3DKMT_CREATEDCFROMMEMORY;\n\n  typedef struct _D3DKMT_CREATEDEVICEFLAGS\n  {\n      UINT LegacyMode : 1;\n      UINT RequestVSync : 1;\n      UINT DisableGpuTimeout : 1;\n      UINT Reserved : 29;\n  } D3DKMT_CREATEDEVICEFLAGS;\n\n  typedef struct _D3DDDI_ALLOCATIONLIST\n  {\n      D3DKMT_HANDLE hAllocation;\n      union\n      {\n          struct\n          {\n              UINT WriteOperation : 1;\n              UINT DoNotRetireInstance : 1;\n              UINT OfferPriority : 3;\n              UINT Reserved : 27;\n          } DUMMYSTRUCTNAME;\n          UINT Value;\n      } DUMMYUNIONNAME;\n  } D3DDDI_ALLOCATIONLIST;\n\n  typedef struct _D3DDDI_PATCHLOCATIONLIST\n  {\n      UINT AllocationIndex;\n      union\n      {\n          struct\n          {\n              UINT SlotId : 24;\n              UINT Reserved : 8;\n          } DUMMYSTRUCTNAME;\n          UINT Value;\n      } DUMMYUNIONNAME;\n      UINT DriverId;\n      UINT AllocationOffset;\n      UINT PatchOffset;\n      UINT SplitOffset;\n  } D3DDDI_PATCHLOCATIONLIST;\n\n  typedef struct _D3DKMT_CREATEDEVICE\n  {\n      union\n      {\n          D3DKMT_HANDLE hAdapter;\n          VOID *pAdapter;\n      } DUMMYUNIONNAME;\n      D3DKMT_CREATEDEVICEFLAGS Flags;\n      D3DKMT_HANDLE hDevice;\n      VOID *pCommandBuffer;\n      UINT CommandBufferSize;\n      D3DDDI_ALLOCATIONLIST *pAllocationList;\n      UINT AllocationListSize;\n      D3DDDI_PATCHLOCATIONLIST *pPatchLocationList;\n      UINT PatchLocationListSize;\n  } D3DKMT_CREATEDEVICE;\n\n  typedef struct _D3DKMT_CREATEKEYEDMUTEX2_FLAGS\n  {\n      union\n      {\n          struct\n          {\n              UINT NtSecuritySharing : 1;\n              UINT Reserved : 31;\n          };\n          UINT Value;\n      };\n  } D3DKMT_CREATEKEYEDMUTEX2_FLAGS;\n\n  typedef struct _D3DKMT_CREATEKEYEDMUTEX2\n  {\n      UINT64 InitialValue;\n      D3DKMT_HANDLE hSharedHandle;\n      D3DKMT_HANDLE hKeyedMutex;\n      void *pPrivateRuntimeData;\n      UINT PrivateRuntimeDataSize;\n      D3DKMT_CREATEKEYEDMUTEX2_FLAGS Flags;\n  } D3DKMT_CREATEKEYEDMUTEX2;\n\n  typedef struct _D3DKMT_DESTROYALLOCATION\n  {\n      D3DKMT_HANDLE hDevice;\n      D3DKMT_HANDLE hResource;\n      const D3DKMT_HANDLE *phAllocationList;\n      UINT AllocationCount;\n  } D3DKMT_DESTROYALLOCATION;\n\n  typedef struct _D3DKMT_DESTROYDCFROMMEMORY\n  {\n      HDC hDc;\n      HANDLE hBitmap;\n  } D3DKMT_DESTROYDCFROMMEMORY;\n\n  typedef struct _D3DKMT_DESTROYDEVICE\n  {\n      D3DKMT_HANDLE hDevice;\n  } D3DKMT_DESTROYDEVICE;\n\n  typedef struct _D3DKMT_DESTROYKEYEDMUTEX\n  {\n      D3DKMT_HANDLE hKeyedMutex;\n  } D3DKMT_DESTROYKEYEDMUTEX;\n\n  typedef struct _D3DKMT_DESTROYSYNCHRONIZATIONOBJECT\n  {\n      D3DKMT_HANDLE hSyncObject;\n  } D3DKMT_DESTROYSYNCHRONIZATIONOBJECT;\n\n  typedef enum _D3DKMT_ESCAPETYPE\n  {\n      D3DKMT_ESCAPE_UPDATE_RESOURCE_WINE = 0x80000000,\n      D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE = 0x80000001,\n  } D3DKMT_ESCAPETYPE;\n\n  typedef struct _D3DDDI_ESCAPEFLAGS\n  {\n      union\n      {\n          struct\n          {\n              UINT HardwareAccess :1;\n              UINT Reserved       :31;\n          };\n          UINT Value;\n      };\n  } D3DDDI_ESCAPEFLAGS;\n\n  typedef struct _D3DKMT_ESCAPE\n  {\n      D3DKMT_HANDLE      hAdapter;\n      D3DKMT_HANDLE      hDevice;\n      D3DKMT_ESCAPETYPE  Type;\n      D3DDDI_ESCAPEFLAGS Flags;\n      void              *pPrivateDriverData;\n      UINT               PrivateDriverDataSize;\n      D3DKMT_HANDLE      hContext;\n  } D3DKMT_ESCAPE;\n\n  typedef struct _D3DKMT_OPENADAPTERFROMLUID\n  {\n      LUID AdapterLuid;\n      D3DKMT_HANDLE hAdapter;\n  } D3DKMT_OPENADAPTERFROMLUID;\n\n  typedef struct _D3DKMT_OPENKEYEDMUTEX\n  {\n      D3DKMT_HANDLE hSharedHandle;\n      D3DKMT_HANDLE hKeyedMutex;\n  } D3DKMT_OPENKEYEDMUTEX;\n\n  typedef struct _D3DDDI_OPENALLOCATIONINFO\n  {\n      D3DKMT_HANDLE hAllocation;\n      const void *pPrivateDriverData;\n      UINT PrivateDriverDataSize;\n  } D3DDDI_OPENALLOCATIONINFO;\n\n  typedef struct _D3DDDI_OPENALLOCATIONINFO2\n  {\n      D3DKMT_HANDLE hAllocation;\n      const void *pPrivateDriverData;\n      UINT PrivateDriverDataSize;\n      D3DGPU_VIRTUAL_ADDRESS GpuVirtualAddress;\n      ULONG_PTR Reserved[6];\n  } D3DDDI_OPENALLOCATIONINFO2;\n\n  typedef struct _D3DKMT_OPENRESOURCE\n  {\n      D3DKMT_HANDLE hDevice;\n      D3DKMT_HANDLE hGlobalShare;\n      UINT NumAllocations;\n      union\n      {\n          D3DDDI_OPENALLOCATIONINFO *pOpenAllocationInfo;\n          D3DDDI_OPENALLOCATIONINFO2 *pOpenAllocationInfo2;\n      };\n      void *pPrivateRuntimeData;\n      UINT PrivateRuntimeDataSize;\n      void *pResourcePrivateDriverData;\n      UINT ResourcePrivateDriverDataSize;\n      void *pTotalPrivateDriverDataBuffer;\n      UINT TotalPrivateDriverDataBufferSize;\n      D3DKMT_HANDLE hResource;\n  } D3DKMT_OPENRESOURCE;\n\n  typedef struct _D3DKMT_OPENRESOURCEFROMNTHANDLE\n  {\n      D3DKMT_HANDLE hDevice;\n      HANDLE hNtHandle;\n      UINT NumAllocations;\n      D3DDDI_OPENALLOCATIONINFO2 *pOpenAllocationInfo2;\n      UINT PrivateRuntimeDataSize;\n      void *pPrivateRuntimeData;\n      UINT ResourcePrivateDriverDataSize;\n      void *pResourcePrivateDriverData;\n      UINT TotalPrivateDriverDataBufferSize;\n      void *pTotalPrivateDriverDataBuffer;\n      D3DKMT_HANDLE hResource;\n      D3DKMT_HANDLE hKeyedMutex;\n      void *pKeyedMutexPrivateRuntimeData;\n      UINT KeyedMutexPrivateRuntimeDataSize;\n      D3DKMT_HANDLE hSyncObject;\n  } D3DKMT_OPENRESOURCEFROMNTHANDLE;\n\n  typedef struct _D3DKMT_OPENSYNCHRONIZATIONOBJECT\n  {\n      D3DKMT_HANDLE hSharedHandle;\n      D3DKMT_HANDLE hSyncObject;\n      UINT64 Reserved[8];\n  } D3DKMT_OPENSYNCHRONIZATIONOBJECT;\n\n  typedef struct _D3DKMT_OPENSYNCOBJECTFROMNTHANDLE\n  {\n      HANDLE hNtHandle;\n      D3DKMT_HANDLE hSyncObject;\n  } D3DKMT_OPENSYNCOBJECTFROMNTHANDLE;\n\n  typedef struct _D3DKMT_QUERYRESOURCEINFO\n  {\n      D3DKMT_HANDLE hDevice;\n      D3DKMT_HANDLE hGlobalShare;\n      void *pPrivateRuntimeData;\n      UINT PrivateRuntimeDataSize;\n      UINT TotalPrivateDriverDataSize;\n      UINT ResourcePrivateDriverDataSize;\n      UINT NumAllocations;\n  } D3DKMT_QUERYRESOURCEINFO;\n\n  typedef struct _D3DKMT_QUERYRESOURCEINFOFROMNTHANDLE\n  {\n      D3DKMT_HANDLE hDevice;\n      HANDLE hNtHandle;\n      void *pPrivateRuntimeData;\n      UINT PrivateRuntimeDataSize;\n      UINT TotalPrivateDriverDataSize;\n      UINT ResourcePrivateDriverDataSize;\n      UINT NumAllocations;\n  } D3DKMT_QUERYRESOURCEINFOFROMNTHANDLE;\n\n  typedef struct _D3DKMT_RELEASEKEYEDMUTEX\n  {\n      D3DKMT_HANDLE hKeyedMutex;\n      UINT64 Key;\n      UINT64 FenceValue;\n  } D3DKMT_RELEASEKEYEDMUTEX;\n\n  typedef struct _UNICODE_STRING {\n      USHORT Length;        /* bytes */\n      USHORT MaximumLength; /* bytes */\n      WCHAR *Buffer;\n  } UNICODE_STRING;\n\n  typedef struct _OBJECT_ATTRIBUTES {\n      ULONG Length;\n      HANDLE RootDirectory;\n      UNICODE_STRING *ObjectName;\n      ULONG Attributes;\n      void *SecurityDescriptor;\n      void *SecurityQualityOfService;\n  } OBJECT_ATTRIBUTES;\n\n  #define OBJ_CASE_INSENSITIVE 0x00000040\n\n  /* undocumented D3D runtime data descriptors */\n\n  struct d3dkmt_dxgi_desc\n  {\n      UINT                        size;\n      UINT                        version;\n      UINT                        width;\n      UINT                        height;\n      DXGI_FORMAT                 format;\n      UINT                        unknown_0;\n      UINT                        unknown_1;\n      UINT                        keyed_mutex;\n      D3DKMT_HANDLE               mutex_handle;\n      D3DKMT_HANDLE               sync_handle;\n      UINT                        nt_shared;\n      UINT                        unknown_2;\n      UINT                        unknown_3;\n      UINT                        unknown_4;\n  };\n\n  struct d3dkmt_d3d9_desc\n  {\n      struct d3dkmt_dxgi_desc     dxgi;\n      D3DFORMAT                   format;\n      D3DRESOURCETYPE             type;\n      UINT                        usage;\n      union\n      {\n          struct\n          {\n              UINT                unknown_0;\n              UINT                width;\n              UINT                height;\n              UINT                levels;\n              UINT                depth;\n          } texture;\n          struct\n          {\n              UINT                unknown_0;\n              UINT                unknown_1;\n              UINT                unknown_2;\n              UINT                width;\n              UINT                height;\n          } surface;\n          struct\n          {\n              UINT                unknown_0;\n              UINT                width;\n              UINT                format;\n              UINT                unknown_1;\n              UINT                unknown_2;\n          } buffer;\n      };\n  };\n\n  static_assert( sizeof(struct d3dkmt_d3d9_desc) == 0x58 );\n\n  struct d3dkmt_d3d11_desc\n  {\n      struct d3dkmt_dxgi_desc     dxgi;\n      D3D11_RESOURCE_DIMENSION    dimension;\n      union\n      {\n          D3D11_BUFFER_DESC       d3d11_buf;\n          D3D11_TEXTURE1D_DESC    d3d11_1d;\n          D3D11_TEXTURE2D_DESC    d3d11_2d;\n          D3D11_TEXTURE3D_DESC    d3d11_3d;\n      };\n  };\n\n  static_assert( sizeof(struct d3dkmt_d3d11_desc) == 0x68 );\n\n  typedef struct D3D12_MIP_REGION\n  {\n      UINT Width;\n      UINT Height;\n      UINT Depth;\n  } D3D12_MIP_REGION;\n\n  typedef struct D3D12_RESOURCE_DESC1\n  {\n      D3D12_RESOURCE_DIMENSION Dimension;\n      UINT64 Alignment;\n      UINT64 Width;\n      UINT Height;\n      UINT16 DepthOrArraySize;\n      UINT16 MipLevels;\n      DXGI_FORMAT Format;\n      DXGI_SAMPLE_DESC SampleDesc;\n      D3D12_TEXTURE_LAYOUT Layout;\n      D3D12_RESOURCE_FLAGS Flags;\n      D3D12_MIP_REGION SamplerFeedbackMipRegion;\n  } D3D12_RESOURCE_DESC1;\n\n  struct d3dkmt_d3d12_desc\n  {\n      struct d3dkmt_d3d11_desc    d3d11;\n      UINT                        unknown_5[4];\n      UINT                        resource_size;\n      UINT                        unknown_6[7];\n      UINT                        resource_align;\n      UINT                        unknown_7[9];\n      union\n      {\n          D3D12_RESOURCE_DESC     desc;\n          D3D12_RESOURCE_DESC1    desc1;\n          UINT                    __pad[16];\n      };\n      UINT64                      unknown_8[1];\n  };\n\n  static_assert( sizeof(struct d3dkmt_d3d12_desc) == 0x108 );\n\n  union d3dkmt_desc\n  {\n      struct d3dkmt_dxgi_desc     dxgi;\n      struct d3dkmt_d3d9_desc     d3d9;   /* if dxgi.size == sizeof(d3d9)  && dxgi.version == 1 && sizeof(desc) == sizeof(d3d9) */\n      struct d3dkmt_d3d11_desc    d3d11;  /* if dxgi.size == sizeof(d3d11) && dxgi.version == 4 && sizeof(desc) >= sizeof(d3d11) */\n      struct d3dkmt_d3d12_desc    d3d12;  /* if dxgi.size == sizeof(d3d11) && dxgi.version == 0 && sizeof(desc) == sizeof(d3d12) */\n  };\n\n  NTSTATUS WINAPI D3DKMTAcquireKeyedMutex(D3DKMT_ACQUIREKEYEDMUTEX *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTCloseAdapter(const D3DKMT_CLOSEADAPTER *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTCreateDCFromMemory(D3DKMT_CREATEDCFROMMEMORY *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTCreateDevice(D3DKMT_CREATEDEVICE *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTCreateKeyedMutex2(D3DKMT_CREATEKEYEDMUTEX2 *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTDestroyAllocation(const D3DKMT_DESTROYALLOCATION *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTDestroyDCFromMemory(const D3DKMT_DESTROYDCFROMMEMORY *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTDestroyDevice(const D3DKMT_DESTROYDEVICE *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTDestroyKeyedMutex(const D3DKMT_DESTROYKEYEDMUTEX *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTDestroySynchronizationObject(const D3DKMT_DESTROYSYNCHRONIZATIONOBJECT *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTEscape(const D3DKMT_ESCAPE *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTOpenAdapterFromLuid(D3DKMT_OPENADAPTERFROMLUID *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTOpenKeyedMutex(D3DKMT_OPENKEYEDMUTEX *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTOpenResource2(D3DKMT_OPENRESOURCE *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTOpenResourceFromNtHandle(D3DKMT_OPENRESOURCEFROMNTHANDLE *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTOpenSynchronizationObject(D3DKMT_OPENSYNCHRONIZATIONOBJECT *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTOpenSyncObjectFromNtHandle(D3DKMT_OPENSYNCOBJECTFROMNTHANDLE *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTQueryResourceInfo(D3DKMT_QUERYRESOURCEINFO *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTQueryResourceInfoFromNtHandle(D3DKMT_QUERYRESOURCEINFOFROMNTHANDLE *desc);\n  NTSTATUS WINAPI D3DKMTReleaseKeyedMutex(D3DKMT_RELEASEKEYEDMUTEX *desc);\n  EXTERN_C WINBASEAPI NTSTATUS WINAPI D3DKMTShareObjects(UINT count, const D3DKMT_HANDLE *handles, OBJECT_ATTRIBUTES *attr, UINT access, HANDLE *handle);\n}\n"
  },
  {
    "path": "src/util/util_lazy.h",
    "content": "#pragma once\n\n#include <mutex>\n\nnamespace dxvk {\n\n  /**\n   * \\brief Lazy-initialized object\n   * \n   * Creates an object on demand with\n   * the given constructor arguments.\n   */\n  template<typename T>\n  class Lazy {\n\n  public:\n\n    template<typename... Args>\n    T& get(Args... args) {\n      if (m_object)\n        return *m_object;\n\n      std::lock_guard lock(m_mutex);\n\n      if (!m_object) {\n        m_object = std::make_unique<T>(\n          std::forward<Args>(args)...);\n      }\n\n      return *m_object;\n    }\n\n  private:\n\n    dxvk::mutex        m_mutex;\n    std::unique_ptr<T> m_object;\n\n  };\n\n}"
  },
  {
    "path": "src/util/util_likely.h",
    "content": "#pragma once\n\n#ifdef __GNUC__\n#define likely(x) __builtin_expect(bool(x),1)\n#define unlikely(x) __builtin_expect(bool(x),0)\n#define force_inline inline __attribute__((always_inline))\n#else\n#define likely(x)   (x)\n#define unlikely(x) (x)\n#define force_inline inline\n#endif\n"
  },
  {
    "path": "src/util/util_lru.h",
    "content": "#include <cstdint>\n#include <list>\n#include <unordered_map>\n\nnamespace dxvk {\n\n  template<typename T>\n  class lru_list {\n\n  public:\n    typedef typename std::list<T>::const_iterator const_iterator;\n\n    void insert(T value) {\n      auto cacheIter = m_cache.find(value);\n      if (cacheIter != m_cache.end())\n        m_list.erase(cacheIter->second);\n\n      m_list.push_back(value);\n      auto iter = m_list.cend();\n      iter--;\n      m_cache[value] = iter;\n    }\n\n    void remove(const T& value) {\n      auto cacheIter = m_cache.find(value);\n      if (cacheIter == m_cache.end())\n        return;\n\n      m_list.erase(cacheIter->second);\n      m_cache.erase(cacheIter);\n    }\n\n    const_iterator remove(const_iterator iter) {\n      auto cacheIter = m_cache.find(*iter);\n      m_cache.erase(cacheIter);\n      return m_list.erase(iter);\n    }\n\n    void touch(const T& value) {\n      auto cacheIter = m_cache.find(value);\n      if (cacheIter == m_cache.end())\n        return;\n\n      m_list.erase(cacheIter->second);\n      m_list.push_back(value);\n      auto iter = m_list.cend();\n      --iter;\n      m_cache[value] = iter;\n    }\n\n    const_iterator leastRecentlyUsedIter() const {\n      return m_list.cbegin();\n    }\n\n    const_iterator leastRecentlyUsedEndIter() const {\n      return m_list.cend();\n    }\n\n    uint32_t size() const noexcept {\n      return m_list.size();\n    }\n\n  private:\n    std::list<T> m_list;\n    std::unordered_map<T, const_iterator> m_cache;\n\n  };\n\n}\n"
  },
  {
    "path": "src/util/util_luid.cpp",
    "content": "#include \"util_luid.h\"\n#include \"util_string.h\"\n\n#include \"./log/log.h\"\n\n#include <mutex>\n#include <vector>\n#include <atomic>\n\nnamespace dxvk {\n\n#ifndef _WIN32\n  static BOOL AllocateLocallyUniqueId(LUID* luid) {\n    static std::atomic<uint32_t> counter = { 0u };\n    // Unsigned bottom part is actually the first\n    // member of the struct.\n    *luid = LUID{ ++counter, 0 };\n    return TRUE;\n  }\n#endif\n\n  LUID GetAdapterLUID(UINT Adapter) {\n    static dxvk::mutex       s_mutex;\n    static std::vector<LUID> s_luids;\n\n    std::lock_guard<dxvk::mutex> lock(s_mutex);\n    uint32_t newLuidCount = Adapter + 1;\n\n    while (s_luids.size() < newLuidCount) {\n      LUID luid = { 0, 0 };\n\n      if (!AllocateLocallyUniqueId(&luid))\n        Logger::err(\"Failed to allocate LUID\");\n      \n        \n      Logger::info(str::format(\"Adapter LUID \", s_luids.size(), \": \",\n        std::hex, luid.HighPart, \":\", luid.LowPart, std::dec));\n\n      s_luids.push_back(luid);\n    }\n\n    return s_luids[Adapter];\n  }\n\n}\n"
  },
  {
    "path": "src/util/util_luid.h",
    "content": "#include \"./com/com_include.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Retrieves an adapter LUID\n   * \n   * Note that this only works reliably within the\n   * module that this function was compiled into.\n   * \\param [in] Adapter The adapter index\n   * \\returns LUID An LUID for that adapter\n   */\n  LUID GetAdapterLUID(UINT Adapter);\n\n}\n"
  },
  {
    "path": "src/util/util_math.h",
    "content": "#pragma once\n\n#include <cmath>\n\nnamespace dxvk {\n  \n  constexpr size_t CACHE_LINE_SIZE = 64;\n  constexpr double pi = 3.14159265359;\n\n  template<typename T>\n  constexpr T clamp(T n, T lo, T hi) {\n    if (n < lo) return lo;\n    if (n > hi) return hi;\n    return n;\n  }\n  \n  template<typename T, typename U = T>\n  constexpr T align(T what, U to) {\n    return (what + to - 1) & ~(to - 1);\n  }\n\n  template<typename T, typename U = T>\n  constexpr T alignDown(T what, U to) {\n    return (what / to) * to;\n  }\n\n  // Equivalent of std::clamp for use with floating point numbers\n  // Handles (-){INFINITY,NAN} cases.\n  // Will return min in cases of NAN, etc.\n  inline float fclamp(float value, float min, float max) {\n    return std::fmin(\n      std::fmax(value, min), max);\n  }\n\n  template<typename T>\n  inline T divCeil(T dividend, T divisor) {\n    return (dividend + divisor - 1) / divisor;\n  }\n  \n}\n"
  },
  {
    "path": "src/util/util_matrix.cpp",
    "content": "#include \"util_matrix.h\"\n\nnamespace dxvk {\n\n        Vector4& Matrix4::operator[](size_t index)       { return data[index]; }\n  const Vector4& Matrix4::operator[](size_t index) const { return data[index]; }\n\n  bool Matrix4::operator==(const Matrix4& m2) const {\n    const Matrix4& m1 = *this;\n    for (uint32_t i = 0; i < 4; i++) {\n      if (m1[i] != m2[i])\n        return false;\n    }\n    return true;\n  }\n\n  bool Matrix4::operator!=(const Matrix4& m2) const { return !operator==(m2); }\n\n  Matrix4 Matrix4::operator+(const Matrix4& other) const {\n    Matrix4 mat;\n    for (uint32_t i = 0; i < 4; i++)\n      mat[i] = data[i] + other.data[i];\n    return mat;\n  }\n\n  Matrix4 Matrix4::operator-(const Matrix4& other) const {\n    Matrix4 mat;\n    for (uint32_t i = 0; i < 4; i++)\n      mat[i] = data[i] - other.data[i];\n    return mat;\n  }\n\n  Matrix4 Matrix4::operator*(const Matrix4& m2) const {\n    const Matrix4& m1 = *this;\n\n    const Vector4 srcA0 = { m1[0] };\n    const Vector4 srcA1 = { m1[1] };\n    const Vector4 srcA2 = { m1[2] };\n    const Vector4 srcA3 = { m1[3] };\n\n    const Vector4 srcB0 = { m2[0] };\n    const Vector4 srcB1 = { m2[1] };\n    const Vector4 srcB2 = { m2[2] };\n    const Vector4 srcB3 = { m2[3] };\n\n    Matrix4 result;\n    result[0] = srcA0 * srcB0[0] + srcA1 * srcB0[1] + srcA2 * srcB0[2] + srcA3 * srcB0[3];\n    result[1] = srcA0 * srcB1[0] + srcA1 * srcB1[1] + srcA2 * srcB1[2] + srcA3 * srcB1[3];\n    result[2] = srcA0 * srcB2[0] + srcA1 * srcB2[1] + srcA2 * srcB2[2] + srcA3 * srcB2[3];\n    result[3] = srcA0 * srcB3[0] + srcA1 * srcB3[1] + srcA2 * srcB3[2] + srcA3 * srcB3[3];\n    return result;\n  }\n\n  Vector4 Matrix4::operator*(const Vector4& v) const {\n    const Matrix4& m = *this;\n\n    const Vector4 mul0 = { m[0] * v[0] };\n    const Vector4 mul1 = { m[1] * v[1] };\n    const Vector4 mul2 = { m[2] * v[2] };\n    const Vector4 mul3 = { m[3] * v[3] };\n\n    const Vector4 add0 = { mul0 + mul1 };\n    const Vector4 add1 = { mul2 + mul3 };\n\n    return add0 + add1;\n  }\n\n  Matrix4 Matrix4::operator*(float scalar) const {\n    Matrix4 mat;\n    for (uint32_t i = 0; i < 4; i++)\n      mat[i] = data[i] * scalar;\n    return mat;\n  }\n\n  Matrix4 Matrix4::operator/(float scalar) const {\n    Matrix4 mat;\n    for (uint32_t i = 0; i < 4; i++)\n      mat[i] = data[i] / scalar;\n    return mat;\n  }\n\n  Matrix4& Matrix4::operator+=(const Matrix4& other) {\n    for (uint32_t i = 0; i < 4; i++)\n      data[i] += other.data[i];\n    return *this;\n  }\n\n  Matrix4& Matrix4::operator-=(const Matrix4& other) {\n    for (uint32_t i = 0; i < 4; i++)\n      data[i] -= other.data[i];\n    return *this;\n  }\n\n  Matrix4& Matrix4::operator*=(const Matrix4& other) {\n    return (*this = (*this) * other);\n  }\n\n  Matrix4 transpose(const Matrix4& m) {\n    Matrix4 result;\n\n    for (uint32_t i = 0; i < 4; i++) {\n      for (uint32_t j = 0; j < 4; j++)\n        result[i][j] = m.data[j][i];\n    }\n    return result;\n  }\n\n  float determinant(const Matrix4& m) {\n    float coef00    =  m[2][2] * m[3][3] - m[3][2] * m[2][3];\n    float coef02    =  m[1][2] * m[3][3] - m[3][2] * m[1][3];\n    float coef03    =  m[1][2] * m[2][3] - m[2][2] * m[1][3];\n\n    float coef04    =  m[2][1] * m[3][3] - m[3][1] * m[2][3];\n    float coef06    =  m[1][1] * m[3][3] - m[3][1] * m[1][3];\n    float coef07    =  m[1][1] * m[2][3] - m[2][1] * m[1][3];\n\n    float coef08    =  m[2][1] * m[3][2] - m[3][1] * m[2][2];\n    float coef10    =  m[1][1] * m[3][2] - m[3][1] * m[1][2];\n    float coef11    =  m[1][1] * m[2][2] - m[2][1] * m[1][2];\n\n    float coef12    =  m[2][0] * m[3][3] - m[3][0] * m[2][3];\n    float coef14    =  m[1][0] * m[3][3] - m[3][0] * m[1][3];\n    float coef15    =  m[1][0] * m[2][3] - m[2][0] * m[1][3];\n\n    float coef16    =  m[2][0] * m[3][2] - m[3][0] * m[2][2];\n    float coef18    =  m[1][0] * m[3][2] - m[3][0] * m[1][2];\n    float coef19    =  m[1][0] * m[2][2] - m[2][0] * m[1][2];\n\n    float coef20    =  m[2][0] * m[3][1] - m[3][0] * m[2][1];\n    float coef22    =  m[1][0] * m[3][1] - m[3][0] * m[1][1];\n    float coef23    =  m[1][0] * m[2][1] - m[2][0] * m[1][1];\n\n    Vector4 fac0    = { coef00, coef00, coef02, coef03 };\n    Vector4 fac1    = { coef04, coef04, coef06, coef07 };\n    Vector4 fac2    = { coef08, coef08, coef10, coef11 };\n    Vector4 fac3    = { coef12, coef12, coef14, coef15 };\n    Vector4 fac4    = { coef16, coef16, coef18, coef19 };\n    Vector4 fac5    = { coef20, coef20, coef22, coef23 };\n\n    Vector4 vec0    = { m[1][0], m[0][0], m[0][0], m[0][0] };\n    Vector4 vec1    = { m[1][1], m[0][1], m[0][1], m[0][1] };\n    Vector4 vec2    = { m[1][2], m[0][2], m[0][2], m[0][2] };\n    Vector4 vec3    = { m[1][3], m[0][3], m[0][3], m[0][3] };\n\n    Vector4 inv0    = { vec1 * fac0 - vec2 * fac1 + vec3 * fac2 };\n    Vector4 inv1    = { vec0 * fac0 - vec2 * fac3 + vec3 * fac4 };\n    Vector4 inv2    = { vec0 * fac1 - vec1 * fac3 + vec3 * fac5 };\n    Vector4 inv3    = { vec0 * fac2 - vec1 * fac4 + vec2 * fac5 };\n\n    Vector4 signA   = { +1, -1, +1, -1 };\n    Vector4 signB   = { -1, +1, -1, +1 };\n    Matrix4 inverse = { inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB };\n\n    Vector4 row0    = { inverse[0][0], inverse[1][0], inverse[2][0], inverse[3][0] };\n\n    Vector4 dot0    = { m[0] * row0 };\n\n    return (dot0.x + dot0.y) + (dot0.z + dot0.w);\n  }\n\n  Matrix4 inverse(const Matrix4& m)\n  {\n    float coef00    = m[2][2] * m[3][3] - m[3][2] * m[2][3];\n    float coef02    = m[1][2] * m[3][3] - m[3][2] * m[1][3];\n    float coef03    = m[1][2] * m[2][3] - m[2][2] * m[1][3];\n    float coef04    = m[2][1] * m[3][3] - m[3][1] * m[2][3];\n    float coef06    = m[1][1] * m[3][3] - m[3][1] * m[1][3];\n    float coef07    = m[1][1] * m[2][3] - m[2][1] * m[1][3];\n    float coef08    = m[2][1] * m[3][2] - m[3][1] * m[2][2];\n    float coef10    = m[1][1] * m[3][2] - m[3][1] * m[1][2];\n    float coef11    = m[1][1] * m[2][2] - m[2][1] * m[1][2];\n    float coef12    = m[2][0] * m[3][3] - m[3][0] * m[2][3];\n    float coef14    = m[1][0] * m[3][3] - m[3][0] * m[1][3];\n    float coef15    = m[1][0] * m[2][3] - m[2][0] * m[1][3];\n    float coef16    = m[2][0] * m[3][2] - m[3][0] * m[2][2];\n    float coef18    = m[1][0] * m[3][2] - m[3][0] * m[1][2];\n    float coef19    = m[1][0] * m[2][2] - m[2][0] * m[1][2];\n    float coef20    = m[2][0] * m[3][1] - m[3][0] * m[2][1];\n    float coef22    = m[1][0] * m[3][1] - m[3][0] * m[1][1];\n    float coef23    = m[1][0] * m[2][1] - m[2][0] * m[1][1];\n  \n    Vector4 fac0    = { coef00, coef00, coef02, coef03 };\n    Vector4 fac1    = { coef04, coef04, coef06, coef07 };\n    Vector4 fac2    = { coef08, coef08, coef10, coef11 };\n    Vector4 fac3    = { coef12, coef12, coef14, coef15 };\n    Vector4 fac4    = { coef16, coef16, coef18, coef19 };\n    Vector4 fac5    = { coef20, coef20, coef22, coef23 };\n  \n    Vector4 vec0    = { m[1][0], m[0][0], m[0][0], m[0][0] };\n    Vector4 vec1    = { m[1][1], m[0][1], m[0][1], m[0][1] };\n    Vector4 vec2    = { m[1][2], m[0][2], m[0][2], m[0][2] };\n    Vector4 vec3    = { m[1][3], m[0][3], m[0][3], m[0][3] };\n  \n    Vector4 inv0    = { vec1 * fac0 - vec2 * fac1 + vec3 * fac2 };\n    Vector4 inv1    = { vec0 * fac0 - vec2 * fac3 + vec3 * fac4 };\n    Vector4 inv2    = { vec0 * fac1 - vec1 * fac3 + vec3 * fac5 };\n    Vector4 inv3    = { vec0 * fac2 - vec1 * fac4 + vec2 * fac5 };\n  \n    Vector4 signA   = { +1, -1, +1, -1 };\n    Vector4 signB   = { -1, +1, -1, +1 };\n    Matrix4 inverse = { inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB };\n\n    Vector4 row0    = { inverse[0][0], inverse[1][0], inverse[2][0], inverse[3][0] };\n\n    Vector4 dot0    = { m[0] * row0 };\n    float dot1      = (dot0.x + dot0.y) + (dot0.z + dot0.w);\n\n    if (unlikely(std::abs(dot1) <= std::numeric_limits<float>::min() * 10)) {\n      return m;\n    }\n\n    return inverse * (1.0f / dot1);\n  }\n\n  Matrix4 hadamardProduct(const Matrix4& a, const Matrix4& b) {\n    Matrix4 result;\n\n    for (uint32_t i = 0; i < 4; i++)\n      result[i] = a[i] * b[i];\n\n    return result;\n  }\n\n  std::ostream& operator<<(std::ostream& os, const Matrix4& m) {\n    os << \"Matrix4(\";\n    for (uint32_t i = 0; i < 4; i++) {\n      os << \"\\n\\t\" << m[i];\n      if (i < 3)\n        os << \", \";\n    }\n    os << \"\\n)\";\n\n    return os;\n  }\n\n}"
  },
  {
    "path": "src/util/util_matrix.h",
    "content": "#pragma once\n\n#include \"util_vector.h\"\n\nnamespace dxvk {\n\n  class Matrix4 {\n\n    public:\n\n    // Identity\n    inline Matrix4() {\n      data[0] = { 1, 0, 0, 0 };\n      data[1] = { 0, 1, 0, 0 };\n      data[2] = { 0, 0, 1, 0 };\n      data[3] = { 0, 0, 0, 1 };\n    }\n\n    // Produces a scalar matrix, x * Identity\n    inline explicit Matrix4(float x) {\n      data[0] = { x, 0, 0, 0 };\n      data[1] = { 0, x, 0, 0 };\n      data[2] = { 0, 0, x, 0 };\n      data[3] = { 0, 0, 0, x };\n    }\n\n    inline Matrix4(\n      const Vector4& v0,\n      const Vector4& v1,\n      const Vector4& v2,\n      const Vector4& v3) {\n      data[0] = v0;\n      data[1] = v1;\n      data[2] = v2;\n      data[3] = v3;\n    }\n\n    inline Matrix4(const float matrix[4][4]) {\n      data[0] = Vector4(matrix[0]);\n      data[1] = Vector4(matrix[1]);\n      data[2] = Vector4(matrix[2]);\n      data[3] = Vector4(matrix[3]);\n    }\n\n    Matrix4(const Matrix4& other) = default;\n    Matrix4& operator=(const Matrix4& other) = default;\n\n    Vector4& operator[](size_t index);\n    const Vector4& operator[](size_t index) const;\n\n    bool operator==(const Matrix4& m2) const;\n    bool operator!=(const Matrix4& m2) const;\n\n    Matrix4 operator+(const Matrix4& other) const;\n    Matrix4 operator-(const Matrix4& other) const;\n\n    Matrix4 operator*(const Matrix4& m2) const;\n    Vector4 operator*(const Vector4& v) const;\n    Matrix4 operator*(float scalar) const;\n\n    Matrix4 operator/(float scalar) const;\n\n    Matrix4& operator+=(const Matrix4& other);\n    Matrix4& operator-=(const Matrix4& other);\n\n    Matrix4& operator*=(const Matrix4& other);\n\n    Vector4 data[4];\n\n  };\n\n  static_assert(sizeof(Matrix4) == sizeof(Vector4) * 4);\n\n  inline Matrix4 operator*(float scalar, const Matrix4& m) { return m * scalar; }\n\n  Matrix4 transpose(const Matrix4& m);\n\n  float determinant(const Matrix4& m);\n\n  Matrix4 inverse(const Matrix4& m);\n\n  Matrix4 hadamardProduct(const Matrix4& a, const Matrix4& b);\n\n  std::ostream& operator<<(std::ostream& os, const Matrix4& m);\n\n}"
  },
  {
    "path": "src/util/util_misc.h",
    "content": "#pragma once\n\n#include <d3d9types.h>\n\nnamespace dxvk {\n\n  inline void DecodeD3DCOLOR(D3DCOLOR color, float* rgba) {\n    // Encoded in D3DCOLOR as argb\n    rgba[3] = (float)((color & 0xff000000) >> 24) / 255.0f;\n    rgba[0] = (float)((color & 0x00ff0000) >> 16) / 255.0f;\n    rgba[1] = (float)((color & 0x0000ff00) >> 8)  / 255.0f;\n    rgba[2] = (float)((color & 0x000000ff))       / 255.0f;\n  }\n\n  /**\n   * \\brief Computes refresh period for a given display refresh rate\n   *\n   * \\param [in] numerator Numerator of refresh rate\n   * \\param [in] denominator Denominator of refresh rate\n   * \\returns Refresh period, in nanoseconds\n   */\n  inline auto computeRefreshPeriod(uint64_t numerator, uint64_t denominator) {\n    using unit = std::chrono::nanoseconds;\n    unit::rep ns = unit::rep(unit::period::den * denominator)\n                 / unit::rep(unit::period::num * numerator);\n    return unit(ns);\n  }\n\n  /**\n   * \\brief Computes refresh count within a given time period\n   *\n   * \\param [in] t0 Start time\n   * \\param [in] t1 End time\n   * \\param [in] refreshPeriod Refresh period\n   * \\returns Number of refreshes between t1 and t0\n   */\n  template<typename TimePoint, typename Duration>\n  uint64_t computeRefreshCount(TimePoint t0, TimePoint t1, Duration refreshPeriod) {\n    if (t1 < t0)\n      return 0;\n\n    auto duration = std::chrono::duration_cast<Duration>(t1 - t0);\n    return duration.count() / refreshPeriod.count();\n  }\n\n  struct scoped_bool {\n    scoped_bool(bool &v) : m_val(v) {\n      m_val = true;\n    }\n    ~scoped_bool() {\n      m_val = false;\n    }\n\n    bool& m_val;\n  };\n}\n"
  },
  {
    "path": "src/util/util_ratio.h",
    "content": "#include <numeric>\n#include <algorithm>\n#include <cstdint>\n#include <string>\n#include <charconv>\n\nnamespace dxvk {\n\n  /**\n   * \\brief Simplest ratio helper\n   */\n  template <typename T>\n  class Ratio {\n\n  public:\n\n    Ratio(T num, T denom) {\n      set(num, denom);\n    }\n\n    Ratio(std::string_view view) {\n      set(0, 0);\n\n      size_t colon = view.find(\":\");\n\n      if (colon == std::string_view::npos)\n        return;\n\n      std::string_view numStr   = view.substr(0, colon);\n      std::string_view denomStr = view.substr(colon + 1);\n\n      T num = 0, denom = 0;\n      std::from_chars(numStr.data(),   numStr.data()   + numStr.size(),   num);\n      std::from_chars(denomStr.data(), denomStr.data() + denomStr.size(), denom);\n\n      set(num, denom);\n    }\n\n    inline T num()    const { return m_num; }\n    inline T denom() const { return m_denom; }\n\n    inline bool undefined() const { return m_denom == 0; }\n\n    inline void set(T num, T denom) {\n      const T gcd = std::gcd(num, denom);\n\n      if (gcd == 0) {\n        m_num   = 0;\n        m_denom = 0;\n\n        return;\n      }\n\n      m_num = num / gcd;\n      m_denom = denom / gcd;\n    }\n\n    inline bool operator == (const Ratio& other) const {\n      return num() == other.num() && denom() == other.denom();\n    }\n\n    inline bool operator != (const Ratio& other) const {\n      return !(*this == other);\n    }\n\n    inline bool operator >= (const Ratio& other) const {\n      return num() * other.denom() >= other.num() * denom();\n    }\n\n    inline bool operator > (const Ratio& other) const {\n      return num() * other.denom() > other.num() * denom();\n    }\n\n    inline bool operator < (const Ratio& other) const {\n      return  !(*this >= other);\n    }\n\n    inline bool operator <= (const Ratio& other) const {\n      return  !(*this > other);\n    }\n\n  private:\n\n    T m_num, m_denom;\n\n  };\n\n}"
  },
  {
    "path": "src/util/util_shared_res.cpp",
    "content": "#include \"util_shared_res.h\"\n#include \"log/log.h\"\n\n#ifdef _WIN32\n#include <winioctl.h>\n#endif\n\nnamespace dxvk {\n\n#ifdef _WIN32\n  #define IOCTL_SHARED_GPU_RESOURCE_OPEN             CTL_CODE(FILE_DEVICE_VIDEO, 1, METHOD_BUFFERED, FILE_WRITE_ACCESS)\n\n  HANDLE openKmtHandle(HANDLE kmt_handle) {\n    HANDLE handle = ::CreateFileA(\"\\\\\\\\.\\\\SharedGpuResource\", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\n    if (handle == INVALID_HANDLE_VALUE)\n      return handle;\n\n    struct\n    {\n        unsigned int kmt_handle;\n        WCHAR name[1];\n    } shared_resource_open = {0};\n    shared_resource_open.kmt_handle = reinterpret_cast<uintptr_t>(kmt_handle);\n\n    bool succeed = ::DeviceIoControl(handle, IOCTL_SHARED_GPU_RESOURCE_OPEN, &shared_resource_open, sizeof(shared_resource_open), NULL, 0, NULL, NULL);\n    if (!succeed) {\n      ::CloseHandle(handle);\n      return INVALID_HANDLE_VALUE;\n    }\n    return handle; \n  }\n\n  #define IOCTL_SHARED_GPU_RESOURCE_SET_METADATA           CTL_CODE(FILE_DEVICE_VIDEO, 4, METHOD_BUFFERED, FILE_WRITE_ACCESS)\n\n  bool setSharedMetadata(HANDLE handle, void *buf, uint32_t bufSize) {\n    DWORD retSize;\n    return ::DeviceIoControl(handle, IOCTL_SHARED_GPU_RESOURCE_SET_METADATA, buf, bufSize, NULL, 0, &retSize, NULL);\n  }\n\n  #define IOCTL_SHARED_GPU_RESOURCE_GET_METADATA           CTL_CODE(FILE_DEVICE_VIDEO, 5, METHOD_BUFFERED, FILE_READ_ACCESS)\n\n  bool getSharedMetadata(HANDLE handle, void *buf, uint32_t bufSize, uint32_t *metadataSize) {\n    DWORD retSize;\n    bool ret = ::DeviceIoControl(handle, IOCTL_SHARED_GPU_RESOURCE_GET_METADATA, NULL, 0, buf, bufSize, &retSize, NULL);\n    if (metadataSize)\n      *metadataSize = retSize;\n    return ret;\n  }\n#else\n  HANDLE openKmtHandle(HANDLE kmt_handle) {\n    Logger::warn(\"openKmtHandle: Shared resources not available on this platform.\");\n    return INVALID_HANDLE_VALUE;\n  }\n\n  bool setSharedMetadata(HANDLE handle, void *buf, uint32_t bufSize) {\n    Logger::warn(\"setSharedMetadata: Shared resources not available on this platform.\");\n    return false;\n  }\n\n  bool getSharedMetadata(HANDLE handle, void *buf, uint32_t bufSize, uint32_t *metadataSize) {\n    Logger::warn(\"getSharedMetadata: Shared resources not available on this platform.\");\n    return false;\n  }\n#endif\n\n}\n"
  },
  {
    "path": "src/util/util_shared_res.h",
    "content": "#pragma once\n\n#include <cstdint>\n\n#include \"./com/com_include.h\"\n\n#include <d3d11_4.h>\n\nnamespace dxvk {\n\n    HANDLE openKmtHandle(HANDLE kmt_handle);\n\n    bool setSharedMetadata(HANDLE handle, void *buf, uint32_t bufSize);\n    bool getSharedMetadata(HANDLE handle, void *buf, uint32_t bufSize, uint32_t *metadataSize);\n\n    struct DxvkSharedTextureMetadata {\n      UINT             Width;\n      UINT             Height;\n      UINT             MipLevels;\n      UINT             ArraySize;\n      DXGI_FORMAT      Format;\n      DXGI_SAMPLE_DESC SampleDesc;\n      D3D11_USAGE      Usage;\n      UINT             BindFlags;\n      UINT             CPUAccessFlags;\n      UINT             MiscFlags;\n      D3D11_TEXTURE_LAYOUT TextureLayout;\n    };\n\n}\n"
  },
  {
    "path": "src/util/util_singleton.h",
    "content": "#pragma once\n\n#include \"rc/util_rc_ptr.h\"\n\n#include \"thread.h\"\n\nnamespace dxvk {\n\n/**\n * \\brief Singleton helper\n *\n * Class that manages a dynamically created \n */\ntemplate<typename T>\nclass Singleton {\n\npublic:\n\n  template<typename... Args>\n  Rc<T> acquire(Args... constantArgs) {\n    std::lock_guard lock(m_mutex);\n\n    if (!(m_useCount++))\n      m_object = new T(constantArgs...);\n\n    return m_object;\n  }\n\n  void release() {\n    std::lock_guard lock(m_mutex);\n\n    if (!(--m_useCount))\n      m_object = nullptr;\n  }\n\nprivate:\n\n  dxvk::mutex m_mutex;\n  size_t      m_useCount  = 0;\n  Rc<T>       m_object    = nullptr;;\n\n};\n\n}\n"
  },
  {
    "path": "src/util/util_sleep.cpp",
    "content": "#include \"util_sleep.h\"\n#include \"util_string.h\"\n\n#include \"./log/log.h\"\n\nusing namespace std::chrono_literals;\n\nnamespace dxvk {\n\n  Sleep Sleep::s_instance;\n\n\n  Sleep::Sleep() {\n\n  }\n\n\n  Sleep::~Sleep() {\n\n  }\n\n\n  void Sleep::initialize() {\n    std::lock_guard lock(m_mutex);\n\n    if (m_initialized.load())\n      return;\n\n    initializePlatformSpecifics();\n    m_sleepThreshold = 4 * m_sleepGranularity;\n\n    m_initialized.store(true, std::memory_order_release);\n  }\n\n\n  void Sleep::initializePlatformSpecifics() {\n#ifdef _WIN32\n    HMODULE ntdll = ::GetModuleHandleW(L\"ntdll.dll\");\n\n    if (ntdll) {\n      NtDelayExecution = reinterpret_cast<NtDelayExecutionProc>(\n        ::GetProcAddress(ntdll, \"NtDelayExecution\"));\n      auto NtQueryTimerResolution = reinterpret_cast<NtQueryTimerResolutionProc>(\n        ::GetProcAddress(ntdll, \"NtQueryTimerResolution\"));\n      auto NtSetTimerResolution = reinterpret_cast<NtSetTimerResolutionProc>(\n        ::GetProcAddress(ntdll, \"NtSetTimerResolution\"));\n\n      ULONG min, max, cur;\n\n      // Wine's implementation of these functions is a stub as of 6.10, which is fine\n      // since it uses select() in NtDelayExecution. This is only relevant for Windows.\n      if (NtQueryTimerResolution && !NtQueryTimerResolution(&min, &max, &cur)) {\n        m_sleepGranularity = TimerDuration(cur);\n\n        if (NtSetTimerResolution && !NtSetTimerResolution(max, TRUE, &cur)) {\n          Logger::info(str::format(\"Setting timer interval to \", (double(max) / 10.0), \" us\"));\n          m_sleepGranularity = TimerDuration(max);\n        }\n      }\n    } else {\n      // Assume 1ms sleep granularity by default\n      m_sleepGranularity = TimerDuration(1ms);\n    }\n#else\n    // Assume 0.5ms sleep granularity by default\n    m_sleepGranularity = TimerDuration(500us);\n#endif\n  }\n\n\n  Sleep::TimePoint Sleep::sleep(TimePoint t0, TimerDuration duration) {\n    if (duration <= TimerDuration::zero())\n      return t0;\n\n    // If necessary, initialize function pointers and some values\n    if (!m_initialized.load(std::memory_order_acquire))\n      initialize();\n\n    // Busy-wait for the last couple of milliseconds since sleeping\n    // on Windows is highly inaccurate and inconsistent.\n    TimerDuration sleepThreshold = m_sleepThreshold;\n\n    if (m_sleepGranularity != TimerDuration::zero())\n      sleepThreshold += duration / 6;\n\n    TimerDuration remaining = duration;\n    TimePoint t1 = t0;\n\n    while (remaining > sleepThreshold) {\n      TimerDuration sleepDuration = remaining - sleepThreshold;\n\n      systemSleep(sleepDuration);\n\n      t1 = dxvk::high_resolution_clock::now();\n      remaining -= std::chrono::duration_cast<TimerDuration>(t1 - t0);\n      t0 = t1;\n    }\n\n    // Busy-wait until we have slept long enough\n    while (remaining > TimerDuration::zero()) {\n      t1 = dxvk::high_resolution_clock::now();\n      remaining -= std::chrono::duration_cast<TimerDuration>(t1 - t0);\n      t0 = t1;\n    }\n\n    return t1;\n  }\n\n\n  void Sleep::systemSleep(TimerDuration duration) {\n#ifdef _WIN32\n    if (NtDelayExecution) {\n      LARGE_INTEGER ticks;\n      ticks.QuadPart = -duration.count();\n\n      NtDelayExecution(FALSE, &ticks);\n    } else {\n      std::this_thread::sleep_for(duration);\n    }\n#else\n    std::this_thread::sleep_for(duration);\n#endif\n  }\n\n}\n"
  },
  {
    "path": "src/util/util_sleep.h",
    "content": "#pragma once\n\n#include \"thread.h\"\n#include \"util_time.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Utility class for accurate sleeping\n   */\n  class Sleep {\n\n  public:\n\n    using TimePoint = dxvk::high_resolution_clock::time_point;\n\n    ~Sleep();\n\n    /**\n     * \\brief Sleeps for a given period of time\n     *\n     * \\param [in] t0 Current time\n     * \\param [in] duration Sleep duration\n     * \\returns Time after sleep has finished\n     */\n    template<typename Rep, typename Period>\n    static TimePoint sleepFor(TimePoint t0, std::chrono::duration<Rep, Period> duration) {\n      return s_instance.sleep(t0, std::chrono::duration_cast<TimerDuration>(duration));\n    }\n\n    /**\n     * \\brief Sleeps until a given time point\n     *\n     * Convenience function that sleeps for the\n     * time difference between t1 and t0.\n     * \\param [in] t0 Current time\n     * \\param [in] t1 Target time\n     * \\returns Time after sleep has finished\n     */\n    static TimePoint sleepUntil(TimePoint t0, TimePoint t1) {\n      return sleepFor(t0, t1 - t0);\n    }\n\n  private:\n\n    static Sleep s_instance;\n\n    dxvk::mutex       m_mutex;\n    std::atomic<bool> m_initialized = { false };\n\n#ifdef _WIN32\n    // On Windows, we use NtDelayExecution which has units of 100ns.\n    using TimerDuration = std::chrono::duration<int64_t, std::ratio<1, 10000000>>;\n    using NtQueryTimerResolutionProc = LONG (NTAPI *) (ULONG*, ULONG*, ULONG*);\n    using NtSetTimerResolutionProc = LONG (NTAPI *) (ULONG, BOOLEAN, ULONG*);\n    using NtDelayExecutionProc = LONG (NTAPI *) (BOOLEAN, LARGE_INTEGER*);\n    NtDelayExecutionProc NtDelayExecution = nullptr;\n#else\n    // On other platforms, we use the std library, which calls through to nanosleep -- which is ns.\n    using TimerDuration = std::chrono::nanoseconds;\n#endif\n\n    TimerDuration m_sleepGranularity = TimerDuration::zero();\n    TimerDuration m_sleepThreshold   = TimerDuration::zero();\n\n    Sleep();\n\n    void initialize();\n\n    void initializePlatformSpecifics();\n\n    TimePoint sleep(TimePoint t0, TimerDuration duration);\n\n    void systemSleep(TimerDuration duration);\n\n  };\n\n}\n"
  },
  {
    "path": "src/util/util_small_vector.h",
    "content": "#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <cstdint>\n#include <memory>\n#include <initializer_list>\n#include <type_traits>\n#include <utility>\n\nnamespace dxvk {\n\n/** Small vector state with stateful allocator */\ntemplate<typename Allocator, bool Stateless>\nstruct SmallVectorState {\n  SmallVectorState(size_t initialCapacity, const Allocator& alloc)\n  : capacity(initialCapacity), allocator(alloc) { }\n\n  size_t capacity = 0;\n  size_t size     = 0;\n\n  Allocator allocator = { };\n\n  Allocator& getAllocator() {\n    return allocator;\n  }\n\n  void setAllocator(const Allocator& alloc) {\n    allocator = alloc;\n  }\n};\n\n\n/** Small vector state with stateless allocator. This avoids unnecessary\n *  padding that would otherwise be caused by an empty struct, and creates\n *  a new allocator instance on the fly instead. */\ntemplate<typename Allocator>\nstruct SmallVectorState<Allocator, true> {\n  SmallVectorState(size_t initialCapacity, const Allocator&)\n  : capacity(initialCapacity) { }\n\n  size_t capacity = 0;\n  size_t size     = 0;\n\n  Allocator getAllocator() {\n    return Allocator();\n  }\n\n  void setAllocator(const Allocator&) { }\n};\n\n\n/** Small vector container with embedded storage for a given number of\n *  elements. Largely functions as a drop-in replacement for std::vector. */\ntemplate<typename T, size_t N, typename Allocator = std::allocator<T>>\nclass small_vector {\n  using storage = std::aligned_storage_t<sizeof(T), alignof(T)>;\n\n  template<typename T_, size_t N_, typename Allocator_>\n  friend class small_vector;\npublic:\n\n  constexpr static size_t EmbeddedCapacity = N;\n\n  using iterator = T*;\n  using const_iterator = const T*;\n\n  small_vector()\n  : small_vector(Allocator()) { }\n\n  /** Initialize small vector with allocator */\n  explicit small_vector(const Allocator& alloc)\n  : m_state(N, alloc) {\n    /* Somewhat nonsensical since we will first populat the,\n     * internal array anyway, but hides a GCC warning */\n    u.m_ptr = nullptr;\n  }\n\n  /** Initialize small vector with size and default element value */\n  explicit small_vector(size_t size, T value = T(), const Allocator& alloc = Allocator())\n  : small_vector(alloc) {\n    resize(size, std::move(value));\n  }\n\n  /** Initialize small vector from initializer list */\n  small_vector(const std::initializer_list<T>& list, const Allocator& alloc = Allocator())\n  : small_vector(alloc) {\n    reserve(list.size());\n\n    for (auto iter = list.begin(); iter != list.end(); iter++)\n      new (ptr(m_state.size++)) T(*iter);\n  }\n\n  /** Move constructor. */\n  small_vector(small_vector&& other)\n  : m_state(N, other.m_state.getAllocator()) {\n    move(std::move(other));\n  }\n\n  /** Move constructor from different small_vector type. */\n  template<size_t N_, typename Allocator_>\n  small_vector(small_vector<T, N_, Allocator_>&& other)\n  : m_state(N, other.m_state.getAllocator()) {\n    move(std::move(other));\n  }\n\n  /** Copy constructor. */\n  small_vector(const small_vector& other)\n  : m_state(N, Allocator()) {\n    copy(other);\n  }\n\n  /** Copy constructor from different small_vector type. */\n  template<size_t N_, typename Allocator_>\n  small_vector(const small_vector<T, N_, Allocator_>& other)\n  : m_state(N, Allocator()) {\n    copy(other);\n  }\n\n  /** Move assignment. */\n  small_vector& operator = (small_vector&& other) {\n    free();\n    move(std::move(other));\n    return *this;\n  }\n\n  /** Move assignment from different small_vector type. */\n  template<size_t N_, typename Allocator_>\n  small_vector& operator = (small_vector<T, N_, Allocator_>&& other) {\n    free();\n    move(std::move(other));\n    return *this;\n  }\n\n  /** Copy assignment. */\n  small_vector& operator = (const small_vector& other) {\n    if (this == &other)\n      return *this;\n\n    clear();\n    copy(other);\n    return *this;\n  }\n\n  /** Copy assignment from different small_vector type. */\n  template<size_t N_, typename Allocator_>\n  small_vector& operator = (const small_vector<T, N_, Allocator_>& other) {\n    clear();\n    copy(other);\n    return *this;\n  }\n\n  ~small_vector() {\n    free();\n  }\n\n  /** Pointer to underlying array */\n  const T* data() const { return ptr(0); }\n        T* data()       { return ptr(0); }\n\n  /** Array size */\n  size_t size() const {\n    return m_state.size;\n  }\n\n  /** Checks whether array is empty. */\n  bool empty() const {\n    return !size();\n  }\n\n  /** Current capacity. Will always be greater\n   *  or equal to \\c EmbeddedCapacity. */\n  size_t capacity() const {\n    return m_state.capacity;\n  }\n\n  /** Checks whether array elements are embedded in the\n   *  object itself or use a separate memory allocation. */\n  bool is_embedded() const {\n    return capacity() <= EmbeddedCapacity;\n  }\n\n  /** Allocates enough storage for \\c n elements. */\n  void reserve(size_t n) {\n    n = pick_capacity(n);\n\n    if (n <= capacity())\n      return;\n\n    T* data = m_state.getAllocator().allocate(n);\n\n    for (size_t i = 0; i < size(); i++) {\n      auto object = std::launder(ptr(i));\n\n      new (&data[i]) T(std::move(*object));\n      object->~T();\n    }\n\n    if (!is_embedded())\n      m_state.getAllocator().deallocate(u.m_ptr, capacity());\n\n    m_state.capacity = n;\n    u.m_ptr = data;\n  }\n\n  /** Creates the given number of elements. */\n  void resize(size_t n, T value = T()) {\n    reserve(n);\n\n    for (size_t i = n; i < size(); i++)\n      std::launder(ptr(i))->~T();\n\n    for (size_t i = size(); i < n; i++)\n      new (ptr(i)) T(value);\n\n    m_state.size = n;\n  }\n\n  /** Appends an element. */\n  void push_back(const T& object) {\n    reserve(m_state.size + 1);\n    new (ptr(m_state.size++)) T(object);\n  }\n\n  /** Appends an element with move semantics. */\n  void push_back(T&& object) {\n    reserve(m_state.size + 1);\n    new (ptr(m_state.size++)) T(std::move(object));\n  }\n\n  /** Constructs an element in-place at the end of the array. */\n  template<typename... Args>\n  T& emplace_back(Args&&... args) {\n    reserve(m_state.size + 1);\n    return *(new (ptr(m_state.size++)) T(std::forward<Args>(args)...));\n  }\n\n  /** Removes an element at a given index. */\n  iterator erase(size_t idx) {\n    std::launder(ptr(idx))->~T();\n\n    for (size_t i = idx; i < size() - 1; i++) {\n      auto object = std::launder(ptr(i + 1));\n\n      new (ptr(i)) T(std::move(*object));\n      object->~T();\n    }\n\n    m_state.size--;\n    return begin() + idx;\n  }\n\n  /** Removes an element at a given iterator. */\n  iterator erase(const T* iter) {\n    return erase(std::distance(cbegin(), iter));\n  }\n\n  /** Inserts element at given iterator */\n  iterator insert(const_iterator iter, const T& element) {\n    auto ptr = insert(std::distance(cbegin(), iter));\n    return new (ptr) T(element);\n  }\n\n  /** Move-inserts element at given iterator */\n  iterator insert(const_iterator iter, T&& element) {\n    auto ptr = insert(std::distance(cbegin(), iter));\n    return new (ptr) T(std::move(element));\n  }\n\n  /** Removes last element from array. */\n  void pop_back() {\n    std::launder(ptr(--m_state.size))->~T();\n  }\n\n  /** Removes all elements from array. Does\n   *  not deallcate any storage. */\n  void clear() {\n    for (size_t i = 1; i <= size(); i++)\n      std::launder(ptr(size() - i))->~T();\n\n    m_state.size = 0;\n  }\n\n  /** Moves array to an allocation of exactly the required\n   *  size, or to embedded storage if possible. */\n  void shrink_to_fit() {\n    if (is_embedded() || size() == capacity())\n      return;\n\n    auto oldCapacity = capacity();\n    auto oldStorage = u.m_ptr;\n\n    /* Capacity must be at least as large as the embedded storage */\n    m_state.capacity = std::max(size(), EmbeddedCapacity);\n\n    if (!is_embedded())\n      u.m_ptr = m_state.getAllocator().allocate(capacity());\n\n    for (size_t i = 0u; i < size(); i++) {\n      new (ptr(i)) T(std::move(oldStorage[i]));\n      oldStorage[i].~T();\n    }\n\n    m_state.getAllocator().deallocate(oldStorage, oldCapacity);\n  }\n\n  /** Provides unchecked access to elements */\n  const T& operator [] (size_t idx) const { return *ptr(idx); }\n        T& operator [] (size_t idx)       { return *ptr(idx); }\n\n  /** Iterator begin/end. */\n  T* begin() { return ptr(0); }\n  T* end() { return begin() + size(); }\n\n  /** Constant iterator begin/end. */\n  const T* begin() const { return ptr(0); }\n  const T* end() const { return begin() + size(); }\n\n  const T* cbegin() const { return begin(); }\n  const T* cend() const { return end(); }\n\n  const T& front() const { return *ptr(0); }\n        T& front()       { return *ptr(0); }\n\n  const T& back() const { return *ptr(size() - 1); }\n        T& back()       { return *ptr(size() - 1); }\n\nprivate:\n\n  SmallVectorState<Allocator, std::allocator_traits<Allocator>::is_always_equal::value> m_state;\n\n  union {\n    T*      m_ptr;\n    storage m_data[N];\n  } u;\n\n  /** Computes ideal capacity for given size. */\n  size_t pick_capacity(size_t n) {\n    size_t newCapacity = capacity();\n\n    while (newCapacity < n)\n      newCapacity *= 2;\n\n    return newCapacity;\n  }\n\n  /** Retrieves raw pointer to element. Must be laundered\n   *  before use since it may point to local storage. */\n  T* ptr(size_t idx) {\n    return is_embedded()\n      ? reinterpret_cast<T*>(u.m_data + idx)\n      : u.m_ptr + idx;\n  }\n\n  const T* ptr(size_t idx) const {\n    return is_embedded()\n      ? reinterpret_cast<const T*>(u.m_data + idx)\n      : u.m_ptr + idx;\n  }\n\n  /** Frees all elements and deallocates storage. */\n  void free() {\n    for (size_t i = 0; i < size(); i++)\n      std::launder(ptr(i))->~T();\n\n    if (!is_embedded())\n      m_state.getAllocator().deallocate(u.m_ptr, capacity());\n\n    m_state.capacity = N;\n    m_state.size = 0;\n  }\n\n  /** Convenience method to move from another vector, assumes\n   *  that the destination vector is empty. */\n  template<size_t N_, typename Allocator_>\n  void move(small_vector<T, N_, Allocator_>&& other) {\n    /* We may inherit the data pointer if there is one,\n     * so we need to inherit the allocator as well. */\n    if constexpr (std::is_same_v<Allocator, Allocator_>)\n      m_state.setAllocator(other.m_state.getAllocator());\n\n    if (!std::is_same_v<Allocator, Allocator_> || other.capacity() <= std::max(N, N_)) {\n      /* Move individual elements if the source capacity is not\n       * greater than either vector's embedded capacity, since\n       * the capacity determines which storage to use. */\n      reserve(other.size());\n\n      for (size_t i = 0; i < other.size(); i++) {\n        auto object = std::launder(other.ptr(i));\n\n        new (ptr(i)) T(std::move(*object));\n        object->~T();\n      }\n\n      m_state.size = std::exchange(other.m_state.size, size_t(0));\n    } else {\n      /* Move the storage itself without reallocating any memory. */\n      m_state.capacity = std::exchange(other.m_state.capacity, N_);\n      m_state.size = std::exchange(other.m_state.size, size_t(0));\n      u.m_ptr = std::exchange(other.u.m_ptr, nullptr);\n    }\n  }\n\n  /** Convenience method to copy from another vector, assumes\n   *  that the destination vector is empty. */\n  template<size_t N_, typename Allocator_>\n  void copy(const small_vector<T, N_, Allocator_>& other) {\n    reserve(other.size());\n\n    for (size_t i = 0; i < other.size(); i++)\n      emplace_back(other[i]);\n  }\n\n  /** Ensures free element at given index */\n  T* insert(size_t idx) {\n    size_t last = size();\n    reserve(last + 1u);\n\n    while (last > idx) {\n      auto prev = std::launder(ptr(last - 1u));\n      new (ptr(last--)) T(std::move(*prev));\n      prev->~T();\n    }\n\n    m_state.size++;\n    return ptr(idx);\n  }\n\n};\n\n}\n"
  },
  {
    "path": "src/util/util_string.cpp",
    "content": "#include \"util_string.h\"\n\nnamespace dxvk::str {\n\n  const uint8_t* decodeTypedChar(\n    const uint8_t*  begin,\n    const uint8_t*  end,\n          uint32_t& ch) {\n    uint32_t first = begin[0];\n\n    if (likely(first < 0x80)) {\n      // Basic ASCII character\n      ch = uint32_t(first);\n      return begin + 1;\n    } else if (unlikely(first < 0xC0)) {\n      // Character starts with a continuation byte,\n      // just skip until we find the next valid prefix\n      while ((begin < end) && (((*begin) & 0xC0) == 0x80))\n        begin += 1;\n\n      ch = uint32_t('?');\n      return begin;\n    } else {\n      // The number of leading 1 bits in the first byte\n      // determines the length of this character\n      size_t length = bit::lzcnt((~first) << 24);\n\n      if (unlikely(begin + length > end)) {\n        ch = uint32_t('?');\n        return end;\n      }\n\n      if (first < 0xE0) {\n        ch = ((uint32_t(begin[0]) & 0x1F) << 6)\n           | ((uint32_t(begin[1]) & 0x3F));\n      } else if (first < 0xF0) {\n        ch = ((uint32_t(begin[0]) & 0x0F) << 12)\n           | ((uint32_t(begin[1]) & 0x3F) << 6)\n           | ((uint32_t(begin[2]) & 0x3F));\n      } else if (first < 0xF8) {\n        ch = ((uint32_t(begin[0]) & 0x07) << 18)\n           | ((uint32_t(begin[1]) & 0x3F) << 12)\n           | ((uint32_t(begin[2]) & 0x3F) << 6)\n           | ((uint32_t(begin[3]) & 0x3F));\n      } else {\n        // Invalid prefix\n        ch = uint32_t('?');\n      }\n\n      return begin + length;\n    }\n  }\n\n  const uint16_t* decodeTypedChar(\n    const uint16_t* begin,\n    const uint16_t* end,\n          uint32_t& ch) {\n    uint32_t first = begin[0];\n\n    if (likely(first < 0xD800)) {\n      ch = first;\n      return begin + 1;\n    } else if (first < 0xDC00) {\n      if (unlikely(begin + 2 > end)) {\n        ch = uint32_t('?');\n        return end;\n      }\n\n      ch = 0x10000\n         + ((uint32_t(begin[0]) & 0x3FF) << 10)\n         + ((uint32_t(begin[1]) & 0x3FF));\n      return begin + 2;\n    } else if (unlikely(first < 0xE000)) {\n      // Stray low surrogate\n      ch = uint32_t('?');\n      return begin + 1;\n    } else {\n      ch = first;\n      return begin + 1;\n    }\n  }\n\n\n  const uint32_t* decodeTypedChar(\n    const uint32_t* begin,\n    const uint32_t* end,\n          uint32_t& ch) {\n    ch = begin[0];\n    return begin + 1;\n  }\n\n\n  size_t encodeTypedChar(\n          uint8_t*  begin,\n          uint8_t*  end,\n          uint32_t  ch) {\n    if (likely(ch < 0x80)) {\n      if (begin) {\n        if (unlikely(begin + 1 > end))\n          return 0;\n\n        begin[0] = uint8_t(ch);\n      }\n\n      return 1;\n    } else if (ch < 0x800) {\n      if (begin) {\n        if (unlikely(begin + 2 > end))\n          return 0;\n\n        begin[0] = uint8_t(0xC0 | (ch >> 6));\n        begin[1] = uint8_t(0x80 | (ch & 0x3F));\n      }\n\n      return 2;\n    } else if (ch < 0x10000) {\n      if (begin) {\n        if (unlikely(begin + 3 > end))\n          return 0;\n\n        begin[0] = uint8_t(0xE0 | ((ch >> 12)));\n        begin[1] = uint8_t(0x80 | ((ch >> 6) & 0x3F));\n        begin[2] = uint8_t(0x80 | ((ch >> 0) & 0x3F));\n      }\n\n      return 3;\n    } else if (ch < 0x200000) {\n      if (begin) {\n        if (unlikely(begin + 4 > end))\n          return 0;\n\n        begin[0] = uint8_t(0xF0 | ((ch >> 18)));\n        begin[1] = uint8_t(0x80 | ((ch >> 12) & 0x3F));\n        begin[2] = uint8_t(0x80 | ((ch >> 6) & 0x3F));\n        begin[3] = uint8_t(0x80 | ((ch >> 0) & 0x3F));\n      }\n\n      return 4;\n    } else {\n      // Invalid code point for UTF-8\n      return 0;\n    }\n  }\n\n\n  size_t encodeTypedChar(\n          uint16_t* begin,\n          uint16_t* end,\n          uint32_t  ch) {\n    if (likely(ch < 0xD800)) {\n      if (begin) {\n        if (unlikely(begin + 1 > end))\n          return 0;\n\n        begin[0] = ch;\n      }\n\n      return 1;\n    } else if (ch < 0xE000) {\n      // Private use code points,\n      // we can't encode these\n      return 0;\n    } else if (ch < 0x10000) {\n      if (begin) {\n        if (unlikely(begin + 1 > end))\n          return 0;\n\n        begin[0] = ch;\n      }\n\n      return 1;\n    } else if (ch < 0x110000) {\n      if (begin) {\n        if (unlikely(begin + 2 > end))\n          return 0;\n\n        ch -= 0x10000;\n        begin[0] = uint16_t(0xD800 + (ch >> 10));\n        begin[1] = uint16_t(0xDC00 + (ch & 0x3FF));\n      }\n\n      return 2;\n    } else {\n      // Invalid code point\n      return 0;\n    }\n  }\n\n\n  size_t encodeTypedChar(\n          uint32_t* begin,\n          uint32_t* end,\n          uint32_t  ch) {\n    if (begin) {\n      if (unlikely(begin + 1 > end))\n        return 0;\n\n      begin[0] = ch;\n    }\n\n    return 1;\n  }\n\n\n  std::string fromws(const WCHAR* ws) {\n    size_t srcLen = length(ws);\n    size_t dstLen = transcodeString<char>(\n      nullptr, 0, ws, srcLen);\n\n    std::string result;\n    result.resize(dstLen);\n\n    transcodeString(result.data(),\n      dstLen, ws, srcLen);\n\n    return result;\n  }\n\n\n  std::wstring tows(const char* mbs) {\n    size_t srcLen = length(mbs);\n    size_t dstLen = transcodeString<wchar_t>(\n      nullptr, 0, mbs, srcLen);\n\n    std::wstring result;\n    result.resize(dstLen);\n\n    transcodeString(result.data(),\n      dstLen, mbs, srcLen);\n\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "src/util/util_string.h",
    "content": "#pragma once\n\n#include <cstring>\n#include <string>\n#include <sstream>\n#include <vector>\n\n#include \"./com/com_include.h\"\n\n#include \"util_bit.h\"\n#include \"util_likely.h\"\n\nnamespace dxvk::str {\n\n  template<size_t S> struct UnicodeChar { };\n  template<> struct UnicodeChar<1> { using type = uint8_t;  };\n  template<> struct UnicodeChar<2> { using type = uint16_t; };\n  template<> struct UnicodeChar<4> { using type = uint32_t; };\n\n  template<typename T>\n  using UnicodeCharType = typename UnicodeChar<sizeof(T)>::type;\n\n  const uint8_t* decodeTypedChar(\n    const uint8_t*  begin,\n    const uint8_t*  end,\n          uint32_t& ch);\n\n  const uint16_t* decodeTypedChar(\n    const uint16_t* begin,\n    const uint16_t* end,\n          uint32_t& ch);\n\n  const uint32_t* decodeTypedChar(\n    const uint32_t* begin,\n    const uint32_t* end,\n          uint32_t& ch);\n\n  size_t encodeTypedChar(\n          uint8_t*  begin,\n          uint8_t*  end,\n          uint32_t  ch);\n\n  size_t encodeTypedChar(\n          uint16_t* begin,\n          uint16_t* end,\n          uint32_t  ch);\n\n  size_t encodeTypedChar(\n          uint32_t* begin,\n          uint32_t* end,\n          uint32_t  ch);\n\n  /**\n   * \\brief Decodes a single character\n   *\n   * Note that \\c begin and \\c end must not be equal.\n   * \\param [in] begin Pointer to current position within the input string\n   * \\param [in] end Pointer to the end of the input string\n   * \\param [out] ch Pointer to the decoded character code\n   * \\returns Pointer to next character in the input string\n   */\n  template<typename T>\n  const T* decodeChar(\n    const T*        begin,\n    const T*        end,\n          uint32_t& ch) {\n    using CharType = UnicodeCharType<T>;\n\n    const CharType* result = decodeTypedChar(\n      reinterpret_cast<const CharType*>(begin),\n      reinterpret_cast<const CharType*>(end),\n      ch);\n\n    return reinterpret_cast<const T*>(result);\n  }\n\n  /**\n   * \\brief Encodes a character\n   *\n   * Note that \\c begin and \\c end may be both be \\c nullptr or equal, in\n   * which case only the length of the encoded character will be returned.\n   * \\param [in] begin Pointer to current position within the output string\n   * \\param [in] end Pointer to the end of the output string\n   * \\param [in] ch Character to encode\n   * \\returns If begin is \\c nullptr , the number of units required to encode\n   *    the character. Otherwise, the number of units written to the output.\n   *    This may return \\c 0 for characters that cannot be written or encoded.\n   */\n  template<typename T>\n  size_t encodeChar(\n          T*        begin,\n          T*        end,\n          uint32_t  ch) {\n    using CharType = UnicodeCharType<T>;\n\n    return encodeTypedChar(\n      reinterpret_cast<CharType*>(begin),\n      reinterpret_cast<CharType*>(end),\n      ch);\n  }\n\n  /**\n   * \\brief Computes length of a null-terminated string\n   *\n   * \\param [in] begin Start of input string\n   * \\returns Number of characters in input string,\n   *    excluding the terminating null character\n   */\n  template<typename S>\n  size_t length(const S* string) {\n    size_t result = 0;\n\n    while (string[result])\n      result += 1;\n\n    return result;\n  }\n\n  /**\n   * \\brief Converts string from one encoding to another\n   *\n   * The output string arguments may be \\c nullptr. In that case, the\n   * total length of the transcoded string will be returned, in units\n   * of the output character type. The output string will only be\n   * null-terminated if the input string is also null-terminated.\n   * \\tparam D Output character type\n   * \\tparam S Input character type\n   * \\param [in] dstBegin Start of output string\n   * \\param [in] dstLength Length of output string\n   * \\param [in] srcBegin Start of input string\n   * \\param [in] srcLength Length of input string\n   * \\returns If \\c dstBegin is \\c nullptr , the total number of output\n   *    characters required to store the output string. Otherwise, the\n   *    total number of characters written to the output string.\n   */\n  template<typename D, typename S>\n  size_t transcodeString(\n          D*      dstBegin,\n          size_t  dstLength,\n    const S*      srcBegin,\n          size_t  srcLength) {\n    size_t totalLength = 0;\n\n    auto dstEnd = dstBegin + dstLength;\n    auto srcEnd = srcBegin + srcLength;\n\n    while (srcBegin < srcEnd) {\n      uint32_t ch;\n\n      srcBegin = decodeChar<S>(srcBegin, srcEnd, ch);\n\n      if (dstBegin)\n        totalLength += encodeChar<D>(dstBegin + totalLength, dstEnd, ch);\n      else\n        totalLength += encodeChar<D>(nullptr, nullptr, ch);\n\n      if (!ch)\n        break;\n    }\n\n    return totalLength;\n  }\n\n  /**\n   * \\brief Creates string object from wide char array\n   *\n   * \\param [in] ws Null-terminated wide string\n   * \\returns Regular string object\n   */\n  std::string fromws(const WCHAR* ws);\n\n  /**\n   * \\brief Creates wide string object from char array\n   *\n   * \\param [in] mbs Null-terminated string\n   * \\returns Wide string object\n   */\n  std::wstring tows(const char* mbs);\n\n#ifdef _WIN32\n  using path_string = std::wstring;\n  inline path_string topath(const char* mbs) { return tows(mbs); }\n#else\n  using path_string = std::string;\n  inline path_string topath(const char* mbs) { return std::string(mbs); }\n#endif\n  \n  inline void format1(std::stringstream&) { }\n\n  template<typename... Tx>\n  void format1(std::stringstream& str, const WCHAR *arg, const Tx&... args) {\n    str << fromws(arg);\n    format1(str, args...);\n  }\n\n  template<typename T, typename... Tx>\n  void format1(std::stringstream& str, const T& arg, const Tx&... args) {\n    str << arg;\n    format1(str, args...);\n  }\n  \n  template<typename... Args>\n  std::string format(const Args&... args) {\n    std::stringstream stream;\n    format1(stream, args...);\n    return stream.str();\n  }\n\n  inline void strlcpy(char* dst, const char* src, size_t count) {\n    if (count > 0) {\n      std::strncpy(dst, src, count - 1);\n      dst[count - 1] = '\\0';\n    }\n  }\n\n  /**\n   * \\brief Split string at one or more delimiters characters\n   * \n   * \\param [in] string String to split\n   * \\param [in] delims Delimiter characters\n   * \\returns Vector of substring views\n  */\n  inline std::vector<std::string_view> split(std::string_view string, std::string_view delims = \" \") {\n    std::vector<std::string_view> tokens;\n\n    for (size_t start = 0; start < string.size(); ) {\n      // Find first delimiter\n      const auto end = string.find_first_of(delims, start);\n\n      // Add non-empty tokens\n      if (start != end)\n        tokens.emplace_back(string.substr(start, end-start));\n\n      // Break at the end of string\n      if (end == std::string_view::npos)\n        break;\n\n      start = end + 1;\n    }\n    return tokens;\n  }\n\n  /** Compares ASCII characters in a case-insensitive way */\n  inline bool compareCharsCaseInsensitive(char a, char b) {\n    if (a >= 'A' && a <= 'Z') a += 'a' - 'A';\n    if (b >= 'A' && b <= 'Z') b += 'a' - 'A';\n    return a == b;\n  }\n\n  /** Compare ASCII string in a case-insensitive way */\n  inline bool compareCaseInsensitive(const char* a, const char* b) {\n    for (size_t i = 0u; a[i] || b[i]; i++) {\n      if (!compareCharsCaseInsensitive(a[i], b[i]))\n        return false;\n    }\n\n    return true;\n  }\n\n}\n"
  },
  {
    "path": "src/util/util_time.h",
    "content": "#pragma once\n\n#include <chrono>\n#include <cstdint>\n\n#if defined(_WIN32) && !defined(__WINE__)\n#include <windows.h>\n#endif\n\nnamespace dxvk {\n\n#if defined(_WIN32) && !defined(__WINE__)\n  struct high_resolution_clock {\n    static constexpr bool is_steady = true;\n\n    using rep        = int64_t;\n    using period     = std::nano;\n    using duration   = std::chrono::nanoseconds;\n    using time_point = std::chrono::time_point<high_resolution_clock>;\n\n    static inline time_point now() noexcept {\n      return get_time_from_counter(get_counter());\n    }\n\n    static inline time_point get_time_from_counter(int64_t counter) {\n      // Keep the frequency static, this doesn't change at all.\n      static const int64_t freq = get_frequency();\n\n      const int64_t whole = (counter / freq) * period::den;\n      const int64_t part  = (counter % freq) * period::den / freq;\n\n      return time_point(duration(whole + part));\n    }\n\n    static inline int64_t get_frequency() {\n      LARGE_INTEGER freq;\n      QueryPerformanceFrequency(&freq);\n\n      return freq.QuadPart;\n    }\n\n    static inline int64_t get_counter() {\n      LARGE_INTEGER count;\n      QueryPerformanceCounter(&count);\n\n      return count.QuadPart;\n    }\n  };\n#else\n  struct high_resolution_clock : public std::chrono::high_resolution_clock {\n    static inline time_point get_time_from_counter(int64_t counter) {\n      return time_point() + duration(counter);\n    }\n\n    static inline int64_t get_frequency() {\n      return period::den;\n    }\n\n    static inline int64_t get_counter() {\n      return now().time_since_epoch().count();\n    }\n  };\n#endif\n\n}"
  },
  {
    "path": "src/util/util_vector.h",
    "content": "#pragma once\n\n#include <iostream>\n\n#include \"util_bit.h\"\n#include \"util_math.h\"\n\nnamespace dxvk {\n\n  template <typename T>\n  struct Vector4Base {\n    Vector4Base()\n      : x{ }, y{ }, z{ }, w{ } { }\n\n    Vector4Base(T splat)\n      : x(splat), y(splat), z(splat), w(splat) { }\n\n    Vector4Base(T x, T y, T z, T w)\n      : x(x), y(y), z(z), w(w) { }\n\n    Vector4Base(const T xyzw[4])\n      : x(xyzw[0]), y(xyzw[1]), z(xyzw[2]), w(xyzw[3]) { }\n\n    Vector4Base(const Vector4Base<T>& other) = default;\n    Vector4Base& operator=(const Vector4Base<T>& other) = default;\n\n    inline       T& operator[](size_t index)       { return data[index]; }\n    inline const T& operator[](size_t index) const { return data[index]; }\n\n    bool operator==(const Vector4Base<T>& other) const {\n      for (uint32_t i = 0; i < 4; i++) {\n        if (data[i] != other.data[i])\n        return false;\n      }\n\n      return true;\n    }\n\n    bool operator!=(const Vector4Base<T>& other) const {\n      return !operator==(other);\n    }\n\n    Vector4Base operator-() const { return {-x, -y, -z, -w}; }\n\n    Vector4Base operator+(const Vector4Base<T>& other) const {\n      return {x + other.x, y + other.y, z + other.z, w + other.w};\n    }\n\n    Vector4Base operator-(const Vector4Base<T>& other) const {\n      return {x - other.x, y - other.y, z - other.z, w - other.w};\n    }\n\n    Vector4Base operator*(T scalar) const {\n      return {scalar * x, scalar * y, scalar * z, scalar * w};\n    }\n\n    Vector4Base operator*(const Vector4Base<T>& other) const {\n      Vector4Base result;\n      for (uint32_t i = 0; i < 4; i++)\n        result[i] = data[i] * other.data[i];\n      return result;\n    }\n\n    Vector4Base operator/(const Vector4Base<T>& other) const {\n      Vector4Base result;\n      for (uint32_t i = 0; i < 4; i++)\n        result[i] = data[i] / other.data[i];\n      return result;\n    }\n\n    Vector4Base operator/(T scalar) const {\n      return {x / scalar, y / scalar, z / scalar, w / scalar};\n    }\n\n    Vector4Base& operator+=(const Vector4Base<T>& other) {\n      x += other.x;\n      y += other.y;\n      z += other.z;\n      w += other.w;\n\n      return *this;\n    }\n\n    Vector4Base& operator-=(const Vector4Base<T>& other) {\n      x -= other.x;\n      y -= other.y;\n      z -= other.z;\n      w -= other.w;\n\n      return *this;\n    }\n\n    Vector4Base& operator*=(T scalar) {\n      x *= scalar;\n      y *= scalar;\n      z *= scalar;\n      w *= scalar;\n\n      return *this;\n    }\n\n    Vector4Base& operator/=(T scalar) {\n      x /= scalar;\n      y /= scalar;\n      z /= scalar;\n      w /= scalar;\n\n      return *this;\n    }\n\n    union {\n      T data[4];\n      struct {\n        T x, y, z, w;\n      };\n      struct {\n        T r, g, b, a;\n      };\n    };\n\n  };\n\n  template <typename T>\n  inline Vector4Base<T> operator*(T scalar, const Vector4Base<T>& vector) {\n    return vector * scalar;\n  }\n\n  template <typename T>\n  float dot(const Vector4Base<T>& a, const Vector4Base<T>& b) {\n    return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;\n  }\n\n  template <typename T>\n  T lengthSqr(const Vector4Base<T>& a) { return dot(a, a); }\n\n  template <typename T>\n  float length(const Vector4Base<T>& a) { return std::sqrt(float(lengthSqr(a))); }\n\n  template <typename T>\n  Vector4Base<T> normalize(const Vector4Base<T>& a) { return a * T(1.0f / length(a)); }\n\n  template <typename T>\n  std::ostream& operator<<(std::ostream& os, const Vector4Base<T>& v) {\n    return os << \"Vector4(\" << v[0] << \", \" << v[1] << \", \" << v[2] << \", \" << v[3] << \")\";\n  }\n\n  using Vector4  = Vector4Base<float>;\n  using Vector4i = Vector4Base<int>;\n\n  static_assert(sizeof(Vector4)  == sizeof(float) * 4);\n  static_assert(sizeof(Vector4i) == sizeof(int)   * 4);\n\n  inline Vector4 replaceNaN(Vector4 a) {\n    #ifdef DXVK_ARCH_X86\n    Vector4 result;\n    __m128 value = _mm_loadu_ps(a.data);\n    __m128 mask  = _mm_cmpeq_ps(value, value);\n           value = _mm_and_ps(value, mask);\n    _mm_storeu_ps(result.data, value);\n    return result;\n    #else\n    for (int i = 0; i < 4; i++)\n      a[i] = std::isnan(a[i]) ? 0.0f : a[i];\n    return a;\n    #endif\n  }\n\n}\n"
  },
  {
    "path": "src/util/util_version.h",
    "content": "#pragma once\n\n#include <cstdint>\n\n#include \"../vulkan/vulkan_loader.h\"\n\n#include \"util_string.h\"\n\nnamespace dxvk {\n\n  /**\n   * \\brief Decoded driver version\n   */\n  class Version {\n\n  public:\n\n    Version() = default;\n\n    Version(uint32_t major, uint32_t minor, uint32_t patch)\n    : m_raw((uint64_t(major) << 48) | (uint64_t(minor) << 24) | uint64_t(patch)) { }\n\n    uint32_t major() const { return uint32_t(m_raw >> 48); }\n    uint32_t minor() const { return uint32_t(m_raw >> 24) & 0xffffffu; }\n    uint32_t patch() const { return uint32_t(m_raw) & 0xffffffu; }\n\n    bool operator == (const Version& other) const { return m_raw == other.m_raw; }\n    bool operator != (const Version& other) const { return m_raw != other.m_raw; }\n    bool operator >= (const Version& other) const { return m_raw >= other.m_raw; }\n    bool operator <= (const Version& other) const { return m_raw <= other.m_raw; }\n    bool operator >  (const Version& other) const { return m_raw >  other.m_raw; }\n    bool operator <  (const Version& other) const { return m_raw <  other.m_raw; }\n\n    std::string toString() const {\n      return str::format(major(), \".\", minor(), \".\", patch());\n    }\n\n    explicit operator bool () const {\n      return m_raw != 0;\n    }\n\n  private:\n\n    uint64_t m_raw = 0;\n\n  };\n\n}\n"
  },
  {
    "path": "src/util/util_win32_compat.h",
    "content": "#pragma once\n\n#if defined(__unix__)\n\n#include <windows.h>\n#include <dlfcn.h>\n\n#include \"log/log.h\"\n\ninline HMODULE LoadLibraryA(LPCSTR lpLibFileName) {\n  return dlopen(lpLibFileName, RTLD_NOW);\n}\n\ninline void FreeLibrary(HMODULE module) {\n  dlclose(module);\n}\n\ninline void* GetProcAddress(HMODULE module, LPCSTR lpProcName) {\n  if (!module)\n    return nullptr;\n\n  return dlsym(module, lpProcName);\n}\n\ninline HANDLE CreateSemaphoreA(\n        SECURITY_ATTRIBUTES*  lpSemaphoreAttributes,\n        LONG                  lInitialCount,\n        LONG                  lMaximumCount,\n        LPCSTR                lpName) {\n  dxvk::Logger::warn(\"CreateSemaphoreA not implemented.\");\n  return nullptr;\n}\n#define CreateSemaphore CreateSemaphoreA\n\ninline BOOL ReleaseSemaphore(\n        HANDLE hSemaphore,\n        LONG   lReleaseCount,\n        LONG*  lpPreviousCount) {\n  dxvk::Logger::warn(\"ReleaseSemaphore not implemented.\");\n  return FALSE;\n}\n\ninline BOOL SetEvent(HANDLE hEvent) {\n  dxvk::Logger::warn(\"SetEvent not implemented.\");\n  return FALSE;\n}\n\ninline BOOL DuplicateHandle(\n        HANDLE hSourceProcessHandle,\n        HANDLE hSourceHandle,\n        HANDLE hTargetProcessHandle,\n        HANDLE* lpTargetHandle,\n        DWORD dwDesiredAccess,\n        BOOL bInheritHandle,\n        DWORD dwOptions) {\n  dxvk::Logger::warn(\"DuplicateHandle not implemented.\");\n  return FALSE;\n}\n\ninline BOOL CloseHandle(HANDLE hObject) {\n  dxvk::Logger::warn(\"CloseHandle not implemented.\");\n  return FALSE;\n}\n\ninline HANDLE GetCurrentProcess() {\n  dxvk::Logger::warn(\"GetCurrentProcess not implemented.\");\n  return nullptr;\n}\n\ninline DWORD GetCurrentProcessId() {\n  dxvk::Logger::warn(\"GetCurrentProcessId not implemented.\");\n  return 0;\n}\n\ninline BOOL ProcessIdToSessionId(DWORD pid, DWORD *id) {\n  dxvk::Logger::warn(\"ProcessIdToSessionId not implemented.\");\n  *id = 0;\n  return FALSE;\n}\n\ninline HDC CreateCompatibleDC(HDC hdc) {\n  dxvk::Logger::warn(\"CreateCompatibleDC not implemented.\");\n  return nullptr;\n}\n\ninline BOOL DeleteDC(HDC hdc) {\n  return FALSE;\n}\n\n#endif\n"
  },
  {
    "path": "src/vulkan/meson.build",
    "content": "vkcommon_src = files([\n  'vulkan_loader.cpp',\n  'vulkan_names.cpp',\n])\n\nthread_dep = dependency('threads')\n\nvkcommon_lib = static_library('vkcommon', vkcommon_src,\n  dependencies        : [ thread_dep ],\n  include_directories : [ dxvk_include_path ],\n)\n\nvkcommon_dep = declare_dependency(\n  link_with           : [ vkcommon_lib ],\n  include_directories : [ dxvk_include_path ],\n)\n"
  },
  {
    "path": "src/vulkan/vulkan_loader.cpp",
    "content": "#include <tuple>\n\n#include \"vulkan_loader.h\"\n\n#include \"../util/log/log.h\"\n\n#include \"../util/util_string.h\"\n#include \"../util/util_win32_compat.h\"\n\nnamespace dxvk::vk {\n\n  static std::pair<HMODULE, PFN_vkGetInstanceProcAddr> loadVulkanLibrary() {\n    static const std::array<const char*, 2> dllNames = {{\n#ifdef _WIN32\n      \"winevulkan.dll\",\n      \"vulkan-1.dll\",\n#else\n      \"libvulkan.so\",\n      \"libvulkan.so.1\",\n#endif\n    }};\n\n    for (auto dllName : dllNames) {\n      HMODULE library = LoadLibraryA(dllName);\n\n      if (!library)\n        continue;\n\n      auto proc = GetProcAddress(library, \"vkGetInstanceProcAddr\");\n\n      if (!proc) {\n        FreeLibrary(library);\n        continue;\n      }\n\n      Logger::info(str::format(\"Vulkan: Found vkGetInstanceProcAddr in \", dllName, \" @ 0x\", std::hex, reinterpret_cast<uintptr_t>(proc)));\n      return std::make_pair(library, reinterpret_cast<PFN_vkGetInstanceProcAddr>(proc));\n    }\n\n    Logger::err(\"Vulkan: vkGetInstanceProcAddr not found\");\n    return { };\n  }\n\n  LibraryLoader::LibraryLoader() {\n    std::tie(m_library, m_getInstanceProcAddr) = loadVulkanLibrary();\n  }\n\n  LibraryLoader::LibraryLoader(PFN_vkGetInstanceProcAddr loaderProc) {\n    m_getInstanceProcAddr = loaderProc;\n  }\n\n  LibraryLoader::~LibraryLoader() {\n    if (m_library)\n      FreeLibrary(m_library);\n  }\n\n  PFN_vkVoidFunction LibraryLoader::sym(VkInstance instance, const char* name) const {\n    return m_getInstanceProcAddr(instance, name);\n  }\n\n  PFN_vkVoidFunction LibraryLoader::sym(const char* name) const {\n    return sym(nullptr, name);\n  }\n\n  \n  InstanceLoader::InstanceLoader(const Rc<LibraryLoader>& library, bool owned, VkInstance instance)\n  : m_library(library), m_instance(instance), m_owned(owned) { }\n  \n  \n  PFN_vkVoidFunction InstanceLoader::sym(const char* name) const {\n    return m_library->sym(m_instance, name);\n  }\n  \n  \n  DeviceLoader::DeviceLoader(const Rc<InstanceLoader>& library, bool owned, VkDevice device)\n  : m_library(library)\n  , m_getDeviceProcAddr(reinterpret_cast<PFN_vkGetDeviceProcAddr>(\n      m_library->sym(\"vkGetDeviceProcAddr\"))),\n    m_device(device), m_owned(owned) { }\n  \n  \n  PFN_vkVoidFunction DeviceLoader::sym(const char* name) const {\n    return m_getDeviceProcAddr(m_device, name);\n  }\n  \n  \n  LibraryFn::LibraryFn() { }\n  LibraryFn::LibraryFn(PFN_vkGetInstanceProcAddr loaderProc)\n  : LibraryLoader(loaderProc) { }\n  LibraryFn::~LibraryFn() { }\n  \n  \n  InstanceFn::InstanceFn(const Rc<LibraryLoader>& library, bool owned, VkInstance instance)\n  : InstanceLoader(library, owned, instance) { }\n  InstanceFn::~InstanceFn() {\n    if (m_owned)\n      this->vkDestroyInstance(m_instance, nullptr);\n  }\n  \n  \n  DeviceFn::DeviceFn(const Rc<InstanceLoader>& library, bool owned, VkDevice device)\n  : DeviceLoader(library, owned, device) { }\n  DeviceFn::~DeviceFn() {\n    if (m_owned)\n      this->vkDestroyDevice(m_device, nullptr);\n  }\n  \n}\n"
  },
  {
    "path": "src/vulkan/vulkan_loader.h",
    "content": "#pragma once\n\n#include \"../util/rc/util_rc.h\"\n#include \"../util/rc/util_rc_ptr.h\"\n\n#define VK_USE_PLATFORM_WIN32_KHR 1\n#include <vulkan/vulkan.h>\n\n#define VULKAN_FN(name) \\\n  ::PFN_ ## name name = reinterpret_cast<::PFN_ ## name>(sym(#name))\n\nusing PFN_wine_vkAcquireKeyedMutex = VkResult (VKAPI_PTR *)(VkDevice device, VkDeviceMemory memory, uint64_t key, uint32_t timeout_ms);\nusing PFN_wine_vkReleaseKeyedMutex = VkResult (VKAPI_PTR *)(VkDevice device, VkDeviceMemory memory, uint64_t key);\n\nnamespace dxvk::vk {\n\n  /**\n   * \\brief Vulkan library loader\n   * \n   * Dynamically loads the vulkan-1 library and\n   * provides methods to load Vulkan functions that\n   * can be called before creating a instance.\n   */\n  struct LibraryLoader : public RcObject {\n    LibraryLoader();\n    LibraryLoader(PFN_vkGetInstanceProcAddr loaderProc);\n    ~LibraryLoader();\n    PFN_vkVoidFunction sym(VkInstance instance, const char* name) const;\n    PFN_vkVoidFunction sym(const char* name) const;\n    PFN_vkGetInstanceProcAddr getLoaderProc() const { return m_getInstanceProcAddr; }\n  protected:\n    HMODULE                   m_library             = nullptr;\n    PFN_vkGetInstanceProcAddr m_getInstanceProcAddr = nullptr;\n  };\n  \n  \n  /**\n   * \\brief Vulkan instance loader\n   * \n   * Loads Vulkan functions that can be\n   * called for a specific instance.\n   */\n  struct InstanceLoader : public RcObject {\n    InstanceLoader(const Rc<LibraryLoader>& library, bool owned, VkInstance instance);\n    PFN_vkVoidFunction sym(const char* name) const;\n    PFN_vkGetInstanceProcAddr getLoaderProc() const { return m_library->getLoaderProc(); }\n    VkInstance instance() const { return m_instance; }\n  protected:\n    Rc<LibraryLoader> m_library;\n    const VkInstance  m_instance;\n    const bool        m_owned;\n  };\n  \n  \n  /**\n   * \\brief Vulkan device loader\n   * \n   * Loads Vulkan functions for a\n   * specific device.\n   */\n  struct DeviceLoader : public RcObject {\n    DeviceLoader(const Rc<InstanceLoader>& library, bool owned, VkDevice device);\n    PFN_vkVoidFunction sym(const char* name) const;\n    VkDevice device() const { return m_device; }\n  protected:\n    Rc<InstanceLoader>            m_library;\n    const PFN_vkGetDeviceProcAddr m_getDeviceProcAddr;\n    const VkDevice                m_device;\n    const bool                    m_owned;\n  };\n  \n  \n  /**\n   * \\brief Vulkan library functions\n   * \n   * Vulkan functions that are called before\n   * creating an actual Vulkan instance.\n   */\n  struct LibraryFn : LibraryLoader {\n    LibraryFn();\n    LibraryFn(PFN_vkGetInstanceProcAddr loaderProc);\n    ~LibraryFn();\n    \n    VULKAN_FN(vkCreateInstance);\n    VULKAN_FN(vkEnumerateInstanceLayerProperties);\n    VULKAN_FN(vkEnumerateInstanceExtensionProperties);\n  };\n  \n  \n  /**\n   * \\brief Vulkan instance functions\n   * \n   * Vulkan functions for a given instance that\n   * are independent of any Vulkan devices.\n   */\n  struct InstanceFn : InstanceLoader {\n    InstanceFn(const Rc<LibraryLoader>& library, bool owned, VkInstance instance);\n    ~InstanceFn();\n    \n    VULKAN_FN(vkCreateDevice);\n    VULKAN_FN(vkDestroyInstance);\n    VULKAN_FN(vkEnumerateDeviceExtensionProperties);\n    VULKAN_FN(vkEnumeratePhysicalDevices);\n    VULKAN_FN(vkGetPhysicalDeviceExternalSemaphoreProperties);\n    VULKAN_FN(vkGetPhysicalDeviceFeatures);\n    VULKAN_FN(vkGetPhysicalDeviceFeatures2);\n    VULKAN_FN(vkGetPhysicalDeviceFormatProperties);\n    VULKAN_FN(vkGetPhysicalDeviceFormatProperties2);\n    VULKAN_FN(vkGetPhysicalDeviceProperties2);\n    VULKAN_FN(vkGetPhysicalDeviceImageFormatProperties);\n    VULKAN_FN(vkGetPhysicalDeviceImageFormatProperties2);\n    VULKAN_FN(vkGetPhysicalDeviceMemoryProperties);\n    VULKAN_FN(vkGetPhysicalDeviceMemoryProperties2);\n    VULKAN_FN(vkGetPhysicalDeviceProperties);\n    VULKAN_FN(vkGetPhysicalDeviceQueueFamilyProperties);\n    VULKAN_FN(vkGetPhysicalDeviceQueueFamilyProperties2);\n    VULKAN_FN(vkGetPhysicalDeviceSparseImageFormatProperties);\n    VULKAN_FN(vkGetPhysicalDeviceSparseImageFormatProperties2);\n\n    #ifdef VK_KHR_get_surface_capabilities2\n    VULKAN_FN(vkGetPhysicalDeviceSurfaceCapabilities2KHR);\n    VULKAN_FN(vkGetPhysicalDeviceSurfaceFormats2KHR);\n    #endif\n    \n    #ifdef VK_KHR_surface\n    #ifdef VK_USE_PLATFORM_XCB_KHR\n    VULKAN_FN(vkCreateXcbSurfaceKHR);\n    VULKAN_FN(vkGetPhysicalDeviceXcbPresentationSupportKHR);\n    #endif\n    \n    #ifdef VK_USE_PLATFORM_XLIB_KHR\n    VULKAN_FN(vkCreateXlibSurfaceKHR);\n    VULKAN_FN(vkGetPhysicalDeviceXlibPresentationSupportKHR);\n    #endif\n    \n    #ifdef VK_USE_PLATFORM_WAYLAND_KHR\n    VULKAN_FN(vkCreateWaylandSurfaceKHR);\n    VULKAN_FN(vkGetPhysicalDeviceWaylandPresentationSupportKHR);\n    #endif\n    \n    #ifdef VK_USE_PLATFORM_WIN32_KHR\n    VULKAN_FN(vkCreateWin32SurfaceKHR);\n    VULKAN_FN(vkGetPhysicalDeviceWin32PresentationSupportKHR);\n    #endif\n    \n    VULKAN_FN(vkDestroySurfaceKHR);\n    \n    VULKAN_FN(vkGetPhysicalDeviceSurfaceSupportKHR);\n    VULKAN_FN(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);\n    VULKAN_FN(vkGetPhysicalDeviceSurfaceFormatsKHR);\n    VULKAN_FN(vkGetPhysicalDeviceSurfacePresentModesKHR);\n    #endif\n    \n    #ifdef VK_EXT_debug_utils\n    VULKAN_FN(vkCmdBeginDebugUtilsLabelEXT);\n    VULKAN_FN(vkCmdEndDebugUtilsLabelEXT);\n    VULKAN_FN(vkCmdInsertDebugUtilsLabelEXT);\n    VULKAN_FN(vkCreateDebugUtilsMessengerEXT);\n    VULKAN_FN(vkDestroyDebugUtilsMessengerEXT);\n    VULKAN_FN(vkSubmitDebugUtilsMessageEXT);\n    #endif\n\n    #ifdef VK_EXT_descriptor_heap\n    VULKAN_FN(vkGetPhysicalDeviceDescriptorSizeEXT);\n    #endif\n\n    #ifdef VK_EXT_full_screen_exclusive\n    VULKAN_FN(vkGetPhysicalDeviceSurfacePresentModes2EXT);\n    #endif\n\n    #ifdef VK_EXT_swapchain_maintenance1\n    VULKAN_FN(vkReleaseSwapchainImagesEXT);\n    #endif\n\n    #ifdef VK_EXT_sample_locations\n    VULKAN_FN(vkGetPhysicalDeviceMultisamplePropertiesEXT);\n    #endif\n  };\n  \n  \n  /**\n   * \\brief Vulkan device functions\n   * \n   * Vulkan functions for a specific Vulkan device.\n   * This ensures that no slow dispatch code is executed.\n   */\n  struct DeviceFn : DeviceLoader {\n    DeviceFn(const Rc<InstanceLoader>& library, bool owned, VkDevice device);\n    ~DeviceFn();\n    \n    VULKAN_FN(vkDestroyDevice);\n    VULKAN_FN(vkGetDeviceQueue);\n    VULKAN_FN(vkQueueSubmit);\n    VULKAN_FN(vkQueueSubmit2);\n    VULKAN_FN(vkQueueWaitIdle);\n    VULKAN_FN(vkDeviceWaitIdle);\n    VULKAN_FN(vkAllocateMemory);\n    VULKAN_FN(vkFreeMemory);\n    VULKAN_FN(vkMapMemory);\n    VULKAN_FN(vkUnmapMemory);\n    VULKAN_FN(vkFlushMappedMemoryRanges);\n    VULKAN_FN(vkInvalidateMappedMemoryRanges);\n    VULKAN_FN(vkGetDeviceMemoryCommitment);\n    VULKAN_FN(vkBindBufferMemory);\n    VULKAN_FN(vkBindImageMemory);\n    VULKAN_FN(vkGetBufferMemoryRequirements);\n    VULKAN_FN(vkGetBufferMemoryRequirements2);\n    VULKAN_FN(vkGetDeviceBufferMemoryRequirements);\n    VULKAN_FN(vkGetImageMemoryRequirements);\n    VULKAN_FN(vkGetImageMemoryRequirements2);\n    VULKAN_FN(vkGetDeviceImageMemoryRequirements);\n    VULKAN_FN(vkGetImageSparseMemoryRequirements);\n    VULKAN_FN(vkGetImageSparseMemoryRequirements2);\n    VULKAN_FN(vkQueueBindSparse);\n    VULKAN_FN(vkCreateFence);\n    VULKAN_FN(vkDestroyFence);\n    VULKAN_FN(vkResetFences);\n    VULKAN_FN(vkGetFenceStatus);\n    VULKAN_FN(vkWaitForFences);\n    VULKAN_FN(vkCreateSemaphore);\n    VULKAN_FN(vkDestroySemaphore);\n    VULKAN_FN(vkCreateEvent);\n    VULKAN_FN(vkDestroyEvent);\n    VULKAN_FN(vkGetEventStatus);\n    VULKAN_FN(vkSetEvent);\n    VULKAN_FN(vkResetEvent);\n    VULKAN_FN(vkCreateQueryPool);\n    VULKAN_FN(vkDestroyQueryPool);\n    VULKAN_FN(vkGetQueryPoolResults);\n    VULKAN_FN(vkCreateBuffer);\n    VULKAN_FN(vkDestroyBuffer);\n    VULKAN_FN(vkCreateBufferView);\n    VULKAN_FN(vkDestroyBufferView);\n    VULKAN_FN(vkCreateImage);\n    VULKAN_FN(vkDestroyImage);\n    VULKAN_FN(vkGetImageSubresourceLayout);\n    VULKAN_FN(vkCreateImageView);\n    VULKAN_FN(vkDestroyImageView);\n    VULKAN_FN(vkCreateShaderModule);\n    VULKAN_FN(vkDestroyShaderModule);\n    VULKAN_FN(vkCreatePipelineCache);\n    VULKAN_FN(vkDestroyPipelineCache);\n    VULKAN_FN(vkGetPipelineCacheData);\n    VULKAN_FN(vkMergePipelineCaches);\n    VULKAN_FN(vkCreateGraphicsPipelines);\n    VULKAN_FN(vkCreateComputePipelines);\n    VULKAN_FN(vkDestroyPipeline);\n    VULKAN_FN(vkCreatePipelineLayout);\n    VULKAN_FN(vkDestroyPipelineLayout);\n    VULKAN_FN(vkCreateSampler);\n    VULKAN_FN(vkDestroySampler);\n    VULKAN_FN(vkCreateDescriptorSetLayout);\n    VULKAN_FN(vkDestroyDescriptorSetLayout);\n    VULKAN_FN(vkCreateDescriptorPool);\n    VULKAN_FN(vkDestroyDescriptorPool);\n    VULKAN_FN(vkResetDescriptorPool);\n    VULKAN_FN(vkAllocateDescriptorSets);\n    VULKAN_FN(vkFreeDescriptorSets);\n    VULKAN_FN(vkUpdateDescriptorSets);\n    VULKAN_FN(vkCreateFramebuffer);\n    VULKAN_FN(vkDestroyFramebuffer);\n    VULKAN_FN(vkCreateRenderPass);\n    VULKAN_FN(vkCreateRenderPass2);\n    VULKAN_FN(vkDestroyRenderPass);\n    VULKAN_FN(vkGetRenderAreaGranularity);\n    VULKAN_FN(vkCreateCommandPool);\n    VULKAN_FN(vkDestroyCommandPool);\n    VULKAN_FN(vkResetCommandPool);\n    VULKAN_FN(vkAllocateCommandBuffers);\n    VULKAN_FN(vkFreeCommandBuffers);\n    VULKAN_FN(vkBeginCommandBuffer);\n    VULKAN_FN(vkEndCommandBuffer);\n    VULKAN_FN(vkResetCommandBuffer);\n    VULKAN_FN(vkCreateDescriptorUpdateTemplate);\n    VULKAN_FN(vkDestroyDescriptorUpdateTemplate);\n    VULKAN_FN(vkUpdateDescriptorSetWithTemplate);\n    VULKAN_FN(vkResetQueryPool);\n    VULKAN_FN(vkGetBufferDeviceAddress);\n    VULKAN_FN(vkGetSemaphoreCounterValue);\n    VULKAN_FN(vkSignalSemaphore);\n    VULKAN_FN(vkWaitSemaphores);\n    VULKAN_FN(vkCmdBindPipeline);\n    VULKAN_FN(vkCmdSetViewport);\n    VULKAN_FN(vkCmdSetScissor);\n    VULKAN_FN(vkCmdSetLineWidth);\n    VULKAN_FN(vkCmdSetDepthBias);\n    VULKAN_FN(vkCmdSetDepthBias2EXT);\n    VULKAN_FN(vkCmdSetBlendConstants);\n    VULKAN_FN(vkCmdSetDepthBounds);\n    VULKAN_FN(vkCmdSetStencilCompareMask);\n    VULKAN_FN(vkCmdSetStencilWriteMask);\n    VULKAN_FN(vkCmdSetStencilReference);\n    VULKAN_FN(vkCmdBindVertexBuffers2);\n    VULKAN_FN(vkCmdSetCullMode);\n    VULKAN_FN(vkCmdSetDepthBoundsTestEnable);\n    VULKAN_FN(vkCmdSetDepthCompareOp);\n    VULKAN_FN(vkCmdSetDepthTestEnable);\n    VULKAN_FN(vkCmdSetDepthWriteEnable);\n    VULKAN_FN(vkCmdSetFrontFace);\n    VULKAN_FN(vkCmdSetPrimitiveTopology);\n    VULKAN_FN(vkCmdSetScissorWithCount);\n    VULKAN_FN(vkCmdSetStencilOp);\n    VULKAN_FN(vkCmdSetStencilTestEnable);\n    VULKAN_FN(vkCmdSetViewportWithCount);\n    VULKAN_FN(vkCmdSetRasterizerDiscardEnable);\n    VULKAN_FN(vkCmdSetDepthBiasEnable);\n    VULKAN_FN(vkCmdSetPrimitiveRestartEnable);\n    VULKAN_FN(vkCmdBindDescriptorSets);\n    VULKAN_FN(vkCmdBindIndexBuffer);\n    VULKAN_FN(vkCmdBindVertexBuffers);\n    VULKAN_FN(vkCmdDraw);\n    VULKAN_FN(vkCmdDrawIndexed);\n    VULKAN_FN(vkCmdDrawIndirect);\n    VULKAN_FN(vkCmdDrawIndirectCount);\n    VULKAN_FN(vkCmdDrawIndexedIndirect);\n    VULKAN_FN(vkCmdDrawIndexedIndirectCount);\n    VULKAN_FN(vkCmdDispatch);\n    VULKAN_FN(vkCmdDispatchIndirect);\n    VULKAN_FN(vkCmdCopyBuffer);\n    VULKAN_FN(vkCmdCopyBuffer2);\n    VULKAN_FN(vkCmdCopyImage);\n    VULKAN_FN(vkCmdCopyImage2);\n    VULKAN_FN(vkCmdBlitImage);\n    VULKAN_FN(vkCmdBlitImage2);\n    VULKAN_FN(vkCmdCopyBufferToImage);\n    VULKAN_FN(vkCmdCopyBufferToImage2);\n    VULKAN_FN(vkCmdCopyImageToBuffer);\n    VULKAN_FN(vkCmdCopyImageToBuffer2);\n    VULKAN_FN(vkCmdUpdateBuffer);\n    VULKAN_FN(vkCmdFillBuffer);\n    VULKAN_FN(vkCmdClearColorImage);\n    VULKAN_FN(vkCmdClearDepthStencilImage);\n    VULKAN_FN(vkCmdClearAttachments);\n    VULKAN_FN(vkCmdResolveImage);\n    VULKAN_FN(vkCmdResolveImage2);\n    VULKAN_FN(vkCmdSetEvent);\n    VULKAN_FN(vkCmdSetEvent2);\n    VULKAN_FN(vkCmdResetEvent);\n    VULKAN_FN(vkCmdResetEvent2);\n    VULKAN_FN(vkCmdWaitEvents);\n    VULKAN_FN(vkCmdWaitEvents2);\n    VULKAN_FN(vkCmdPipelineBarrier);\n    VULKAN_FN(vkCmdPipelineBarrier2);\n    VULKAN_FN(vkCmdBeginQuery);\n    VULKAN_FN(vkCmdEndQuery);\n    VULKAN_FN(vkCmdResetQueryPool);\n    VULKAN_FN(vkCmdWriteTimestamp);\n    VULKAN_FN(vkCmdWriteTimestamp2);\n    VULKAN_FN(vkCmdCopyQueryPoolResults);\n    VULKAN_FN(vkCmdPushConstants);\n    VULKAN_FN(vkCmdBeginRenderPass);\n    VULKAN_FN(vkCmdBeginRenderPass2);\n    VULKAN_FN(vkCmdNextSubpass);\n    VULKAN_FN(vkCmdNextSubpass2);\n    VULKAN_FN(vkCmdEndRenderPass);\n    VULKAN_FN(vkCmdEndRenderPass2);\n    VULKAN_FN(vkCmdBeginRendering);\n    VULKAN_FN(vkCmdEndRendering);\n    VULKAN_FN(vkCmdExecuteCommands);\n\n    #ifdef VK_KHR_swapchain\n    VULKAN_FN(vkCreateSwapchainKHR);\n    VULKAN_FN(vkDestroySwapchainKHR);\n    VULKAN_FN(vkGetSwapchainImagesKHR);\n    VULKAN_FN(vkAcquireNextImageKHR);\n    VULKAN_FN(vkQueuePresentKHR);\n    #endif\n\n    #ifdef VK_EXT_conditional_rendering\n    VULKAN_FN(vkCmdBeginConditionalRenderingEXT);\n    VULKAN_FN(vkCmdEndConditionalRenderingEXT);\n    #endif\n\n    #ifdef VK_EXT_descriptor_buffer\n    VULKAN_FN(vkGetDescriptorSetLayoutSizeEXT);\n    VULKAN_FN(vkGetDescriptorSetLayoutBindingOffsetEXT);\n    VULKAN_FN(vkGetDescriptorEXT);\n    VULKAN_FN(vkCmdBindDescriptorBuffersEXT);\n    VULKAN_FN(vkCmdSetDescriptorBufferOffsetsEXT);\n    VULKAN_FN(vkCmdBindDescriptorBufferEmbeddedSamplersEXT);\n    VULKAN_FN(vkGetBufferOpaqueCaptureDescriptorDataEXT);\n    VULKAN_FN(vkGetImageOpaqueCaptureDescriptorDataEXT);\n    VULKAN_FN(vkGetImageViewOpaqueCaptureDescriptorDataEXT);\n    VULKAN_FN(vkGetSamplerOpaqueCaptureDescriptorDataEXT);\n    VULKAN_FN(vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT);\n    #endif\n\n    #ifdef VK_EXT_descriptor_heap\n    VULKAN_FN(vkWriteSamplerDescriptorsEXT);\n    VULKAN_FN(vkWriteResourceDescriptorsEXT);\n    VULKAN_FN(vkCmdBindSamplerHeapEXT);\n    VULKAN_FN(vkCmdBindResourceHeapEXT);\n    VULKAN_FN(vkCmdPushDataEXT);\n    VULKAN_FN(vkGetImageOpaqueCaptureDataEXT);\n    VULKAN_FN(vkRegisterCustomBorderColorEXT);\n    VULKAN_FN(vkUnregisterCustomBorderColorEXT);\n    #endif\n\n    #ifdef VK_EXT_debug_utils\n    VULKAN_FN(vkQueueBeginDebugUtilsLabelEXT);\n    VULKAN_FN(vkQueueEndDebugUtilsLabelEXT);\n    VULKAN_FN(vkQueueInsertDebugUtilsLabelEXT);\n    VULKAN_FN(vkSetDebugUtilsObjectNameEXT);\n    VULKAN_FN(vkSetDebugUtilsObjectTagEXT);\n    #endif\n\n    #ifdef VK_EXT_extended_dynamic_state3\n    VULKAN_FN(vkCmdSetTessellationDomainOriginEXT);\n    VULKAN_FN(vkCmdSetDepthClampEnableEXT);\n    VULKAN_FN(vkCmdSetPolygonModeEXT);\n    VULKAN_FN(vkCmdSetRasterizationSamplesEXT);\n    VULKAN_FN(vkCmdSetSampleMaskEXT);\n    VULKAN_FN(vkCmdSetAlphaToCoverageEnableEXT);\n    VULKAN_FN(vkCmdSetAlphaToOneEnableEXT);\n    VULKAN_FN(vkCmdSetLogicOpEnableEXT);\n    VULKAN_FN(vkCmdSetColorBlendEnableEXT);\n    VULKAN_FN(vkCmdSetColorBlendEquationEXT);\n    VULKAN_FN(vkCmdSetColorWriteMaskEXT);\n    VULKAN_FN(vkCmdSetRasterizationStreamEXT);\n    VULKAN_FN(vkCmdSetConservativeRasterizationModeEXT);\n    VULKAN_FN(vkCmdSetExtraPrimitiveOverestimationSizeEXT);\n    VULKAN_FN(vkCmdSetDepthClipEnableEXT);\n    VULKAN_FN(vkCmdSetSampleLocationsEnableEXT);\n    VULKAN_FN(vkCmdSetLineRasterizationModeEXT);\n    #endif\n\n    #ifdef VK_EXT_full_screen_exclusive\n    VULKAN_FN(vkAcquireFullScreenExclusiveModeEXT);\n    VULKAN_FN(vkReleaseFullScreenExclusiveModeEXT);\n    VULKAN_FN(vkGetDeviceGroupSurfacePresentModes2EXT);\n    #endif\n\n    #ifdef VK_EXT_hdr_metadata\n    VULKAN_FN(vkSetHdrMetadataEXT);\n    #endif\n\n    #ifdef VK_EXT_pageable_device_local_memory\n    VULKAN_FN(vkSetDeviceMemoryPriorityEXT);\n    #endif\n\n    #ifdef VK_EXT_multi_draw\n    VULKAN_FN(vkCmdDrawMultiEXT);\n    VULKAN_FN(vkCmdDrawMultiIndexedEXT);\n    #endif\n\n    #ifdef VK_EXT_sample_locations\n    VULKAN_FN(vkCmdSetSampleLocationsEXT);\n    #endif\n\n    #ifdef VK_EXT_shader_module_identifier\n    VULKAN_FN(vkGetShaderModuleCreateInfoIdentifierEXT);\n    VULKAN_FN(vkGetShaderModuleIdentifierEXT);\n    #endif\n\n    #ifdef VK_EXT_transform_feedback\n    VULKAN_FN(vkCmdBindTransformFeedbackBuffersEXT);\n    VULKAN_FN(vkCmdBeginTransformFeedbackEXT);\n    VULKAN_FN(vkCmdEndTransformFeedbackEXT);\n    VULKAN_FN(vkCmdDrawIndirectByteCountEXT);\n    VULKAN_FN(vkCmdBeginQueryIndexedEXT);\n    VULKAN_FN(vkCmdEndQueryIndexedEXT);\n    #endif\n\n    #ifdef VK_NVX_image_view_handle\n    VULKAN_FN(vkGetImageViewHandleNVX);\n    VULKAN_FN(vkGetImageViewAddressNVX);\n    #endif\n\n    #ifdef VK_NVX_binary_import\n    VULKAN_FN(vkCreateCuModuleNVX);\n    VULKAN_FN(vkCreateCuFunctionNVX);\n    VULKAN_FN(vkDestroyCuModuleNVX);\n    VULKAN_FN(vkDestroyCuFunctionNVX);\n    VULKAN_FN(vkCmdCuLaunchKernelNVX);\n    #endif\n\n    #ifdef VK_KHR_external_memory_win32\n    VULKAN_FN(vkGetMemoryWin32HandleKHR);\n    VULKAN_FN(vkGetMemoryWin32HandlePropertiesKHR);\n    #endif\n\n    #ifdef VK_KHR_external_semaphore_win32\n    VULKAN_FN(vkGetSemaphoreWin32HandleKHR);\n    VULKAN_FN(vkImportSemaphoreWin32HandleKHR);\n    #endif\n\n    #ifdef VK_KHR_maintenance5\n    VULKAN_FN(vkCmdBindIndexBuffer2KHR);\n    VULKAN_FN(vkGetRenderingAreaGranularityKHR);\n    VULKAN_FN(vkGetDeviceImageSubresourceLayoutKHR);\n    VULKAN_FN(vkGetImageSubresourceLayout2KHR);\n    #endif\n\n    #ifdef VK_KHR_maintenance6\n    VULKAN_FN(vkCmdBindDescriptorSets2KHR);\n    VULKAN_FN(vkCmdPushConstants2KHR);\n    VULKAN_FN(vkCmdPushDescriptorSet2KHR);\n    VULKAN_FN(vkCmdPushDescriptorSetWithTemplate2KHR);\n    VULKAN_FN(vkCmdSetDescriptorBufferOffsets2EXT);\n    VULKAN_FN(vkCmdBindDescriptorBufferEmbeddedSamplers2EXT);\n    #endif\n\n    #ifdef VK_KHR_present_wait\n    VULKAN_FN(vkWaitForPresentKHR);\n    #endif\n\n    #ifdef VK_KHR_present_wait2\n    VULKAN_FN(vkWaitForPresent2KHR);\n    #endif\n\n    #ifdef VK_KHR_win32_keyed_mutex\n    // Wine additions to actually use this extension.\n    VULKAN_FN(wine_vkAcquireKeyedMutex);\n    VULKAN_FN(wine_vkReleaseKeyedMutex);\n    #endif\n\n    #ifdef VK_NV_low_latency2\n    VULKAN_FN(vkSetLatencySleepModeNV);\n    VULKAN_FN(vkLatencySleepNV);\n    VULKAN_FN(vkSetLatencyMarkerNV);\n    VULKAN_FN(vkGetLatencyTimingsNV);\n    VULKAN_FN(vkQueueNotifyOutOfBandNV);\n    #endif\n  };\n  \n}\n"
  },
  {
    "path": "src/vulkan/vulkan_names.cpp",
    "content": "#include \"vulkan_names.h\"\n    \nstd::ostream& operator << (std::ostream& os, VkPipelineCacheHeaderVersion e) {\n  switch (e) {\n    ENUM_NAME(VK_PIPELINE_CACHE_HEADER_VERSION_ONE);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkResult e) {\n  switch (e) {\n    ENUM_NAME(VK_SUCCESS);\n    ENUM_NAME(VK_NOT_READY);\n    ENUM_NAME(VK_TIMEOUT);\n    ENUM_NAME(VK_EVENT_SET);\n    ENUM_NAME(VK_EVENT_RESET);\n    ENUM_NAME(VK_INCOMPLETE);\n    ENUM_NAME(VK_ERROR_OUT_OF_HOST_MEMORY);\n    ENUM_NAME(VK_ERROR_OUT_OF_DEVICE_MEMORY);\n    ENUM_NAME(VK_ERROR_INITIALIZATION_FAILED);\n    ENUM_NAME(VK_ERROR_DEVICE_LOST);\n    ENUM_NAME(VK_ERROR_MEMORY_MAP_FAILED);\n    ENUM_NAME(VK_ERROR_LAYER_NOT_PRESENT);\n    ENUM_NAME(VK_ERROR_EXTENSION_NOT_PRESENT);\n    ENUM_NAME(VK_ERROR_FEATURE_NOT_PRESENT);\n    ENUM_NAME(VK_ERROR_INCOMPATIBLE_DRIVER);\n    ENUM_NAME(VK_ERROR_TOO_MANY_OBJECTS);\n    ENUM_NAME(VK_ERROR_FORMAT_NOT_SUPPORTED);\n    ENUM_NAME(VK_ERROR_FRAGMENTED_POOL);\n    ENUM_NAME(VK_ERROR_SURFACE_LOST_KHR);\n    ENUM_NAME(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);\n    ENUM_NAME(VK_SUBOPTIMAL_KHR);\n    ENUM_NAME(VK_ERROR_OUT_OF_DATE_KHR);\n    ENUM_NAME(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);\n    ENUM_NAME(VK_ERROR_VALIDATION_FAILED_EXT);\n    ENUM_NAME(VK_ERROR_INVALID_SHADER_NV);\n    ENUM_NAME(VK_ERROR_OUT_OF_POOL_MEMORY_KHR);\n    ENUM_NAME(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkFormat e) {\n  switch (e) {\n    ENUM_NAME(VK_FORMAT_UNDEFINED);\n    ENUM_NAME(VK_FORMAT_R4G4_UNORM_PACK8);\n    ENUM_NAME(VK_FORMAT_R4G4B4A4_UNORM_PACK16);\n    ENUM_NAME(VK_FORMAT_B4G4R4A4_UNORM_PACK16);\n    ENUM_NAME(VK_FORMAT_R5G6B5_UNORM_PACK16);\n    ENUM_NAME(VK_FORMAT_B5G6R5_UNORM_PACK16);\n    ENUM_NAME(VK_FORMAT_R5G5B5A1_UNORM_PACK16);\n    ENUM_NAME(VK_FORMAT_B5G5R5A1_UNORM_PACK16);\n    ENUM_NAME(VK_FORMAT_A1R5G5B5_UNORM_PACK16);\n    ENUM_NAME(VK_FORMAT_R8_UNORM);\n    ENUM_NAME(VK_FORMAT_R8_SNORM);\n    ENUM_NAME(VK_FORMAT_R8_USCALED);\n    ENUM_NAME(VK_FORMAT_R8_SSCALED);\n    ENUM_NAME(VK_FORMAT_R8_UINT);\n    ENUM_NAME(VK_FORMAT_R8_SINT);\n    ENUM_NAME(VK_FORMAT_R8_SRGB);\n    ENUM_NAME(VK_FORMAT_R8G8_UNORM);\n    ENUM_NAME(VK_FORMAT_R8G8_SNORM);\n    ENUM_NAME(VK_FORMAT_R8G8_USCALED);\n    ENUM_NAME(VK_FORMAT_R8G8_SSCALED);\n    ENUM_NAME(VK_FORMAT_R8G8_UINT);\n    ENUM_NAME(VK_FORMAT_R8G8_SINT);\n    ENUM_NAME(VK_FORMAT_R8G8_SRGB);\n    ENUM_NAME(VK_FORMAT_R8G8B8_UNORM);\n    ENUM_NAME(VK_FORMAT_R8G8B8_SNORM);\n    ENUM_NAME(VK_FORMAT_R8G8B8_USCALED);\n    ENUM_NAME(VK_FORMAT_R8G8B8_SSCALED);\n    ENUM_NAME(VK_FORMAT_R8G8B8_UINT);\n    ENUM_NAME(VK_FORMAT_R8G8B8_SINT);\n    ENUM_NAME(VK_FORMAT_R8G8B8_SRGB);\n    ENUM_NAME(VK_FORMAT_B8G8R8_UNORM);\n    ENUM_NAME(VK_FORMAT_B8G8R8_SNORM);\n    ENUM_NAME(VK_FORMAT_B8G8R8_USCALED);\n    ENUM_NAME(VK_FORMAT_B8G8R8_SSCALED);\n    ENUM_NAME(VK_FORMAT_B8G8R8_UINT);\n    ENUM_NAME(VK_FORMAT_B8G8R8_SINT);\n    ENUM_NAME(VK_FORMAT_B8G8R8_SRGB);\n    ENUM_NAME(VK_FORMAT_R8G8B8A8_UNORM);\n    ENUM_NAME(VK_FORMAT_R8G8B8A8_SNORM);\n    ENUM_NAME(VK_FORMAT_R8G8B8A8_USCALED);\n    ENUM_NAME(VK_FORMAT_R8G8B8A8_SSCALED);\n    ENUM_NAME(VK_FORMAT_R8G8B8A8_UINT);\n    ENUM_NAME(VK_FORMAT_R8G8B8A8_SINT);\n    ENUM_NAME(VK_FORMAT_R8G8B8A8_SRGB);\n    ENUM_NAME(VK_FORMAT_B8G8R8A8_UNORM);\n    ENUM_NAME(VK_FORMAT_B8G8R8A8_SNORM);\n    ENUM_NAME(VK_FORMAT_B8G8R8A8_USCALED);\n    ENUM_NAME(VK_FORMAT_B8G8R8A8_SSCALED);\n    ENUM_NAME(VK_FORMAT_B8G8R8A8_UINT);\n    ENUM_NAME(VK_FORMAT_B8G8R8A8_SINT);\n    ENUM_NAME(VK_FORMAT_B8G8R8A8_SRGB);\n    ENUM_NAME(VK_FORMAT_A8B8G8R8_UNORM_PACK32);\n    ENUM_NAME(VK_FORMAT_A8B8G8R8_SNORM_PACK32);\n    ENUM_NAME(VK_FORMAT_A8B8G8R8_USCALED_PACK32);\n    ENUM_NAME(VK_FORMAT_A8B8G8R8_SSCALED_PACK32);\n    ENUM_NAME(VK_FORMAT_A8B8G8R8_UINT_PACK32);\n    ENUM_NAME(VK_FORMAT_A8B8G8R8_SINT_PACK32);\n    ENUM_NAME(VK_FORMAT_A8B8G8R8_SRGB_PACK32);\n    ENUM_NAME(VK_FORMAT_A2R10G10B10_UNORM_PACK32);\n    ENUM_NAME(VK_FORMAT_A2R10G10B10_SNORM_PACK32);\n    ENUM_NAME(VK_FORMAT_A2R10G10B10_USCALED_PACK32);\n    ENUM_NAME(VK_FORMAT_A2R10G10B10_SSCALED_PACK32);\n    ENUM_NAME(VK_FORMAT_A2R10G10B10_UINT_PACK32);\n    ENUM_NAME(VK_FORMAT_A2R10G10B10_SINT_PACK32);\n    ENUM_NAME(VK_FORMAT_A2B10G10R10_UNORM_PACK32);\n    ENUM_NAME(VK_FORMAT_A2B10G10R10_SNORM_PACK32);\n    ENUM_NAME(VK_FORMAT_A2B10G10R10_USCALED_PACK32);\n    ENUM_NAME(VK_FORMAT_A2B10G10R10_SSCALED_PACK32);\n    ENUM_NAME(VK_FORMAT_A2B10G10R10_UINT_PACK32);\n    ENUM_NAME(VK_FORMAT_A2B10G10R10_SINT_PACK32);\n    ENUM_NAME(VK_FORMAT_R16_UNORM);\n    ENUM_NAME(VK_FORMAT_R16_SNORM);\n    ENUM_NAME(VK_FORMAT_R16_USCALED);\n    ENUM_NAME(VK_FORMAT_R16_SSCALED);\n    ENUM_NAME(VK_FORMAT_R16_UINT);\n    ENUM_NAME(VK_FORMAT_R16_SINT);\n    ENUM_NAME(VK_FORMAT_R16_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R16G16_UNORM);\n    ENUM_NAME(VK_FORMAT_R16G16_SNORM);\n    ENUM_NAME(VK_FORMAT_R16G16_USCALED);\n    ENUM_NAME(VK_FORMAT_R16G16_SSCALED);\n    ENUM_NAME(VK_FORMAT_R16G16_UINT);\n    ENUM_NAME(VK_FORMAT_R16G16_SINT);\n    ENUM_NAME(VK_FORMAT_R16G16_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R16G16B16_UNORM);\n    ENUM_NAME(VK_FORMAT_R16G16B16_SNORM);\n    ENUM_NAME(VK_FORMAT_R16G16B16_USCALED);\n    ENUM_NAME(VK_FORMAT_R16G16B16_SSCALED);\n    ENUM_NAME(VK_FORMAT_R16G16B16_UINT);\n    ENUM_NAME(VK_FORMAT_R16G16B16_SINT);\n    ENUM_NAME(VK_FORMAT_R16G16B16_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R16G16B16A16_UNORM);\n    ENUM_NAME(VK_FORMAT_R16G16B16A16_SNORM);\n    ENUM_NAME(VK_FORMAT_R16G16B16A16_USCALED);\n    ENUM_NAME(VK_FORMAT_R16G16B16A16_SSCALED);\n    ENUM_NAME(VK_FORMAT_R16G16B16A16_UINT);\n    ENUM_NAME(VK_FORMAT_R16G16B16A16_SINT);\n    ENUM_NAME(VK_FORMAT_R16G16B16A16_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R32_UINT);\n    ENUM_NAME(VK_FORMAT_R32_SINT);\n    ENUM_NAME(VK_FORMAT_R32_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R32G32_UINT);\n    ENUM_NAME(VK_FORMAT_R32G32_SINT);\n    ENUM_NAME(VK_FORMAT_R32G32_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R32G32B32_UINT);\n    ENUM_NAME(VK_FORMAT_R32G32B32_SINT);\n    ENUM_NAME(VK_FORMAT_R32G32B32_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R32G32B32A32_UINT);\n    ENUM_NAME(VK_FORMAT_R32G32B32A32_SINT);\n    ENUM_NAME(VK_FORMAT_R32G32B32A32_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R64_UINT);\n    ENUM_NAME(VK_FORMAT_R64_SINT);\n    ENUM_NAME(VK_FORMAT_R64_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R64G64_UINT);\n    ENUM_NAME(VK_FORMAT_R64G64_SINT);\n    ENUM_NAME(VK_FORMAT_R64G64_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R64G64B64_UINT);\n    ENUM_NAME(VK_FORMAT_R64G64B64_SINT);\n    ENUM_NAME(VK_FORMAT_R64G64B64_SFLOAT);\n    ENUM_NAME(VK_FORMAT_R64G64B64A64_UINT);\n    ENUM_NAME(VK_FORMAT_R64G64B64A64_SINT);\n    ENUM_NAME(VK_FORMAT_R64G64B64A64_SFLOAT);\n    ENUM_NAME(VK_FORMAT_B10G11R11_UFLOAT_PACK32);\n    ENUM_NAME(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32);\n    ENUM_NAME(VK_FORMAT_D16_UNORM);\n    ENUM_NAME(VK_FORMAT_X8_D24_UNORM_PACK32);\n    ENUM_NAME(VK_FORMAT_D32_SFLOAT);\n    ENUM_NAME(VK_FORMAT_S8_UINT);\n    ENUM_NAME(VK_FORMAT_D16_UNORM_S8_UINT);\n    ENUM_NAME(VK_FORMAT_D24_UNORM_S8_UINT);\n    ENUM_NAME(VK_FORMAT_D32_SFLOAT_S8_UINT);\n    ENUM_NAME(VK_FORMAT_BC1_RGB_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC1_RGB_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC1_RGBA_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC1_RGBA_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC2_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC2_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC3_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC3_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC4_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC4_SNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC5_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC5_SNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC6H_UFLOAT_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC6H_SFLOAT_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC7_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_BC7_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_EAC_R11_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_EAC_R11_SNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_EAC_R11G11_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_EAC_R11G11_SNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_4x4_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_4x4_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_5x4_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_5x4_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_5x5_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_5x5_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_6x5_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_6x5_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_6x6_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_6x6_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_8x5_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_8x5_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_8x6_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_8x6_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_8x8_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_8x8_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_10x5_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_10x5_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_10x6_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_10x6_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_10x8_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_10x8_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_10x10_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_10x10_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_12x10_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_12x10_SRGB_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_12x12_UNORM_BLOCK);\n    ENUM_NAME(VK_FORMAT_ASTC_12x12_SRGB_BLOCK);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkImageType e) {\n  switch (e) {\n    ENUM_NAME(VK_IMAGE_TYPE_1D);\n    ENUM_NAME(VK_IMAGE_TYPE_2D);\n    ENUM_NAME(VK_IMAGE_TYPE_3D);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkImageTiling e) {\n  switch (e) {\n    ENUM_NAME(VK_IMAGE_TILING_OPTIMAL);\n    ENUM_NAME(VK_IMAGE_TILING_LINEAR);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkImageLayout e) {\n  switch (e) {\n    ENUM_NAME(VK_IMAGE_LAYOUT_UNDEFINED);\n    ENUM_NAME(VK_IMAGE_LAYOUT_GENERAL);\n    ENUM_NAME(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);\n    ENUM_NAME(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);\n    ENUM_NAME(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL);\n    ENUM_NAME(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);\n    ENUM_NAME(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);\n    ENUM_NAME(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n    ENUM_NAME(VK_IMAGE_LAYOUT_PREINITIALIZED);\n    ENUM_NAME(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);\n    ENUM_NAME(VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkImageViewType e) {\n  switch (e) {\n    ENUM_NAME(VK_IMAGE_VIEW_TYPE_1D);\n    ENUM_NAME(VK_IMAGE_VIEW_TYPE_2D);\n    ENUM_NAME(VK_IMAGE_VIEW_TYPE_3D);\n    ENUM_NAME(VK_IMAGE_VIEW_TYPE_CUBE);\n    ENUM_NAME(VK_IMAGE_VIEW_TYPE_1D_ARRAY);\n    ENUM_NAME(VK_IMAGE_VIEW_TYPE_2D_ARRAY);\n    ENUM_NAME(VK_IMAGE_VIEW_TYPE_CUBE_ARRAY);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkPresentModeKHR e) {\n  switch (e) {\n    ENUM_NAME(VK_PRESENT_MODE_IMMEDIATE_KHR);\n    ENUM_NAME(VK_PRESENT_MODE_MAILBOX_KHR);\n    ENUM_NAME(VK_PRESENT_MODE_FIFO_KHR);\n    ENUM_NAME(VK_PRESENT_MODE_FIFO_RELAXED_KHR);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkColorSpaceKHR e) {\n  switch (e) {\n    ENUM_NAME(VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);\n    ENUM_NAME(VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_BT709_LINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_BT709_NONLINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_BT2020_LINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_HDR10_ST2084_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_DOLBYVISION_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_HDR10_HLG_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_PASS_THROUGH_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT);\n    ENUM_NAME(VK_COLOR_SPACE_DISPLAY_NATIVE_AMD);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkFrontFace e) {\n  switch (e) {\n    ENUM_NAME(VK_FRONT_FACE_COUNTER_CLOCKWISE);\n    ENUM_NAME(VK_FRONT_FACE_CLOCKWISE);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\nstd::ostream& operator << (std::ostream& os, VkPolygonMode e) {\n  switch (e) {\n    ENUM_NAME(VK_POLYGON_MODE_FILL);\n    ENUM_NAME(VK_POLYGON_MODE_LINE);\n    ENUM_NAME(VK_POLYGON_MODE_POINT);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\nstd::ostream& operator << (std::ostream& os, VkBlendFactor e) {\n  switch (e) {\n    ENUM_NAME(VK_BLEND_FACTOR_ZERO);\n    ENUM_NAME(VK_BLEND_FACTOR_ONE);\n    ENUM_NAME(VK_BLEND_FACTOR_SRC_COLOR);\n    ENUM_NAME(VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR);\n    ENUM_NAME(VK_BLEND_FACTOR_DST_COLOR);\n    ENUM_NAME(VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR);\n    ENUM_NAME(VK_BLEND_FACTOR_SRC_ALPHA);\n    ENUM_NAME(VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);\n    ENUM_NAME(VK_BLEND_FACTOR_DST_ALPHA);\n    ENUM_NAME(VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA);\n    ENUM_NAME(VK_BLEND_FACTOR_CONSTANT_COLOR);\n    ENUM_NAME(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR);\n    ENUM_NAME(VK_BLEND_FACTOR_CONSTANT_ALPHA);\n    ENUM_NAME(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA);\n    ENUM_NAME(VK_BLEND_FACTOR_SRC_ALPHA_SATURATE);\n    ENUM_NAME(VK_BLEND_FACTOR_SRC1_COLOR);\n    ENUM_NAME(VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR);\n    ENUM_NAME(VK_BLEND_FACTOR_SRC1_ALPHA);\n    ENUM_NAME(VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\nstd::ostream& operator << (std::ostream& os, VkBlendOp e) {\n  switch (e) {\n    ENUM_NAME(VK_BLEND_OP_ADD);\n    ENUM_NAME(VK_BLEND_OP_SUBTRACT);\n    ENUM_NAME(VK_BLEND_OP_REVERSE_SUBTRACT);\n    ENUM_NAME(VK_BLEND_OP_MIN);\n    ENUM_NAME(VK_BLEND_OP_MAX);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\nstd::ostream& operator << (std::ostream& os, VkCompareOp e) {\n  switch (e) {\n    ENUM_NAME(VK_COMPARE_OP_NEVER);\n    ENUM_NAME(VK_COMPARE_OP_LESS);\n    ENUM_NAME(VK_COMPARE_OP_EQUAL);\n    ENUM_NAME(VK_COMPARE_OP_LESS_OR_EQUAL);\n    ENUM_NAME(VK_COMPARE_OP_GREATER);\n    ENUM_NAME(VK_COMPARE_OP_NOT_EQUAL);\n    ENUM_NAME(VK_COMPARE_OP_GREATER_OR_EQUAL);\n    ENUM_NAME(VK_COMPARE_OP_ALWAYS);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\nstd::ostream& operator << (std::ostream& os, VkVertexInputRate e) {\n  switch (e) {\n    ENUM_NAME(VK_VERTEX_INPUT_RATE_VERTEX);\n    ENUM_NAME(VK_VERTEX_INPUT_RATE_INSTANCE);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\nstd::ostream& operator << (std::ostream& os, VkPrimitiveTopology e) {\n  switch (e) {\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_POINT_LIST);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_LINE_LIST);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY);\n    ENUM_NAME(VK_PRIMITIVE_TOPOLOGY_PATCH_LIST);\n    ENUM_DEFAULT(e);\n  }\n  return os;\n}\n\nstd::ostream& operator << (std::ostream& os, VkOffset2D e) {\n  return os << \"(\" << e.x << \",\" << e.y << \")\";\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkOffset3D e) {\n  return os << \"(\" << e.x << \",\" << e.y << \",\" << e.z << \")\";\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkExtent2D e) {\n  return os << \"(\" << e.width << \",\" << e.height << \")\";\n}\n\n\nstd::ostream& operator << (std::ostream& os, VkExtent3D e) {\n  return os << \"(\" << e.width << \",\" << e.height << \",\" << e.depth << \")\";\n}\n"
  },
  {
    "path": "src/vulkan/vulkan_names.h",
    "content": "#pragma once\n\n#include <ostream>\n\n#include \"../util/util_enum.h\"\n\n#include \"vulkan_loader.h\"\n\nstd::ostream& operator << (std::ostream& os, VkPipelineCacheHeaderVersion e);\nstd::ostream& operator << (std::ostream& os, VkResult e);\nstd::ostream& operator << (std::ostream& os, VkFormat e);\nstd::ostream& operator << (std::ostream& os, VkImageType e);\nstd::ostream& operator << (std::ostream& os, VkImageTiling e);\nstd::ostream& operator << (std::ostream& os, VkImageLayout e);\nstd::ostream& operator << (std::ostream& os, VkImageViewType e);\nstd::ostream& operator << (std::ostream& os, VkPresentModeKHR e);\nstd::ostream& operator << (std::ostream& os, VkColorSpaceKHR e);\nstd::ostream& operator << (std::ostream& os, VkFrontFace e);\nstd::ostream& operator << (std::ostream& os, VkPolygonMode e);\nstd::ostream& operator << (std::ostream& os, VkBlendFactor e);\nstd::ostream& operator << (std::ostream& os, VkBlendOp e);\nstd::ostream& operator << (std::ostream& os, VkCompareOp e);\nstd::ostream& operator << (std::ostream& os, VkVertexInputRate e);\nstd::ostream& operator << (std::ostream& os, VkPrimitiveTopology e);\nstd::ostream& operator << (std::ostream& os, VkOffset2D e);\nstd::ostream& operator << (std::ostream& os, VkOffset3D e);\nstd::ostream& operator << (std::ostream& os, VkExtent2D e);\nstd::ostream& operator << (std::ostream& os, VkExtent3D e);\n"
  },
  {
    "path": "src/vulkan/vulkan_util.h",
    "content": "#pragma once\n\n#include <cstring>\n#include <utility>\n\n#include \"vulkan_loader.h\"\n\n#if defined(_MSC_VER)\n// Unary minus on unsigned type\n#pragma warning( disable : 4146 )\n#endif\n\nnamespace dxvk::vk {\n\n  constexpr static VkAccessFlags AccessReadMask\n    = VK_ACCESS_INDIRECT_COMMAND_READ_BIT\n    | VK_ACCESS_INDEX_READ_BIT\n    | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT\n    | VK_ACCESS_UNIFORM_READ_BIT\n    | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT\n    | VK_ACCESS_SHADER_READ_BIT\n    | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT\n    | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT\n    | VK_ACCESS_TRANSFER_READ_BIT\n    | VK_ACCESS_MEMORY_READ_BIT\n    | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT;\n    \n  constexpr static VkAccessFlags AccessWriteMask\n    = VK_ACCESS_SHADER_WRITE_BIT\n    | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT\n    | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT\n    | VK_ACCESS_TRANSFER_WRITE_BIT\n    | VK_ACCESS_MEMORY_WRITE_BIT\n    | VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT\n    | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;\n\n  constexpr static VkAccessFlags AccessDeviceMask\n    = AccessWriteMask | AccessReadMask;\n\n  constexpr static VkAccessFlags AccessHostMask\n    = VK_ACCESS_HOST_READ_BIT\n    | VK_ACCESS_HOST_WRITE_BIT;\n\n  constexpr static VkAccessFlags AccessGfxSideEffectMask\n    = VK_ACCESS_SHADER_WRITE_BIT\n    | VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT\n    | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;\n\n  constexpr static VkPipelineStageFlags StageDeviceMask\n    = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT\n    | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT\n    | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT\n    | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT\n    | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT\n    | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT\n    | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT\n    | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT\n    | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n    | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT\n    | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT\n    | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT\n    | VK_PIPELINE_STAGE_TRANSFER_BIT\n    | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT\n    | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT\n    | VK_PIPELINE_STAGE_ALL_COMMANDS_BIT\n    | VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT;\n\n  inline VkImageSubresourceRange makeSubresourceRange(\n    const VkImageSubresourceLayers& layers) {\n    VkImageSubresourceRange range;\n    range.aspectMask      = layers.aspectMask;\n    range.baseMipLevel    = layers.mipLevel;\n    range.levelCount      = 1;\n    range.baseArrayLayer  = layers.baseArrayLayer;\n    range.layerCount      = layers.layerCount;\n    return range;\n  }\n\n  inline VkImageSubresourceRange makeSubresourceRange(\n    const VkImageSubresource& subres) {\n    VkImageSubresourceRange range;\n    range.aspectMask      = subres.aspectMask;\n    range.baseMipLevel    = subres.mipLevel;\n    range.levelCount      = 1;\n    range.baseArrayLayer  = subres.arrayLayer;\n    range.layerCount      = 1;\n    return range;\n  }\n\n  inline VkImageSubresourceLayers makeSubresourceLayers(\n    const VkImageSubresource& subres) {\n    VkImageSubresourceLayers layers;\n    layers.aspectMask     = subres.aspectMask;\n    layers.mipLevel       = subres.mipLevel;\n    layers.baseArrayLayer = subres.arrayLayer;\n    layers.layerCount     = 1;\n    return layers;\n  }\n\n  inline VkImageSubresourceLayers pickSubresourceLayers(\n    const VkImageSubresourceRange&  range,\n          uint32_t                  level) {\n    VkImageSubresourceLayers layers;\n    layers.aspectMask     = range.aspectMask;\n    layers.mipLevel       = range.baseMipLevel + level;\n    layers.baseArrayLayer = range.baseArrayLayer;\n    layers.layerCount     = range.layerCount;\n    return layers;\n  }\n\n  inline VkImageSubresource pickSubresource(\n    const VkImageSubresourceLayers& range,\n          uint32_t                  layer) {\n    VkImageSubresource subres;\n    subres.aspectMask = range.aspectMask;\n    subres.mipLevel   = range.mipLevel;\n    subres.arrayLayer = range.baseArrayLayer + layer;\n    return subres;\n  }\n\n  inline VkImageSubresource pickSubresource(\n    const VkImageSubresourceRange&  range,\n          uint32_t                  level,\n          uint32_t                  layer) {\n    VkImageSubresource subres;\n    subres.aspectMask = range.aspectMask;\n    subres.mipLevel   = range.baseMipLevel   + level;\n    subres.arrayLayer = range.baseArrayLayer + layer;\n    return subres;\n  }\n\n  inline bool checkSubresourceRangeOverlap(\n    const VkImageSubresourceRange&  a,\n    const VkImageSubresourceRange&  b) {\n    return a.baseMipLevel < b.baseMipLevel + b.levelCount\n        && a.baseMipLevel + a.levelCount > b.baseMipLevel\n        && a.baseArrayLayer < b.baseArrayLayer + b.layerCount\n        && a.baseArrayLayer + a.layerCount > b.baseArrayLayer;\n  }\n\n  inline bool checkSubresourceRangeSuperset(\n    const VkImageSubresourceRange&  a,\n    const VkImageSubresourceRange&  b) {\n    return a.baseMipLevel                <= b.baseMipLevel\n        && a.baseMipLevel + a.levelCount >= b.baseMipLevel + b.levelCount\n        && a.baseArrayLayer                <= b.baseArrayLayer\n        && a.baseArrayLayer + a.layerCount >= b.baseArrayLayer + b.layerCount;\n  }\n\n  inline VkImageAspectFlags getWritableAspectsForLayout(VkImageLayout layout) {\n    switch (layout) {\n      case VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT:\n      case VK_IMAGE_LAYOUT_GENERAL:\n        return VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;\n      case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:\n        return VK_IMAGE_ASPECT_COLOR_BIT;\n      case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:\n        return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;\n      case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:\n        return VK_IMAGE_ASPECT_DEPTH_BIT;\n      case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:\n        return VK_IMAGE_ASPECT_STENCIL_BIT;\n      case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:\n      case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:\n        return 0;\n      default:\n        Logger::err(str::format(\"Unhandled image layout \", layout));\n        return 0;\n    }\n  }\n\n  inline uint32_t getPlaneCount(VkImageAspectFlags aspects) {\n    // Use a 16-bit integer as a lookup table. This works because\n    // plane aspects use consecutive bits in the image aspect enum.\n    const uint32_t shift = (aspects / VK_IMAGE_ASPECT_PLANE_0_BIT) * 2;\n    const uint32_t counts = 0xffa5;\n    return (counts >> shift) & 0x3;\n  }\n\n  inline uint32_t getPlaneIndex(VkImageAspectFlags aspect) {\n    // Works for up to PLANE_2_BIT due to enum poperties\n    return aspect / VK_IMAGE_ASPECT_PLANE_1_BIT;\n  }\n\n  inline VkImageAspectFlagBits getPlaneAspect(uint32_t plane) {\n    return VkImageAspectFlagBits(VK_IMAGE_ASPECT_PLANE_0_BIT << plane);\n  }\n\n  inline VkImageAspectFlags getNextAspect(VkImageAspectFlags& mask) {\n    if (likely(mask & (VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))) {\n      // Depth-stencil isn't considered multi-planar\n      return std::exchange(mask, VkImageAspectFlags(0));\n    } else {\n      VkImageAspectFlags result = mask & -mask;\n      mask &= ~result;\n      return result;\n    }\n  }\n\n\n  inline uint64_t getObjectHandle(uint64_t handle) {\n    return handle;\n  }\n\n\n  template<typename T>\n  uint64_t getObjectHandle(T* object) {\n    return reinterpret_cast<uintptr_t>(object);\n  }\n\n\n  inline bool isValidDebugName(const char* name) {\n    return name && name[0];\n  }\n\n\n  /**\n   * \\brief Queries sRGB and non-sSRGB format pair\n   *\n   * \\param [in] format Format to look up\n   * \\returns Pair of the corresponding non-SRGB and sRGB formats.\n   *    If the format in quesion has no sRGB equivalent, this\n   *    function returns \\c VK_FORMAT_UNDEFINED.\n   */\n  inline std::pair<VkFormat, VkFormat> getSrgbFormatPair(VkFormat format) {\n    static const std::array<std::pair<VkFormat, VkFormat>, 3> srgbFormatMap = {{\n      { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB },\n      { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB },\n      { VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_FORMAT_A8B8G8R8_SRGB_PACK32 },\n    }};\n\n    for (const auto& f : srgbFormatMap) {\n      if (f.first == format || f.second == format)\n        return f;\n    }\n\n    return std::make_pair(VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED);\n  }\n\n\n  /**\n   * \\brief Makes debug label\n   *\n   * \\param [in] color Color, as BGR with implied opaque alpha\n   * \\param [in] text Label text\n   */\n  inline VkDebugUtilsLabelEXT makeLabel(uint32_t color, const char* text) {\n    VkDebugUtilsLabelEXT label = { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT };\n    label.color[0] = ((color >> 16u) & 0xffu) / 255.0f;\n    label.color[1] = ((color >> 8u)  & 0xffu) / 255.0f;\n    label.color[2] = ((color >> 0u)  & 0xffu) / 255.0f;\n    label.color[3] = color ? 1.0f : 0.0f;\n    label.pLabelName = text;\n    return label;\n  }\n\n\n  inline const void* scanChain(const void* pNext, VkStructureType sType) {\n    auto chain = reinterpret_cast<const VkBaseInStructure*>(pNext);\n\n    while (chain && chain->sType != sType)\n      chain = chain->pNext;\n\n    return chain;\n  }\n\n  inline void* scanChain(void* pNext, VkStructureType sType) {\n    auto chain = reinterpret_cast<VkBaseOutStructure*>(pNext);\n\n    while (chain && chain->sType != sType)\n      chain = chain->pNext;\n\n    return chain;\n  }\n\n  template<typename Fn>\n  inline void iterChain(void* pNext, const Fn& fn) {\n    auto chain = reinterpret_cast<const VkBaseInStructure*>(pNext);\n\n    while (chain) {\n      fn(chain);\n      chain = chain->pNext;\n    }\n  }\n\n  inline VkExtensionProperties makeExtension(const char* name) {\n    VkExtensionProperties result = { };\n    std::strncpy(result.extensionName, name, sizeof(result.extensionName) - 1u);\n    return result;\n  }\n\n  struct SortExtension {\n    inline bool operator () (const VkExtensionProperties& a, const VkExtensionProperties& b) const {\n      return std::strncmp(a.extensionName, b.extensionName, sizeof(a.extensionName)) < 0;\n    }\n  };\n\n}\n\n\ninline bool operator == (\n  const VkImageSubresourceRange& a,\n  const VkImageSubresourceRange& b) {\n  return a.aspectMask     == b.aspectMask\n      && a.baseMipLevel   == b.baseMipLevel\n      && a.levelCount     == b.levelCount\n      && a.baseArrayLayer == b.baseArrayLayer\n      && a.layerCount     == b.layerCount;\n}\n\n\ninline bool operator != (\n  const VkImageSubresourceRange& a,\n  const VkImageSubresourceRange& b) {\n  return !operator == (a, b);\n}\n\n\ninline bool operator == (\n  const VkImageSubresourceLayers& a,\n  const VkImageSubresourceLayers& b) {\n  return a.aspectMask     == b.aspectMask\n      && a.mipLevel       == b.mipLevel\n      && a.baseArrayLayer == b.baseArrayLayer\n      && a.layerCount     == b.layerCount;\n}\n\n\ninline bool operator != (\n  const VkImageSubresourceLayers& a,\n  const VkImageSubresourceLayers& b) {\n  return !operator == (a, b);\n}\n\n\ninline bool operator == (VkExtent3D a, VkExtent3D b) {\n  return a.width  == b.width\n      && a.height == b.height\n      && a.depth  == b.depth;\n}\n\n\ninline bool operator != (VkExtent3D a, VkExtent3D b) {\n  return a.width  != b.width\n      || a.height != b.height\n      || a.depth  != b.depth;\n}\n\n\ninline bool operator == (VkExtent2D a, VkExtent2D b) {\n  return a.width  == b.width\n      && a.height == b.height;\n}\n\n\ninline bool operator != (VkExtent2D a, VkExtent2D b) {\n  return a.width  != b.width\n      || a.height != b.height;\n}\n\n\ninline bool operator == (VkOffset3D a, VkOffset3D b) {\n  return a.x == b.x\n      && a.y == b.y\n      && a.z == b.z;\n}\n\n\ninline bool operator != (VkOffset3D a, VkOffset3D b) {\n  return a.x != b.x\n      || a.y != b.y\n      || a.z != b.z;\n}\n\n\ninline bool operator == (VkOffset2D a, VkOffset2D b) {\n  return a.x == b.x\n      && a.y == b.y;\n}\n\n\ninline bool operator != (VkOffset2D a, VkOffset2D b) {\n  return a.x != b.x\n      || a.y != b.y;\n}\n"
  },
  {
    "path": "src/wsi/glfw/wsi_monitor_glfw.cpp",
    "content": "#if defined(DXVK_WSI_GLFW)\n\n#include \"../wsi_monitor.h\"\n\n#include \"wsi/native_glfw.h\"\n#include \"wsi_platform_glfw.h\"\n\n#include \"../../util/util_string.h\"\n#include \"../../util/log/log.h\"\n\n#include <string>\n#include <sstream>\n\nnamespace dxvk::wsi {\n\n  HMONITOR GlfwWsiDriver::getDefaultMonitor() {\n    return enumMonitors(0);\n  }\n\n\n  HMONITOR GlfwWsiDriver::enumMonitors(uint32_t index) {\n    return isDisplayValid(int32_t(index))\n         ? toHmonitor(index)\n         : nullptr;\n  }\n\n  HMONITOR GlfwWsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) {\n    return enumMonitors(index);\n  }\n\n  bool GlfwWsiDriver::getDisplayName(\n      HMONITOR hMonitor,\n      WCHAR            (&Name)[32]) {\n    const int32_t displayId = fromHmonitor(hMonitor);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    std::wstringstream nameStream;\n    nameStream << LR\"(\\\\.\\DISPLAY)\" << (displayId + 1);\n\n    std::wstring name = nameStream.str();\n\n    std::memset(Name, 0, sizeof(Name));\n    name.copy(Name, name.length(), 0);\n\n    return true;\n  }\n\n\n  bool GlfwWsiDriver::getDesktopCoordinates(\n      HMONITOR hMonitor,\n      RECT* pRect) {\n    const int32_t displayId = fromHmonitor(hMonitor);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    int32_t displayCount = 0;\n    GLFWmonitor** monitors = glfwGetMonitors(&displayCount);\n    GLFWmonitor* monitor = monitors[displayId];\n\n    int32_t x;\n    int32_t y;\n    int32_t w;\n    int32_t h;\n    glfwGetMonitorWorkarea(monitor, &x, &y, &w, &h);\n\n    pRect->left = x;\n    pRect->top = y;\n    pRect->right = x + w;\n    pRect->bottom = y + h;\n\n    return true;\n  }\n\n  static inline uint32_t roundToNextPow2(uint32_t num) {\n    if (num-- == 0)\n      return 0;\n\n    num |= num >> 1;\n    num |= num >> 2;\n    num |= num >> 4;\n    num |= num >> 8;\n    num |= num >> 16;\n\n    return ++num;\n  }\n\n\n  static inline void convertMode(const GLFWvidmode& mode, WsiMode* pMode) {\n    pMode->width = uint32_t(mode.width);\n    pMode->height = uint32_t(mode.height);\n    pMode->refreshRate = WsiRational{uint32_t(mode.refreshRate) * 1000, 1000};\n    // BPP should always be a power of two\n    // to match Windows behaviour of including padding.\n    pMode->bitsPerPixel = roundToNextPow2(mode.blueBits + mode.redBits + mode.greenBits);\n    pMode->interlaced = false;\n  }\n\n\n  bool GlfwWsiDriver::getDisplayMode(\n      HMONITOR hMonitor,\n      uint32_t ModeNumber,\n      WsiMode* pMode) {\n    const int32_t displayId = fromHmonitor(hMonitor);\n    int32_t displayCount = 0;\n    GLFWmonitor** monitors = glfwGetMonitors(&displayCount);\n    GLFWmonitor* monitor = monitors[displayId];\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    int32_t count = 0;\n    const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count);\n\n    if(ModeNumber >= uint32_t(count))\n      return false;\n\n    convertMode(modes[ModeNumber], pMode);\n\n    return true;\n  }\n\n\n  bool GlfwWsiDriver::getCurrentDisplayMode(\n      HMONITOR hMonitor,\n      WsiMode* pMode) {\n    const int32_t displayId = fromHmonitor(hMonitor);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    int32_t displayCount = 0;\n    GLFWmonitor** monitors = glfwGetMonitors(&displayCount);\n    GLFWmonitor* monitor = monitors[displayId];\n\n    const GLFWvidmode* mode = glfwGetVideoMode(monitor);\n\n    convertMode(*mode, pMode);\n\n    return true;\n  }\n\n\n  bool GlfwWsiDriver::getDesktopDisplayMode(\n      HMONITOR hMonitor,\n      WsiMode* pMode) {\n    const int32_t displayId = fromHmonitor(hMonitor);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    int32_t displayCount = 0;\n    GLFWmonitor** monitors = glfwGetMonitors(&displayCount);\n    GLFWmonitor* monitor = monitors[displayId];\n\n    //TODO: actually implement this properly, currently we just grab the current one\n    convertMode(*glfwGetVideoMode(monitor), pMode);\n\n    return true;\n  }\n\n  std::vector<uint8_t> GlfwWsiDriver::getMonitorEdid(HMONITOR hMonitor) {\n    Logger::err(\"getMonitorEdid not implemented on this platform.\");\n    return {};\n  }\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/glfw/wsi_platform_glfw.cpp",
    "content": "#if defined(DXVK_WSI_GLFW)\n\n#include \"wsi_platform_glfw.h\"\n#include \"../../util/util_error.h\"\n#include \"../../util/util_string.h\"\n#include \"../../util/util_win32_compat.h\"\n\nnamespace dxvk::wsi {\n\n  GlfwWsiDriver::GlfwWsiDriver() {\n    libglfw = LoadLibraryA( // FIXME: Get soname as string from meson\n#if defined(_WIN32)\n        \"glfw.dll\"\n#elif defined(__APPLE__)\n        \"libglfw.3.dylib\"\n#else\n        \"libglfw.so.3\"\n#endif\n      );\n    if (libglfw == nullptr)\n      throw DxvkError(\"GLFW WSI: Failed to load GLFW DLL.\");\n\n    #define GLFW_PROC(ret, name, params) \\\n      name = reinterpret_cast<pfn_##name>(GetProcAddress(libglfw, #name)); \\\n      if (name == nullptr) { \\\n        FreeLibrary(libglfw); \\\n        libglfw = nullptr; \\\n        throw DxvkError(\"GLFW WSI: Failed to load \" #name \".\"); \\\n      }\n    #include \"wsi_platform_glfw_funcs.h\"\n  }\n\n  GlfwWsiDriver::~GlfwWsiDriver() {\n    FreeLibrary(libglfw);\n  }\n\n  std::vector<const char *> GlfwWsiDriver::getInstanceExtensions() {\n    if (!glfwVulkanSupported())\n      throw DxvkError(str::format(\"GLFW WSI: Vulkan is not supported in any capacity!\"));\n\n    uint32_t extensionCount = 0;\n    const char** extensionArray = glfwGetRequiredInstanceExtensions(&extensionCount);\n\n    if (extensionCount == 0)\n      throw DxvkError(str::format(\"GLFW WSI: Failed to get required instance extensions\"));\n\n    std::vector<const char *> names;\n    for (uint32_t i = 0; i < extensionCount; ++i) {\n      names.push_back(extensionArray[i]);\n    }\n\n    return names;\n  }\n\n  static bool createGlfwWsiDriver(WsiDriver **driver) {\n    try {\n      *driver = new GlfwWsiDriver();\n    } catch (const DxvkError& e) {\n      return false;\n    }\n    return true;\n  }\n\n  WsiBootstrap GlfwWSI = {\n    \"GLFW\",\n    createGlfwWsiDriver\n  };\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/glfw/wsi_platform_glfw.h",
    "content": "#pragma once\n\n#include \"../../vulkan/vulkan_loader.h\"\n#include <GLFW/glfw3.h>\n\n#include \"../wsi_platform.h\"\n\nnamespace dxvk::wsi {\n\n  class GlfwWsiDriver : public WsiDriver {\n  private:\n    HMODULE libglfw;\n    #define GLFW_PROC(ret, name, params) \\\n      typedef ret (*pfn_##name) params; \\\n      pfn_##name name;\n    #include \"wsi_platform_glfw_funcs.h\"\n\n    inline bool isDisplayValid(int32_t displayId) {\n      int32_t displayCount = 0;\n      glfwGetMonitors(&displayCount);\n\n      return displayId < displayCount && displayId >= 0;\n    }\n\n  public:\n    GlfwWsiDriver();\n    ~GlfwWsiDriver();\n\n    // Platform\n    virtual std::vector<const char *> getInstanceExtensions();\n\n    // Monitor\n    virtual HMONITOR getDefaultMonitor();\n\n    virtual HMONITOR enumMonitors(uint32_t index);\n\n    virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index);\n\n    virtual bool getDisplayName(\n            HMONITOR         hMonitor,\n            WCHAR            (&Name)[32]);\n\n    virtual bool getDesktopCoordinates(\n            HMONITOR         hMonitor,\n            RECT*            pRect);\n\n    virtual bool getDisplayMode(\n            HMONITOR         hMonitor,\n            uint32_t         modeNumber,\n            WsiMode*         pMode);\n\n    virtual bool getCurrentDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode);\n\n    virtual bool getDesktopDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode);\n\n    virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor);\n\n    // Window\n\n    virtual void getWindowSize(\n            HWND      hWindow,\n            uint32_t* pWidth,\n            uint32_t* pWeight);\n\n    virtual void resizeWindow(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            uint32_t         width,\n            uint32_t         weight);\n\n    virtual void saveWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             saveStyle);\n\n    virtual void restoreWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             restoreCoordinates);\n\n    virtual bool setWindowMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n      const WsiMode&         mode);\n\n    virtual bool enterFullscreenMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            [[maybe_unused]]\n            bool             modeSwitch);\n\n    virtual bool leaveFullscreenMode(\n            HWND             hWindow,\n            DxvkWindowState* pState);\n\n    virtual bool restoreDisplayMode();\n\n    virtual HMONITOR getWindowMonitor(HWND hWindow);\n\n    virtual bool isWindow(HWND hWindow);\n\n    virtual bool isMinimized(HWND hWindow);\n\n    virtual bool isOccluded(HWND hWindow);\n\n    virtual void updateFullscreenWindow(\n            HMONITOR hMonitor,\n            HWND     hWindow,\n            bool     forceTopmost);\n\n    virtual VkResult createSurface(\n            HWND                hWindow,\n            PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n            VkInstance          instance,\n            VkSurfaceKHR*       pSurface);\n  };\n\n}\n"
  },
  {
    "path": "src/wsi/glfw/wsi_platform_glfw_funcs.h",
    "content": "GLFW_PROC(VkResult, glfwCreateWindowSurface, (VkInstance, GLFWwindow*, const VkAllocationCallbacks*, VkSurfaceKHR*))\nGLFW_PROC(GLFWmonitor**, glfwGetMonitors, (int*))\nGLFW_PROC(void, glfwGetMonitorWorkarea, (GLFWmonitor*, int*, int*, int*, int*))\nGLFW_PROC(GLFWmonitor*, glfwGetPrimaryMonitor, (void))\nGLFW_PROC(const char**, glfwGetRequiredInstanceExtensions, (uint32_t*))\nGLFW_PROC(const GLFWvidmode*, glfwGetVideoMode, (GLFWmonitor*))\nGLFW_PROC(const GLFWvidmode*, glfwGetVideoModes, (GLFWmonitor*, int*))\nGLFW_PROC(int, glfwGetWindowAttrib, (GLFWwindow*, int))\nGLFW_PROC(void, glfwGetWindowSize, (GLFWwindow*, int*, int*))\nGLFW_PROC(void, glfwSetWindowMonitor, (GLFWwindow*, GLFWmonitor*, int, int, int, int, int))\nGLFW_PROC(void, glfwSetWindowSize, (GLFWwindow*, int, int))\nGLFW_PROC(int, glfwVulkanSupported, (void))\n#undef GLFW_PROC\n"
  },
  {
    "path": "src/wsi/glfw/wsi_window_glfw.cpp",
    "content": "#if defined(DXVK_WSI_GLFW)\n\n#include \"../wsi_window.h\"\n\n#include \"native/wsi/native_glfw.h\"\n#include \"wsi_platform_glfw.h\"\n\n#include \"../../util/util_string.h\"\n#include \"../../util/log/log.h\"\n\n#include <windows.h>\n#include \"../../vulkan/vulkan_loader.h\"\n#include <GLFW/glfw3.h>\n\nnamespace dxvk::wsi {\n\n  void GlfwWsiDriver::getWindowSize(\n      HWND hWindow,\n      uint32_t* pWidth,\n      uint32_t* pHeight) {\n    GLFWwindow* window = fromHwnd(hWindow);\n\n    int32_t w, h;\n    glfwGetWindowSize(window, &w, &h);\n\n    if (pWidth)\n      *pWidth = uint32_t(w);\n\n    if (pHeight)\n      *pHeight = uint32_t(h);\n  }\n\n\n  void GlfwWsiDriver::resizeWindow(\n      HWND hWindow,\n      DxvkWindowState* pState,\n      uint32_t Width,\n      uint32_t Height) {\n    GLFWwindow* window = fromHwnd(hWindow);\n\n    glfwSetWindowSize(window, int32_t(Width), int32_t(Height));\n  }\n\n\n  void GlfwWsiDriver::saveWindowState(\n      HWND Window,\n      DxvkWindowState* pState,\n      bool saveStyle) {\n  }\n\n\n  void GlfwWsiDriver::restoreWindowState(\n      HWND hWindow,\n      DxvkWindowState* pState,\n      bool restoreCoordinates) {\n  }\n\n\n  bool GlfwWsiDriver::setWindowMode(\n      HMONITOR hMonitor,\n      HWND hWindow,\n      DxvkWindowState* pState,\n      const WsiMode& pMode) {\n    const int32_t displayId = fromHmonitor(hMonitor);\n    GLFWwindow* window = fromHwnd(hWindow);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    int32_t displayCount = 0;\n    GLFWmonitor** monitors = glfwGetMonitors(&displayCount);\n    GLFWmonitor* monitor = monitors[displayId];\n\n    GLFWvidmode wantedMode = {};\n    wantedMode.width = pMode.width;\n    wantedMode.height = pMode.height;\n    wantedMode.refreshRate = pMode.refreshRate.numerator != 0\n                 ? pMode.refreshRate.numerator / pMode.refreshRate.denominator\n                 : 0;\n    // TODO: Implement lookup format for bitsPerPixel here.\n\n    glfwSetWindowMonitor(window, monitor, 0, 0, wantedMode.width, wantedMode.width, wantedMode.refreshRate);\n\n    return true;\n  }\n\n  bool GlfwWsiDriver::enterFullscreenMode(\n      HMONITOR hMonitor,\n      HWND hWindow,\n      DxvkWindowState* pState,\n      bool ModeSwitch) {\n    const int32_t displayId = fromHmonitor(hMonitor);\n    GLFWwindow* window = fromHwnd(hWindow);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    GLFWmonitor* monitor = glfwGetPrimaryMonitor();\n    auto videoMode = glfwGetVideoMode(monitor);\n\n    // TODO: Set this on the correct monitor.\n    // Docs aren't clear on this...\n    glfwSetWindowMonitor(window, monitor, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate);\n\n    return true;\n  }\n\n\n  bool GlfwWsiDriver::leaveFullscreenMode(\n      HWND hWindow,\n      DxvkWindowState* pState) {\n    GLFWwindow* window = fromHwnd(hWindow);\n\n    GLFWmonitor* monitor = glfwGetPrimaryMonitor();\n    auto videoMode = glfwGetVideoMode(monitor);\n    glfwSetWindowMonitor(window, nullptr, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate);\n\n    return true;\n  }\n\n\n  bool GlfwWsiDriver::restoreDisplayMode() {\n    // Don't need to do anything with GLFW here.\n    return true;\n  }\n\n\n  HMONITOR GlfwWsiDriver::getWindowMonitor(HWND hWindow) {\n    // TODO: implement this with glfwGetWindowMonitor \n    //  (or maybe not? glfwGetWindowMonitor only seems to reference *fullscreen* windows)\n    // GLFWwindow* window = fromHwnd(hWindow);\n    const int32_t displayId = 0;\n\n    return toHmonitor(displayId);\n  }\n\n\n  bool GlfwWsiDriver::isWindow(HWND hWindow) {\n    GLFWwindow* window = fromHwnd(hWindow);\n    return window != nullptr;\n  }\n\n\n  bool GlfwWsiDriver::isMinimized(HWND hWindow) {\n    GLFWwindow* window = fromHwnd(hWindow);\n    return glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0;\n  }\n\n\n  bool GlfwWsiDriver::isOccluded(HWND hWindow) {\n    return false;\n  }\n\n\n  void GlfwWsiDriver::updateFullscreenWindow(\n      HMONITOR hMonitor,\n      HWND     hWindow,\n      bool     forceTopmost) {\n    // Don't need to do anything with GLFW here.\n  }\n\n  VkResult GlfwWsiDriver::createSurface(\n      HWND hWindow,\n      PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n      VkInstance                instance,\n      VkSurfaceKHR* pSurface) {\n    GLFWwindow* window = fromHwnd(hWindow);\n\n    return glfwCreateWindowSurface(instance, window, nullptr, pSurface);\n  }\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/meson.build",
    "content": "wsi_src = [\n  'wsi_edid.cpp',\n  'wsi_platform.cpp',\n  'win32/wsi_monitor_win32.cpp',\n  'win32/wsi_platform_win32.cpp',\n  'win32/wsi_window_win32.cpp',\n  'sdl3/wsi_monitor_sdl3.cpp',\n  'sdl3/wsi_platform_sdl3.cpp',\n  'sdl3/wsi_window_sdl3.cpp',\n  'sdl2/wsi_monitor_sdl2.cpp',\n  'sdl2/wsi_platform_sdl2.cpp',\n  'sdl2/wsi_window_sdl2.cpp',\n  'glfw/wsi_monitor_glfw.cpp',\n  'glfw/wsi_platform_glfw.cpp',\n  'glfw/wsi_window_glfw.cpp',\n]\n\nwsi_deps = [ dep_displayinfo ]\n\nif platform == 'windows'\n  lib_setupapi = cpp.find_library('setupapi')\n  wsi_deps += [ lib_setupapi ]\nelse\n  wsi_deps += [\n    lib_sdl3.partial_dependency(compile_args: true, includes: true),\n    lib_sdl2.partial_dependency(compile_args: true, includes: true),\n    lib_glfw.partial_dependency(compile_args: true, includes: true),\n  ]\nendif\n\nwsi_lib = static_library('wsi', wsi_src,\n  dependencies        : wsi_deps,\n  include_directories : [ dxvk_include_path ])\n\nwsi_dep = declare_dependency(\n  link_with           : [ wsi_lib ])\n"
  },
  {
    "path": "src/wsi/sdl2/wsi_monitor_sdl2.cpp",
    "content": "#if defined(DXVK_WSI_SDL2)\n\n#include \"../wsi_monitor.h\"\n\n#include \"wsi/native_sdl2.h\"\n#include \"wsi_platform_sdl2.h\"\n\n#include \"../../util/util_string.h\"\n#include \"../../util/log/log.h\"\n\n#include <string>\n#include <sstream>\n\n\nnamespace dxvk::wsi {\n\n  HMONITOR Sdl2WsiDriver::getDefaultMonitor() {\n    return enumMonitors(0);\n  }\n\n\n  HMONITOR Sdl2WsiDriver::enumMonitors(uint32_t index) {\n    return isDisplayValid(int32_t(index))\n      ? toHmonitor(index)\n      : nullptr;\n  }\n\n  HMONITOR Sdl2WsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) {\n    return enumMonitors(index);\n  }\n\n  bool Sdl2WsiDriver::getDisplayName(\n          HMONITOR         hMonitor,\n          WCHAR            (&Name)[32]) {\n    const int32_t displayId    = fromHmonitor(hMonitor);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    std::wstringstream nameStream;\n    nameStream << LR\"(\\\\.\\DISPLAY)\" << (displayId + 1);\n\n    std::wstring name = nameStream.str();\n\n    std::memset(Name, 0, sizeof(Name));\n    name.copy(Name, name.length(), 0);\n\n    return true;\n  }\n\n\n  bool Sdl2WsiDriver::getDesktopCoordinates(\n          HMONITOR         hMonitor,\n          RECT*            pRect) {\n    const int32_t displayId    = fromHmonitor(hMonitor);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    SDL_Rect rect = { };\n    SDL_GetDisplayBounds(displayId, &rect);\n\n    pRect->left   = rect.x;\n    pRect->top    = rect.y;\n    pRect->right  = rect.x + rect.w;\n    pRect->bottom = rect.y + rect.h;\n\n    return true;\n  }\n\n\n  static inline uint32_t roundToNextPow2(uint32_t num) {\n    if (num-- == 0)\n      return 0;\n\n    num |= num >> 1; num |= num >> 2;\n    num |= num >> 4; num |= num >> 8;\n    num |= num >> 16;\n\n    return ++num;\n  }\n\n\n  static inline void convertMode(const SDL_DisplayMode& mode, WsiMode* pMode) {\n    pMode->width          = uint32_t(mode.w);\n    pMode->height         = uint32_t(mode.h);\n    pMode->refreshRate    = WsiRational{ uint32_t(mode.refresh_rate) * 1000, 1000 }; \n    // BPP should always be a power of two\n    // to match Windows behaviour of including padding.\n    pMode->bitsPerPixel   = roundToNextPow2(SDL_BITSPERPIXEL(mode.format));\n    pMode->interlaced     = false;\n  }\n\n\n  bool Sdl2WsiDriver::getDisplayMode(\n          HMONITOR         hMonitor,\n          uint32_t         ModeNumber,\n          WsiMode*         pMode) {\n    const int32_t displayId    = fromHmonitor(hMonitor);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    SDL_DisplayMode mode = { };\n    if (SDL_GetDisplayMode(displayId, ModeNumber, &mode) != 0)\n      return false;\n\n    convertMode(mode, pMode);\n\n    return true;\n  }\n\n\n  bool Sdl2WsiDriver::getCurrentDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode) {\n    const int32_t displayId    = fromHmonitor(hMonitor);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    SDL_DisplayMode mode = { };\n    if (SDL_GetCurrentDisplayMode(displayId, &mode) != 0) {\n      Logger::err(str::format(\"SDL_GetCurrentDisplayMode: \", SDL_GetError()));\n      return false;\n    }\n\n    convertMode(mode, pMode);\n\n    return true;\n  }\n\n\n  bool Sdl2WsiDriver::getDesktopDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode) {\n    const int32_t displayId    = fromHmonitor(hMonitor);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    SDL_DisplayMode mode = { };\n    if (SDL_GetDesktopDisplayMode(displayId, &mode) != 0) {\n      Logger::err(str::format(\"SDL_GetCurrentDisplayMode: \", SDL_GetError()));\n      return false;\n    }\n\n    convertMode(mode, pMode);\n\n    return true;\n  }\n\n  std::vector<uint8_t> Sdl2WsiDriver::getMonitorEdid(HMONITOR hMonitor) {\n    Logger::err(\"getMonitorEdid not implemented on this platform.\");\n    return {};\n  }\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/sdl2/wsi_platform_sdl2.cpp",
    "content": "#if defined(DXVK_WSI_SDL2)\n\n#include \"wsi_platform_sdl2.h\"\n#include \"../../util/util_error.h\"\n#include \"../../util/util_string.h\"\n#include \"../../util/util_win32_compat.h\"\n\n#include <SDL_vulkan.h>\n\nnamespace dxvk::wsi {\n\n  Sdl2WsiDriver::Sdl2WsiDriver() {\n    libsdl = LoadLibraryA( // FIXME: Get soname as string from meson\n#if defined(_WIN32)\n        \"SDL2.dll\"\n#elif defined(__APPLE__)\n        \"libSDL2-2.0.0.dylib\"\n#else\n        \"libSDL2-2.0.so.0\"\n#endif\n      );\n    if (libsdl == nullptr)\n      throw DxvkError(\"SDL2 WSI: Failed to load SDL2 DLL.\");\n\n    #define SDL_PROC(ret, name, params) \\\n      name = reinterpret_cast<pfn_##name>(GetProcAddress(libsdl, #name)); \\\n      if (name == nullptr) { \\\n        FreeLibrary(libsdl); \\\n        libsdl = nullptr; \\\n        throw DxvkError(\"SDL2 WSI: Failed to load \" #name \".\"); \\\n      }\n    #include \"wsi_platform_sdl2_funcs.h\"\n  }\n\n  Sdl2WsiDriver::~Sdl2WsiDriver() {\n    FreeLibrary(libsdl);\n  }\n\n  std::vector<const char *> Sdl2WsiDriver::getInstanceExtensions() {\n    SDL_Vulkan_LoadLibrary(nullptr);\n\n    uint32_t extensionCount = 0;\n    if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, nullptr))\n      throw DxvkError(str::format(\"SDL2 WSI: Failed to get instance extension count. \", SDL_GetError()));\n\n    auto extensionNames = std::vector<const char *>(extensionCount);\n    if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &extensionCount, extensionNames.data()))\n      throw DxvkError(str::format(\"SDL2 WSI: Failed to get instance extensions. \", SDL_GetError()));\n\n    return extensionNames;\n  }\n\n  static bool createSdl2WsiDriver(WsiDriver **driver) {\n    try {\n      *driver = new Sdl2WsiDriver();\n    } catch (const DxvkError& e) {\n      return false;\n    }\n    return true;\n  }\n\n  WsiBootstrap Sdl2WSI = {\n    \"SDL2\",\n    createSdl2WsiDriver\n  };\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/sdl2/wsi_platform_sdl2.h",
    "content": "#pragma once\n\n#include <SDL.h>\n\n#include \"../wsi_platform.h\"\n\nnamespace dxvk::wsi {\n\n  class Sdl2WsiDriver : public WsiDriver {\n  private:\n    HMODULE libsdl;\n    #define SDL_PROC(ret, name, params) \\\n      typedef ret (SDLCALL *pfn_##name) params; \\\n      pfn_##name name;\n    #include \"wsi_platform_sdl2_funcs.h\"\n\n    inline bool isDisplayValid(int32_t displayId) {\n      const int32_t displayCount = SDL_GetNumVideoDisplays();\n\n      return displayId < displayCount && displayId >= 0;\n    }\n\n  public:\n    Sdl2WsiDriver();\n    ~Sdl2WsiDriver();\n\n    // Platform\n    virtual std::vector<const char *> getInstanceExtensions();\n\n    // Monitor\n    virtual HMONITOR getDefaultMonitor();\n\n    virtual HMONITOR enumMonitors(uint32_t index);\n\n    virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index);\n\n    virtual bool getDisplayName(\n            HMONITOR         hMonitor,\n            WCHAR            (&Name)[32]);\n\n    virtual bool getDesktopCoordinates(\n            HMONITOR         hMonitor,\n            RECT*            pRect);\n\n    virtual bool getDisplayMode(\n            HMONITOR         hMonitor,\n            uint32_t         modeNumber,\n            WsiMode*         pMode);\n\n    virtual bool getCurrentDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode);\n\n    virtual bool getDesktopDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode);\n\n    virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor);\n\n    // Window\n\n    virtual void getWindowSize(\n            HWND      hWindow,\n            uint32_t* pWidth,\n            uint32_t* pWeight);\n\n    virtual void resizeWindow(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            uint32_t         width,\n            uint32_t         weight);\n\n    virtual void saveWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             saveStyle);\n\n    virtual void restoreWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             restoreCoordinates);\n\n    virtual bool setWindowMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n      const WsiMode&         mode);\n\n    virtual bool enterFullscreenMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            [[maybe_unused]]\n            bool             modeSwitch);\n\n    virtual bool leaveFullscreenMode(\n            HWND             hWindow,\n            DxvkWindowState* pState);\n\n    virtual bool restoreDisplayMode();\n\n    virtual HMONITOR getWindowMonitor(HWND hWindow);\n\n    virtual bool isWindow(HWND hWindow);\n\n    virtual bool isMinimized(HWND hWindow);\n\n    virtual bool isOccluded(HWND hWindow);\n\n    virtual void updateFullscreenWindow(\n            HMONITOR hMonitor,\n            HWND     hWindow,\n            bool     forceTopmost);\n\n    virtual VkResult createSurface(\n            HWND                hWindow,\n            PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n            VkInstance          instance,\n            VkSurfaceKHR*       pSurface);\n  };\n  \n}\n"
  },
  {
    "path": "src/wsi/sdl2/wsi_platform_sdl2_funcs.h",
    "content": "SDL_PROC(SDL_DisplayMode*, SDL_GetClosestDisplayMode, (int, const SDL_DisplayMode*, SDL_DisplayMode*))\nSDL_PROC(int, SDL_GetCurrentDisplayMode, (int, SDL_DisplayMode*))\nSDL_PROC(int, SDL_GetDesktopDisplayMode, (int, SDL_DisplayMode*))\nSDL_PROC(int, SDL_GetDisplayBounds, (int, SDL_Rect*))\nSDL_PROC(int, SDL_GetDisplayMode, (int, int, SDL_DisplayMode*))\nSDL_PROC(const char*, SDL_GetError, (void))\nSDL_PROC(int, SDL_GetNumVideoDisplays, (void))\nSDL_PROC(int, SDL_GetWindowDisplayIndex, (SDL_Window*))\nSDL_PROC(int, SDL_SetWindowDisplayMode, (SDL_Window*, const SDL_DisplayMode*))\nSDL_PROC(int, SDL_SetWindowFullscreen, (SDL_Window*, Uint32))\nSDL_PROC(SDL_WindowFlags, SDL_GetWindowFlags, (SDL_Window *))\nSDL_PROC(void, SDL_GetWindowSize, (SDL_Window*, int*, int*))\nSDL_PROC(void, SDL_SetWindowSize, (SDL_Window*, int, int))\nSDL_PROC(SDL_bool, SDL_Vulkan_CreateSurface, (SDL_Window*, VkInstance, VkSurfaceKHR*))\nSDL_PROC(SDL_bool, SDL_Vulkan_GetInstanceExtensions, (SDL_Window*, unsigned int*, const char**))\nSDL_PROC(int, SDL_Vulkan_LoadLibrary, (const char*))\n#undef SDL_PROC\n"
  },
  {
    "path": "src/wsi/sdl2/wsi_window_sdl2.cpp",
    "content": "#if defined(DXVK_WSI_SDL2)\n\n#include \"../wsi_window.h\"\n\n#include \"native/wsi/native_sdl2.h\"\n#include \"wsi_platform_sdl2.h\"\n\n#include \"../../util/util_string.h\"\n#include \"../../util/log/log.h\"\n\n#include <windows.h>\n#include <SDL_vulkan.h>\n\nnamespace dxvk::wsi {\n\n  void Sdl2WsiDriver::getWindowSize(\n        HWND      hWindow,\n        uint32_t* pWidth,\n        uint32_t* pHeight) {\n    SDL_Window* window = fromHwnd(hWindow);\n\n    int32_t w, h;\n    SDL_GetWindowSize(window, &w, &h);\n\n    if (pWidth)\n      *pWidth = uint32_t(w);\n\n    if (pHeight)\n      *pHeight = uint32_t(h);\n  }\n\n\n  void Sdl2WsiDriver::resizeWindow(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          uint32_t         Width,\n          uint32_t         Height) {\n    SDL_Window* window = fromHwnd(hWindow);\n\n    SDL_SetWindowSize(window, int32_t(Width), int32_t(Height));\n  }\n\n\n  void Sdl2WsiDriver::saveWindowState(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             saveStyle) {\n  }\n\n\n  void Sdl2WsiDriver::restoreWindowState(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             restoreCoordinates) {\n  }\n\n\n  bool Sdl2WsiDriver::setWindowMode(\n          HMONITOR         hMonitor,\n          HWND             hWindow,\n          DxvkWindowState* pState,\n    const WsiMode&         pMode) {\n    const int32_t displayId    = fromHmonitor(hMonitor);\n    SDL_Window* window         = fromHwnd(hWindow);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    SDL_DisplayMode wantedMode = { };\n    wantedMode.w            = pMode.width;\n    wantedMode.h            = pMode.height;\n    wantedMode.refresh_rate = pMode.refreshRate.numerator != 0\n      ? pMode.refreshRate.numerator / pMode.refreshRate.denominator\n      : 0;\n    // TODO: Implement lookup format for bitsPerPixel here.\n\n    SDL_DisplayMode mode = { };\n    if (SDL_GetClosestDisplayMode(displayId, &wantedMode, &mode) == nullptr) {\n      Logger::err(str::format(\"SDL2 WSI: setWindowMode: SDL_GetClosestDisplayMode: \", SDL_GetError()));\n      return false;\n    }\n\n    if (SDL_SetWindowDisplayMode(window, &mode) != 0) {\n      Logger::err(str::format(\"SDL2 WSI: setWindowMode: SDL_SetWindowDisplayMode: \", SDL_GetError()));\n      return false;\n    }\n\n    return true;\n  }\n\n\n\n  bool Sdl2WsiDriver::enterFullscreenMode(\n          HMONITOR         hMonitor,\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             ModeSwitch) {\n    const int32_t displayId    = fromHmonitor(hMonitor);\n    SDL_Window* window         = fromHwnd(hWindow);\n\n    if (!isDisplayValid(displayId))\n      return false;\n\n    uint32_t flags = ModeSwitch\n        ? SDL_WINDOW_FULLSCREEN\n        : SDL_WINDOW_FULLSCREEN_DESKTOP;\n    \n    // TODO: Set this on the correct monitor.\n    // Docs aren't clear on this...\n    if (SDL_SetWindowFullscreen(window, flags) != 0) {\n      Logger::err(str::format(\"SDL2 WSI: enterFullscreenMode: SDL_SetWindowFullscreen: \", SDL_GetError()));\n      return false;\n    }\n\n    return true;\n  }\n\n\n  bool Sdl2WsiDriver::leaveFullscreenMode(\n          HWND             hWindow,\n          DxvkWindowState* pState) {\n    SDL_Window* window = fromHwnd(hWindow);\n\n    if (SDL_SetWindowFullscreen(window, 0) != 0) {\n      Logger::err(str::format(\"SDL2 WSI: leaveFullscreenMode: SDL_SetWindowFullscreen: \", SDL_GetError()));\n      return false;\n    }\n\n    return true;\n  }\n\n\n  bool Sdl2WsiDriver::restoreDisplayMode() {\n    // Don't need to do anything with SDL2 here.\n    return true;\n  }\n\n\n  HMONITOR Sdl2WsiDriver::getWindowMonitor(HWND hWindow) {\n    SDL_Window* window      = fromHwnd(hWindow);\n    const int32_t displayId = SDL_GetWindowDisplayIndex(window);\n\n    return toHmonitor(displayId);\n  }\n\n\n  bool Sdl2WsiDriver::isWindow(HWND hWindow) {\n    SDL_Window* window = fromHwnd(hWindow);\n    return window != nullptr;\n  }\n\n\n  bool Sdl2WsiDriver::isMinimized(HWND hWindow) {\n    SDL_Window* window = fromHwnd(hWindow);\n    return (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0;\n  }\n\n\n  bool Sdl2WsiDriver::isOccluded(HWND hWindow) {\n    return false;\n  }\n\n\n  void Sdl2WsiDriver::updateFullscreenWindow(\n          HMONITOR hMonitor,\n          HWND     hWindow,\n          bool     forceTopmost) {\n    // Don't need to do anything with SDL2 here.\n  }\n\n\n  VkResult Sdl2WsiDriver::createSurface(\n          HWND                      hWindow,\n          PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n          VkInstance                instance,\n          VkSurfaceKHR*             pSurface) {\n    SDL_Window* window = fromHwnd(hWindow);\n\n    return SDL_Vulkan_CreateSurface(window, instance, pSurface)\n         ? VK_SUCCESS\n         : VK_ERROR_OUT_OF_HOST_MEMORY;\n  }\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/sdl3/wsi_monitor_sdl3.cpp",
    "content": "#if defined(DXVK_WSI_SDL3)\n\n#include \"../wsi_monitor.h\"\n\n#include \"wsi/native_sdl3.h\"\n#include \"wsi_platform_sdl3.h\"\n\n#include \"../../util/util_string.h\"\n#include \"../../util/log/log.h\"\n\n#include <string>\n#include <sstream>\n\n\nnamespace dxvk::wsi {\n\n  HMONITOR Sdl3WsiDriver::getDefaultMonitor() {\n    return enumMonitors(0);\n  }\n\n\n  HMONITOR Sdl3WsiDriver::enumMonitors(uint32_t index) {\n    int count = 0;\n\n    SDL_DisplayID* displays = SDL_GetDisplays(&count);\n\n    HMONITOR result = displays && int(index) < count\n      ? toHmonitor(displays[index])\n      : nullptr;\n\n    SDL_free(displays);\n    return result;\n  }\n\n\n  HMONITOR Sdl3WsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) {\n    return enumMonitors(index);\n  }\n\n\n  bool Sdl3WsiDriver::getDisplayName(\n          HMONITOR         hMonitor,\n          WCHAR            (&Name)[32]) {\n    SDL_DisplayID displayId = fromHmonitor(hMonitor);\n\n    if (!displayId)\n      return false;\n\n    std::wstringstream nameStream;\n    nameStream << LR\"(\\\\.\\DISPLAY)\" << displayId;\n\n    std::wstring name = nameStream.str();\n\n    std::memset(Name, 0, sizeof(Name));\n    name.copy(Name, name.length(), 0);\n\n    return true;\n  }\n\n\n  bool Sdl3WsiDriver::getDesktopCoordinates(\n          HMONITOR         hMonitor,\n          RECT*            pRect) {\n    SDL_DisplayID displayId = fromHmonitor(hMonitor);\n\n    if (!displayId)\n      return false;\n\n    SDL_Rect rect = { };\n    SDL_GetDisplayBounds(displayId, &rect);\n\n    pRect->left   = rect.x;\n    pRect->top    = rect.y;\n    pRect->right  = rect.x + rect.w;\n    pRect->bottom = rect.y + rect.h;\n\n    return true;\n  }\n\n\n  bool Sdl3WsiDriver::getDisplayMode(\n          HMONITOR         hMonitor,\n          uint32_t         ModeNumber,\n          WsiMode*         pMode) {\n    SDL_DisplayID displayId = fromHmonitor(hMonitor);\n\n    if (!displayId)\n      return false;\n\n    int count = 0;\n    auto* modes = SDL_GetFullscreenDisplayModes(displayId, &count);\n\n    if (!modes) {\n      Logger::err(str::format(\"SDL_GetFullscreenDisplayModes: \", SDL_GetError()));\n      return false;\n    }\n\n    if (int(ModeNumber) >= count) {\n      SDL_free(modes);\n      return false;\n    }\n\n    convertMode(*modes[ModeNumber], pMode);\n\n    SDL_free(modes);\n    return true;\n  }\n\n\n  bool Sdl3WsiDriver::getCurrentDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode) {\n    SDL_DisplayID displayId = fromHmonitor(hMonitor);\n\n    if (!displayId)\n      return false;\n\n    auto* mode = SDL_GetCurrentDisplayMode(displayId);\n\n    if (!mode) {\n      Logger::err(str::format(\"SDL_GetCurrentDisplayMode: \", SDL_GetError()));\n      return false;\n    }\n\n    convertMode(*mode, pMode);\n    return true;\n  }\n\n\n  bool Sdl3WsiDriver::getDesktopDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode) {\n    SDL_DisplayID displayId = fromHmonitor(hMonitor);\n\n    if (!displayId)\n      return false;\n\n    auto* mode = SDL_GetDesktopDisplayMode(displayId);\n\n    if (!mode) {\n      Logger::err(str::format(\"SDL_GetDesktopDisplayMode: \", SDL_GetError()));\n      return false;\n    }\n\n    convertMode(*mode, pMode);\n    return true;\n  }\n\n\n  std::vector<uint8_t> Sdl3WsiDriver::getMonitorEdid(HMONITOR hMonitor) {\n    Logger::err(\"getMonitorEdid not implemented on this platform.\");\n    return {};\n  }\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/sdl3/wsi_platform_sdl3.cpp",
    "content": "#if defined(DXVK_WSI_SDL3)\n\n#include \"wsi_platform_sdl3.h\"\n#include \"../../util/util_error.h\"\n#include \"../../util/util_string.h\"\n#include \"../../util/util_win32_compat.h\"\n\n#include <SDL3/SDL_vulkan.h>\n\nnamespace dxvk::wsi {\n\n  Sdl3WsiDriver::Sdl3WsiDriver() {\n    libsdl = LoadLibraryA( // FIXME: Get soname as string from meson\n#if defined(_WIN32)\n        \"SDL3.dll\"\n#elif defined(__APPLE__)\n        \"libSDL3.0.dylib\"\n#else\n        \"libSDL3.so.0\"\n#endif\n      );\n    if (libsdl == nullptr)\n      throw DxvkError(\"SDL3 WSI: Failed to load SDL3 DLL.\");\n\n    #define SDL_PROC(ret, name, params) \\\n      name = reinterpret_cast<pfn_##name>(GetProcAddress(libsdl, #name)); \\\n      if (name == nullptr) { \\\n        FreeLibrary(libsdl); \\\n        libsdl = nullptr; \\\n        throw DxvkError(\"SDL3 WSI: Failed to load \" #name \".\"); \\\n      }\n    #include \"wsi_platform_sdl3_funcs.h\"\n\n    if (!SDL_InitSubSystem(SDL_INIT_VIDEO))\n      throw DxvkError(\"SDL3 WSI: Failed to initialize video subsystem.\"); \\\n  }\n\n  Sdl3WsiDriver::~Sdl3WsiDriver() {\n    SDL_QuitSubSystem(SDL_INIT_VIDEO);\n    FreeLibrary(libsdl);\n  }\n\n  std::vector<const char *> Sdl3WsiDriver::getInstanceExtensions() {\n    if (!SDL_Vulkan_LoadLibrary(nullptr))\n      throw DxvkError(str::format(\"SDL3 WSI: Failed to load Vulkan library: \", SDL_GetError()));\n\n    uint32_t extensionCount = 0;\n    auto extensions = SDL_Vulkan_GetInstanceExtensions(&extensionCount);\n\n    if (!extensions)\n      throw DxvkError(str::format(\"SDL3 WSI: Failed to get instance extensions: \", SDL_GetError()));\n\n    std::vector<const char*> result(extensionCount);\n\n    for (uint32_t i = 0; i < extensionCount; i++)\n      result[i] = extensions[i];\n\n    return result;\n  }\n\n  static bool createSdl3WsiDriver(WsiDriver **driver) {\n    try {\n      *driver = new Sdl3WsiDriver();\n    } catch (const DxvkError& e) {\n      Logger::err(str::format(e.message()));\n      return false;\n    }\n    return true;\n  }\n\n  WsiBootstrap Sdl3WSI = {\n    \"SDL3\",\n    createSdl3WsiDriver\n  };\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/sdl3/wsi_platform_sdl3.h",
    "content": "#pragma once\n\n#include <SDL3/SDL.h>\n\n#include \"../wsi_platform.h\"\n\n#include \"../util/util_bit.h\"\n\nnamespace dxvk::wsi {\n\n  class Sdl3WsiDriver : public WsiDriver {\n  private:\n    HMODULE libsdl;\n    #define SDL_PROC(ret, name, params) \\\n      typedef ret (SDLCALL *pfn_##name) params; \\\n      pfn_##name name;\n    #include \"wsi_platform_sdl3_funcs.h\"\n\n    static void convertMode(const SDL_DisplayMode& mode, WsiMode* pMode) {\n      pMode->width          = uint32_t(mode.w);\n      pMode->height         = uint32_t(mode.h);\n      if (mode.refresh_rate_numerator) {\n        pMode->refreshRate  = WsiRational {\n          uint32_t(mode.refresh_rate_numerator),\n          uint32_t(mode.refresh_rate_denominator) };\n      } else if (mode.refresh_rate > 0.0f) {\n        pMode->refreshRate  = WsiRational {\n          uint32_t(mode.refresh_rate * 1000.0f),\n          1000 };\n      } else {\n        // Platform gave us no refresh rate to work with, assume 60Hz :(\n        pMode->refreshRate  = WsiRational { 60, 1 };\n      }\n      // BPP should always be a power of two\n      // to match Windows behaviour of including padding.\n      pMode->bitsPerPixel   = (uint32_t(-1) >> bit::lzcnt(uint32_t(SDL_BITSPERPIXEL(mode.format) - 1u))) + 1u;\n      pMode->interlaced     = false;\n    }\n  public:\n    Sdl3WsiDriver();\n    ~Sdl3WsiDriver();\n\n    // Platform\n    virtual std::vector<const char *> getInstanceExtensions();\n\n    // Monitor\n    virtual HMONITOR getDefaultMonitor();\n\n    virtual HMONITOR enumMonitors(uint32_t index);\n\n    virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index);\n\n    virtual bool getDisplayName(\n            HMONITOR         hMonitor,\n            WCHAR            (&Name)[32]);\n\n    virtual bool getDesktopCoordinates(\n            HMONITOR         hMonitor,\n            RECT*            pRect);\n\n    virtual bool getDisplayMode(\n            HMONITOR         hMonitor,\n            uint32_t         modeNumber,\n            WsiMode*         pMode);\n\n    virtual bool getCurrentDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode);\n\n    virtual bool getDesktopDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode);\n\n    virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor);\n\n    // Window\n\n    virtual void getWindowSize(\n            HWND      hWindow,\n            uint32_t* pWidth,\n            uint32_t* pWeight);\n\n    virtual void resizeWindow(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            uint32_t         width,\n            uint32_t         weight);\n\n    virtual void saveWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             saveStyle);\n\n    virtual void restoreWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             restoreCoordinates);\n\n    virtual bool setWindowMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n      const WsiMode&         mode);\n\n    virtual bool enterFullscreenMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            [[maybe_unused]]\n            bool             modeSwitch);\n\n    virtual bool leaveFullscreenMode(\n            HWND             hWindow,\n            DxvkWindowState* pState);\n\n    virtual bool restoreDisplayMode();\n\n    virtual HMONITOR getWindowMonitor(HWND hWindow);\n\n    virtual bool isWindow(HWND hWindow);\n\n    virtual bool isMinimized(HWND hWindow);\n\n    virtual bool isOccluded(HWND hWindow);\n\n    virtual void updateFullscreenWindow(\n            HMONITOR hMonitor,\n            HWND     hWindow,\n            bool     forceTopmost);\n\n    virtual VkResult createSurface(\n            HWND                hWindow,\n            PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n            VkInstance          instance,\n            VkSurfaceKHR*       pSurface);\n  };\n  \n}\n"
  },
  {
    "path": "src/wsi/sdl3/wsi_platform_sdl3_funcs.h",
    "content": "SDL_PROC(const char*, SDL_GetError, (void))\nSDL_PROC(SDL_DisplayID*, SDL_GetDisplays, (int*))\nSDL_PROC(bool, SDL_GetDisplayBounds, (SDL_DisplayID, SDL_Rect*))\nSDL_PROC(bool, SDL_GetDisplayUsableBounds, (SDL_DisplayID, SDL_Rect*))\nSDL_PROC(SDL_DisplayMode*, SDL_GetCurrentDisplayMode, (SDL_DisplayID))\nSDL_PROC(SDL_DisplayMode*, SDL_GetDesktopDisplayMode, (SDL_DisplayID))\nSDL_PROC(SDL_DisplayMode**, SDL_GetFullscreenDisplayModes, (SDL_DisplayID, int*))\nSDL_PROC(bool, SDL_GetClosestFullscreenDisplayMode, (SDL_DisplayID, int, int, float, bool, SDL_DisplayMode*))\nSDL_PROC(SDL_DisplayID, SDL_GetDisplayForWindow, (SDL_Window*))\nSDL_PROC(SDL_WindowFlags, SDL_GetWindowFlags, (SDL_Window *))\nSDL_PROC(bool, SDL_GetWindowSizeInPixels, (SDL_Window*, int*, int*))\nSDL_PROC(bool, SDL_SetWindowSize, (SDL_Window*, int, int))\nSDL_PROC(bool, SDL_SetWindowPosition, (SDL_Window*, int, int))\nSDL_PROC(bool, SDL_SetWindowFullscreen, (SDL_Window*, bool))\nSDL_PROC(bool, SDL_SetWindowFullscreenMode, (SDL_Window*, const SDL_DisplayMode*))\nSDL_PROC(char const* const*, SDL_Vulkan_GetInstanceExtensions, (Uint32*))\nSDL_PROC(bool, SDL_Vulkan_CreateSurface, (SDL_Window*, VkInstance, const VkAllocationCallbacks*, VkSurfaceKHR*))\nSDL_PROC(bool, SDL_Vulkan_LoadLibrary, (const char*))\nSDL_PROC(void, SDL_free, (void*))\nSDL_PROC(bool, SDL_InitSubSystem, (SDL_InitFlags))\nSDL_PROC(void, SDL_QuitSubSystem, (SDL_InitFlags))\n#undef SDL_PROC\n"
  },
  {
    "path": "src/wsi/sdl3/wsi_window_sdl3.cpp",
    "content": "#if defined(DXVK_WSI_SDL3)\n\n#include \"../wsi_window.h\"\n\n#include \"native/wsi/native_sdl3.h\"\n#include \"wsi_platform_sdl3.h\"\n\n#include \"../../util/util_string.h\"\n#include \"../../util/log/log.h\"\n\n#include <windows.h>\n#include <SDL3/SDL_vulkan.h>\n\nnamespace dxvk::wsi {\n\n  void Sdl3WsiDriver::getWindowSize(\n        HWND      hWindow,\n        uint32_t* pWidth,\n        uint32_t* pHeight) {\n    SDL_Window* window = fromHwnd(hWindow);\n\n    int w = 0;\n    int h = 0;\n\n    if (!SDL_GetWindowSizeInPixels(window, &w, &h))\n      Logger::err(str::format(\"SDL3 WSI: SDL_GetWindowSizeinPixels: \", SDL_GetError()));\n\n    if (pWidth)\n      *pWidth = uint32_t(w);\n\n    if (pHeight)\n      *pHeight = uint32_t(h);\n  }\n\n\n  void Sdl3WsiDriver::resizeWindow(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          uint32_t         Width,\n          uint32_t         Height) {\n    SDL_Window* window = fromHwnd(hWindow);\n\n    if (!SDL_SetWindowSize(window, int32_t(Width), int32_t(Height)))\n      Logger::err(str::format(\"SDL3 WSI: SDL_SetWindowSize: \", SDL_GetError()));\n  }\n\n\n  void Sdl3WsiDriver::saveWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             saveStyle) {\n  }\n\n\n  void Sdl3WsiDriver::restoreWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             restoreCoordinates) {\n  }\n\n\n  bool Sdl3WsiDriver::setWindowMode(\n          HMONITOR         hMonitor,\n          HWND             hWindow,\n          DxvkWindowState* pState,\n    const WsiMode&         pMode) {\n    SDL_DisplayID displayId = fromHmonitor(hMonitor);\n\n    if (!displayId)\n      return false;\n\n    pState->sdl3.fullscreenMode = pMode;\n    return true;\n  }\n\n\n\n  bool Sdl3WsiDriver::enterFullscreenMode(\n          HMONITOR         hMonitor,\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             ModeSwitch) {\n    SDL_DisplayID displayId = fromHmonitor(hMonitor);\n    SDL_Window* window = fromHwnd(hWindow);\n\n    if (!displayId)\n      return false;\n\n    SDL_Rect bounds = { };\n\n    if (!SDL_GetDisplayUsableBounds(displayId, &bounds)) {\n      Logger::err(str::format(\"SDL3 WSI: enterFullscreenMode: SDL_GetDisplayUsableBounds: \", SDL_GetError()));\n      return false;\n    }\n\n    if (!SDL_SetWindowPosition(window, bounds.x, bounds.y)) {\n      Logger::err(str::format(\"SDL3 WSI: enterFullscreenMode: SDL_SetWindowPosition: \", SDL_GetError()));\n      return false;\n    }\n\n    SDL_DisplayMode closestMode = { };\n\n    if (ModeSwitch) {\n      const auto& mode = pState->sdl3.fullscreenMode;\n\n      if (!SDL_GetClosestFullscreenDisplayMode(displayId, mode.width, mode.height,\n          float(mode.refreshRate.numerator) / float(mode.refreshRate.denominator), true, &closestMode)) {\n        Logger::err(str::format(\"SDL3 WSI: enterFullscreenMode: SDL_GetClosestFullscreenDisplayMode: \", SDL_GetError()));\n        return false;\n      }\n    }\n\n    if (!SDL_SetWindowFullscreenMode(window, ModeSwitch ? &closestMode : nullptr)) {\n      Logger::err(str::format(\"SDL3 WSI: enterFullscreenMode: SDL_SetWindowFullscreenMode: \", SDL_GetError()));\n      return false;\n    }\n\n    if (!SDL_SetWindowFullscreen(window, true)) {\n      Logger::err(str::format(\"SDL3 WSI: enterFullscreenMode: SDL_SetWindowFullscreen: \", SDL_GetError()));\n      return false;\n    }\n\n    return true;\n  }\n\n\n  bool Sdl3WsiDriver::leaveFullscreenMode(\n          HWND             hWindow,\n          DxvkWindowState* pState) {\n    SDL_Window* window = fromHwnd(hWindow);\n\n    if (!SDL_SetWindowFullscreen(window, false)) {\n      Logger::err(str::format(\"SDL3 WSI: leaveFullscreenMode: SDL_SetWindowFullscreen: \", SDL_GetError()));\n      return false;\n    }\n\n    return true;\n  }\n\n\n  bool Sdl3WsiDriver::restoreDisplayMode() {\n    // Don't need to do anything with SDL3 here.\n    return true;\n  }\n\n\n  HMONITOR Sdl3WsiDriver::getWindowMonitor(HWND hWindow) {\n    return toHmonitor(SDL_GetDisplayForWindow(fromHwnd(hWindow)));\n  }\n\n\n  bool Sdl3WsiDriver::isWindow(HWND hWindow) {\n    SDL_Window* window = fromHwnd(hWindow);\n    return window != nullptr;\n  }\n\n\n  bool Sdl3WsiDriver::isMinimized(HWND hWindow) {\n    SDL_Window* window = fromHwnd(hWindow);\n    return (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0;\n  }\n\n\n  bool Sdl3WsiDriver::isOccluded(HWND hWindow) {\n    return false;\n  }\n\n\n  void Sdl3WsiDriver::updateFullscreenWindow(\n          HMONITOR hMonitor,\n          HWND     hWindow,\n          bool     forceTopmost) {\n    // Don't need to do anything with SDL3 here.\n  }\n\n\n  VkResult Sdl3WsiDriver::createSurface(\n          HWND                      hWindow,\n          PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n          VkInstance                instance,\n          VkSurfaceKHR*             pSurface) {\n    SDL_Window* window = fromHwnd(hWindow);\n\n    return SDL_Vulkan_CreateSurface(window, instance, nullptr, pSurface)\n      ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY;\n  }\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/win32/wsi_monitor_win32.cpp",
    "content": "#if defined(DXVK_WSI_WIN32)\n\n#include \"wsi_platform_win32.h\"\n\n#include \"../../util/util_string.h\"\n#include \"../../util/log/log.h\"\n#include \"../../util/util_string.h\"\n\n#include <cstring>\n#include <set>\n\n#include <setupapi.h>\n#include <ntddvdeo.h>\n#include <cfgmgr32.h>\n\nnamespace dxvk::wsi {\n\n  HMONITOR Win32WsiDriver::getDefaultMonitor() {\n    return ::MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);\n  }\n\n  struct MonitorEnumInfo {\n    std::set<std::wstring> *gdiDeviceNames;\n    UINT      iMonitorId;\n    HMONITOR  oMonitor;\n  };\n\n  static BOOL CALLBACK MonitorEnumProc(\n          HMONITOR                  hmon,\n          HDC                       hdc,\n          LPRECT                    rect,\n          LPARAM                    lp) {\n    auto data = reinterpret_cast<MonitorEnumInfo*>(lp);\n\n    if (data->gdiDeviceNames)\n    {\n      MONITORINFOEXW monitorInfo;\n\n      monitorInfo.cbSize = sizeof(monitorInfo);\n      GetMonitorInfoW(hmon, (MONITORINFO *)&monitorInfo);\n      if (data->gdiDeviceNames->find(monitorInfo.szDevice) == data->gdiDeviceNames->end())\n        return TRUE;\n    }\n    if (data->iMonitorId--)\n      return TRUE;\n    data->oMonitor = hmon;\n    return FALSE;\n  }\n\n  HMONITOR Win32WsiDriver::enumMonitors(uint32_t index) {\n    MonitorEnumInfo info;\n    info.iMonitorId = index;\n    info.oMonitor   = nullptr;\n    info.gdiDeviceNames = nullptr;\n\n    ::EnumDisplayMonitors(\n      nullptr, nullptr, &MonitorEnumProc,\n      reinterpret_cast<LPARAM>(&info));\n\n    return info.oMonitor;\n  }\n\n  HMONITOR Win32WsiDriver::enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) {\n    if (!numLUIDs)\n      return enumMonitors(index);\n\n    std::vector<DISPLAYCONFIG_PATH_INFO> paths;\n    std::vector<DISPLAYCONFIG_MODE_INFO> modes;\n    std::set<std::pair<uint32_t, uint32_t>> sources;\n    std::set<std::wstring> gdiDeviceNames;\n    UINT32 pathCount = 0;\n    UINT32 modeCount = 0;\n    LONG result;\n\n    do {\n      if ((result = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount))) {\n        Logger::err(str::format(\"GetDisplayConfigBufferSizes failed, result \", result));\n        return enumMonitors(index);\n      }\n\n      paths.resize(pathCount);\n      modes.resize(modeCount);\n\n      result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS,\n        &pathCount, paths.data(), &modeCount, modes.data(), nullptr);\n    } while (result == ERROR_INSUFFICIENT_BUFFER);\n\n    if (result) {\n      Logger::err(str::format(\"QueryDisplayConfig failed, result \", result));\n      return enumMonitors(index);\n    }\n\n    paths.resize(pathCount);\n    modes.resize(modeCount);\n\n    MonitorEnumInfo info;\n    info.iMonitorId = index;\n    info.oMonitor = nullptr;\n    info.gdiDeviceNames = &gdiDeviceNames;\n\n    for (const auto &path : paths) {\n      uint32_t i;\n\n      for (i = 0; i < numLUIDs; ++i) {\n        if (!std::memcmp(&path.sourceInfo.adapterId, adapterLUID[i], sizeof(path.sourceInfo.adapterId)))\n          break;\n      }\n\n      if (i == numLUIDs)\n        continue;\n\n      /* Mirrored displays appear as multiple paths with the same\n       * GDI device name, that comes as single dxgi output. */\n      if (!sources.insert(std::pair<uint32_t, uint32_t>(i, path.sourceInfo.id)).second)\n        continue;\n\n      DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName = { };\n      deviceName.header.adapterId = path.sourceInfo.adapterId;\n      deviceName.header.id = path.sourceInfo.id;\n      deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;\n      deviceName.header.size = sizeof(deviceName);\n\n      if ((result = DisplayConfigGetDeviceInfo(&deviceName.header))) {\n        Logger::err(str::format(\"DisplayConfigGetDeviceInfo failed, result \", result));\n        return enumMonitors(index);\n      }\n\n      gdiDeviceNames.insert(deviceName.viewGdiDeviceName);\n    }\n    ::EnumDisplayMonitors(\n      nullptr, nullptr, &MonitorEnumProc,\n      reinterpret_cast<LPARAM>(&info));\n    return info.oMonitor;\n  }\n\n\n  bool Win32WsiDriver::getDisplayName(\n          HMONITOR         hMonitor,\n          WCHAR            (&Name)[32]) {\n    // Query monitor info to get the device name\n    ::MONITORINFOEXW monInfo;\n    monInfo.cbSize = sizeof(monInfo);\n\n    if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {\n      Logger::err(\"Win32 WSI: getDisplayName: Failed to query monitor info\");\n      return false;\n    }\n\n    std::memcpy(Name, monInfo.szDevice, sizeof(Name));\n\n    return true;\n  }\n\n\n  bool Win32WsiDriver::getDesktopCoordinates(\n          HMONITOR         hMonitor,\n          RECT*            pRect) {\n    ::MONITORINFOEXW monInfo;\n    monInfo.cbSize = sizeof(monInfo);\n\n    if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {\n      Logger::err(\"Win32 WSI: getDisplayName: Failed to query monitor info\");\n      return false;\n    }\n\n    *pRect = monInfo.rcMonitor;\n\n    return true;\n  }\n\n  static inline void convertMode(const DEVMODEW& mode, WsiMode* pMode) {\n    pMode->width         = mode.dmPelsWidth;\n    pMode->height        = mode.dmPelsHeight;\n    pMode->refreshRate   = WsiRational{ mode.dmDisplayFrequency * 1000, 1000 }; \n    pMode->bitsPerPixel  = mode.dmBitsPerPel;\n    pMode->interlaced    = mode.dmDisplayFlags & DM_INTERLACED;\n  }\n\n\n  static inline bool retrieveDisplayMode(\n          HMONITOR         hMonitor,\n          DWORD            modeNumber,\n          WsiMode*         pMode) {\n    // Query monitor info to get the device name\n    ::MONITORINFOEXW monInfo;\n    monInfo.cbSize = sizeof(monInfo);\n\n    if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {\n      Logger::err(\"Win32 WSI: retrieveDisplayMode: Failed to query monitor info\");\n      return false;\n    }\n\n    DEVMODEW devMode = { };\n    devMode.dmSize = sizeof(devMode);\n    \n    if (!::EnumDisplaySettingsW(monInfo.szDevice, modeNumber, &devMode))\n      return false;\n\n    convertMode(devMode, pMode);\n\n    return true;\n  }\n\n\n  bool Win32WsiDriver::getDisplayMode(\n          HMONITOR         hMonitor,\n          uint32_t         modeNumber,\n          WsiMode*         pMode) {\n    return retrieveDisplayMode(hMonitor, modeNumber, pMode);\n  }\n\n\n  bool Win32WsiDriver::getCurrentDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode) {\n    return retrieveDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, pMode);\n  }\n\n\n  bool Win32WsiDriver::getDesktopDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode) {\n    return retrieveDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, pMode);\n  }\n\n  static std::wstring getMonitorDevicePath(HMONITOR hMonitor) {\n    // Get the device name of the monitor.\n    MONITORINFOEXW monInfo;\n    monInfo.cbSize = sizeof(monInfo);\n    if (!::GetMonitorInfoW(hMonitor, &monInfo)) {\n      Logger::err(\"getMonitorDevicePath: Failed to get monitor info.\");\n      return {};\n    }\n\n    // Try and find the monitor device path that matches\n    // the name of our HMONITOR from the monitor info.\n    LONG result = ERROR_SUCCESS;\n    std::vector<DISPLAYCONFIG_PATH_INFO> paths;\n    std::vector<DISPLAYCONFIG_MODE_INFO> modes;\n    do {\n      uint32_t pathCount = 0, modeCount = 0;\n      if ((result = ::GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount)) != ERROR_SUCCESS) {\n        Logger::err(str::format(\"getMonitorDevicePath: GetDisplayConfigBufferSizes failed. ret: \", result, \" LastError: \", GetLastError()));\n        return {};\n      }\n      paths.resize(pathCount);\n      modes.resize(modeCount);\n      result = ::QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.data(), &modeCount, modes.data(), nullptr);\n    } while (result == ERROR_INSUFFICIENT_BUFFER);\n\n    if (result != ERROR_SUCCESS) {\n      Logger::err(str::format(\"getMonitorDevicePath: QueryDisplayConfig failed. ret: \", result, \" LastError: \", GetLastError()));\n      return {};\n    }\n\n    // Link a source name -> target name\n    for (const auto& path : paths) {\n      DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName;\n      sourceName.header.type      = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;\n      sourceName.header.size      = sizeof(sourceName);\n      sourceName.header.adapterId = path.sourceInfo.adapterId;\n      sourceName.header.id        = path.sourceInfo.id;\n      if ((result = ::DisplayConfigGetDeviceInfo(&sourceName.header)) != ERROR_SUCCESS) {\n        Logger::err(str::format(\"getMonitorDevicePath: DisplayConfigGetDeviceInfo with DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME failed. ret: \", result, \" LastError: \", GetLastError()));\n        continue;\n      }\n\n      DISPLAYCONFIG_TARGET_DEVICE_NAME targetName;\n      targetName.header.type      = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;\n      targetName.header.size      = sizeof(targetName);\n      targetName.header.adapterId = path.targetInfo.adapterId;\n      targetName.header.id        = path.targetInfo.id;\n      if ((result = ::DisplayConfigGetDeviceInfo(&targetName.header)) != ERROR_SUCCESS) {\n        Logger::err(str::format(\"getMonitorDevicePath: DisplayConfigGetDeviceInfo with DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME failed. ret: \", result, \" LastError: \", GetLastError()));\n        continue;\n      }\n\n      // Does the source match the GDI device we are looking for?\n      // If so, return the target back.\n      if (!wcscmp(sourceName.viewGdiDeviceName, monInfo.szDevice))\n        return targetName.monitorDevicePath;\n    }\n\n    Logger::err(\"getMonitorDevicePath: Failed to find a link from source -> target.\");\n    return {};\n  }\n\n  static WsiEdidData readMonitorEdidFromKey(HKEY deviceRegKey) {\n    DWORD edidSize = 0;\n    if (::RegQueryValueExW(deviceRegKey, L\"EDID\", nullptr, nullptr, nullptr, &edidSize) != ERROR_SUCCESS) {\n      Logger::err(\"readMonitorEdidFromKey: Failed to get EDID reg key size\");\n      return {};\n    }\n\n    WsiEdidData edidData(edidSize);\n    if (::RegQueryValueExW(deviceRegKey, L\"EDID\", nullptr, nullptr, edidData.data(), &edidSize) != ERROR_SUCCESS) {\n      Logger::err(\"readMonitorEdidFromKey: Failed to get EDID reg key data\");\n      return {};\n    }\n\n    return edidData;\n  }\n\n  struct DxvkDeviceInterfaceDetail {\n    // SP_DEVICE_INTERFACE_DETAIL_DATA_W contains an array called\n    // \"ANYSIZE_ARRAY\" which is just 1 wchar_t in size.\n    // Incredible, safe, and smart API design.\n    // Allocate some chars after so it can fill these in.\n    SP_DEVICE_INTERFACE_DETAIL_DATA_W base;\n    wchar_t extraChars[MAX_DEVICE_ID_LEN];\n  };\n\n  WsiEdidData Win32WsiDriver::getMonitorEdid(HMONITOR hMonitor) {\n    static constexpr GUID GUID_DEVINTERFACE_MONITOR = { 0xe6f07b5f, 0xee97, 0x4a90, 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 };\n\n    std::wstring monitorDevicePath = getMonitorDevicePath(hMonitor);\n    if (monitorDevicePath.empty()) {\n      Logger::err(\"getMonitorEdid: Failed to get monitor device path.\");\n      return {};\n    }\n\n    const HDEVINFO devInfo = ::SetupDiGetClassDevsW(&GUID_DEVINTERFACE_MONITOR, nullptr, nullptr, DIGCF_DEVICEINTERFACE);\n\n    SP_DEVICE_INTERFACE_DATA interfaceData;\n    memset(&interfaceData, 0, sizeof(interfaceData));\n    interfaceData.cbSize = sizeof(interfaceData);\n\n    for (DWORD monitorIdx = 0; ::SetupDiEnumDeviceInterfaces(devInfo, nullptr, &GUID_DEVINTERFACE_MONITOR, monitorIdx, &interfaceData); monitorIdx++) {\n      DxvkDeviceInterfaceDetail detailData;\n      // Josh: I'm taking no chances here. I don't trust this API at all.\n      memset(&detailData, 0, sizeof(detailData));\n      detailData.base.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);\n\n      SP_DEVINFO_DATA devInfoData;\n      memset(&devInfoData, 0, sizeof(devInfoData));\n      devInfoData.cbSize = sizeof(devInfoData);\n\n      if (!::SetupDiGetDeviceInterfaceDetailW(devInfo, &interfaceData, &detailData.base, sizeof(detailData), nullptr, &devInfoData))\n        continue;\n\n      // Check that this monitor matches the same one we are looking for.\n      // Note: For some reason the casing mismatches here, because this\n      // is a well-designed API.\n      // If it isn't what we are looking for, move along.\n      if (_wcsicmp(monitorDevicePath.c_str(), detailData.base.DevicePath) != 0)\n        continue;\n\n      HKEY deviceRegKey = ::SetupDiOpenDevRegKey(devInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);\n      if (deviceRegKey == INVALID_HANDLE_VALUE) {\n        Logger::err(\"getMonitorEdid: Failed to open monitor device registry key.\");\n        return {};\n      }\n\n      auto edidData = readMonitorEdidFromKey(deviceRegKey);\n\n      ::RegCloseKey(deviceRegKey);\n\n      return edidData;\n    }\n\n    Logger::err(\"getMonitorEdid: Failed to find device interface for monitor using setupapi.\");\n    return {};\n  }\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/win32/wsi_platform_win32.cpp",
    "content": "#if defined(DXVK_WSI_WIN32)\n\n#include \"wsi_platform_win32.h\"\n\nnamespace dxvk::wsi {\n\n  std::vector<const char *> Win32WsiDriver::getInstanceExtensions() {\n    return { VK_KHR_WIN32_SURFACE_EXTENSION_NAME };\n  }\n\n  static bool createWin32WsiDriver(WsiDriver **driver) {\n    *driver = new Win32WsiDriver();\n    return true;\n  }\n\n  WsiBootstrap Win32WSI = {\n    \"Win32\",\n    createWin32WsiDriver\n  };\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/win32/wsi_platform_win32.h",
    "content": "#pragma once\n\n#include <windows.h>\n\n#include \"../wsi_platform.h\"\n\nnamespace dxvk::wsi {\n\n  class Win32WsiDriver : public WsiDriver {\n  private:\n    uint64_t m_lastForegroundTimestamp = 0;\n\n  public:\n    // Platform\n    virtual std::vector<const char *> getInstanceExtensions();\n\n    // Monitor\n    virtual HMONITOR getDefaultMonitor();\n\n    virtual HMONITOR enumMonitors(uint32_t index);\n\n    virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index);\n\n    virtual bool getDisplayName(\n            HMONITOR         hMonitor,\n            WCHAR            (&Name)[32]);\n\n    virtual bool getDesktopCoordinates(\n            HMONITOR         hMonitor,\n            RECT*            pRect);\n\n    virtual bool getDisplayMode(\n            HMONITOR         hMonitor,\n            uint32_t         modeNumber,\n            WsiMode*         pMode);\n\n    virtual bool getCurrentDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode);\n\n    virtual bool getDesktopDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode);\n\n    virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor);\n\n    // Window\n\n    virtual void getWindowSize(\n            HWND      hWindow,\n            uint32_t* pWidth,\n            uint32_t* pWeight);\n\n    virtual void resizeWindow(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            uint32_t         width,\n            uint32_t         weight);\n\n    virtual void saveWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             saveStyle);\n\n    virtual void restoreWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             restoreCoordinates);\n\n    virtual bool setWindowMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n      const WsiMode&         mode);\n\n    virtual bool enterFullscreenMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            [[maybe_unused]]\n            bool             modeSwitch);\n\n    virtual bool leaveFullscreenMode(\n            HWND             hWindow,\n            DxvkWindowState* pState);\n\n    virtual bool restoreDisplayMode();\n\n    virtual HMONITOR getWindowMonitor(HWND hWindow);\n\n    virtual bool isWindow(HWND hWindow);\n\n    virtual bool isMinimized(HWND hWindow);\n\n    virtual bool isOccluded(HWND hWindow);\n\n    virtual void updateFullscreenWindow(\n            HMONITOR hMonitor,\n            HWND     hWindow,\n            bool     forceTopmost);\n\n    virtual VkResult createSurface(\n            HWND                hWindow,\n            PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n            VkInstance          instance,\n            VkSurfaceKHR*       pSurface);\n  };\n\n}\n"
  },
  {
    "path": "src/wsi/win32/wsi_window_win32.cpp",
    "content": "#if defined(DXVK_WSI_WIN32)\n\n#include \"wsi_platform_win32.h\"\n\n#include \"../../util/util_string.h\"\n#include \"../../util/util_gdi.h\"\n#include \"../../util/log/log.h\"\n\nnamespace dxvk::wsi {\n\n  static bool getMonitorDisplayMode(\n          HMONITOR                hMonitor,\n          DWORD                   modeNum,\n          DEVMODEW*               pMode) {\n    ::MONITORINFOEXW monInfo;\n    monInfo.cbSize = sizeof(monInfo);\n\n    if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {\n      Logger::err(\"Failed to query monitor info\");\n      return false;\n    }\n\n    return ::EnumDisplaySettingsW(monInfo.szDevice, modeNum, pMode);\n  }\n\n\n  static bool setMonitorDisplayMode(\n          HMONITOR                hMonitor,\n          DEVMODEW*               pMode) {\n    ::MONITORINFOEXW monInfo;\n    monInfo.cbSize = sizeof(monInfo);\n\n    if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {\n      Logger::err(\"Failed to query monitor info\");\n      return false;\n    }\n\n    Logger::info(str::format(\"Setting display mode: \",\n      pMode->dmPelsWidth, \"x\", pMode->dmPelsHeight, \"@\",\n      pMode->dmDisplayFrequency));\n\n    DEVMODEW curMode = { };\n    curMode.dmSize = sizeof(curMode);\n\n    if (getMonitorDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, &curMode)) {\n      bool eq = curMode.dmPelsWidth  == pMode->dmPelsWidth\n             && curMode.dmPelsHeight == pMode->dmPelsHeight\n             && curMode.dmBitsPerPel == pMode->dmBitsPerPel;\n\n      if (pMode->dmFields & DM_DISPLAYFREQUENCY)\n        eq &= curMode.dmDisplayFrequency == pMode->dmDisplayFrequency;\n      if (pMode->dmFields & DM_DISPLAYFLAGS)\n        eq &= curMode.dmDisplayFlags == pMode->dmDisplayFlags;\n      if (pMode->dmFields & DM_DISPLAYORIENTATION)\n        eq &= curMode.dmDisplayOrientation == pMode->dmDisplayOrientation;\n      if (pMode->dmFields & DM_POSITION)\n        eq &= curMode.dmPosition.x == pMode->dmPosition.x && curMode.dmPosition.y == pMode->dmPosition.y;\n\n      if (eq)\n        return true;\n    }\n\n    LONG status = ::ChangeDisplaySettingsExW(monInfo.szDevice,\n      pMode, nullptr, CDS_FULLSCREEN, nullptr);\n\n    if (status != DISP_CHANGE_SUCCESSFUL) {\n      pMode->dmFields &= ~DM_DISPLAYFREQUENCY;\n\n      status = ::ChangeDisplaySettingsExW(monInfo.szDevice,\n        pMode, nullptr, CDS_FULLSCREEN, nullptr);\n    }\n\n    return status == DISP_CHANGE_SUCCESSFUL;\n  }\n\n\n  static BOOL CALLBACK restoreDisplayModeCallback(\n          HMONITOR                hMonitor,\n          HDC                     hDC,\n          LPRECT                  pRect,\n          LPARAM                  pUserdata) {\n    auto success = reinterpret_cast<bool*>(pUserdata);\n\n    DEVMODEW devMode = { };\n    devMode.dmSize = sizeof(devMode);\n\n    if (!getMonitorDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, &devMode)) {\n      *success = false;\n      return false;\n    }\n\n    Logger::info(str::format(\"Restoring display mode: \",\n      devMode.dmPelsWidth, \"x\", devMode.dmPelsHeight, \"@\",\n      devMode.dmDisplayFrequency));\n\n    if (!setMonitorDisplayMode(hMonitor, &devMode)) {\n      *success = false;\n      return false;\n    }\n\n    return true;\n  }\n\n\n  void Win32WsiDriver::getWindowSize(\n        HWND      hWindow,\n        uint32_t* pWidth,\n        uint32_t* pHeight) {\n    RECT rect = { };\n    ::GetClientRect(hWindow, &rect);\n    \n    if (pWidth)\n      *pWidth = rect.right - rect.left;\n    \n    if (pHeight)\n      *pHeight = rect.bottom - rect.top;\n  }\n\n\n  void Win32WsiDriver::resizeWindow(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          uint32_t         width,\n          uint32_t         height) {\n    // Adjust window position and size\n    RECT newRect = { 0, 0, 0, 0 };\n    RECT oldRect = { 0, 0, 0, 0 };\n    \n    ::GetWindowRect(hWindow, &oldRect);\n    ::SetRect(&newRect, 0, 0, width, height);\n    ::AdjustWindowRectEx(&newRect,\n      ::GetWindowLongW(hWindow, GWL_STYLE), FALSE,\n      ::GetWindowLongW(hWindow, GWL_EXSTYLE));\n    ::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);\n    ::OffsetRect(&newRect, oldRect.left, oldRect.top);    \n    ::MoveWindow(hWindow, newRect.left, newRect.top,\n        newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);\n  }\n\n\n  void Win32WsiDriver::saveWindowState(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             saveStyle) {\n    if (saveStyle) {\n      LONG style   = ::GetWindowLongW(hWindow, GWL_STYLE);\n      LONG exstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE);\n\n      pState->win.style = style;\n      pState->win.exstyle = exstyle;\n    }\n\n    ::GetWindowRect(hWindow, &pState->win.rect);\n  }\n\n\n  void Win32WsiDriver::restoreWindowState(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             restoreCoordinates) {\n    UINT flags = SWP_FRAMECHANGED | SWP_NOACTIVATE;\n    const RECT rect = pState->win.rect;\n\n    if (!restoreCoordinates)\n      flags |= SWP_NOSIZE | SWP_NOMOVE;\n\n    ::SetWindowPos(hWindow, (pState->win.exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST,\n      rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags);\n  }\n\n\n  bool Win32WsiDriver::setWindowMode(\n          HMONITOR                hMonitor,\n          HWND                    hWindow,\n          DxvkWindowState*        pState,\n    const WsiMode&                mode) {\n    ::MONITORINFOEXW monInfo;\n    monInfo.cbSize = sizeof(monInfo);\n\n    if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {\n      Logger::err(\"Win32 WSI: setWindowMode: Failed to query monitor info\");\n      return false;\n    }\n    \n    DEVMODEW devMode = { };\n    devMode.dmSize       = sizeof(devMode);\n    devMode.dmFields     = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;\n    devMode.dmPelsWidth  = mode.width;\n    devMode.dmPelsHeight = mode.height;\n    devMode.dmBitsPerPel = mode.bitsPerPixel;\n    \n    if (mode.refreshRate.numerator != 0)  {\n      devMode.dmFields |= DM_DISPLAYFREQUENCY;\n      devMode.dmDisplayFrequency = mode.refreshRate.numerator\n                                 / mode.refreshRate.denominator;\n    }\n    \n    Logger::info(str::format(\"Setting display mode: \",\n      devMode.dmPelsWidth, \"x\", devMode.dmPelsHeight, \"@\",\n      devMode.dmDisplayFrequency));\n    \n    return setMonitorDisplayMode(hMonitor, &devMode);\n  }\n\n\n  bool Win32WsiDriver::enterFullscreenMode(\n          HMONITOR         hMonitor,\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          [[maybe_unused]]\n          bool             modeSwitch) {\n    RECT rect = { };\n    getDesktopCoordinates(hMonitor, &rect);\n\n    D3DKMT_ESCAPE escape = { };\n    escape.Type = D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE;\n    escape.pPrivateDriverData = &rect;\n    escape.PrivateDriverDataSize = sizeof(rect);\n    escape.hContext = HandleToUlong(hWindow);\n    D3DKMTEscape(&escape);\n\n    // Find a display mode that matches what we need\n    ::GetWindowRect(hWindow, &pState->win.rect);\n\n    // Change the window flags to remove the decoration etc.\n    LONG style   = ::GetWindowLongW(hWindow, GWL_STYLE);\n    LONG exstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE);\n    \n    style   &= ~WS_OVERLAPPEDWINDOW;\n    exstyle &= ~WS_EX_OVERLAPPEDWINDOW;\n    \n    ::SetWindowLongW(hWindow, GWL_STYLE, style);\n    ::SetWindowLongW(hWindow, GWL_EXSTYLE, exstyle);\n\n    ::SetWindowPos(hWindow, HWND_TOPMOST,\n      rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,\n      SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);\n\n    m_lastForegroundTimestamp = 0;\n    return true;\n  }\n\n\n  bool Win32WsiDriver::leaveFullscreenMode(\n          HWND             hWindow,\n          DxvkWindowState* pState) {\n    // Only restore the window style if the application hasn't\n    // changed them. This is in line with what native DXGI does.\n    LONG curStyle   = ::GetWindowLongW(hWindow, GWL_STYLE)   & ~WS_VISIBLE;\n    LONG curExstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE) & ~WS_EX_TOPMOST;\n\n    if (curStyle   == (pState->win.style   & ~(WS_VISIBLE    | WS_OVERLAPPEDWINDOW))\n     && curExstyle == (pState->win.exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) {\n      ::SetWindowLongW(hWindow, GWL_STYLE,   pState->win.style);\n      ::SetWindowLongW(hWindow, GWL_EXSTYLE, pState->win.exstyle);\n    }\n\n    RECT empty = { };\n    D3DKMT_ESCAPE escape = { };\n    escape.Type = D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE;\n    escape.pPrivateDriverData = &empty;\n    escape.PrivateDriverDataSize = sizeof(empty);\n    escape.hContext = HandleToUlong(hWindow);\n    D3DKMTEscape(&escape);\n\n    return true;\n  }\n\n\n  bool Win32WsiDriver::restoreDisplayMode() {\n    bool success = true;\n    bool result = ::EnumDisplayMonitors(nullptr, nullptr,\n      &restoreDisplayModeCallback,\n      reinterpret_cast<LPARAM>(&success));\n\n    return result && success;\n  }\n\n\n  HMONITOR Win32WsiDriver::getWindowMonitor(HWND hWindow) {\n    RECT windowRect = { 0, 0, 0, 0 };\n    ::GetWindowRect(hWindow, &windowRect);\n    \n    HMONITOR monitor = ::MonitorFromPoint(\n      { (windowRect.left + windowRect.right) / 2,\n        (windowRect.top + windowRect.bottom) / 2 },\n      MONITOR_DEFAULTTOPRIMARY);\n\n    return monitor;\n  }\n\n\n  bool Win32WsiDriver::isWindow(HWND hWindow) {\n    return ::IsWindow(hWindow);\n  }\n\n\n  bool Win32WsiDriver::isMinimized(HWND hWindow) {\n    return (::GetWindowLongW(hWindow, GWL_STYLE) & WS_MINIMIZE) != 0;\n  }\n\n\n  bool Win32WsiDriver::isOccluded(HWND hWindow) {\n    if (::GetForegroundWindow() == hWindow)\n    {\n      m_lastForegroundTimestamp = GetTickCount64();\n      return false;\n    }\n    return m_lastForegroundTimestamp && GetTickCount64() - m_lastForegroundTimestamp > 100;\n  }\n\n\n  void Win32WsiDriver::updateFullscreenWindow(\n          HMONITOR hMonitor,\n          HWND     hWindow,\n          bool     forceTopmost) {\n    RECT bounds = { };\n    wsi::getDesktopCoordinates(hMonitor, &bounds);\n\n    D3DKMT_ESCAPE escape = { };\n    escape.Type = D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE;\n    escape.pPrivateDriverData = &bounds;\n    escape.PrivateDriverDataSize = sizeof(bounds);\n    escape.hContext = HandleToUlong(hWindow);\n    D3DKMTEscape(&escape);\n\n    // In D3D9, changing display modes re-forces the window\n    // to become top most, whereas in DXGI, it does not.\n    if (forceTopmost) {\n      ::SetWindowPos(hWindow, HWND_TOPMOST,\n        bounds.left, bounds.top,\n        bounds.right - bounds.left, bounds.bottom - bounds.top,\n        SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);\n    } else {\n      ::MoveWindow(hWindow, bounds.left, bounds.top,\n        bounds.right - bounds.left, bounds.bottom - bounds.top, TRUE);\n    }\n  }\n\n\n  VkResult Win32WsiDriver::createSurface(\n          HWND                hWindow,\n          PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n          VkInstance          instance,\n          VkSurfaceKHR*       pSurface) {\n    HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(\n      GetWindowLongPtr(hWindow, GWLP_HINSTANCE));\n\n    auto pfnVkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(\n      pfnVkGetInstanceProcAddr(instance, \"vkCreateWin32SurfaceKHR\"));\n\n    if (!pfnVkCreateWin32SurfaceKHR)\n      return VK_ERROR_FEATURE_NOT_PRESENT;\n\n    VkWin32SurfaceCreateInfoKHR info = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };\n    info.hinstance  = hInstance;\n    info.hwnd       = hWindow;\n    \n    return (*pfnVkCreateWin32SurfaceKHR)(instance, &info, nullptr, pSurface);\n  }\n\n}\n\n#endif\n"
  },
  {
    "path": "src/wsi/wsi_edid.cpp",
    "content": "extern \"C\" {\n#include \"libdisplay-info/info.h\"\n#include \"libdisplay-info/edid.h\"\n#include \"libdisplay-info/cta.h\"\n}\n\n#include \"wsi_edid.h\"\n\n#include \"../util/util_string.h\"\n#include \"../util/log/log.h\"\n\nnamespace dxvk::wsi {\n\n  std::optional<WsiDisplayMetadata> parseColorimetryInfo(\n    const WsiEdidData&        edidData) {\n    WsiDisplayMetadata metadata = {};\n\n    di_info* info = di_info_parse_edid(edidData.data(), edidData.size());\n\n    if (!info) {\n      Logger::err(str::format(\"wsi: parseColorimetryInfo: Failed to get parse edid.\"));\n      return std::nullopt;\n    }\n\n    const di_edid* edid = di_info_get_edid(info);\n\n    const di_edid_chromaticity_coords* chroma = di_edid_get_chromaticity_coords(edid);\n    const di_cta_hdr_static_metadata_block* hdr_static_metadata = nullptr;\n    const di_cta_colorimetry_block* colorimetry = nullptr;\n\n    const di_edid_cta* cta = nullptr;\n\n    for (auto exts = di_edid_get_extensions(edid); *exts != nullptr; exts++) {\n      if ((cta = di_edid_ext_get_cta(*exts)))\n        break;\n    }\n\n    if (cta) {\n      for (auto blocks = di_edid_cta_get_data_blocks(cta); *blocks != nullptr; blocks++) {\n        if (!hdr_static_metadata && (hdr_static_metadata = di_cta_data_block_get_hdr_static_metadata(*blocks)))\n          continue;\n        if (!colorimetry && (colorimetry = di_cta_data_block_get_colorimetry(*blocks)))\n          continue;\n      }\n    }\n\n    if (chroma) {\n      metadata.redPrimary[0]   = chroma->red_x;\n      metadata.redPrimary[1]   = chroma->red_y;\n      metadata.greenPrimary[0] = chroma->green_x;\n      metadata.greenPrimary[1] = chroma->green_y;\n      metadata.bluePrimary[0]  = chroma->blue_x;\n      metadata.bluePrimary[1]  = chroma->blue_y;\n      metadata.whitePoint[0]   = chroma->white_x;\n      metadata.whitePoint[1]   = chroma->white_y;\n    }\n\n    if (hdr_static_metadata) {\n      metadata.maxFullFrameLuminance = hdr_static_metadata->desired_content_max_frame_avg_luminance;\n      metadata.minLuminance          = hdr_static_metadata->desired_content_min_luminance;\n      metadata.maxLuminance          = hdr_static_metadata->desired_content_max_luminance;\n    }\n\n    metadata.supportsST2084 = chroma && colorimetry && colorimetry->bt2020_rgb &&\n      hdr_static_metadata && hdr_static_metadata->eotfs && hdr_static_metadata->eotfs->pq;\n\n    di_info_destroy(info);\n    return metadata;\n  }\n\n}"
  },
  {
    "path": "src/wsi/wsi_edid.h",
    "content": "#pragma once\n\n#include <vector>\n#include <cstdint>\n#include <optional>\n\nnamespace dxvk::wsi {\n\n  /**\n   * \\brief Edid blob\n   */\n  using WsiEdidData = std::vector<uint8_t>;\n\n  /**\n   * \\brief Display colorimetry info\n   */\n  struct WsiDisplayMetadata {\n    bool supportsST2084;\n\n    float redPrimary[2];\n    float greenPrimary[2];\n    float bluePrimary[2];\n    float whitePoint[2];\n    float minLuminance;\n    float maxLuminance;\n    float maxFullFrameLuminance;\n  };\n\n  /**\n    * \\brief Parse colorimetry info from the EDID\n    *\n    * \\param [in] edidData The edid blob\n    * \\returns The display metadata + colorimetry info\n    */\n  std::optional<WsiDisplayMetadata> parseColorimetryInfo(\n    const WsiEdidData&        edidData);\n\n  inline void NormalizeDisplayMetadata(bool isHDR, wsi::WsiDisplayMetadata& metadata) {\n    // Use some dummy info when we have no hdr static metadata for the\n    // display or we were unable to obtain an EDID.\n    //\n    // These dummy values are the same as what Windows DXGI will output\n    // for panels with broken EDIDs such as LG OLEDs displays which\n    // have an entirely zeroed out luminance section in the hdr static\n    // metadata block.\n    //\n    // (Spec has 0 as 'undefined', which isn't really useful for an app\n    // to tonemap against.)\n    if (metadata.minLuminance == 0.0f)\n      metadata.minLuminance = isHDR ? 0.01f : 0.5f;\n\n    if (metadata.maxLuminance == 0.0f)\n      metadata.maxLuminance = isHDR ? 1499.0f : 270.0f;\n\n    if (metadata.maxFullFrameLuminance == 0.0f)\n      metadata.maxFullFrameLuminance = isHDR ? 799.0f : 270.0f;\n\n    // If we have no RedPrimary/GreenPrimary/BluePrimary/WhitePoint due to\n    // the lack of a monitor exposing the chroma block or the lack of an EDID,\n    // simply just fall back to Rec.709 or P3 values depending on the default\n    // ColorSpace we started in.\n    // (Don't change based on punting, as this should be static for a display.)\n    if (metadata.redPrimary[0]   == 0.0f && metadata.redPrimary[1]   == 0.0f\n     && metadata.greenPrimary[0] == 0.0f && metadata.greenPrimary[1] == 0.0f\n     && metadata.bluePrimary[0]  == 0.0f && metadata.bluePrimary[1]  == 0.0f\n     && metadata.whitePoint[0]   == 0.0f && metadata.whitePoint[1]   == 0.0f) {\n      if (!isHDR) {\n        // sRGB ColorSpace -> Rec.709 Primaries\n        metadata.redPrimary[0]   = 0.640f;\n        metadata.redPrimary[1]   = 0.330f;\n        metadata.greenPrimary[0] = 0.300f;\n        metadata.greenPrimary[1] = 0.600f;\n        metadata.bluePrimary[0]  = 0.150f;\n        metadata.bluePrimary[1]  = 0.060f;\n        metadata.whitePoint[0]   = 0.3127f;\n        metadata.whitePoint[1]   = 0.3290f;\n      } else {\n        // HDR10 ColorSpace -> P3 Primaries\n        metadata.redPrimary[0]   = 0.680f;\n        metadata.redPrimary[1]   = 0.320f;\n        metadata.greenPrimary[0] = 0.265f;\n        metadata.greenPrimary[1] = 0.690f;\n        metadata.bluePrimary[0]  = 0.150f;\n        metadata.bluePrimary[1]  = 0.060f;\n        metadata.whitePoint[0]   = 0.3127f;\n        metadata.whitePoint[1]   = 0.3290f;\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/wsi/wsi_monitor.h",
    "content": "#pragma once\n\n#include <windows.h>\n\n#include <array>\n#include <vector>\n#include <cstdint>\n\n#include \"wsi_edid.h\"\n\nnamespace dxvk::wsi {\n\n  /**\n   * \\brief Rational number. Eg. 2/3\n   */\n  struct WsiRational {\n    uint32_t numerator;\n    uint32_t denominator;\n  };\n\n  /**\n   * \\brief Display mode\n   */\n  struct WsiMode {\n    uint32_t     width;\n    uint32_t     height;\n    WsiRational  refreshRate;\n    uint32_t     bitsPerPixel;\n    bool         interlaced;\n  };\n\n  /**\n    * \\brief Default monitor\n    *\n    * \\returns The monitor of given index\n    */\n  HMONITOR getDefaultMonitor();\n\n  /**\n    * \\brief Enumerators monitors on the system\n    *\n    * \\returns The monitor of given index\n    */\n  HMONITOR enumMonitors(uint32_t index);\n\n  /**\n    * \\brief Enumerators monitors on the system\n    * \\param [in] adapterLUID array of adapters' LUIDs\n    * \\param [in] numLUIDs adapterLUID array size (0 for all monitors)\n    * \\param [in] index Monitor index within enumeration\n    *\n    * \\returns The monitor of given index\n    */\n  HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index);\n\n  /**\n    * \\brief Get the GDI name of a HMONITOR\n    *\n    * Get the GDI Device Name of a HMONITOR to\n    * return to the end user.\n    *\n    * This typically looks like \\.\\\\DISPLAY1\n    * and has a maximum length of 32 chars.\n    *\n    * \\param [in] hMonitor The monitor\n    * \\param [out] Name The GDI display name\n    * \\returns \\c true on success, \\c false if an error occured\n    */\n  bool getDisplayName(\n          HMONITOR         hMonitor,\n          WCHAR            (&Name)[32]);\n\n  /**\n    * \\brief Get the encompassing coords of a monitor\n    */\n  bool getDesktopCoordinates(\n          HMONITOR         hMonitor,\n          RECT*            pRect);\n\n  /**\n    * \\brief Get the nth display mode\n    * \n    * \\param [in] hMonitor The monitor\n    * \\param [in] modeNumber The nth mode\n    * \\param [out] pMode The resultant mode\n    * \\returns \\c true on success, \\c false if the mode could not be found\n    */\n  bool getDisplayMode(\n          HMONITOR         hMonitor,\n          uint32_t         modeNumber,\n          WsiMode*         pMode);\n\n  /**\n    * \\brief Get the current display mode\n    *\n    * This is the display mode right now.\n    * \n    * \\param [in] hMonitor The monitor\n    * \\param [out] pMode The resultant mode\n    * \\returns \\c true on success, \\c false on failure\n    */\n  bool getCurrentDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode);\n\n  /**\n    * \\brief Get the current display mode\n    *\n    * This is the display mode of the user's\n    * default desktop.\n    * \n    * \\param [in] hMonitor The monitor\n    * \\param [out] pMode The resultant mode\n    * \\returns \\c true on success, \\c false on failure\n    */\n  bool getDesktopDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode);\n\n  /**\n    * \\brief Get the size of a monitor\n    *\n    * Helper function to grab the size of a monitor\n    * using getDesktopCoordinates to mirror the window code.\n    */\n  inline void getMonitorClientSize(\n          HMONITOR                hMonitor,\n          UINT*                   pWidth,\n          UINT*                   pHeight) {\n    RECT rect = { };\n    getDesktopCoordinates(hMonitor, &rect);\n\n    if (pWidth)\n      *pWidth = rect.right - rect.left;\n    \n    if (pHeight)\n      *pHeight = rect.bottom - rect.top;\n  }\n\n  /**\n    * \\brief Get the EDID of a monitor\n    *\n    * Helper function to grab the EDID of a monitor.\n    * This is needed to get the HDR static metadata + colorimetry\n    * info of a display for exposing HDR.\n    *\n    * \\param [in] hMonitor The monitor\n    * \\returns \\c EDID if successful, an empty vector if failure.\n    */\n  WsiEdidData getMonitorEdid(HMONITOR hMonitor);\n\n}\n"
  },
  {
    "path": "src/wsi/wsi_platform.cpp",
    "content": "#include \"wsi_platform.h\"\n#include \"wsi_monitor.h\"\n#include \"wsi_window.h\"\n#include \"../util/util_env.h\"\n#include \"../util/util_error.h\"\n\nnamespace dxvk::wsi {\n  static WsiDriver* s_driver = nullptr;\n  static int s_refcount = 0;\n\n  static const WsiBootstrap *wsiBootstrap[] = {\n#if defined(DXVK_WSI_WIN32)\n    &Win32WSI,\n#endif\n#if defined(DXVK_WSI_SDL3)\n    &Sdl3WSI,\n#endif\n#if defined(DXVK_WSI_SDL2)\n    &Sdl2WSI,\n#endif\n#if defined(DXVK_WSI_GLFW)\n    &GlfwWSI,\n#endif\n  };\n\n  void init() {\n    if (s_refcount++ > 0)\n      return;\n\n    std::string hint = dxvk::env::getEnvVar(\"DXVK_WSI_DRIVER\");\n    if (hint == \"\") {\n        // At least for Windows, it is reasonable to fall back to a default;\n        // for other platforms however we _need_ to know which WSI to use!\n#if defined(DXVK_WSI_WIN32)\n        hint = \"Win32\";\n#else\n        throw DxvkError(\"DXVK_WSI_DRIVER environment variable unset\");\n#endif\n    }\n\n    bool success = false;\n    for (const WsiBootstrap *b : wsiBootstrap) {\n      if (hint == b->name && b->createDriver(&s_driver)) {\n        success = true;\n        break;\n      }\n    }\n\n    if (!success)\n      throw DxvkError(\"Failed to initialize WSI.\");\n  }\n\n  void quit() {\n    if (s_refcount == 0)\n      return;\n\n    s_refcount--;\n    if (s_refcount == 0) {\n      delete s_driver;\n      s_driver = nullptr;\n    }\n  }\n\n  std::vector<const char *> getInstanceExtensions() {\n    return s_driver->getInstanceExtensions();\n  }\n\n  void getWindowSize(\n          HWND      hWindow,\n          uint32_t* pWidth,\n          uint32_t* pHeight) {\n    s_driver->getWindowSize(hWindow, pWidth, pHeight);\n  }\n\n  void resizeWindow(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          uint32_t         width,\n          uint32_t         height) {\n    s_driver->resizeWindow(hWindow, pState, width, height);\n  }\n\n  void saveWindowState(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             saveStyle) {\n    s_driver->saveWindowState(hWindow, pState, saveStyle);\n  }\n\n  void restoreWindowState(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             restoreCoordinates) {\n    s_driver->restoreWindowState(hWindow, pState, restoreCoordinates);\n  }\n\n  bool setWindowMode(\n          HMONITOR         hMonitor,\n          HWND             hWindow,\n          DxvkWindowState* pState,\n    const WsiMode&         mode) {\n    return s_driver->setWindowMode(hMonitor, hWindow, pState, mode);\n  }\n\n  bool enterFullscreenMode(\n          HMONITOR         hMonitor,\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          [[maybe_unused]]\n          bool             modeSwitch) {\n    return s_driver->enterFullscreenMode(hMonitor, hWindow, pState, modeSwitch);\n  }\n\n  bool leaveFullscreenMode(\n          HWND             hWindow,\n          DxvkWindowState* pState) {\n    return s_driver->leaveFullscreenMode(hWindow, pState);\n  }\n\n  bool restoreDisplayMode() {\n    return s_driver->restoreDisplayMode();\n  }\n\n  HMONITOR getWindowMonitor(HWND hWindow) {\n    return s_driver->getWindowMonitor(hWindow);\n  }\n\n  bool isWindow(HWND hWindow) {\n    return s_driver->isWindow(hWindow);\n  }\n\n  bool isMinimized(HWND hWindow) {\n    return s_driver->isMinimized(hWindow);\n  }\n\n  bool isOccluded(HWND hWindow) {\n    return s_driver->isOccluded(hWindow);\n  }\n\n  void updateFullscreenWindow(\n          HMONITOR hMonitor,\n          HWND     hWindow,\n          bool     forceTopmost) {\n    s_driver->updateFullscreenWindow(hMonitor, hWindow, forceTopmost);\n  }\n\n  VkResult createSurface(\n          HWND                hWindow,\n          PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n          VkInstance          instance,\n          VkSurfaceKHR*       pSurface) {\n    return s_driver->createSurface(hWindow, pfnVkGetInstanceProcAddr, instance, pSurface);\n  }\n\n  HMONITOR getDefaultMonitor() {\n    return s_driver->getDefaultMonitor();\n  }\n\n  HMONITOR enumMonitors(uint32_t index) {\n    return s_driver->enumMonitors(index);\n  }\n\n  HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) {\n    return s_driver->enumMonitors(adapterLUID, numLUIDs, index);\n  }\n\n  bool getDisplayName(\n          HMONITOR         hMonitor,\n          WCHAR            (&Name)[32]) {\n    return s_driver->getDisplayName(hMonitor, Name);\n  }\n\n  bool getDesktopCoordinates(\n          HMONITOR         hMonitor,\n          RECT*            pRect) {\n    return s_driver->getDesktopCoordinates(hMonitor, pRect);\n  }\n\n  bool getDisplayMode(\n          HMONITOR         hMonitor,\n          uint32_t         modeNumber,\n          WsiMode*         pMode) {\n    return s_driver->getDisplayMode(hMonitor, modeNumber, pMode);\n  }\n\n  bool getCurrentDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode) {\n    return s_driver->getCurrentDisplayMode(hMonitor, pMode);\n  }\n\n  bool getDesktopDisplayMode(\n          HMONITOR         hMonitor,\n          WsiMode*         pMode) {\n    return s_driver->getDesktopDisplayMode(hMonitor, pMode);\n  }\n\n  WsiEdidData getMonitorEdid(HMONITOR hMonitor) {\n    return s_driver->getMonitorEdid(hMonitor);\n  }\n\n}\n"
  },
  {
    "path": "src/wsi/wsi_platform.h",
    "content": "#pragma once\n\n#include \"wsi_window.h\"\n\n#include <vector>\n\nnamespace dxvk::wsi {\n\n  class WsiDriver {\n  public:\n    virtual ~WsiDriver() {\n    }\n\n    // Platform\n    virtual std::vector<const char *> getInstanceExtensions() = 0;\n\n    // Monitor\n    virtual HMONITOR getDefaultMonitor() = 0;\n\n    virtual HMONITOR enumMonitors(uint32_t index) = 0;\n\n    virtual HMONITOR enumMonitors(const LUID *adapterLUID[], uint32_t numLUIDs, uint32_t index) = 0;\n\n    virtual bool getDisplayName(\n            HMONITOR         hMonitor,\n            WCHAR            (&Name)[32]) = 0;\n\n    virtual bool getDesktopCoordinates(\n            HMONITOR         hMonitor,\n            RECT*            pRect) = 0;\n\n    virtual bool getDisplayMode(\n            HMONITOR         hMonitor,\n            uint32_t         modeNumber,\n            WsiMode*         pMode) = 0;\n\n    virtual bool getCurrentDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode) = 0;\n\n    virtual bool getDesktopDisplayMode(\n            HMONITOR         hMonitor,\n            WsiMode*         pMode) = 0;\n\n    virtual WsiEdidData getMonitorEdid(HMONITOR hMonitor) = 0;\n\n    // Window\n\n    virtual void getWindowSize(\n            HWND      hWindow,\n            uint32_t* pWidth,\n            uint32_t* pWeight) = 0;\n\n    virtual void resizeWindow(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            uint32_t         width,\n            uint32_t         weight) = 0;\n\n    virtual void saveWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             saveStyle) = 0;\n\n    virtual void restoreWindowState(\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            bool             restoreCoordinates) = 0;\n\n    virtual bool setWindowMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n      const WsiMode&         mode) = 0;\n\n    virtual bool enterFullscreenMode(\n            HMONITOR         hMonitor,\n            HWND             hWindow,\n            DxvkWindowState* pState,\n            [[maybe_unused]]\n            bool             modeSwitch) = 0;\n\n    virtual bool leaveFullscreenMode(\n            HWND             hWindow,\n            DxvkWindowState* pState) = 0;\n\n    virtual bool restoreDisplayMode() = 0;\n\n    virtual HMONITOR getWindowMonitor(HWND hWindow) = 0;\n\n    virtual bool isWindow(HWND hWindow) = 0;\n\n    virtual bool isMinimized(HWND hWindow) = 0;\n\n    virtual bool isOccluded(HWND hWindow) = 0;\n\n    virtual void updateFullscreenWindow(\n            HMONITOR hMonitor,\n            HWND     hWindow,\n            bool     forceTopmost) = 0;\n\n    virtual VkResult createSurface(\n            HWND                hWindow,\n            PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n            VkInstance          instance,\n            VkSurfaceKHR*       pSurface) = 0;\n  };\n\n  struct WsiBootstrap {\n    const std::string name;\n    bool (*createDriver)(WsiDriver **driver);\n  };\n\n#if defined(DXVK_WSI_WIN32)\n  extern WsiBootstrap Win32WSI;\n#endif\n#if defined(DXVK_WSI_SDL3)\n  extern WsiBootstrap Sdl3WSI;\n#endif\n#if defined(DXVK_WSI_SDL2)\n  extern WsiBootstrap Sdl2WSI;\n#endif\n#if defined(DXVK_WSI_GLFW)\n  extern WsiBootstrap GlfwWSI;\n#endif\n\n  void init();\n  void quit();\n  std::vector<const char *> getInstanceExtensions();\n\n}\n"
  },
  {
    "path": "src/wsi/wsi_window.h",
    "content": "#pragma once\n\n#include <windows.h>\n\n#include \"wsi_monitor.h\"\n\n#include \"../vulkan/vulkan_loader.h\"\n\nnamespace dxvk::wsi {\n\n  /**\n    * \\brief Impl-dependent state\n    */\n  struct DxvkWindowState {\n#if defined(DXVK_WSI_WIN32)\n    struct {\n      LONG style   = 0;\n      LONG exstyle = 0;\n      RECT rect    = { 0, 0, 0, 0 };\n    } win;\n#endif\n#if defined(DXVK_WSI_SDL3)\n    struct {\n      WsiMode fullscreenMode = { };\n    } sdl3;\n#endif\n#if defined(DXVK_WSI_SDL2)\n    // Nothing to store\n#endif\n#if defined(DXVK_WSI_GLFW)\n    // Nothing to store\n#endif\n  };\n\n  /**\n    * \\brief The size of the window\n    * \n    * \\param [in] hWindow The window\n    * \\param [out] pWidth The width (optional)\n    * \\param [out] pHeight The height (optional)\n    */\n  void getWindowSize(\n          HWND      hWindow,\n          uint32_t* pWidth,\n          uint32_t* pWeight);\n\n  /**\n    * \\brief Resize a window\n    * \n    * \\param [in] hWindow The window\n    * \\param [in] pState The swapchain's window state\n    * \\param [in] width The new width\n    * \\param [in] height The new height\n    */\n  void resizeWindow(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          uint32_t         width,\n          uint32_t         weight);\n\n  /**\n    * \\brief Save window state\n    *\n    * \\param [in] hWindow The window\n    * \\param [in] pState The swapchain's window state\n    * \\param [in] saveStyle Whether to save window style and exstyle\n    */\n  void saveWindowState(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             saveStyle);\n\n  /**\n    * \\brief Restore window state\n    *\n    * \\param [in] hWindow The window\n    * \\param [in] pState The swapchain's window state\n    * \\param [in] restoreCoordinates Whether to restore window position and dimension\n    */\n  void restoreWindowState(\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          bool             restoreCoordinates);\n\n  /**\n    * \\brief Sets the display mode for a window/monitor\n    * \n    * \\param [in] hMonitor The monitor\n    * \\param [in] hWindow The window (may be unused on some platforms)\n    * \\param [in] pState The swapchain's window state\n    * \\param [in] mode The mode\n    * \\returns \\c true on success, \\c false on failure\n    */\n  bool setWindowMode(\n          HMONITOR         hMonitor,\n          HWND             hWindow,\n          DxvkWindowState* pState,\n    const WsiMode&         mode);\n\n  /**\n    * \\brief Enter fullscreen mode for a window & monitor\n    * \n    * \\param [in] hMonitor The monitor\n    * \\param [in] hWindow The window (may be unused on some platforms)\n    * \\param [in] pState The swapchain's window state\n    * \\param [in] modeSwitch Whether mode switching is allowed\n    * \\returns \\c true on success, \\c false on failure\n    */\n  bool enterFullscreenMode(\n          HMONITOR         hMonitor,\n          HWND             hWindow,\n          DxvkWindowState* pState,\n          [[maybe_unused]]\n          bool             modeSwitch);\n\n  /**\n    * \\brief Exit fullscreen mode for a window\n    * \n    * \\param [in] hWindow The window\n    * \\param [in] pState The swapchain's window state\n    * \\returns \\c true on success, \\c false on failure\n    */\n  bool leaveFullscreenMode(\n          HWND             hWindow,\n          DxvkWindowState* pState);\n\n  /**\n    * \\brief Restores the display mode if necessary\n    * \n    * \\returns \\c true on success, \\c false on failure\n    */\n  bool restoreDisplayMode();\n\n  /**\n    * \\brief The monitor a window is on\n    * \n    * \\param [in] hWindow The window\n    * \\returns The monitor the window is on\n    */\n  HMONITOR getWindowMonitor(HWND hWindow);\n\n  /**\n    * \\brief Is a HWND a window?\n    *\n    * \\param [in] hWindow The window\n    * \\returns Is it a window?\n    */\n  bool isWindow(HWND hWindow);\n\n  /**\n    * \\brief Is window minimized?\n    *\n    * \\param [in] hWindow The window\n    * \\returns Is window minimized?\n    */\n  bool isMinimized(HWND hWindow);\n\n  /**\n    * \\brief Is window occluded?\n    *\n    * \\param [in] hWindow The window\n    * \\returns Is window occluded?\n    */\n  bool isOccluded(HWND hWindow);\n\n  /**\n    * \\brief Update a fullscreen window's position/size\n    *\n    * \\param [in] hMonitor The monitor\n    * \\param [in] hWindow The window\n    * \\param [in] forceTopmost Whether to force the window to become topmost again (D3D9 behaviour)\n    */\n  void updateFullscreenWindow(\n          HMONITOR hMonitor,\n          HWND     hWindow,\n          bool     forceTopmost);\n\n  /**\n    * \\brief Create a surface for a window\n    * \n    * \\param [in] hWindow The window\n    * \\param [in] pfnVkGetInstanceProcAddr \\c vkGetInstanceProcAddr pointer\n    * \\param [in] instance Vulkan instance\n    * \\param [out] pSurface The surface\n    */\n  VkResult createSurface(\n          HWND                hWindow,\n          PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,\n          VkInstance          instance,\n          VkSurfaceKHR*       pSurface);\n\n}\n"
  },
  {
    "path": "version.h.in",
    "content": "#pragma once\n\n#define DXVK_VERSION \"@VCS_TAG@\"\n"
  }
]